From c0b18e3a68336cbeb113cc041b7f328359b4c98d Mon Sep 17 00:00:00 2001
From: beicause <1494181792@qq.com>
Date: Fri, 19 Jul 2024 17:01:32 +0800
Subject: [PATCH 1/7] svg: add lottie resource loader importer using thorvg
add json importer to import lottie as `.lottiejson` or `.ctex`. breaks builtin text editor for json file. `.lottiejson` is dynamically generated lottie texture.
---
doc/classes/LottieTexture2D.xml | 75 +
modules/svg/SCsub | 26 +-
modules/svg/config.py | 8 +-
.../svg/editor/resource_importer_lottie.cpp | 176 +
modules/svg/editor/resource_importer_lottie.h | 70 +
modules/svg/lottie_texture.cpp | 374 ++
modules/svg/lottie_texture.h | 129 +
modules/svg/register_types.cpp | 42 +
thirdparty/thorvg/AUTHORS | 2 +-
.../src/loaders/lottie/rapidjson/allocators.h | 693 ++++
.../lottie/rapidjson/cursorstreamwrapper.h | 78 +
.../src/loaders/lottie/rapidjson/document.h | 3043 +++++++++++++++
.../loaders/lottie/rapidjson/encodedstream.h | 299 ++
.../src/loaders/lottie/rapidjson/encodings.h | 716 ++++
.../src/loaders/lottie/rapidjson/error/en.h | 176 +
.../loaders/lottie/rapidjson/error/error.h | 285 ++
.../loaders/lottie/rapidjson/filereadstream.h | 99 +
.../lottie/rapidjson/filewritestream.h | 104 +
.../thorvg/src/loaders/lottie/rapidjson/fwd.h | 151 +
.../lottie/rapidjson/internal/biginteger.h | 297 ++
.../loaders/lottie/rapidjson/internal/clzll.h | 71 +
.../loaders/lottie/rapidjson/internal/diyfp.h | 261 ++
.../loaders/lottie/rapidjson/internal/dtoa.h | 249 ++
.../lottie/rapidjson/internal/ieee754.h | 78 +
.../loaders/lottie/rapidjson/internal/itoa.h | 308 ++
.../loaders/lottie/rapidjson/internal/meta.h | 186 +
.../loaders/lottie/rapidjson/internal/pow10.h | 55 +
.../loaders/lottie/rapidjson/internal/regex.h | 739 ++++
.../loaders/lottie/rapidjson/internal/stack.h | 232 ++
.../lottie/rapidjson/internal/strfunc.h | 83 +
.../lottie/rapidjson/internal/strtod.h | 293 ++
.../loaders/lottie/rapidjson/internal/swap.h | 46 +
.../loaders/lottie/rapidjson/istreamwrapper.h | 128 +
.../loaders/lottie/rapidjson/memorybuffer.h | 70 +
.../loaders/lottie/rapidjson/memorystream.h | 71 +
.../lottie/rapidjson/msinttypes/inttypes.h | 316 ++
.../lottie/rapidjson/msinttypes/stdint.h | 300 ++
.../loaders/lottie/rapidjson/ostreamwrapper.h | 81 +
.../src/loaders/lottie/rapidjson/pointer.h | 1470 ++++++++
.../loaders/lottie/rapidjson/prettywriter.h | 277 ++
.../src/loaders/lottie/rapidjson/rapidjson.h | 742 ++++
.../src/loaders/lottie/rapidjson/reader.h | 2246 ++++++++++++
.../src/loaders/lottie/rapidjson/schema.h | 3262 +++++++++++++++++
.../src/loaders/lottie/rapidjson/stream.h | 223 ++
.../loaders/lottie/rapidjson/stringbuffer.h | 121 +
.../thorvg/src/loaders/lottie/rapidjson/uri.h | 481 +++
.../src/loaders/lottie/rapidjson/writer.h | 710 ++++
.../thorvg/src/loaders/lottie/thorvg_lottie.h | 94 +
.../src/loaders/lottie/tvgLottieAnimation.cpp | 88 +
.../src/loaders/lottie/tvgLottieBuilder.cpp | 1386 +++++++
.../src/loaders/lottie/tvgLottieBuilder.h | 133 +
.../src/loaders/lottie/tvgLottieCommon.h | 98 +
.../loaders/lottie/tvgLottieExpressions.cpp | 1398 +++++++
.../src/loaders/lottie/tvgLottieExpressions.h | 168 +
.../loaders/lottie/tvgLottieInterpolator.cpp | 140 +
.../loaders/lottie/tvgLottieInterpolator.h | 45 +
.../src/loaders/lottie/tvgLottieLoader.cpp | 407 ++
.../src/loaders/lottie/tvgLottieLoader.h | 83 +
.../src/loaders/lottie/tvgLottieModel.cpp | 460 +++
.../src/loaders/lottie/tvgLottieModel.h | 865 +++++
.../src/loaders/lottie/tvgLottieModifier.cpp | 365 ++
.../src/loaders/lottie/tvgLottieModifier.h | 72 +
.../src/loaders/lottie/tvgLottieParser.cpp | 1444 ++++++++
.../src/loaders/lottie/tvgLottieParser.h | 120 +
.../loaders/lottie/tvgLottieParserHandler.cpp | 235 ++
.../loaders/lottie/tvgLottieParserHandler.h | 200 +
.../src/loaders/lottie/tvgLottieProperty.h | 839 +++++
.../loaders/lottie/tvgLottieRenderPooler.h | 58 +
thirdparty/thorvg/update-thorvg.sh | 4 +
69 files changed, 28641 insertions(+), 3 deletions(-)
create mode 100644 doc/classes/LottieTexture2D.xml
create mode 100644 modules/svg/editor/resource_importer_lottie.cpp
create mode 100644 modules/svg/editor/resource_importer_lottie.h
create mode 100644 modules/svg/lottie_texture.cpp
create mode 100644 modules/svg/lottie_texture.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/allocators.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/cursorstreamwrapper.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/document.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/encodedstream.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/encodings.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/error/en.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/error/error.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/filereadstream.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/filewritestream.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/fwd.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/internal/biginteger.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/internal/clzll.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/internal/diyfp.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/internal/dtoa.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/internal/ieee754.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/internal/itoa.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/internal/meta.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/internal/pow10.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/internal/regex.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/internal/stack.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/internal/strfunc.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/internal/strtod.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/internal/swap.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/istreamwrapper.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/memorybuffer.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/memorystream.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/msinttypes/inttypes.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/msinttypes/stdint.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/ostreamwrapper.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/pointer.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/prettywriter.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/rapidjson.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/reader.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/schema.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/stream.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/stringbuffer.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/uri.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/rapidjson/writer.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/thorvg_lottie.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieAnimation.cpp
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieBuilder.cpp
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieBuilder.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieCommon.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieExpressions.cpp
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieExpressions.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieInterpolator.cpp
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieInterpolator.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieLoader.cpp
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieLoader.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieModel.cpp
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieModel.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieModifier.cpp
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieModifier.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieParser.cpp
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieParser.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieParserHandler.cpp
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieParserHandler.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieProperty.h
create mode 100644 thirdparty/thorvg/src/loaders/lottie/tvgLottieRenderPooler.h
diff --git a/doc/classes/LottieTexture2D.xml b/doc/classes/LottieTexture2D.xml
new file mode 100644
index 000000000000..f9ee3c793e38
--- /dev/null
+++ b/doc/classes/LottieTexture2D.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Returns the Lottie animation duration.
+
+
+
+
+
+ Returns the Lottie animation total frame.
+
+
+
+
+
+ Returns the Lottie image size of each frame.
+
+
+
+
+
+
+
+
+
+
+
+ Sets all properties at once, which is more efficient than setting properties one by one.
+
+
+
+
+
+
+ Validates if [param p_json] is a Lottie file.
+
+
+
+
+
+ The columns of the sprite sheet, automatically arranged if not more than 0.
+
+
+ The begin frame of the sprite sheet. Range of 0 to [method get_lottie_frame_count]
+
+
+ The frame count of the sprite sheet.
+
+
+ The end frame of the sprite sheet. Range of [member frame_begin] to [method get_lottie_frame_count]
+
+
+ The Lottie JSON file.
+
+
+
+ The scaling factor of the Lottie. Set it to change the texture size.
+
+
+
diff --git a/modules/svg/SCsub b/modules/svg/SCsub
index a32be0e41a4e..6a4ce77f907c 100644
--- a/modules/svg/SCsub
+++ b/modules/svg/SCsub
@@ -58,6 +58,20 @@ thirdparty_sources = [
"src/renderer/sw_engine/tvgSwStroke.cpp",
]
+if env["lottie"]:
+ thirdparty_sources += [
+ "src/renderer/tvgAnimation.cpp",
+ # Lottie loader
+ "src/loaders/lottie/tvgLottieAnimation.cpp",
+ "src/loaders/lottie/tvgLottieBuilder.cpp",
+ "src/loaders/lottie/tvgLottieInterpolator.cpp",
+ "src/loaders/lottie/tvgLottieLoader.cpp",
+ "src/loaders/lottie/tvgLottieModel.cpp",
+ "src/loaders/lottie/tvgLottieModifier.cpp",
+ "src/loaders/lottie/tvgLottieParser.cpp",
+ "src/loaders/lottie/tvgLottieParserHandler.cpp",
+ ]
+
if env["module_webp_enabled"]:
thirdparty_sources += ["src/loaders/external_webp/tvgWebpLoader.cpp"]
env_svg.Append(CPPDEFINES=["THORVG_WEBP_LOADER_SUPPORT"])
@@ -68,6 +82,8 @@ env_svg.Prepend(CPPPATH=[thirdparty_dir + "inc"])
# Enable ThorVG static object linking.
env_svg.Append(CPPDEFINES=["TVG_STATIC"])
+if env["lottie"]:
+ env_svg.Append(CPPDEFINES=["THORVG_LOTTIE_LOADER_SUPPORT", "LOTTIE_ENABLED"])
env_thirdparty = env_svg.Clone()
env_thirdparty.disable_warnings()
@@ -82,6 +98,9 @@ env_thirdparty.Prepend(
thirdparty_dir + "src/loaders/jpg",
]
)
+
+if env["lottie"]:
+ env_thirdparty.Prepend(CPPPATH=[thirdparty_dir + "src/loaders/lottie"])
if env["builtin_libpng"]:
env_thirdparty.Prepend(CPPPATH=["#thirdparty/libpng"])
if env["module_webp_enabled"]:
@@ -96,7 +115,12 @@ env.modules_sources += thirdparty_obj
module_obj = []
-env_svg.add_source_files(module_obj, "*.cpp")
+env_svg.add_source_files(module_obj, ["register_types.cpp", "image_loader_svg.cpp"])
+if env["lottie"]:
+ env_svg.add_source_files(module_obj, "lottie_texture.cpp")
+ if env.editor_build:
+ env_svg.add_source_files(module_obj, "editor/resource_importer_lottie.cpp")
+
env.modules_sources += module_obj
# Needed to force rebuilding the module files when the thirdparty library is updated.
diff --git a/modules/svg/config.py b/modules/svg/config.py
index d22f9454ed25..a047cca395d7 100644
--- a/modules/svg/config.py
+++ b/modules/svg/config.py
@@ -3,4 +3,10 @@ def can_build(env, platform):
def configure(env):
- pass
+ from SCons.Script import BoolVariable, Help, Variables
+
+ env_vars = Variables()
+ env_vars.Add(BoolVariable("lottie", "Enable Lottie support using thorvg", True))
+
+ env_vars.Update(env)
+ Help(env_vars.GenerateHelpText(env))
diff --git a/modules/svg/editor/resource_importer_lottie.cpp b/modules/svg/editor/resource_importer_lottie.cpp
new file mode 100644
index 000000000000..1d9e3e62d198
--- /dev/null
+++ b/modules/svg/editor/resource_importer_lottie.cpp
@@ -0,0 +1,176 @@
+/**************************************************************************/
+/* resource_importer_lottie.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "resource_importer_lottie.h"
+#include "core/io/dir_access.h"
+#include "core/io/json.h"
+#include "modules/svg/lottie_texture.h"
+
+String ResourceImporterLottieJSON::get_importer_name() const {
+ return "lottie_texture_2d";
+}
+String ResourceImporterLottieJSON::get_visible_name() const {
+ return "LottieTexture2D";
+}
+int ResourceImporterLottieJSON::get_preset_count() const {
+ return 0;
+}
+bool ResourceImporterLottieJSON::get_option_visibility(const String &p_path, const String &p_option, const HashMap &p_options) const {
+ return true;
+}
+void ResourceImporterLottieJSON::get_recognized_extensions(List *p_extensions) const {
+ p_extensions->push_back("json");
+}
+String ResourceImporterLottieJSON::get_save_extension() const {
+ return "lottiejson";
+}
+String ResourceImporterLottieJSON::get_resource_type() const {
+ return "LottieTexture2D";
+}
+void ResourceImporterLottieJSON::get_import_options(const String &p_path, List *r_options, int p_preset) const {
+ String str = FileAccess::get_file_as_string(p_path);
+ Ref json;
+ json.instantiate();
+ Error err = json->parse(str, true);
+ if (err != OK) {
+ ERR_PRINT("Error parsing JSON file: " + p_path);
+ return;
+ }
+ if (!LottieTexture2D::validate(json)) {
+ ERR_PRINT("Invalid Lottie file: " + p_path);
+ return;
+ }
+ Ref lottie = memnew(LottieTexture2D);
+ lottie->update(json, 0, 0, 0, 1, 0);
+ int total_frame_count = lottie->get_lottie_frame_count();
+ int fps = total_frame_count / lottie->get_lottie_duration();
+ int default_frame_count = fps <= 30 ? total_frame_count : total_frame_count * 30 / fps;
+ int columns = Math::ceil(Math::sqrt((float)default_frame_count));
+ int rows = Math::ceil(((float)default_frame_count) / columns);
+ Vector2 texture_size = lottie->get_lottie_image_size() * Vector2(columns, rows);
+ int default_size_limit = 1024;
+ float default_scale = 1.0;
+ if (texture_size[texture_size.max_axis_index()] > default_size_limit) {
+ default_scale = default_size_limit / texture_size[texture_size.max_axis_index()];
+ }
+
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lottie/scale"), default_scale));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lottie/frame_begin"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lottie/frame_end"), total_frame_count - 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lottie/frame_count"), default_frame_count));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lottie/columns"), 0));
+}
+Error ResourceImporterLottieJSON::import(const String &p_source_file, const String &p_save_path, const HashMap &p_options, List *r_platform_variants, List *r_gen_files, Variant *r_metadata) {
+ // Invalid Lottie file if no import options
+ if (p_options.is_empty()) {
+ return ERR_INVALID_DATA;
+ }
+ Ref json;
+ json.instantiate();
+ Error err = json->parse(FileAccess::get_file_as_string(p_source_file), true);
+ if (err != OK) {
+ String err_text = "Error parsing JSON file at '" + p_source_file + "', on line " + itos(json->get_error_line()) + ": " + json->get_error_message();
+ ERR_PRINT(err_text);
+ return ERR_INVALID_DATA;
+ }
+ const float scale = p_options["lottie/scale"];
+ const float frame_begin = p_options["lottie/frame_begin"];
+ const float frame_end = p_options["lottie/frame_end"];
+ const int frame_count = p_options["lottie/frame_count"];
+ const int columns = p_options["lottie/columns"];
+ Ref lottie;
+ lottie.instantiate();
+ lottie->update(json, frame_begin, frame_end, frame_count, scale, columns);
+ err = ResourceSaver::save(lottie, p_save_path + ".lottiejson");
+ return err;
+}
+
+ResourceImporterLottieJSON::ResourceImporterLottieJSON() {}
+
+ResourceImporterLottieJSON::~ResourceImporterLottieJSON() {}
+
+//////////////////////////////////////////////////////////////
+
+String ResourceImporterLottieCTEX::get_importer_name() const {
+ return "lottie_compressed_texture_2d";
+}
+String ResourceImporterLottieCTEX::get_visible_name() const {
+ return "CompressedTexture2D";
+}
+int ResourceImporterLottieCTEX::get_preset_count() const {
+ return 1;
+}
+String ResourceImporterLottieCTEX::get_preset_name(int p_idx) const {
+ return p_idx == 0 ? ResourceImporterTexture::get_preset_name(PRESET_2D) : "";
+}
+void ResourceImporterLottieCTEX::get_import_options(const String &p_path, List *r_options, int p_preset) const {
+ lottie_json_importer->get_import_options(p_path, r_options, p_preset);
+ if (r_options->is_empty()) {
+ return;
+ }
+ ResourceImporterTexture::get_import_options(p_path, r_options, p_preset);
+}
+bool ResourceImporterLottieCTEX::get_option_visibility(const String &p_path, const String &p_option, const HashMap &p_options) const {
+ return ResourceImporterTexture::get_option_visibility(p_path, p_option, p_options);
+}
+void ResourceImporterLottieCTEX::get_recognized_extensions(List *p_extensions) const {
+ p_extensions->push_back("json");
+}
+Error ResourceImporterLottieCTEX::import(const String &p_source_file, const String &p_save_path, const HashMap &p_options, List *r_platform_variants, List *r_gen_files, Variant *r_metadata) {
+ // Invalid Lottie file if no import options
+ if (p_options.is_empty()) {
+ return ERR_INVALID_DATA;
+ }
+ Ref json;
+ json.instantiate();
+ Error err = json->parse(FileAccess::get_file_as_string(p_source_file), true);
+ if (err != OK) {
+ String err_text = "Error parsing JSON file at '" + p_source_file + "', on line " + itos(json->get_error_line()) + ": " + json->get_error_message();
+ ERR_PRINT(err_text);
+ return ERR_INVALID_DATA;
+ }
+ const float scale = p_options["lottie/scale"];
+ const float frame_begin = p_options["lottie/frame_begin"];
+ const float frame_end = p_options["lottie/frame_end"];
+ const int frame_count = p_options["lottie/frame_count"];
+ const int columns = p_options["lottie/columns"];
+ Ref lottie;
+ lottie.instantiate();
+ lottie->update(json, frame_begin, frame_end, frame_count, scale, columns);
+
+ String tmp_image = p_save_path + ".tmp.webp";
+ err = lottie->get_image()->save_webp(tmp_image);
+ if (err == OK) {
+ err = ResourceImporterTexture::import(tmp_image, p_save_path, p_options, r_platform_variants, r_gen_files, r_metadata);
+ Ref d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+ err = d->remove(tmp_image);
+ }
+ return err;
+}
diff --git a/modules/svg/editor/resource_importer_lottie.h b/modules/svg/editor/resource_importer_lottie.h
new file mode 100644
index 000000000000..ace6e59e878c
--- /dev/null
+++ b/modules/svg/editor/resource_importer_lottie.h
@@ -0,0 +1,70 @@
+/**************************************************************************/
+/* resource_importer_lottie.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef RESOURCE_IMPORTER_LOTTIE_H
+#define RESOURCE_IMPORTER_LOTTIE_H
+
+#include "editor/import/resource_importer_texture.h"
+
+class ResourceImporterLottieJSON : public ResourceImporter {
+ GDCLASS(ResourceImporterLottieJSON, ResourceImporter);
+
+public:
+ virtual String get_importer_name() const override;
+ virtual String get_visible_name() const override;
+ virtual int get_preset_count() const override;
+ virtual void get_import_options(const String &p_path, List *r_options, int p_preset = 0) const override;
+ virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap &p_options) const override;
+ virtual void get_recognized_extensions(List *p_extensions) const override;
+ String get_save_extension() const override;
+ String get_resource_type() const override;
+ virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap &p_options, List *r_platform_variants, List *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+
+ ResourceImporterLottieJSON();
+ ~ResourceImporterLottieJSON();
+};
+
+class ResourceImporterLottieCTEX : public ResourceImporterTexture {
+ GDCLASS(ResourceImporterLottieCTEX, ResourceImporterTexture);
+
+ Ref lottie_json_importer = memnew(ResourceImporterLottieJSON);
+
+public:
+ virtual String get_importer_name() const override;
+ virtual String get_visible_name() const override;
+ virtual int get_preset_count() const override;
+ virtual String get_preset_name(int p_idx) const override;
+ virtual void get_import_options(const String &p_path, List *r_options, int p_preset = 0) const override;
+ virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap &p_options) const override;
+ virtual void get_recognized_extensions(List *p_extensions) const override;
+ virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap &p_options, List *r_platform_variants, List *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+};
+
+#endif // RESOURCE_IMPORTER_LOTTIE_H
diff --git a/modules/svg/lottie_texture.cpp b/modules/svg/lottie_texture.cpp
new file mode 100644
index 000000000000..8525e841966a
--- /dev/null
+++ b/modules/svg/lottie_texture.cpp
@@ -0,0 +1,374 @@
+/**************************************************************************/
+/* lottie_texture.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "lottie_texture.h"
+
+#include "core/os/memory.h"
+
+#include
+
+LottieTexture2D::LottieTexture2D() {
+ tvg::Result res = sw_canvas->push(tvg::cast(picture));
+ if (res != tvg::Result::Success) {
+ ERR_FAIL_MSG("LottieTexture2D: Couldn't insert ThorVG picture on canvas.");
+ }
+}
+
+void LottieTexture2D::_load_lottie_json() {
+ if (json.is_null()) {
+ return;
+ }
+ String lottie_str = json->get_parsed_text();
+ if (lottie_str.is_empty()) {
+ // don't sort keys, otherwise ThorVG can't load it
+ lottie_str = JSON::stringify(json->get_data(), "", false);
+ }
+ tvg::Result result = picture->load(lottie_str.utf8(), lottie_str.utf8().size(), "lottie", true);
+ if (result != tvg::Result::Success && result != tvg::Result::InsufficientCondition) {
+ ERR_FAIL_MSG(vformat("LottieTexture2D: Couldn't load Lottie: %s.",
+ result == tvg::Result::InvalidArguments ? "InvalidArguments"
+ : result == tvg::Result::NonSupport ? "NonSupport"
+ : "Unknown Error"));
+ }
+}
+
+void LottieTexture2D::_update_image() {
+ if (origin_width < 0 && origin_height < 0) {
+ float fw, fh;
+ picture->size(&fw, &fh);
+ origin_width = fw;
+ origin_height = fh;
+ }
+ if (json.is_null() || frame_count <= 0) {
+ if (image.is_null()) {
+ image = Image::create_empty(1, 1, false, Image::FORMAT_RGBA8);
+ }
+ if (texture.is_null()) {
+ texture = RenderingServer::get_singleton()->texture_2d_create(image);
+ }
+ return;
+ }
+
+ int _columns = columns <= 0 ? Math::ceil(Math::sqrt((float)frame_count)) : columns;
+ int _rows = Math::ceil(((float)frame_count) / _columns);
+
+ uint32_t w = MAX(1, round(origin_width * scale));
+ uint32_t h = MAX(1, round(origin_height * scale));
+
+ const uint32_t max_dimension = 16384;
+ if (w * _columns > max_dimension || h * _rows > max_dimension) {
+ WARN_PRINT(vformat(
+ String::utf8("LottieTexture2D: Target canvas dimensions %d×%d (with scale %.2f, rows %d, columns %d) exceed the max supported dimensions %d×%d. The target canvas will be scaled down."),
+ w, h, scale, _rows, _columns, max_dimension, max_dimension));
+ w = MIN(w, max_dimension / _columns);
+ h = MIN(h, max_dimension / _rows);
+ scale = MIN(w / origin_width, h / origin_height);
+ }
+ picture->size(w, h);
+
+ bool can_update = image.is_valid() && (uint32_t)image->get_width() == w * _columns && (uint32_t)image->get_height() == h * _rows;
+ if (!can_update) {
+ image = Image::create_empty(w * _columns, h * _rows, false, Image::FORMAT_RGBA8);
+ }
+
+ uint32_t *buffer = (uint32_t *)memalloc(sizeof(uint32_t) * w * h);
+ memset(buffer, 0, sizeof(uint32_t) * w * h);
+
+ sw_canvas->sync();
+ tvg::Result res = sw_canvas->target(buffer, w, w, h, tvg::SwCanvas::ARGB8888S);
+ if (res != tvg::Result::Success) {
+ memfree(buffer);
+ ERR_FAIL_MSG("LottieTexture2D: Couldn't set target on ThorVG canvas.");
+ }
+
+ for (int row = 0; row < _rows; row++) {
+ for (int column = 0; column < _columns; column++) {
+ if (row * _columns + column >= frame_count) {
+ break;
+ }
+ float progress = ((float)(row * _columns + column)) / frame_count;
+ float current_frame = frame_begin + (frame_end - frame_begin) * progress;
+
+ animation->frame(current_frame);
+ res = sw_canvas->update(picture);
+ if (res != tvg::Result::Success) {
+ memfree(buffer);
+ ERR_FAIL_MSG("LottieTexture2D: Couldn't update ThorVG pictures on canvas.");
+ }
+ res = sw_canvas->draw();
+ if (res != tvg::Result::Success) {
+ memfree(buffer);
+ ERR_FAIL_MSG("LottieTexture2D: Couldn't draw ThorVG pictures on canvas.");
+ }
+ res = sw_canvas->sync();
+ if (res != tvg::Result::Success) {
+ memfree(buffer);
+ ERR_FAIL_MSG("LottieTexture2D: Couldn't sync ThorVG canvas.");
+ }
+
+ for (uint32_t y = 0; y < h; y++) {
+ for (uint32_t x = 0; x < w; x++) {
+ uint32_t n = buffer[y * w + x];
+ Color color;
+ color.set_r8((n >> 16) & 0xff);
+ color.set_g8((n >> 8) & 0xff);
+ color.set_b8(n & 0xff);
+ color.set_a8((n >> 24) & 0xff);
+ image->set_pixel(x + w * column, y + h * row, color);
+ }
+ }
+ sw_canvas->clear(false);
+ }
+ }
+ memfree(buffer);
+
+ if (texture.is_null()) {
+ texture = RenderingServer::get_singleton()->texture_2d_create(image);
+ } else {
+ if (can_update) {
+ RenderingServer::get_singleton()->texture_2d_update(texture, image);
+ } else {
+ RID new_texture = RenderingServer::get_singleton()->texture_2d_create(image);
+ RenderingServer::get_singleton()->texture_replace(texture, new_texture);
+ }
+ }
+ emit_changed();
+}
+
+bool LottieTexture2D::validate(Ref p_json) {
+ String str = p_json->get_parsed_text();
+ if (str.is_empty()) {
+ str = p_json->stringify(p_json->get_data(), "", false);
+ }
+ // use ThorVG to check if it's Lottie file.
+ std::unique_ptr picture = tvg::Picture::gen();
+ tvg::Result res = picture->load(str.utf8(), str.utf8().size(), "lottie", true);
+ return res == tvg::Result::Success;
+}
+
+Ref LottieTexture2D::create_from_json(Ref p_json) {
+ if (!validate(p_json)) {
+ return nullptr;
+ }
+ Ref ret = memnew(LottieTexture2D);
+ Dictionary dict = p_json->get_data();
+ float p_scale = dict.get("gd_scale", 1.0);
+ float p_frame_begin = dict.get("gd_frame_begin", 0);
+ float p_frame_end = dict.get("gd_frame_end", 0);
+ int p_frame_count = dict.get("gd_frame_count", 1);
+ int p_columns = dict.get("gd_columns", 0);
+ ret->update(p_json, p_frame_begin, p_frame_end, p_frame_count, p_scale, p_columns);
+ return ret;
+}
+
+void LottieTexture2D::update(Ref p_json, float p_frame_begin, float p_frame_end, int p_frame_count, float p_scale, int p_columns) {
+ frame_begin = p_frame_begin;
+ frame_end = p_frame_end;
+ frame_count = p_frame_count;
+ scale = p_scale;
+ json = p_json;
+ columns = p_columns;
+ _load_lottie_json();
+ _update_image();
+}
+
+void LottieTexture2D::set_json(Ref p_json) {
+ json = p_json;
+ _load_lottie_json();
+ _update_image();
+}
+
+void LottieTexture2D::set_scale(float p_scale) {
+ scale = p_scale;
+ _update_image();
+}
+
+void LottieTexture2D::set_frame_begin(float p_frame_begin) {
+ frame_begin = CLAMP(p_frame_begin, 0, get_lottie_frame_count());
+ if (frame_begin > frame_end) {
+ frame_end = frame_begin;
+ }
+ _update_image();
+}
+
+void LottieTexture2D::set_frame_end(float p_frame_end) {
+ frame_end = CLAMP(p_frame_end, frame_begin, get_lottie_frame_count() - 1);
+ _update_image();
+}
+
+void LottieTexture2D::set_frame_count(int p_frame_count) {
+ frame_count = p_frame_count;
+ _update_image();
+};
+
+void LottieTexture2D::set_columns(int p_columns) {
+ columns = MIN(p_columns, frame_count);
+ _update_image();
+}
+
+RID LottieTexture2D::get_rid() const {
+ if (texture.is_null()) {
+ texture = RenderingServer::get_singleton()->texture_2d_placeholder_create();
+ }
+ return texture;
+}
+
+LottieTexture2D::~LottieTexture2D() {
+ if (texture.is_valid()) {
+ RenderingServer::get_singleton()->free(texture);
+ }
+}
+
+void LottieTexture2D::_bind_methods() {
+ ClassDB::bind_static_method("LottieTexture2D", D_METHOD("validate", "p_json"), &LottieTexture2D::validate);
+ ClassDB::bind_static_method("LottieTexture2D", D_METHOD("create_from_json", "p_json"), &LottieTexture2D::create_from_json);
+ ClassDB::bind_method(D_METHOD("update", "p_json", "p_frame_begin", "p_frame_end", "p_frame_count", "p_scale", "p_columns"), &LottieTexture2D::update);
+ ClassDB::bind_method(D_METHOD("set_json", "p_json"), &LottieTexture2D::set_json);
+ ClassDB::bind_method(D_METHOD("get_json"), &LottieTexture2D::get_json);
+ ClassDB::bind_method(D_METHOD("set_scale", "p_scale"), &LottieTexture2D::set_scale);
+ ClassDB::bind_method(D_METHOD("get_scale"), &LottieTexture2D::get_scale);
+ ClassDB::bind_method(D_METHOD("set_frame_begin", "p_frame_begin"), &LottieTexture2D::set_frame_begin);
+ ClassDB::bind_method(D_METHOD("get_frame_begin"), &LottieTexture2D::get_frame_begin);
+ ClassDB::bind_method(D_METHOD("set_frame_end", "p_frame_end"), &LottieTexture2D::set_frame_end);
+ ClassDB::bind_method(D_METHOD("get_frame_end"), &LottieTexture2D::get_frame_end);
+ ClassDB::bind_method(D_METHOD("set_frame_count", "p_frame_count"), &LottieTexture2D::set_frame_count);
+ ClassDB::bind_method(D_METHOD("get_frame_count"), &LottieTexture2D::get_frame_count);
+ ClassDB::bind_method(D_METHOD("set_columns", "p_columns"), &LottieTexture2D::set_columns);
+ ClassDB::bind_method(D_METHOD("get_columns"), &LottieTexture2D::get_columns);
+ ClassDB::bind_method(D_METHOD("get_lottie_duration"), &LottieTexture2D::get_lottie_duration);
+ ClassDB::bind_method(D_METHOD("get_lottie_frame_count"), &LottieTexture2D::get_lottie_frame_count);
+ ClassDB::bind_method(D_METHOD("get_lottie_image_size"), &LottieTexture2D::get_lottie_image_size);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "json", PROPERTY_HINT_RESOURCE_TYPE, "JSON"), "set_json", "get_json");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scale"), "set_scale", "get_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frame_begin"), "set_frame_begin", "get_frame_begin");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frame_end"), "set_frame_end", "get_frame_end");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "frame_count"), "set_frame_count", "get_frame_count");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "columns"), "set_columns", "get_columns");
+}
+
+////////////////
+
+Ref ResourceFormatLoaderLottie::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+ if (r_error) {
+ *r_error = ERR_FILE_CANT_OPEN;
+ }
+
+ Ref ret = Ref();
+
+ if (!FileAccess::exists(p_path)) {
+ *r_error = ERR_FILE_NOT_FOUND;
+ return ret;
+ }
+
+ Ref json;
+ json.instantiate();
+
+ Error err = json->parse(FileAccess::get_file_as_string(p_path), true);
+ if (err != OK) {
+ String err_text = "Error parsing JSON file at '" + p_path + "', on line " + itos(json->get_error_line()) + ": " + json->get_error_message();
+ if (r_error) {
+ *r_error = err;
+ }
+ ERR_PRINT(err_text);
+ return ret;
+ }
+
+ if (get_resource_type(p_path) == "LottieTexture2D") {
+ ret = LottieTexture2D::create_from_json(json);
+ } else {
+ if (r_error) {
+ *r_error = ERR_INVALID_DATA;
+ }
+ return ret;
+ }
+
+ if (r_error) {
+ *r_error = OK;
+ }
+
+ return ret;
+}
+
+void ResourceFormatLoaderLottie::get_recognized_extensions(List *p_extensions) const {
+ p_extensions->push_back("lottiejson");
+}
+
+bool ResourceFormatLoaderLottie::handles_type(const String &p_type) const {
+ return p_type == "LottieTexture2D";
+}
+
+String ResourceFormatLoaderLottie::get_resource_type(const String &p_path) const {
+ String el = p_path.get_extension().to_lower();
+ if (el != "lottiejson") {
+ return "";
+ }
+ return "LottieTexture2D";
+}
+
+////////////////
+
+Error ResourceFormatSaverLottie::save(const Ref &p_resource, const String &p_path, uint32_t p_flags) {
+ Ref lottie = p_resource;
+ ERR_FAIL_COND_V(lottie.is_null(), ERR_INVALID_PARAMETER);
+
+ // Lottie JSON object allows storing additional data
+ Dictionary dict = lottie->get_json().is_valid() ? (Dictionary)lottie->get_json()->get_data() : Dictionary();
+ dict["gd_scale"] = lottie->get_scale();
+ dict["gd_frame_begin"] = lottie->get_frame_begin();
+ dict["gd_frame_end"] = lottie->get_frame_end();
+ dict["gd_frame_count"] = lottie->get_frame_count();
+ dict["gd_columns"] = lottie->get_columns();
+
+ String source = JSON::stringify(dict, "", false);
+
+ Error err;
+ Ref file = FileAccess::open(p_path, FileAccess::WRITE, &err);
+
+ ERR_FAIL_COND_V_MSG(err, err, "Cannot save lottie json '" + p_path + "'.");
+
+ file->store_string(source);
+ if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
+ return ERR_CANT_CREATE;
+ }
+
+ return OK;
+}
+
+void ResourceFormatSaverLottie::get_recognized_extensions(const Ref &p_resource, List *p_extensions) const {
+ Ref lottie = p_resource;
+ if (lottie.is_valid()) {
+ p_extensions->push_back("lottiejson");
+ }
+}
+
+bool ResourceFormatSaverLottie::recognize(const Ref &p_resource) const {
+ return p_resource->is_class("LottieTexture2D");
+}
diff --git a/modules/svg/lottie_texture.h b/modules/svg/lottie_texture.h
new file mode 100644
index 000000000000..e115605a3b86
--- /dev/null
+++ b/modules/svg/lottie_texture.h
@@ -0,0 +1,129 @@
+/**************************************************************************/
+/* lottie_texture.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef LOTTIE_TEXTURE_H
+#define LOTTIE_TEXTURE_H
+
+#include "core/io/json.h"
+#include "scene/resources/texture.h"
+
+#include
+
+class LottieTexture2D : public Texture2D {
+ GDCLASS(LottieTexture2D, Texture2D);
+
+ std::unique_ptr sw_canvas = tvg::SwCanvas::gen();
+ std::unique_ptr animation = tvg::Animation::gen();
+ tvg::Picture *picture = animation->picture();
+ mutable RID texture;
+ Ref image;
+ Ref json = nullptr;
+
+ float scale = 1.0;
+ float origin_width = -1, origin_height = -1;
+
+ float frame_begin = 0;
+ float frame_end = 0;
+ int frame_count = 1;
+ int columns = 0;
+
+ void _load_lottie_json();
+ void _update_image();
+
+protected:
+ static void _bind_methods();
+
+public:
+ static bool validate(Ref p_json);
+
+ static Ref create_from_json(Ref p_json);
+
+ void update(Ref p_json, float p_frame_begin, float p_frame_end, int p_frame_count, float p_scale, int p_columns);
+
+ void set_json(Ref p_json);
+ Ref get_json() { return json; };
+
+ void set_scale(float p_scale);
+ float get_scale() { return scale; };
+
+ void set_frame_begin(float p_frame_begin);
+ float get_frame_begin() { return frame_begin; };
+
+ void set_frame_end(float p_frame_end);
+ float get_frame_end() { return frame_end; };
+
+ void set_frame_count(int p_frame_count);
+ int get_frame_count() { return frame_count; };
+
+ void set_columns(int p_columns);
+ int get_columns() { return columns; }
+
+ float get_lottie_duration() { return animation->duration(); };
+ float get_lottie_frame_count() { return animation->totalFrame(); };
+ Size2 get_lottie_image_size() {
+ float w, h;
+ picture->size(&w, &h);
+ return Size2(w, h);
+ }
+
+ int get_width() const override {
+ return image.is_valid() ? image->get_width() : 0;
+ };
+ int get_height() const override {
+ return image.is_valid() ? image->get_height() : 0;
+ };
+ virtual bool has_alpha() const override { return true; };
+ virtual Ref get_image() const override { return image; };
+ virtual RID get_rid() const override;
+
+ LottieTexture2D();
+ ~LottieTexture2D();
+};
+
+class ResourceFormatLoaderLottie : public ResourceFormatLoader {
+ GDCLASS(ResourceFormatLoaderLottie, ResourceFormatLoader);
+
+public:
+ virtual Ref load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override;
+ virtual void get_recognized_extensions(List *p_extensions) const override;
+ virtual bool handles_type(const String &p_type) const override;
+ virtual String get_resource_type(const String &p_path) const override;
+};
+
+class ResourceFormatSaverLottie : public ResourceFormatSaver {
+ GDCLASS(ResourceFormatSaverLottie, ResourceFormatSaver);
+
+public:
+ virtual Error save(const Ref &p_resource, const String &p_path, uint32_t p_flags = 0) override;
+ virtual void get_recognized_extensions(const Ref &p_resource, List *p_extensions) const override;
+ virtual bool recognize(const Ref &p_resource) const override;
+};
+
+#endif // LOTTIE_TEXTURE_H
diff --git a/modules/svg/register_types.cpp b/modules/svg/register_types.cpp
index 82d816d83312..e60bac3f2539 100644
--- a/modules/svg/register_types.cpp
+++ b/modules/svg/register_types.cpp
@@ -40,6 +40,20 @@
#define TVG_THREADS 0
#endif
+#ifdef LOTTIE_ENABLED
+#include "lottie_texture.h"
+
+static Ref resource_loader_lottie;
+static Ref resource_saver_lottie;
+
+#ifdef TOOLS_ENABLED
+#include "editor/resource_importer_lottie.h"
+
+static Ref resource_importer_lottie_ctex;
+static Ref resource_importer_lottie_json;
+#endif // TOOLS_ENABLED
+#endif // LOTTIE_ENABLED
+
static Ref image_loader_svg;
void initialize_svg_module(ModuleInitializationLevel p_level) {
@@ -55,6 +69,20 @@ void initialize_svg_module(ModuleInitializationLevel p_level) {
image_loader_svg.instantiate();
ImageLoader::add_image_format_loader(image_loader_svg);
+
+#ifdef LOTTIE_ENABLED
+ resource_loader_lottie.instantiate();
+ resource_saver_lottie.instantiate();
+ ResourceLoader::add_resource_format_loader(resource_loader_lottie);
+ ResourceSaver::add_resource_format_saver(resource_saver_lottie);
+ ClassDB::register_class();
+#ifdef TOOLS_ENABLED
+ resource_importer_lottie_ctex.instantiate();
+ resource_importer_lottie_json.instantiate();
+ ResourceFormatImporter::get_singleton()->add_importer(resource_importer_lottie_ctex);
+ ResourceFormatImporter::get_singleton()->add_importer(resource_importer_lottie_json);
+#endif // TOOLS_ENABLED
+#endif // LOTTIE_ENABLED
}
void uninitialize_svg_module(ModuleInitializationLevel p_level) {
@@ -69,5 +97,19 @@ void uninitialize_svg_module(ModuleInitializationLevel p_level) {
ImageLoader::remove_image_format_loader(image_loader_svg);
image_loader_svg.unref();
+
+#ifdef LOTTIE_ENABLED
+ ResourceLoader::remove_resource_format_loader(resource_loader_lottie);
+ ResourceSaver::remove_resource_format_saver(resource_saver_lottie);
+ resource_loader_lottie.unref();
+ resource_saver_lottie.unref();
+
+#ifdef TOOLS_ENABLED
+ ResourceFormatImporter::get_singleton()->remove_importer(resource_importer_lottie_ctex);
+ ResourceFormatImporter::get_singleton()->remove_importer(resource_importer_lottie_json);
+ resource_importer_lottie_ctex.unref();
+ resource_importer_lottie_json.unref();
+#endif // TOOLS_ENABLED
+#endif // LOTTIE_ENABLED
tvg::Initializer::term(tvg::CanvasEngine::Sw);
}
diff --git a/thirdparty/thorvg/AUTHORS b/thirdparty/thorvg/AUTHORS
index e00e91a6967b..81c11aa5e3e9 100644
--- a/thirdparty/thorvg/AUTHORS
+++ b/thirdparty/thorvg/AUTHORS
@@ -28,6 +28,6 @@ Nattu Adnan
Gabor Kiss-Vamosi
Lorcán Mc Donagh
Lucas Niu
-Francisco Ramírez
+Francisco Ramírez
Abdelrahman Ashraf
Neo Xu
diff --git a/thirdparty/thorvg/src/loaders/lottie/rapidjson/allocators.h b/thirdparty/thorvg/src/loaders/lottie/rapidjson/allocators.h
new file mode 100644
index 000000000000..35650aff4c98
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/lottie/rapidjson/allocators.h
@@ -0,0 +1,693 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_ALLOCATORS_H_
+#define RAPIDJSON_ALLOCATORS_H_
+
+#include "rapidjson.h"
+#include "internal/meta.h"
+
+#include
+#include
+
+#if RAPIDJSON_HAS_CXX11
+#include
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+///////////////////////////////////////////////////////////////////////////////
+// Allocator
+
+/*! \class rapidjson::Allocator
+ \brief Concept for allocating, resizing and freeing memory block.
+
+ Note that Malloc() and Realloc() are non-static but Free() is static.
+
+ So if an allocator need to support Free(), it needs to put its pointer in
+ the header of memory block.
+
+\code
+concept Allocator {
+ static const bool kNeedFree; //!< Whether this allocator needs to call Free().
+
+ // Allocate a memory block.
+ // \param size of the memory block in bytes.
+ // \returns pointer to the memory block.
+ void* Malloc(size_t size);
+
+ // Resize a memory block.
+ // \param originalPtr The pointer to current memory block. Null pointer is permitted.
+ // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
+ // \param newSize the new size in bytes.
+ void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
+
+ // Free a memory block.
+ // \param pointer to the memory block. Null pointer is permitted.
+ static void Free(void *ptr);
+};
+\endcode
+*/
+
+
+/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY
+ \ingroup RAPIDJSON_CONFIG
+ \brief User-defined kDefaultChunkCapacity definition.
+
+ User can define this as any \c size that is a power of 2.
+*/
+
+#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY
+#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024)
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CrtAllocator
+
+//! C-runtime library allocator.
+/*! This class is just wrapper for standard C library memory routines.
+ \note implements Allocator concept
+*/
+class CrtAllocator {
+public:
+ static const bool kNeedFree = true;
+ void* Malloc(size_t size) {
+ if (size) // behavior of malloc(0) is implementation defined.
+ return RAPIDJSON_MALLOC(size);
+ else
+ return NULL; // standardize to returning NULL.
+ }
+ void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
+ (void)originalSize;
+ if (newSize == 0) {
+ RAPIDJSON_FREE(originalPtr);
+ return NULL;
+ }
+ return RAPIDJSON_REALLOC(originalPtr, newSize);
+ }
+ static void Free(void *ptr) RAPIDJSON_NOEXCEPT { RAPIDJSON_FREE(ptr); }
+
+ bool operator==(const CrtAllocator&) const RAPIDJSON_NOEXCEPT {
+ return true;
+ }
+ bool operator!=(const CrtAllocator&) const RAPIDJSON_NOEXCEPT {
+ return false;
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// MemoryPoolAllocator
+
+//! Default memory allocator used by the parser and DOM.
+/*! This allocator allocate memory blocks from pre-allocated memory chunks.
+
+ It does not free memory blocks. And Realloc() only allocate new memory.
+
+ The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default.
+
+ User may also supply a buffer as the first chunk.
+
+ If the user-buffer is full then additional chunks are allocated by BaseAllocator.
+
+ The user-buffer is not deallocated by this allocator.
+
+ \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
+ \note implements Allocator concept
+*/
+template
+class MemoryPoolAllocator {
+ //! Chunk header for perpending to each chunk.
+ /*! Chunks are stored as a singly linked list.
+ */
+ struct ChunkHeader {
+ size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
+ size_t size; //!< Current size of allocated memory in bytes.
+ ChunkHeader *next; //!< Next chunk in the linked list.
+ };
+
+ struct SharedData {
+ ChunkHeader *chunkHead; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
+ BaseAllocator* ownBaseAllocator; //!< base allocator created by this object.
+ size_t refcount;
+ bool ownBuffer;
+ };
+
+ static const size_t SIZEOF_SHARED_DATA = RAPIDJSON_ALIGN(sizeof(SharedData));
+ static const size_t SIZEOF_CHUNK_HEADER = RAPIDJSON_ALIGN(sizeof(ChunkHeader));
+
+ static inline ChunkHeader *GetChunkHead(SharedData *shared)
+ {
+ return reinterpret_cast(reinterpret_cast(shared) + SIZEOF_SHARED_DATA);
+ }
+ static inline uint8_t *GetChunkBuffer(SharedData *shared)
+ {
+ return reinterpret_cast(shared->chunkHead) + SIZEOF_CHUNK_HEADER;
+ }
+
+ static const size_t kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity.
+
+public:
+ static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
+ static const bool kRefCounted = true; //!< Tell users that this allocator is reference counted on copy
+
+ //! Constructor with chunkSize.
+ /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
+ \param baseAllocator The allocator for allocating memory chunks.
+ */
+ explicit
+ MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
+ chunk_capacity_(chunkSize),
+ baseAllocator_(baseAllocator ? baseAllocator : RAPIDJSON_NEW(BaseAllocator)()),
+ shared_(static_cast(baseAllocator_ ? baseAllocator_->Malloc(SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER) : 0))
+ {
+ RAPIDJSON_ASSERT(baseAllocator_ != 0);
+ RAPIDJSON_ASSERT(shared_ != 0);
+ if (baseAllocator) {
+ shared_->ownBaseAllocator = 0;
+ }
+ else {
+ shared_->ownBaseAllocator = baseAllocator_;
+ }
+ shared_->chunkHead = GetChunkHead(shared_);
+ shared_->chunkHead->capacity = 0;
+ shared_->chunkHead->size = 0;
+ shared_->chunkHead->next = 0;
+ shared_->ownBuffer = true;
+ shared_->refcount = 1;
+ }
+
+ //! Constructor with user-supplied buffer.
+ /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
+
+ The user buffer will not be deallocated when this allocator is destructed.
+
+ \param buffer User supplied buffer.
+ \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
+ \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
+ \param baseAllocator The allocator for allocating memory chunks.
+ */
+ MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
+ chunk_capacity_(chunkSize),
+ baseAllocator_(baseAllocator),
+ shared_(static_cast(AlignBuffer(buffer, size)))
+ {
+ RAPIDJSON_ASSERT(size >= SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER);
+ shared_->chunkHead = GetChunkHead(shared_);
+ shared_->chunkHead->capacity = size - SIZEOF_SHARED_DATA - SIZEOF_CHUNK_HEADER;
+ shared_->chunkHead->size = 0;
+ shared_->chunkHead->next = 0;
+ shared_->ownBaseAllocator = 0;
+ shared_->ownBuffer = false;
+ shared_->refcount = 1;
+ }
+
+ MemoryPoolAllocator(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT :
+ chunk_capacity_(rhs.chunk_capacity_),
+ baseAllocator_(rhs.baseAllocator_),
+ shared_(rhs.shared_)
+ {
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
+ ++shared_->refcount;
+ }
+ MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT
+ {
+ RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
+ ++rhs.shared_->refcount;
+ this->~MemoryPoolAllocator();
+ baseAllocator_ = rhs.baseAllocator_;
+ chunk_capacity_ = rhs.chunk_capacity_;
+ shared_ = rhs.shared_;
+ return *this;
+ }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ MemoryPoolAllocator(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT :
+ chunk_capacity_(rhs.chunk_capacity_),
+ baseAllocator_(rhs.baseAllocator_),
+ shared_(rhs.shared_)
+ {
+ RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
+ rhs.shared_ = 0;
+ }
+ MemoryPoolAllocator& operator=(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT
+ {
+ RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
+ this->~MemoryPoolAllocator();
+ baseAllocator_ = rhs.baseAllocator_;
+ chunk_capacity_ = rhs.chunk_capacity_;
+ shared_ = rhs.shared_;
+ rhs.shared_ = 0;
+ return *this;
+ }
+#endif
+
+ //! Destructor.
+ /*! This deallocates all memory chunks, excluding the user-supplied buffer.
+ */
+ ~MemoryPoolAllocator() RAPIDJSON_NOEXCEPT {
+ if (!shared_) {
+ // do nothing if moved
+ return;
+ }
+ if (shared_->refcount > 1) {
+ --shared_->refcount;
+ return;
+ }
+ Clear();
+ BaseAllocator *a = shared_->ownBaseAllocator;
+ if (shared_->ownBuffer) {
+ baseAllocator_->Free(shared_);
+ }
+ RAPIDJSON_DELETE(a);
+ }
+
+ //! Deallocates all memory chunks, excluding the first/user one.
+ void Clear() RAPIDJSON_NOEXCEPT {
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
+ for (;;) {
+ ChunkHeader* c = shared_->chunkHead;
+ if (!c->next) {
+ break;
+ }
+ shared_->chunkHead = c->next;
+ baseAllocator_->Free(c);
+ }
+ shared_->chunkHead->size = 0;
+ }
+
+ //! Computes the total capacity of allocated memory chunks.
+ /*! \return total capacity in bytes.
+ */
+ size_t Capacity() const RAPIDJSON_NOEXCEPT {
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
+ size_t capacity = 0;
+ for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next)
+ capacity += c->capacity;
+ return capacity;
+ }
+
+ //! Computes the memory blocks allocated.
+ /*! \return total used bytes.
+ */
+ size_t Size() const RAPIDJSON_NOEXCEPT {
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
+ size_t size = 0;
+ for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next)
+ size += c->size;
+ return size;
+ }
+
+ //! Whether the allocator is shared.
+ /*! \return true or false.
+ */
+ bool Shared() const RAPIDJSON_NOEXCEPT {
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
+ return shared_->refcount > 1;
+ }
+
+ //! Allocates a memory block. (concept Allocator)
+ void* Malloc(size_t size) {
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
+ if (!size)
+ return NULL;
+
+ size = RAPIDJSON_ALIGN(size);
+ if (RAPIDJSON_UNLIKELY(shared_->chunkHead->size + size > shared_->chunkHead->capacity))
+ if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size))
+ return NULL;
+
+ void *buffer = GetChunkBuffer(shared_) + shared_->chunkHead->size;
+ shared_->chunkHead->size += size;
+ return buffer;
+ }
+
+ //! Resizes a memory block (concept Allocator)
+ void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
+ if (originalPtr == 0)
+ return Malloc(newSize);
+
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
+ if (newSize == 0)
+ return NULL;
+
+ originalSize = RAPIDJSON_ALIGN(originalSize);
+ newSize = RAPIDJSON_ALIGN(newSize);
+
+ // Do not shrink if new size is smaller than original
+ if (originalSize >= newSize)
+ return originalPtr;
+
+ // Simply expand it if it is the last allocation and there is sufficient space
+ if (originalPtr == GetChunkBuffer(shared_) + shared_->chunkHead->size - originalSize) {
+ size_t increment = static_cast(newSize - originalSize);
+ if (shared_->chunkHead->size + increment <= shared_->chunkHead->capacity) {
+ shared_->chunkHead->size += increment;
+ return originalPtr;
+ }
+ }
+
+ // Realloc process: allocate and copy memory, do not free original buffer.
+ if (void* newBuffer = Malloc(newSize)) {
+ if (originalSize)
+ std::memcpy(newBuffer, originalPtr, originalSize);
+ return newBuffer;
+ }
+ else
+ return NULL;
+ }
+
+ //! Frees a memory block (concept Allocator)
+ static void Free(void *ptr) RAPIDJSON_NOEXCEPT { (void)ptr; } // Do nothing
+
+ //! Compare (equality) with another MemoryPoolAllocator
+ bool operator==(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT {
+ RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0);
+ RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0);
+ return shared_ == rhs.shared_;
+ }
+ //! Compare (inequality) with another MemoryPoolAllocator
+ bool operator!=(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT {
+ return !operator==(rhs);
+ }
+
+private:
+ //! Creates a new chunk.
+ /*! \param capacity Capacity of the chunk in bytes.
+ \return true if success.
+ */
+ bool AddChunk(size_t capacity) {
+ if (!baseAllocator_)
+ shared_->ownBaseAllocator = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)();
+ if (ChunkHeader* chunk = static_cast(baseAllocator_->Malloc(SIZEOF_CHUNK_HEADER + capacity))) {
+ chunk->capacity = capacity;
+ chunk->size = 0;
+ chunk->next = shared_->chunkHead;
+ shared_->chunkHead = chunk;
+ return true;
+ }
+ else
+ return false;
+ }
+
+ static inline void* AlignBuffer(void* buf, size_t &size)
+ {
+ RAPIDJSON_NOEXCEPT_ASSERT(buf != 0);
+ const uintptr_t mask = sizeof(void*) - 1;
+ const uintptr_t ubuf = reinterpret_cast(buf);
+ if (RAPIDJSON_UNLIKELY(ubuf & mask)) {
+ const uintptr_t abuf = (ubuf + mask) & ~mask;
+ RAPIDJSON_ASSERT(size >= abuf - ubuf);
+ buf = reinterpret_cast(abuf);
+ size -= abuf - ubuf;
+ }
+ return buf;
+ }
+
+ size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
+ BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
+ SharedData *shared_; //!< The shared data of the allocator
+};
+
+namespace internal {
+ template
+ struct IsRefCounted :
+ public FalseType
+ { };
+ template
+ struct IsRefCounted::Type> :
+ public TrueType
+ { };
+}
+
+template
+inline T* Realloc(A& a, T* old_p, size_t old_n, size_t new_n)
+{
+ RAPIDJSON_NOEXCEPT_ASSERT(old_n <= (std::numeric_limits::max)() / sizeof(T) && new_n <= (std::numeric_limits::max)() / sizeof(T));
+ return static_cast(a.Realloc(old_p, old_n * sizeof(T), new_n * sizeof(T)));
+}
+
+template
+inline T *Malloc(A& a, size_t n = 1)
+{
+ return Realloc(a, NULL, 0, n);
+}
+
+template
+inline void Free(A& a, T *p, size_t n = 1)
+{
+ static_cast(Realloc(a, p, n, 0));
+}
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++) // std::allocator can safely be inherited
+#endif
+
+template
+class StdAllocator :
+ public std::allocator
+{
+ typedef std::allocator allocator_type;
+#if RAPIDJSON_HAS_CXX11
+ typedef std::allocator_traits traits_type;
+#else
+ typedef allocator_type traits_type;
+#endif
+
+public:
+ typedef BaseAllocator BaseAllocatorType;
+
+ StdAllocator() RAPIDJSON_NOEXCEPT :
+ allocator_type(),
+ baseAllocator_()
+ { }
+
+ StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT :
+ allocator_type(rhs),
+ baseAllocator_(rhs.baseAllocator_)
+ { }
+
+ template
+ StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT :
+ allocator_type(rhs),
+ baseAllocator_(rhs.baseAllocator_)
+ { }
+
+#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
+ StdAllocator(StdAllocator&& rhs) RAPIDJSON_NOEXCEPT :
+ allocator_type(std::move(rhs)),
+ baseAllocator_(std::move(rhs.baseAllocator_))
+ { }
+#endif
+#if RAPIDJSON_HAS_CXX11
+ using propagate_on_container_move_assignment = std::true_type;
+ using propagate_on_container_swap = std::true_type;
+#endif
+
+ /* implicit */
+ StdAllocator(const BaseAllocator& allocator) RAPIDJSON_NOEXCEPT :
+ allocator_type(),
+ baseAllocator_(allocator)
+ { }
+
+ ~StdAllocator() RAPIDJSON_NOEXCEPT
+ { }
+
+ template
+ struct rebind {
+ typedef StdAllocator other;
+ };
+
+ typedef typename traits_type::size_type size_type;
+ typedef typename traits_type::difference_type difference_type;
+
+ typedef typename traits_type::value_type value_type;
+ typedef typename traits_type::pointer pointer;
+ typedef typename traits_type::const_pointer const_pointer;
+
+#if RAPIDJSON_HAS_CXX11
+
+ typedef typename std::add_lvalue_reference::type &reference;
+ typedef typename std::add_lvalue_reference::type>::type &const_reference;
+
+ pointer address(reference r) const RAPIDJSON_NOEXCEPT
+ {
+ return std::addressof(r);
+ }
+ const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT
+ {
+ return std::addressof(r);
+ }
+
+ size_type max_size() const RAPIDJSON_NOEXCEPT
+ {
+ return traits_type::max_size(*this);
+ }
+
+ template
+ void construct(pointer p, Args&&... args)
+ {
+ traits_type::construct(*this, p, std::forward(args)...);
+ }
+ void destroy(pointer p)
+ {
+ traits_type::destroy(*this, p);
+ }
+
+#else // !RAPIDJSON_HAS_CXX11
+
+ typedef typename allocator_type::reference reference;
+ typedef typename allocator_type::const_reference const_reference;
+
+ pointer address(reference r) const RAPIDJSON_NOEXCEPT
+ {
+ return allocator_type::address(r);
+ }
+ const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT
+ {
+ return allocator_type::address(r);
+ }
+
+ size_type max_size() const RAPIDJSON_NOEXCEPT
+ {
+ return allocator_type::max_size();
+ }
+
+ void construct(pointer p, const_reference r)
+ {
+ allocator_type::construct(p, r);
+ }
+ void destroy(pointer p)
+ {
+ allocator_type::destroy(p);
+ }
+
+#endif // !RAPIDJSON_HAS_CXX11
+
+ template
+ U* allocate(size_type n = 1, const void* = 0)
+ {
+ return RAPIDJSON_NAMESPACE::Malloc(baseAllocator_, n);
+ }
+ template
+ void deallocate(U* p, size_type n = 1)
+ {
+ RAPIDJSON_NAMESPACE::Free(baseAllocator_, p, n);
+ }
+
+ pointer allocate(size_type n = 1, const void* = 0)
+ {
+ return allocate(n);
+ }
+ void deallocate(pointer p, size_type n = 1)
+ {
+ deallocate(p, n);
+ }
+
+#if RAPIDJSON_HAS_CXX11
+ using is_always_equal = std::is_empty;
+#endif
+
+ template
+ bool operator==(const StdAllocator& rhs) const RAPIDJSON_NOEXCEPT
+ {
+ return baseAllocator_ == rhs.baseAllocator_;
+ }
+ template
+ bool operator!=(const StdAllocator& rhs) const RAPIDJSON_NOEXCEPT
+ {
+ return !operator==(rhs);
+ }
+
+ //! rapidjson Allocator concept
+ static const bool kNeedFree = BaseAllocator::kNeedFree;
+ static const bool kRefCounted = internal::IsRefCounted::Value;
+ void* Malloc(size_t size)
+ {
+ return baseAllocator_.Malloc(size);
+ }
+ void* Realloc(void* originalPtr, size_t originalSize, size_t newSize)
+ {
+ return baseAllocator_.Realloc(originalPtr, originalSize, newSize);
+ }
+ static void Free(void *ptr) RAPIDJSON_NOEXCEPT
+ {
+ BaseAllocator::Free(ptr);
+ }
+
+private:
+ template
+ friend class StdAllocator; // access to StdAllocator.*
+
+ BaseAllocator baseAllocator_;
+};
+
+#if !RAPIDJSON_HAS_CXX17 // std::allocator deprecated in C++17
+template
+class StdAllocator :
+ public std::allocator
+{
+ typedef std::allocator allocator_type;
+
+public:
+ typedef BaseAllocator BaseAllocatorType;
+
+ StdAllocator() RAPIDJSON_NOEXCEPT :
+ allocator_type(),
+ baseAllocator_()
+ { }
+
+ StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT :
+ allocator_type(rhs),
+ baseAllocator_(rhs.baseAllocator_)
+ { }
+
+ template
+ StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT :
+ allocator_type(rhs),
+ baseAllocator_(rhs.baseAllocator_)
+ { }
+
+ /* implicit */
+ StdAllocator(const BaseAllocator& baseAllocator) RAPIDJSON_NOEXCEPT :
+ allocator_type(),
+ baseAllocator_(baseAllocator)
+ { }
+
+ ~StdAllocator() RAPIDJSON_NOEXCEPT
+ { }
+
+ template
+ struct rebind {
+ typedef StdAllocator other;
+ };
+
+ typedef typename allocator_type::value_type value_type;
+
+private:
+ template
+ friend class StdAllocator; // access to StdAllocator.*
+
+ BaseAllocator baseAllocator_;
+};
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_POP
+#endif
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_ENCODINGS_H_
diff --git a/thirdparty/thorvg/src/loaders/lottie/rapidjson/cursorstreamwrapper.h b/thirdparty/thorvg/src/loaders/lottie/rapidjson/cursorstreamwrapper.h
new file mode 100644
index 000000000000..fd6513db14a6
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/lottie/rapidjson/cursorstreamwrapper.h
@@ -0,0 +1,78 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_
+#define RAPIDJSON_CURSORSTREAMWRAPPER_H_
+
+#include "stream.h"
+
+#if defined(__GNUC__)
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(effc++)
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800
+RAPIDJSON_DIAG_PUSH
+RAPIDJSON_DIAG_OFF(4702) // unreachable code
+RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
+#endif
+
+RAPIDJSON_NAMESPACE_BEGIN
+
+
+//! Cursor stream wrapper for counting line and column number if error exists.
+/*!
+ \tparam InputStream Any stream that implements Stream Concept
+*/
+template >
+class CursorStreamWrapper : public GenericStreamWrapper {
+public:
+ typedef typename Encoding::Ch Ch;
+
+ CursorStreamWrapper(InputStream& is):
+ GenericStreamWrapper(is), line_(1), col_(0) {}
+
+ // counting line and column number
+ Ch Take() {
+ Ch ch = this->is_.Take();
+ if(ch == '\n') {
+ line_ ++;
+ col_ = 0;
+ } else {
+ col_ ++;
+ }
+ return ch;
+ }
+
+ //! Get the error line number, if error exists.
+ size_t GetLine() const { return line_; }
+ //! Get the error column number, if error exists.
+ size_t GetColumn() const { return col_; }
+
+private:
+ size_t line_; //!< Current Line
+ size_t col_; //!< Current Column
+};
+
+#if defined(_MSC_VER) && _MSC_VER <= 1800
+RAPIDJSON_DIAG_POP
+#endif
+
+#if defined(__GNUC__)
+RAPIDJSON_DIAG_POP
+#endif
+
+RAPIDJSON_NAMESPACE_END
+
+#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_
diff --git a/thirdparty/thorvg/src/loaders/lottie/rapidjson/document.h b/thirdparty/thorvg/src/loaders/lottie/rapidjson/document.h
new file mode 100644
index 000000000000..2cd9a70a6003
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/lottie/rapidjson/document.h
@@ -0,0 +1,3043 @@
+// Tencent is pleased to support the open source community by making RapidJSON available.
+//
+// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.
+//
+// Licensed under the MIT License (the "License"); you may not use this file except
+// in compliance with the License. You may obtain a copy of the License at
+//
+// http://opensource.org/licenses/MIT
+//
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+#ifndef RAPIDJSON_DOCUMENT_H_
+#define RAPIDJSON_DOCUMENT_H_
+
+/*! \file document.h */
+
+#include "reader.h"
+#include "internal/meta.h"
+#include "internal/strfunc.h"
+#include "memorystream.h"
+#include "encodedstream.h"
+#include // placement new
+#include
+#ifdef __cpp_lib_three_way_comparison
+#include
+#endif
+
+RAPIDJSON_DIAG_PUSH
+#ifdef __clang__
+RAPIDJSON_DIAG_OFF(padded)
+RAPIDJSON_DIAG_OFF(switch-enum)
+RAPIDJSON_DIAG_OFF(c++98-compat)
+#elif defined(_MSC_VER)
+RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
+RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data
+#endif
+
+#ifdef __GNUC__
+RAPIDJSON_DIAG_OFF(effc++)
+#endif // __GNUC__
+
+#ifdef GetObject
+// see https://github.com/Tencent/rapidjson/issues/1448
+// a former included windows.h might have defined a macro called GetObject, which affects
+// GetObject defined here. This ensures the macro does not get applied
+#pragma push_macro("GetObject")
+#define RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED
+#undef GetObject
+#endif
+
+#ifndef RAPIDJSON_NOMEMBERITERATORCLASS
+#include // std::random_access_iterator_tag
+#endif
+
+#if RAPIDJSON_USE_MEMBERSMAP
+#include