diff options
Diffstat (limited to 'modules')
-rw-r--r-- | modules/svg/SCsub | 56 | ||||
-rw-r--r-- | modules/svg/image_loader_svg.cpp | 190 | ||||
-rw-r--r-- | modules/svg/image_loader_svg.h | 34 | ||||
-rw-r--r-- | modules/svg/register_types.cpp | 10 |
4 files changed, 158 insertions, 132 deletions
diff --git a/modules/svg/SCsub b/modules/svg/SCsub index c7228a8d0b..bb03147731 100644 --- a/modules/svg/SCsub +++ b/modules/svg/SCsub @@ -9,16 +9,66 @@ env_svg = env_modules.Clone() thirdparty_obj = [] -thirdparty_dir = "#thirdparty/nanosvg/" +thirdparty_dir = "#thirdparty/thorvg/" thirdparty_sources = [ - "nanosvg.cc", + "src/lib/tvgBezier.cpp", + "src/lib/tvgCanvas.cpp", + "src/lib/tvgFill.cpp", + "src/lib/tvgGlCanvas.cpp", + "src/lib/tvgInitializer.cpp", + "src/lib/tvgLinearGradient.cpp", + "src/lib/tvgLoader.cpp", + "src/lib/tvgLzw.cpp", + "src/lib/tvgPaint.cpp", + "src/lib/tvgPicture.cpp", + "src/lib/tvgRadialGradient.cpp", + "src/lib/tvgRender.cpp", + "src/lib/tvgSaver.cpp", + "src/lib/tvgScene.cpp", + "src/lib/tvgShape.cpp", + "src/lib/tvgSwCanvas.cpp", + "src/lib/tvgTaskScheduler.cpp", + "src/loaders/raw/tvgRawLoader.cpp", + "src/loaders/svg/tvgXmlParser.cpp", + "src/loaders/svg/tvgSvgUtil.cpp", + "src/loaders/svg/tvgSvgSceneBuilder.cpp", + "src/loaders/svg/tvgSvgPath.cpp", + "src/loaders/svg/tvgSvgLoader.cpp", + "src/loaders/tvg/tvgTvgBinInterpreter.cpp", + "src/loaders/tvg/tvgTvgLoader.cpp", + "src/loaders/jpg/tvgJpgLoader.cpp", + "src/loaders/jpg/tvgJpgd.cpp", + "src/loaders/external_png/tvgPngLoader.cpp", + "src/lib/sw_engine/tvgSwFill.cpp", + "src/lib/sw_engine/tvgSwImage.cpp", + "src/lib/sw_engine/tvgSwMath.cpp", + "src/lib/sw_engine/tvgSwMemPool.cpp", + "src/lib/sw_engine/tvgSwRaster.cpp", + "src/lib/sw_engine/tvgSwRenderer.cpp", + "src/lib/sw_engine/tvgSwRle.cpp", + "src/lib/sw_engine/tvgSwShape.cpp", + "src/lib/sw_engine/tvgSwStroke.cpp", + "src/savers/tvg/tvgTvgSaver.cpp", ] + thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] -env_svg.Prepend(CPPPATH=[thirdparty_dir]) +env_svg.Prepend(CPPPATH=[thirdparty_dir + "inc"]) env_thirdparty = env_svg.Clone() env_thirdparty.disable_warnings() +env_thirdparty.Prepend( + CPPPATH=[ + thirdparty_dir + "src/lib", + thirdparty_dir + "src/lib/sw_engine", + thirdparty_dir + "src/loaders/raw", + thirdparty_dir + "src/loaders/svg", + thirdparty_dir + "src/loaders/jpg", + thirdparty_dir + "src/loaders/png", + thirdparty_dir + "src/loaders/tvg", + thirdparty_dir + "src/savers/tvg", + ] +) env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources) env.modules_sources += thirdparty_obj diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index bf43d7a4ad..96b83bf25a 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -30,132 +30,118 @@ #include "image_loader_svg.h" -#include <nanosvg.h> -#include <nanosvgrast.h> - -void SVGRasterizer::rasterize(NSVGimage *p_image, float p_tx, float p_ty, float p_scale, unsigned char *p_dst, int p_w, int p_h, int p_stride) { - nsvgRasterize(rasterizer, p_image, p_tx, p_ty, p_scale, p_dst, p_w, p_h, p_stride); -} - -SVGRasterizer::SVGRasterizer() { - rasterizer = nsvgCreateRasterizer(); -} - -SVGRasterizer::~SVGRasterizer() { - nsvgDeleteRasterizer(rasterizer); -} - -SVGRasterizer ImageLoaderSVG::rasterizer; - -inline void change_nsvg_paint_color(NSVGpaint *p_paint, const uint32_t p_old, const uint32_t p_new) { - if (p_paint->type == NSVG_PAINT_COLOR) { - if (p_paint->color << 8 == p_old << 8) { - p_paint->color = (p_paint->color & 0xFF000000) | (p_new & 0x00FFFFFF); - } - } - - if (p_paint->type == NSVG_PAINT_LINEAR_GRADIENT || p_paint->type == NSVG_PAINT_RADIAL_GRADIENT) { - for (int stop_index = 0; stop_index < p_paint->gradient->nstops; stop_index++) { - if (p_paint->gradient->stops[stop_index].color << 8 == p_old << 8) { - p_paint->gradient->stops[stop_index].color = p_new; +#include "core/os/memory.h" +#include "core/variant/variant.h" + +#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`. + // 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`. + + const int prefix_len = p_prefix.length(); + int pos = r_string.find(p_prefix); + while (pos != -1) { + pos += prefix_len; // Skip prefix. + int end_pos = r_string.find("\"", pos); + ERR_FAIL_COND_MSG(end_pos == -1, vformat("Malformed SVG string after property \"%s\".", p_prefix)); + 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); } } + // Search for other occurrences. + pos = r_string.find(p_prefix, pos); } } -void ImageLoaderSVG::_convert_colors(NSVGimage *p_svg_image) { - for (NSVGshape *shape = p_svg_image->shapes; shape != nullptr; shape = shape->next) { - for (int i = 0; i < replace_colors.old_colors.size(); i++) { - change_nsvg_paint_color(&(shape->stroke), replace_colors.old_colors[i], replace_colors.new_colors[i]); - change_nsvg_paint_color(&(shape->fill), replace_colors.old_colors[i], replace_colors.new_colors[i]); - } - } -} +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)); -void ImageLoaderSVG::set_convert_colors(Dictionary *p_replace_color) { - if (p_replace_color) { - Dictionary replace_color = *p_replace_color; - for (int i = 0; i < replace_color.keys().size(); i++) { - Variant o_c = replace_color.keys()[i]; - Variant n_c = replace_color[replace_color.keys()[i]]; - if (o_c.get_type() == Variant::COLOR && n_c.get_type() == Variant::COLOR) { - Color old_color = o_c; - Color new_color = n_c; - replace_colors.old_colors.push_back(old_color.to_abgr32()); - replace_colors.new_colors.push_back(new_color.to_abgr32()); - } - } - } else { - replace_colors.old_colors.clear(); - replace_colors.new_colors.clear(); + 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(Ref<Image> p_image, const Vector<uint8_t> *p_data, float p_scale, bool upsample, bool convert_colors) { - NSVGimage *svg_image; - const uint8_t *src_r = p_data->ptr(); - svg_image = nsvgParse((char *)src_r, "px", 96); - if (svg_image == nullptr) { - ERR_PRINT("SVG Corrupted"); - return ERR_FILE_CORRUPT; - } + std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen(); + PackedByteArray bytes = p_string.to_utf8_buffer(); - if (convert_colors) { - _convert_colors(svg_image); + tvg::Result result = picture->load((const char *)bytes.ptr(), bytes.size(), "svg", true); + if (result != tvg::Result::Success) { + return; } + float fw, fh; + picture->viewbox(nullptr, nullptr, &fw, &fh); - const float upscale = upsample ? 2.0 : 1.0; - - const int w = (int)(svg_image->width * p_scale * upscale); - ERR_FAIL_COND_V_MSG(w > Image::MAX_WIDTH, ERR_PARAMETER_RANGE_ERROR, vformat("Can't create image from SVG with scale %s, the resulting image size exceeds max width.", rtos(p_scale))); - - const int h = (int)(svg_image->height * p_scale * upscale); - ERR_FAIL_COND_V_MSG(h > Image::MAX_HEIGHT, ERR_PARAMETER_RANGE_ERROR, vformat("Can't create image from SVG with scale %s, the resulting image size exceeds max height.", rtos(p_scale))); - - Vector<uint8_t> dst_image; - dst_image.resize(w * h * 4); + uint32_t width = MIN(fw * p_scale, 16 * 1024); + uint32_t height = MIN(fh * p_scale, 16 * 1024); + picture->size(width, height); - uint8_t *dw = dst_image.ptrw(); + std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen(); + // Note: memalloc here, be sure to memfree before any return. + uint32_t *buffer = (uint32_t *)memalloc(sizeof(uint32_t) * width * height); - rasterizer.rasterize(svg_image, 0, 0, p_scale * upscale, (unsigned char *)dw, w, h, w * 4); - - p_image->create(w, h, false, Image::FORMAT_RGBA8, dst_image); - if (upsample) { - p_image->shrink_x2(); + 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."); } - nsvgDelete(svg_image); + res = sw_canvas->push(move(picture)); + if (res != tvg::Result::Success) { + memfree(buffer); + ERR_FAIL_MSG("ImageLoaderSVG can't create image."); + } - return OK; -} + res = sw_canvas->draw(); + if (res != tvg::Result::Success) { + memfree(buffer); + ERR_FAIL_MSG("ImageLoaderSVG can't create image."); + } -Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, const char *p_svg_str, float p_scale, bool upsample, bool convert_colors) { - size_t str_len = strlen(p_svg_str); - Vector<uint8_t> src_data; - src_data.resize(str_len + 1); - uint8_t *src_w = src_data.ptrw(); - memcpy(src_w, p_svg_str, str_len + 1); + res = sw_canvas->sync(); + if (res != tvg::Result::Success) { + memfree(buffer); + ERR_FAIL_MSG("ImageLoaderSVG can't create image."); + } - return _create_image(p_image, &src_data, p_scale, upsample, convert_colors); -} + Vector<uint8_t> image; + image.resize(width * height * sizeof(uint32_t)); + + for (uint32_t y = 0; y < height; y++) { + for (uint32_t x = 0; x < width; x++) { + uint32_t n = buffer[y * width + x]; + const size_t offset = sizeof(uint32_t) * width * y + sizeof(uint32_t) * x; + image.write[offset + 0] = (n >> 16) & 0xff; + image.write[offset + 1] = (n >> 8) & 0xff; + image.write[offset + 2] = n & 0xff; + image.write[offset + 3] = (n >> 24) & 0xff; + } + } -Error ImageLoaderSVG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) { - uint64_t size = f->get_length(); - Vector<uint8_t> src_image; - src_image.resize(size + 1); - uint8_t *src_w = src_image.ptrw(); - f->get_buffer(src_w, size); - src_w[size] = '\0'; + res = sw_canvas->clear(true); + memfree(buffer); - return _create_image(p_image, &src_image, p_scale, 1.0); + p_image->create(width, height, false, Image::FORMAT_RGBA8, image); } void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("svg"); - p_extensions->push_back("svgz"); } -ImageLoaderSVG::ImageLoaderSVG() { +Error ImageLoaderSVG::load_image(Ref<Image> p_image, FileAccess *p_fileaccess, bool p_force_linear, 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) { + p_image->srgb_to_linear(); + } + return OK; } - -ImageLoaderSVG::ReplaceColors ImageLoaderSVG::replace_colors; diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h index 03307c319e..d0bd71d92d 100644 --- a/modules/svg/image_loader_svg.h +++ b/modules/svg/image_loader_svg.h @@ -32,38 +32,18 @@ #define IMAGE_LOADER_SVG_H #include "core/io/image_loader.h" -#include "core/string/ustring.h" - -// Forward declare and include thirdparty headers in .cpp. -struct NSVGrasterizer; -struct NSVGimage; - -class SVGRasterizer { - NSVGrasterizer *rasterizer; - -public: - void rasterize(NSVGimage *p_image, float p_tx, float p_ty, float p_scale, unsigned char *p_dst, int p_w, int p_h, int p_stride); - - SVGRasterizer(); - ~SVGRasterizer(); -}; class ImageLoaderSVG : public ImageFormatLoader { - static struct ReplaceColors { - List<uint32_t> old_colors; - List<uint32_t> new_colors; - } replace_colors; - static SVGRasterizer rasterizer; - static void _convert_colors(NSVGimage *p_svg_image); - static Error _create_image(Ref<Image> p_image, const Vector<uint8_t> *p_data, float p_scale, bool upsample, bool convert_colors = false); + Dictionary replace_colors; + void _replace_color_property(const String &p_prefix, String &r_string); public: - static void set_convert_colors(Dictionary *p_replace_color = nullptr); - static Error create_image_from_string(Ref<Image> p_image, const char *p_svg_str, float p_scale, bool upsample, bool convert_colors = false); + // Called by the editor to handle theme icon colors. + void set_replace_colors(Dictionary p_replace_colors) { replace_colors = p_replace_colors; } + void create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, bool p_convert_color); - virtual Error load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale); - virtual void get_recognized_extensions(List<String> *p_extensions) const; - ImageLoaderSVG(); + virtual Error load_image(Ref<Image> p_image, FileAccess *p_fileaccess, bool p_force_linear, float p_scale) override; + virtual void get_recognized_extensions(List<String> *p_extensions) const override; }; #endif // IMAGE_LOADER_SVG_H diff --git a/modules/svg/register_types.cpp b/modules/svg/register_types.cpp index 1560af95c3..a4341c6f1e 100644 --- a/modules/svg/register_types.cpp +++ b/modules/svg/register_types.cpp @@ -32,13 +32,23 @@ #include "image_loader_svg.h" +#include <thorvg.h> + static ImageLoaderSVG *image_loader_svg = nullptr; void register_svg_types() { + tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw; + if (tvg::Initializer::init(tvgEngine, 0) != tvg::Result::Success) { + return; + } image_loader_svg = memnew(ImageLoaderSVG); ImageLoader::add_image_format_loader(image_loader_svg); } void unregister_svg_types() { + if (!image_loader_svg) { + return; + } memdelete(image_loader_svg); + tvg::Initializer::term(tvg::CanvasEngine::Sw); } |