diff options
Diffstat (limited to 'modules/svg/image_loader_svg.cpp')
-rw-r--r-- | modules/svg/image_loader_svg.cpp | 88 |
1 files changed, 61 insertions, 27 deletions
diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index 87e2fae2d0..b194e7cb3f 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -35,13 +35,19 @@ #include <thorvg.h> -void ImageLoaderSVG::_replace_color_property(const String &p_prefix, String &r_string) { - // Replace colors in the SVG based on what is configured in `replace_colors`. +HashMap<Color, Color> ImageLoaderSVG::forced_color_map = HashMap<Color, Color>(); + +void ImageLoaderSVG::set_forced_color_map(const HashMap<Color, Color> &p_color_map) { + forced_color_map = p_color_map; +} + +void ImageLoaderSVG::_replace_color_property(const HashMap<Color, Color> &p_color_map, const String &p_prefix, String &r_string) { + // Replace colors in the SVG based on what is passed in `p_color_map`. // Used to change the colors of editor icons based on the used theme. // The strings being replaced are typically of the form: // fill="#5abbef" // But can also be 3-letter codes, include alpha, be "none" or a named color - // string ("blue"). So we convert to Godot Color to compare with `replace_colors`. + // string ("blue"). So we convert to Godot Color to compare with `p_color_map`. const int prefix_len = p_prefix.length(); int pos = r_string.find(p_prefix); @@ -52,8 +58,8 @@ void ImageLoaderSVG::_replace_color_property(const String &p_prefix, String &r_s const String color_code = r_string.substr(pos, end_pos - pos); if (color_code != "none" && !color_code.begins_with("url(")) { const Color color = Color(color_code); // Handles both HTML codes and named colors. - if (replace_colors.has(color)) { - r_string = r_string.left(pos) + "#" + replace_colors[color].operator Color().to_html(false) + r_string.substr(end_pos); + if (p_color_map.has(color)) { + r_string = r_string.left(pos) + "#" + p_color_map[color].to_html(false) + r_string.substr(end_pos); } } // Search for other occurrences. @@ -61,27 +67,30 @@ void ImageLoaderSVG::_replace_color_property(const String &p_prefix, String &r_s } } -void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, bool p_convert_color) { - ERR_FAIL_COND(Math::is_zero_approx(p_scale)); - - if (p_convert_color) { - _replace_color_property("stop-color=\"", p_string); - _replace_color_property("fill=\"", p_string); - _replace_color_property("stroke=\"", p_string); - } +Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample) { + ERR_FAIL_COND_V_MSG(Math::is_zero_approx(p_scale), ERR_INVALID_PARAMETER, "ImageLoaderSVG: Can't load SVG with a scale of 0."); std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen(); - PackedByteArray bytes = p_string.to_utf8_buffer(); - tvg::Result result = picture->load((const char *)bytes.ptr(), bytes.size(), "svg", true); + tvg::Result result = picture->load((const char *)p_buffer.ptr(), p_buffer.size(), "svg", true); if (result != tvg::Result::Success) { - return; + return ERR_INVALID_DATA; } float fw, fh; picture->size(&fw, &fh); - uint32_t width = MIN(round(fw * p_scale), 16 * 1024); - uint32_t height = MIN(round(fh * p_scale), 16 * 1024); + uint32_t width = round(fw * p_scale); + uint32_t height = round(fh * p_scale); + + const uint32_t max_dimension = 16384; + if (width > max_dimension || height > max_dimension) { + WARN_PRINT(vformat( + String::utf8("ImageLoaderSVG: Target canvas dimensions %d×%d (with scale %.2f) exceed the max supported dimensions %d×%d. The target canvas will be scaled down."), + width, height, p_scale, max_dimension, max_dimension)); + width = MIN(width, max_dimension); + height = MIN(height, max_dimension); + } + picture->size(width, height); std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen(); @@ -91,25 +100,25 @@ void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_strin tvg::Result res = sw_canvas->target(buffer, width, width, height, tvg::SwCanvas::ARGB8888_STRAIGHT); if (res != tvg::Result::Success) { memfree(buffer); - ERR_FAIL_MSG("ImageLoaderSVG can't create image."); + ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't set target on ThorVG canvas."); } res = sw_canvas->push(std::move(picture)); if (res != tvg::Result::Success) { memfree(buffer); - ERR_FAIL_MSG("ImageLoaderSVG can't create image."); + ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't insert ThorVG picture on canvas."); } res = sw_canvas->draw(); if (res != tvg::Result::Success) { memfree(buffer); - ERR_FAIL_MSG("ImageLoaderSVG can't create image."); + ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't draw ThorVG pictures on canvas."); } res = sw_canvas->sync(); if (res != tvg::Result::Success) { memfree(buffer); - ERR_FAIL_MSG("ImageLoaderSVG can't create image."); + ERR_FAIL_V_MSG(FAILED, "ImageLoaderSVG: Couldn't sync ThorVG canvas."); } Vector<uint8_t> image; @@ -129,18 +138,43 @@ void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_strin res = sw_canvas->clear(true); memfree(buffer); - p_image->create(width, height, false, Image::FORMAT_RGBA8, image); + p_image->set_data(width, height, false, Image::FORMAT_RGBA8, image); + return OK; +} + +Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map) { + if (p_color_map.size()) { + _replace_color_property(p_color_map, "stop-color=\"", p_string); + _replace_color_property(p_color_map, "fill=\"", p_string); + _replace_color_property(p_color_map, "stroke=\"", p_string); + } + + PackedByteArray bytes = p_string.to_utf8_buffer(); + + return create_image_from_utf8_buffer(p_image, bytes, p_scale, p_upsample); } void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("svg"); } -Error ImageLoaderSVG::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, bool p_force_linear, float p_scale) { +Error ImageLoaderSVG::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) { String svg = p_fileaccess->get_as_utf8_string(); - create_image_from_string(p_image, svg, p_scale, false, false); - ERR_FAIL_COND_V(p_image->is_empty(), FAILED); - if (p_force_linear) { + + Error err; + if (p_flags & FLAG_CONVERT_COLORS) { + err = create_image_from_string(p_image, svg, p_scale, false, forced_color_map); + } else { + err = create_image_from_string(p_image, svg, p_scale, false, HashMap<Color, Color>()); + } + + if (err != OK) { + return err; + } else if (p_image->is_empty()) { + return ERR_INVALID_DATA; + } + + if (p_flags & FLAG_FORCE_LINEAR) { p_image->srgb_to_linear(); } return OK; |