Merge pull request #78248 from felaugmar/load-svg-adjustable-scale

Added `Image::load_svg_from_(buffer|string)`
This commit is contained in:
Yuri Sizov 2023-07-12 15:09:03 +02:00
commit 6960a1d0e8
15 changed files with 96 additions and 44 deletions

View file

@ -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<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());

View file

@ -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);
@ -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<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();

View file

@ -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" />

View file

@ -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);

View file

@ -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<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()) {

View file

@ -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;
}
@ -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);
@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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.