Skip to content

Commit

Permalink
Merge pull request #78248 from felaugmar/load-svg-adjustable-scale
Browse files Browse the repository at this point in the history
Added `Image::load_svg_from_(buffer|string)`
  • Loading branch information
YuriSizov committed Jul 12, 2023
2 parents a33b548 + 26eb3db commit 6960a1d
Show file tree
Hide file tree
Showing 15 changed files with 96 additions and 44 deletions.
26 changes: 26 additions & 0 deletions core/io/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -3825,6 +3829,28 @@ Error Image::load_bmp_from_buffer(const Vector<uint8_t> &p_array) {
return _load_from_buffer(p_array, _bmp_mem_loader_func);
}

Error Image::load_svg_from_buffer(const Vector<uint8_t> &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> 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());
Expand Down
5 changes: 5 additions & 0 deletions core/io/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ typedef Vector<uint8_t> (*SavePNGBufferFunc)(const Ref<Image> &p_img);
typedef Error (*SaveJPGFunc)(const String &p_path, const Ref<Image> &p_img, float p_quality);
typedef Vector<uint8_t> (*SaveJPGBufferFunc)(const Ref<Image> &p_img, float p_quality);
typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size);
typedef Ref<Image> (*ScalableImageMemLoadFunc)(const uint8_t *p_data, int p_size, float p_scale);
typedef Error (*SaveWebPFunc)(const String &p_path, const Ref<Image> &p_img, const bool p_lossy, const float p_quality);
typedef Vector<uint8_t> (*SaveWebPBufferFunc)(const Ref<Image> &p_img, const bool p_lossy, const float p_quality);

Expand Down Expand Up @@ -148,6 +149,7 @@ class Image : public Resource {
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);
Expand Down Expand Up @@ -401,6 +403,9 @@ class Image : public Resource {
Error load_tga_from_buffer(const Vector<uint8_t> &p_array);
Error load_bmp_from_buffer(const Vector<uint8_t> &p_array);

Error load_svg_from_buffer(const Vector<uint8_t> &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();
Expand Down
19 changes: 19 additions & 0 deletions doc/classes/Image.xml
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,25 @@
Loads an image from the binary contents of a PNG file.
</description>
</method>
<method name="load_svg_from_buffer">
<return type="int" enum="Error" />
<param index="0" name="buffer" type="PackedByteArray" />
<param index="1" name="scale" type="float" default="1.0" />
<description>
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.
</description>
</method>
<method name="load_svg_from_string">
<return type="int" enum="Error" />
<param index="0" name="svg_str" type="String" />
<param index="1" name="scale" type="float" default="1.0" />
<description>
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.
</description>
</method>
<method name="load_tga_from_buffer">
<return type="int" enum="Error" />
<param index="0" name="buffer" type="PackedByteArray" />
Expand Down
3 changes: 1 addition & 2 deletions editor/editor_themes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,7 @@ static Ref<ImageTexture> 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<ImageTexture>(), "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);
Expand Down
18 changes: 2 additions & 16 deletions editor/plugins/asset_library_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down Expand Up @@ -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<Image> img = Ref<Image>(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()) {
Expand Down
22 changes: 20 additions & 2 deletions modules/svg/image_loader_svg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,22 @@ void ImageLoaderSVG::_replace_color_property(const HashMap<Color, Color> &p_colo
}
}

Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample) {
Ref<Image> ImageLoaderSVG::load_mem_svg(const uint8_t *p_svg, int p_size, float p_scale) {
Ref<Image> img;
img.instantiate();

Error err = create_image_from_utf8_buffer(img, p_svg, p_size, p_scale, false);
ERR_FAIL_COND_V(err, Ref<Image>());

return img;
}

Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> 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<tvg::Picture> 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;
}
Expand Down Expand Up @@ -142,6 +152,10 @@ Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> p_image, const Pa
return OK;
}

Error ImageLoaderSVG::create_image_from_utf8_buffer(Ref<Image> 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<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);
Expand Down Expand Up @@ -179,3 +193,7 @@ Error ImageLoaderSVG::load_image(Ref<Image> p_image, Ref<FileAccess> p_fileacces
}
return OK;
}

ImageLoaderSVG::ImageLoaderSVG() {
Image::_svg_scalable_mem_loader_func = load_mem_svg;
}
12 changes: 9 additions & 3 deletions modules/svg/image_loader_svg.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,22 @@
class ImageLoaderSVG : public ImageFormatLoader {
static HashMap<Color, Color> forced_color_map;

void _replace_color_property(const HashMap<Color, Color> &p_color_map, const String &p_prefix, String &r_string);
static void _replace_color_property(const HashMap<Color, Color> &p_color_map, const String &p_prefix, String &r_string);

static Ref<Image> load_mem_svg(const uint8_t *p_svg, int p_size, float p_scale);

public:
static void set_forced_color_map(const HashMap<Color, Color> &p_color_map);

Error create_image_from_utf8_buffer(Ref<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample);
Error create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map);
static Error create_image_from_utf8_buffer(Ref<Image> 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<Image> p_image, const PackedByteArray &p_buffer, float p_scale, bool p_upsample);

static Error create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, const HashMap<Color, Color> &p_color_map);

virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> p_fileaccess, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) override;
virtual void get_recognized_extensions(List<String> *p_extensions) const override;

ImageLoaderSVG();
};

#endif // IMAGE_LOADER_SVG_H
5 changes: 2 additions & 3 deletions platform/android/export/export_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3299,11 +3299,10 @@ EditorExportPlatformAndroid::EditorExportPlatformAndroid() {
Ref<Image> 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

Expand Down
3 changes: 1 addition & 2 deletions platform/ios/export/export_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1978,8 +1978,7 @@ EditorExportPlatformIOS::EditorExportPlatformIOS() {
Ref<Image> 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

Expand Down
5 changes: 2 additions & 3 deletions platform/linuxbsd/export/export_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -521,11 +521,10 @@ EditorExportPlatformLinuxBSD::EditorExportPlatformLinuxBSD() {
Ref<Image> 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

Expand Down
5 changes: 2 additions & 3 deletions platform/macos/export/export_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2442,11 +2442,10 @@ EditorExportPlatformMacOS::EditorExportPlatformMacOS() {
Ref<Image> 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

Expand Down
3 changes: 1 addition & 2 deletions platform/uwp/export/export_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -515,8 +515,7 @@ EditorExportPlatformUWP::EditorExportPlatformUWP() {
Ref<Image> 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
Expand Down
5 changes: 2 additions & 3 deletions platform/web/export/export_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -674,11 +674,10 @@ EditorExportPlatformWeb::EditorExportPlatformWeb() {
Ref<Image> 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

Expand Down
5 changes: 2 additions & 3 deletions platform/windows/export/export_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1011,11 +1011,10 @@ EditorExportPlatformWindows::EditorExportPlatformWindows() {
Ref<Image> 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

Expand Down
4 changes: 2 additions & 2 deletions scene/resources/default_theme/default_theme.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ static Ref<ImageTexture> 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<Color, Color>());

Error err = ImageLoaderSVG::create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, HashMap<Color, Color>());
ERR_FAIL_COND_V_MSG(err != OK, Ref<ImageTexture>(), "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.
Expand Down

0 comments on commit 6960a1d

Please sign in to comment.