From 26eb3db234ec6e16a954f34f6aa7e68e7a926848 Mon Sep 17 00:00:00 2001 From: Felipe Augusto Marques Date: Wed, 14 Jun 2023 20:27:56 -0300 Subject: [PATCH] Added Image's `load_svg_from_(buffer|string)` No core dependency to the svg module. --- core/io/image.cpp | 26 +++++++++++++++++++ core/io/image.h | 5 ++++ doc/classes/Image.xml | 19 ++++++++++++++ editor/editor_themes.cpp | 3 +-- .../plugins/asset_library_editor_plugin.cpp | 18 ++----------- modules/svg/image_loader_svg.cpp | 22 ++++++++++++++-- modules/svg/image_loader_svg.h | 12 ++++++--- platform/android/export/export_plugin.cpp | 5 ++-- platform/ios/export/export_plugin.cpp | 3 +-- platform/linuxbsd/export/export_plugin.cpp | 5 ++-- platform/macos/export/export_plugin.cpp | 5 ++-- platform/uwp/export/export_plugin.cpp | 3 +-- platform/web/export/export_plugin.cpp | 5 ++-- platform/windows/export/export_plugin.cpp | 5 ++-- .../resources/default_theme/default_theme.cpp | 4 +-- 15 files changed, 96 insertions(+), 44 deletions(-) diff --git a/core/io/image.cpp b/core/io/image.cpp index 9bb987b670c..1711e4c2657 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -3004,6 +3004,7 @@ ImageMemLoadFunc Image::_jpg_mem_loader_func = nullptr; ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr; ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr; ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr; +ScalableImageMemLoadFunc Image::_svg_scalable_mem_loader_func = nullptr; void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr; void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr; @@ -3476,6 +3477,9 @@ void Image::_bind_methods() { ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer); ClassDB::bind_method(D_METHOD("load_bmp_from_buffer", "buffer"), &Image::load_bmp_from_buffer); + ClassDB::bind_method(D_METHOD("load_svg_from_buffer", "buffer", "scale"), &Image::load_svg_from_buffer, DEFVAL(1.0)); + ClassDB::bind_method(D_METHOD("load_svg_from_string", "svg_str", "scale"), &Image::load_svg_from_string, DEFVAL(1.0)); + ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "_set_data", "_get_data"); BIND_CONSTANT(MAX_WIDTH); @@ -3825,6 +3829,28 @@ Error Image::load_bmp_from_buffer(const Vector &p_array) { return _load_from_buffer(p_array, _bmp_mem_loader_func); } +Error Image::load_svg_from_buffer(const Vector &p_array, float scale) { + ERR_FAIL_NULL_V_MSG( + _svg_scalable_mem_loader_func, + ERR_UNAVAILABLE, + "The SVG module isn't enabled. Recompile the Godot editor or export template binary with the `module_svg_enabled=yes` SCons option."); + + int buffer_size = p_array.size(); + + ERR_FAIL_COND_V(buffer_size == 0, ERR_INVALID_PARAMETER); + + Ref image = _svg_scalable_mem_loader_func(p_array.ptr(), buffer_size, scale); + ERR_FAIL_COND_V(!image.is_valid(), ERR_PARSE_ERROR); + + copy_internals_from(image); + + return OK; +} + +Error Image::load_svg_from_string(const String &p_svg_str, float scale) { + return load_svg_from_buffer(p_svg_str.to_utf8_buffer(), scale); +} + void Image::convert_rg_to_ra_rgba8() { ERR_FAIL_COND(format != FORMAT_RGBA8); ERR_FAIL_COND(!data.size()); diff --git a/core/io/image.h b/core/io/image.h index 8e353a8bb76..f877b00ee60 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -48,6 +48,7 @@ typedef Vector (*SavePNGBufferFunc)(const Ref &p_img); typedef Error (*SaveJPGFunc)(const String &p_path, const Ref &p_img, float p_quality); typedef Vector (*SaveJPGBufferFunc)(const Ref &p_img, float p_quality); typedef Ref (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size); +typedef Ref (*ScalableImageMemLoadFunc)(const uint8_t *p_data, int p_size, float p_scale); typedef Error (*SaveWebPFunc)(const String &p_path, const Ref &p_img, const bool p_lossy, const float p_quality); typedef Vector (*SaveWebPBufferFunc)(const Ref &p_img, const bool p_lossy, const float p_quality); @@ -148,6 +149,7 @@ public: static ImageMemLoadFunc _webp_mem_loader_func; static ImageMemLoadFunc _tga_mem_loader_func; static ImageMemLoadFunc _bmp_mem_loader_func; + static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func; static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels); static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels); @@ -401,6 +403,9 @@ public: Error load_tga_from_buffer(const Vector &p_array); Error load_bmp_from_buffer(const Vector &p_array); + Error load_svg_from_buffer(const Vector &p_array, float scale = 1.0); + Error load_svg_from_string(const String &p_svg_str, float scale = 1.0); + void convert_rg_to_ra_rgba8(); void convert_ra_rgba8_to_rg(); void convert_rgba8_to_bgra8(); diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 130f848a73c..6aebd025cf8 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -333,6 +333,25 @@ Loads an image from the binary contents of a PNG file. + + + + + + Loads an image from the UTF-8 binary contents of an [b]uncompressed[/b] SVG file ([b].svg[/b]). + [b]Note:[/b] Beware when using compressed SVG files (like [b].svgz[/b]), they need to be [code]decompressed[/code] before loading. + [b]Note:[/b] This method is only available in engine builds with the SVG module enabled. By default, the SVG module is enabled, but it can be disabled at build-time using the [code]module_svg_enabled=no[/code] SCons option. + + + + + + + + Loads an image from the string contents of a SVG file ([b].svg[/b]). + [b]Note:[/b] This method is only available in engine builds with the SVG module enabled. By default, the SVG module is enabled, but it can be disabled at build-time using the [code]module_svg_enabled=no[/code] SCons option. + + diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 2ff53dd9f12..f5d800cca81 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -244,8 +244,7 @@ static Ref editor_generate_icon(int p_index, float p_scale, float // Generating upsampled icons is slower, and the benefit is hardly visible // with integer editor scales. const bool upsample = !Math::is_equal_approx(Math::round(p_scale), p_scale); - ImageLoaderSVG img_loader; - Error err = img_loader.create_image_from_string(img, editor_icons_sources[p_index], p_scale, upsample, p_convert_colors); + Error err = ImageLoaderSVG::create_image_from_string(img, editor_icons_sources[p_index], p_scale, upsample, p_convert_colors); ERR_FAIL_COND_V_MSG(err != OK, Ref(), "Failed generating icon, unsupported or invalid SVG data in editor theme."); if (p_saturation != 1.0) { img->adjust_bcs(1.0, 1.0, p_saturation); diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index eeb0fd5f664..b2e40fa6c08 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -43,11 +43,6 @@ #include "editor/project_settings_editor.h" #include "scene/gui/menu_button.h" -#include "modules/modules_enabled.gen.h" // For svg. -#ifdef MODULE_SVG_ENABLED -#include "modules/svg/image_loader_svg.h" -#endif - static inline void setup_http_request(HTTPRequest *request) { request->set_use_threads(EDITOR_DEF("asset_library/use_threads", true)); @@ -775,18 +770,9 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PackedB image->copy_internals_from(Image::_webp_mem_loader_func(r, len)); } else if ((memcmp(&r[0], &bmp_signature[0], 2) == 0) && Image::_bmp_mem_loader_func) { image->copy_internals_from(Image::_bmp_mem_loader_func(r, len)); + } else if (Image::_svg_scalable_mem_loader_func) { + image->copy_internals_from(Image::_svg_scalable_mem_loader_func(r, len, 1.0)); } -#ifdef MODULE_SVG_ENABLED - else { - ImageLoaderSVG svg_loader; - Ref img = Ref(memnew(Image)); - Error err = svg_loader.create_image_from_utf8_buffer(img, image_data, 1.0, false); - - if (err == OK) { - image->copy_internals_from(img); - } - } -#endif } if (!image->is_empty()) { diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp index ad7feeda497..76391559148 100644 --- a/modules/svg/image_loader_svg.cpp +++ b/modules/svg/image_loader_svg.cpp @@ -67,12 +67,22 @@ void ImageLoaderSVG::_replace_color_property(const HashMap &p_colo } } -Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample) { +Ref ImageLoaderSVG::load_mem_svg(const uint8_t *p_svg, int p_size, float p_scale) { + Ref img; + img.instantiate(); + + Error err = create_image_from_utf8_buffer(img, p_svg, p_size, p_scale, false); + ERR_FAIL_COND_V(err, Ref()); + + return img; +} + +Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref p_image, const uint8_t *p_buffer, int p_buffer_size, 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 picture = tvg::Picture::gen(); - tvg::Result result = picture->load((const char *)p_buffer.ptr(), p_buffer.size(), "svg", true); + tvg::Result result = picture->load((const char *)p_buffer, p_buffer_size, "svg", true); if (result != tvg::Result::Success) { return ERR_INVALID_DATA; } @@ -142,6 +152,10 @@ Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref p_image, const Pa return OK; } +Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample) { + return create_image_from_utf8_buffer(p_image, p_buffer.ptr(), p_buffer.size(), p_scale, p_upsample); +} + Error ImageLoaderSVG::create_image_from_string(Ref p_image, String p_string, float p_scale, bool p_upsample, const HashMap &p_color_map) { if (p_color_map.size()) { _replace_color_property(p_color_map, "stop-color=\"", p_string); @@ -179,3 +193,7 @@ Error ImageLoaderSVG::load_image(Ref p_image, Ref p_fileacces } return OK; } + +ImageLoaderSVG::ImageLoaderSVG() { + Image::_svg_scalable_mem_loader_func = load_mem_svg; +} diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h index e0d2849a621..90e9458c370 100644 --- a/modules/svg/image_loader_svg.h +++ b/modules/svg/image_loader_svg.h @@ -36,16 +36,22 @@ class ImageLoaderSVG : public ImageFormatLoader { static HashMap forced_color_map; - void _replace_color_property(const HashMap &p_color_map, const String &p_prefix, String &r_string); + static void _replace_color_property(const HashMap &p_color_map, const String &p_prefix, String &r_string); + + static Ref load_mem_svg(const uint8_t *p_svg, int p_size, float p_scale); public: static void set_forced_color_map(const HashMap &p_color_map); - Error create_image_from_utf8_buffer(Ref p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample); - Error create_image_from_string(Ref p_image, String p_string, float p_scale, bool p_upsample, const HashMap &p_color_map); + static Error create_image_from_utf8_buffer(Ref p_image, const uint8_t *p_buffer, int p_buffer_size, float p_scale, bool p_upsample); + static Error create_image_from_utf8_buffer(Ref p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample); + + static Error create_image_from_string(Ref p_image, String p_string, float p_scale, bool p_upsample, const HashMap &p_color_map); virtual Error load_image(Ref p_image, Ref p_fileaccess, BitField p_flags, float p_scale) override; virtual void get_recognized_extensions(List *p_extensions) const override; + + ImageLoaderSVG(); }; #endif // IMAGE_LOADER_SVG_H diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 5d31c4911c5..5a50aa90813 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -3291,11 +3291,10 @@ EditorExportPlatformAndroid::EditorExportPlatformAndroid() { Ref img = memnew(Image); const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE); - ImageLoaderSVG img_loader; - img_loader.create_image_from_string(img, _android_logo_svg, EDSCALE, upsample, false); + ImageLoaderSVG::create_image_from_string(img, _android_logo_svg, EDSCALE, upsample, false); logo = ImageTexture::create_from_image(img); - img_loader.create_image_from_string(img, _android_run_icon_svg, EDSCALE, upsample, false); + ImageLoaderSVG::create_image_from_string(img, _android_run_icon_svg, EDSCALE, upsample, false); run_icon = ImageTexture::create_from_image(img); #endif diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 1b9b02d0e07..4860ef0ad76 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -1970,8 +1970,7 @@ EditorExportPlatformIOS::EditorExportPlatformIOS() { Ref img = memnew(Image); const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE); - ImageLoaderSVG img_loader; - img_loader.create_image_from_string(img, _ios_logo_svg, EDSCALE, upsample, false); + ImageLoaderSVG::create_image_from_string(img, _ios_logo_svg, EDSCALE, upsample, false); logo = ImageTexture::create_from_image(img); #endif diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp index f74bdf35166..40151b1a029 100644 --- a/platform/linuxbsd/export/export_plugin.cpp +++ b/platform/linuxbsd/export/export_plugin.cpp @@ -521,11 +521,10 @@ EditorExportPlatformLinuxBSD::EditorExportPlatformLinuxBSD() { Ref img = memnew(Image); const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE); - ImageLoaderSVG img_loader; - img_loader.create_image_from_string(img, _linuxbsd_logo_svg, EDSCALE, upsample, false); + ImageLoaderSVG::create_image_from_string(img, _linuxbsd_logo_svg, EDSCALE, upsample, false); set_logo(ImageTexture::create_from_image(img)); - img_loader.create_image_from_string(img, _linuxbsd_run_icon_svg, EDSCALE, upsample, false); + ImageLoaderSVG::create_image_from_string(img, _linuxbsd_run_icon_svg, EDSCALE, upsample, false); run_icon = ImageTexture::create_from_image(img); #endif diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 2d185db812a..0dc6d0dcee9 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -2442,11 +2442,10 @@ EditorExportPlatformMacOS::EditorExportPlatformMacOS() { Ref img = memnew(Image); const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE); - ImageLoaderSVG img_loader; - img_loader.create_image_from_string(img, _macos_logo_svg, EDSCALE, upsample, false); + ImageLoaderSVG::create_image_from_string(img, _macos_logo_svg, EDSCALE, upsample, false); logo = ImageTexture::create_from_image(img); - img_loader.create_image_from_string(img, _macos_run_icon_svg, EDSCALE, upsample, false); + ImageLoaderSVG::create_image_from_string(img, _macos_run_icon_svg, EDSCALE, upsample, false); run_icon = ImageTexture::create_from_image(img); #endif diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp index 0332fbf7187..6b3e6501185 100644 --- a/platform/uwp/export/export_plugin.cpp +++ b/platform/uwp/export/export_plugin.cpp @@ -515,8 +515,7 @@ EditorExportPlatformUWP::EditorExportPlatformUWP() { Ref img = memnew(Image); const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE); - ImageLoaderSVG img_loader; - img_loader.create_image_from_string(img, _uwp_logo_svg, EDSCALE, upsample, false); + ImageLoaderSVG::create_image_from_string(img, _uwp_logo_svg, EDSCALE, upsample, false); logo = ImageTexture::create_from_image(img); #endif diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index 0bc3b92f090..d5834d08d68 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -666,11 +666,10 @@ EditorExportPlatformWeb::EditorExportPlatformWeb() { Ref img = memnew(Image); const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE); - ImageLoaderSVG img_loader; - img_loader.create_image_from_string(img, _web_logo_svg, EDSCALE, upsample, false); + ImageLoaderSVG::create_image_from_string(img, _web_logo_svg, EDSCALE, upsample, false); logo = ImageTexture::create_from_image(img); - img_loader.create_image_from_string(img, _web_run_icon_svg, EDSCALE, upsample, false); + ImageLoaderSVG::create_image_from_string(img, _web_run_icon_svg, EDSCALE, upsample, false); run_icon = ImageTexture::create_from_image(img); #endif diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp index b521a649be1..0ef07c32757 100644 --- a/platform/windows/export/export_plugin.cpp +++ b/platform/windows/export/export_plugin.cpp @@ -1011,11 +1011,10 @@ EditorExportPlatformWindows::EditorExportPlatformWindows() { Ref img = memnew(Image); const bool upsample = !Math::is_equal_approx(Math::round(EDSCALE), EDSCALE); - ImageLoaderSVG img_loader; - img_loader.create_image_from_string(img, _windows_logo_svg, EDSCALE, upsample, false); + ImageLoaderSVG::create_image_from_string(img, _windows_logo_svg, EDSCALE, upsample, false); set_logo(ImageTexture::create_from_image(img)); - img_loader.create_image_from_string(img, _windows_run_icon_svg, EDSCALE, upsample, false); + ImageLoaderSVG::create_image_from_string(img, _windows_run_icon_svg, EDSCALE, upsample, false); run_icon = ImageTexture::create_from_image(img); #endif diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 1bfcf8d3acb..9cec99cb220 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -83,8 +83,8 @@ static Ref generate_icon(int p_index) { // Generating upsampled icons is slower, and the benefit is hardly visible // with integer scales. const bool upsample = !Math::is_equal_approx(Math::round(scale), scale); - ImageLoaderSVG img_loader; - Error err = img_loader.create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, HashMap()); + + Error err = ImageLoaderSVG::create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, HashMap()); ERR_FAIL_COND_V_MSG(err != OK, Ref(), "Failed generating icon, unsupported or invalid SVG data in default theme."); #else // If the SVG module is disabled, we can't really display the UI well, but at least we won't crash.