From b3f09cab6b6c3e016d1b43dd474d658ac5c0056f Mon Sep 17 00:00:00 2001 From: Jinny You Date: Thu, 21 Mar 2024 15:11:43 +0900 Subject: [PATCH] lottie: Support the slot reverting feature Implemented the ability to revert Lottie slots by calling override with nullptr. This functionality allows for the complete reversal of applied slots. usage: - `animation->override(nullptr)` Co-Authored-By: Hermet Park --- src/bindings/capi/thorvg_capi.h | 2 +- src/loaders/lottie/thorvg_lottie.h | 2 +- src/loaders/lottie/tvgLottieLoader.cpp | 47 ++++++++++------ src/loaders/lottie/tvgLottieLoader.h | 1 + src/loaders/lottie/tvgLottieModel.h | 78 ++++++++++++++++++++++++-- src/loaders/lottie/tvgLottieParser.cpp | 10 ++-- src/loaders/lottie/tvgLottieParser.h | 2 +- src/loaders/lottie/tvgLottieProperty.h | 4 +- 8 files changed, 112 insertions(+), 34 deletions(-) diff --git a/src/bindings/capi/thorvg_capi.h b/src/bindings/capi/thorvg_capi.h index 3f3727df..291dae6c 100644 --- a/src/bindings/capi/thorvg_capi.h +++ b/src/bindings/capi/thorvg_capi.h @@ -2357,7 +2357,7 @@ TVG_API Tvg_Animation* tvg_lottie_animation_new(); * \brief Override the lottie properties through the slot data. (Experimental API) * * \param[in] animation The Tvg_Animation object to override the property with the slot. -* \param[in] slot The lottie slot data in json. +* \param[in] slot The Lottie slot data in json, or @c nullptr to reset. * * \return Tvg_Animation A new Tvg_LottieAnimation object. * \retval TVG_RESULT_SUCCESS Succeed. diff --git a/src/loaders/lottie/thorvg_lottie.h b/src/loaders/lottie/thorvg_lottie.h index c0bc8e9c..7f59dfb1 100644 --- a/src/loaders/lottie/thorvg_lottie.h +++ b/src/loaders/lottie/thorvg_lottie.h @@ -25,7 +25,7 @@ public: /** * @brief Override Lottie properties using slot data. * - * @param[in] slot The Lottie slot data in JSON format. + * @param[in] slot The Lottie slot data in JSON format to override, or @c nullptr to reset. * * @retval Result::Success When succeed. * @retval Result::InsufficientCondition In case the animation is not loaded. diff --git a/src/loaders/lottie/tvgLottieLoader.cpp b/src/loaders/lottie/tvgLottieLoader.cpp index 14b0ec5a..de245d61 100644 --- a/src/loaders/lottie/tvgLottieLoader.cpp +++ b/src/loaders/lottie/tvgLottieLoader.cpp @@ -302,28 +302,39 @@ Paint* LottieLoader::paint() bool LottieLoader::override(const char* slot) { - if (!slot || !comp || comp->slots.count == 0) return false; + if (!comp || comp->slots.count == 0) return false; - //TODO: Crashed, does this necessary? - auto temp = strdup(slot); - - //parsing slot json - LottieParser parser(temp, dirName); - - auto idx = 0; auto success = true; - while (auto sid = parser.sid(idx == 0)) { - for (auto s = comp->slots.begin(); s < comp->slots.end(); ++s) { - if (strcmp((*s)->sid, sid)) continue; - if (!parser.parse(*s)) success = false; - break; + + //override slots + if (slot) { + //TODO: Crashed, does this necessary? + auto temp = strdup(slot); + + //parsing slot json + LottieParser parser(temp, dirName); + + auto idx = 0; + while (auto sid = parser.sid(idx == 0)) { + for (auto s = comp->slots.begin(); s < comp->slots.end(); ++s) { + if (strcmp((*s)->sid, sid)) continue; + if (!parser.apply(*s)) success = false; + break; + } + ++idx; } - ++idx; + + if (idx < 1) success = false; + free(temp); + overriden = success; + + //reset slots + } else if (overriden) { + for (auto s = comp->slots.begin(); s < comp->slots.end(); ++s) { + (*s)->reset(); + } + overriden = false; } - - if (idx < 1) success = false; - - free(temp); return success; } diff --git a/src/loaders/lottie/tvgLottieLoader.h b/src/loaders/lottie/tvgLottieLoader.h index 94385a61..f28573b2 100644 --- a/src/loaders/lottie/tvgLottieLoader.h +++ b/src/loaders/lottie/tvgLottieLoader.h @@ -44,6 +44,7 @@ public: char* dirName = nullptr; //base resource directory bool copy = false; //"content" is owned by this loader + bool overriden = false; //overridden properties with slots. LottieLoader(); ~LottieLoader(); diff --git a/src/loaders/lottie/tvgLottieModel.h b/src/loaders/lottie/tvgLottieModel.h index 1b194fb9..d9b1f5de 100644 --- a/src/loaders/lottie/tvgLottieModel.h +++ b/src/loaders/lottie/tvgLottieModel.h @@ -632,19 +632,89 @@ struct LottieLayer : LottieGroup struct LottieSlot { - char* sid; - Array objs; - LottieProperty::Type type; + struct Pair { + LottieObject* obj; + LottieProperty* prop; + }; + + void assign(LottieObject* target) + { + //apply slot object to all targets + for (auto pair = pairs.begin(); pair < pairs.end(); ++pair) { + //backup the original properties before overwriting + if (!overriden) { + switch (type) { + case LottieProperty::Type::ColorStop: { + pair->prop = new LottieColorStop; + *static_cast(pair->prop) = static_cast(pair->obj)->colorStops; + break; + } + case LottieProperty::Type::Color: { + pair->prop = new LottieColor; + *static_cast(pair->prop) = static_cast(pair->obj)->color; + break; + } + case LottieProperty::Type::TextDoc: { + pair->prop = new LottieTextDoc; + *static_cast(pair->prop) = static_cast(pair->obj)->doc; + break; + } + default: break; + } + } + //FIXME: it overrides the object's whole properties, but it acutally needs a single property of it. + pair->obj->override(target); + } + overriden = true; + } + + void reset() + { + if (!overriden) return; + + for (auto pair = pairs.begin(); pair < pairs.end(); ++pair) { + switch (type) { + case LottieProperty::Type::ColorStop: { + static_cast(pair->obj)->colorStops.release(); + static_cast(pair->obj)->colorStops = *static_cast(pair->prop); + break; + } + case LottieProperty::Type::Color: { + static_cast(pair->obj)->color.release(); + static_cast(pair->obj)->color = *static_cast(pair->prop); + break; + } + case LottieProperty::Type::TextDoc: { + static_cast(pair->obj)->doc.release(); + static_cast(pair->obj)->doc = *static_cast(pair->prop); + break; + } + default: break; + } + delete(pair->prop); + pair->prop = nullptr; + } + overriden = false; + } LottieSlot(char* sid, LottieObject* obj, LottieProperty::Type type) : sid(sid), type(type) { - objs.push(obj); + pairs.push({obj}); } ~LottieSlot() { free(sid); + if (!overriden) return; + for (auto pair = pairs.begin(); pair < pairs.end(); ++pair) { + delete(pair->prop); + } } + + char* sid; + Array pairs; + LottieProperty::Type type; + bool overriden = false; }; diff --git a/src/loaders/lottie/tvgLottieParser.cpp b/src/loaders/lottie/tvgLottieParser.cpp index b266ec3b..c9bdda6c 100644 --- a/src/loaders/lottie/tvgLottieParser.cpp +++ b/src/loaders/lottie/tvgLottieParser.cpp @@ -478,7 +478,7 @@ void LottieParser::parseProperty(T& prop, LottieObject* obj) //append object if the slot already exists. for (auto slot = comp->slots.begin(); slot < comp->slots.end(); ++slot) { if (strcmp((*slot)->sid, sid)) continue; - (*slot)->objs.push(obj); + (*slot)->pairs.push({obj}); return; } comp->slots.push(new LottieSlot(sid, obj, type)); @@ -1250,10 +1250,11 @@ const char* LottieParser::sid(bool first) } -bool LottieParser::parse(LottieSlot* slot) +bool LottieParser::apply(LottieSlot* slot) { enterObject(); + //OPTIMIZE: we can create the property directly, without object LottieObject* obj = nullptr; //slot object switch (slot->type) { @@ -1278,10 +1279,7 @@ bool LottieParser::parse(LottieSlot* slot) if (!obj || Invalid()) return false; - //apply slot object to all targets - for (auto target = slot->objs.begin(); target < slot->objs.end(); ++target) { - (*target)->override(obj); - } + slot->assign(obj); delete(obj); diff --git a/src/loaders/lottie/tvgLottieParser.h b/src/loaders/lottie/tvgLottieParser.h index 49367f97..9e218107 100644 --- a/src/loaders/lottie/tvgLottieParser.h +++ b/src/loaders/lottie/tvgLottieParser.h @@ -36,7 +36,7 @@ public: } bool parse(); - bool parse(LottieSlot* slot); + bool apply(LottieSlot* slot); const char* sid(bool first = false); LottieComposition* comp = nullptr; diff --git a/src/loaders/lottie/tvgLottieProperty.h b/src/loaders/lottie/tvgLottieProperty.h index 66fe2ab2..37cab0ae 100644 --- a/src/loaders/lottie/tvgLottieProperty.h +++ b/src/loaders/lottie/tvgLottieProperty.h @@ -204,6 +204,7 @@ uint32_t bsearch(T* frames, float frameNo) struct LottieProperty { enum class Type : uint8_t { Point = 0, Float, Opacity, Color, PathSet, ColorStop, Position, TextDoc, Invalid }; + virtual ~LottieProperty() {} }; @@ -258,7 +259,6 @@ struct LottieGenericProperty : LottieProperty T& operator=(const T& other) { //shallow copy, used for slot overriding - delete(frames); if (other.frames) { frames = other.frames; const_cast(other).frames = nullptr; @@ -467,7 +467,6 @@ struct LottieColorStop : LottieProperty LottieColorStop& operator=(const LottieColorStop& other) { //shallow copy, used for slot overriding - release(); if (other.frames) { frames = other.frames; const_cast(other).frames = nullptr; @@ -613,7 +612,6 @@ struct LottieTextDoc : LottieProperty LottieTextDoc& operator=(const LottieTextDoc& other) { //shallow copy, used for slot overriding - release(); if (other.frames) { frames = other.frames; const_cast(other).frames = nullptr;