diff --git a/src/loaders/lottie/thorvg_lottie.h b/src/loaders/lottie/thorvg_lottie.h index b4607939..7dd3d259 100644 --- a/src/loaders/lottie/thorvg_lottie.h +++ b/src/loaders/lottie/thorvg_lottie.h @@ -22,17 +22,6 @@ class TVG_API LottieAnimation final : public Animation public: ~LottieAnimation() override; - /** - * @brief Override Lottie properties using slot data. - * - * @param[in] slot The Lottie slot data in JSON format to override, or @c nullptr to reset. - * - * @retval Result::InsufficientCondition In case the animation is not loaded. - * - * @since 1.0 - */ - Result override(const char* slot) noexcept; - /** * @brief Specifies a segment by marker. * @@ -110,6 +99,56 @@ public: */ Result assign(const char* layer, uint32_t ix, const char* var, float val); + /** + * @brief Creates a new slot based on the given Lottie slot data. + * + * This function parses the provided JSON-formatted slot data and generates + * a new slot for animation control. The returned slot ID can be used to apply + * or delete the slot later. + * + * @param[in] slot A JSON string representing the Lottie slot data. + * + * @return A unique, non-zero slot ID on success. Returns @c 0 if the slot generation fails. + * + * @see apply(uint32_t id) + * @see del(uint32_t id) + * + * @since 1.0 + */ + uint32_t gen(const char* slot) noexcept; + + /** + * @brief Applies a previously generated slot to the animation. + * + * This function applies the animation parameters defined by a slot. + * If the provided slot ID is 0, all previously applied slots will be reset. + * + * @param[in] id The ID of the slot to apply. Use 0 to reset all slots. + * + * @retval Result::InvalidArguments If the animation is not loaded or the slot ID is invalid. + * + * @see gen(const char* slot) + * + * @since 1.0 + */ + Result apply(uint32_t id) noexcept; + + /** + * @brief Deletes a previously generated slot. + * + * This function removes a slot by its ID. + * + * @param[in] id The ID of the slot to delete. Retrieve the ID from gen(). + * + * @retval Result::InvalidArguments If the animation is not loaded or the slot ID is invalid. + * + * @note This function should be paired with gen. + * @see gen(const char* slot) + * + * @since 1.0 + */ + Result del(uint32_t id) noexcept; + /** * @brief Creates a new LottieAnimation object. * diff --git a/src/loaders/lottie/tvgLottieAnimation.cpp b/src/loaders/lottie/tvgLottieAnimation.cpp index adaa1c01..e0804dc5 100644 --- a/src/loaders/lottie/tvgLottieAnimation.cpp +++ b/src/loaders/lottie/tvgLottieAnimation.cpp @@ -30,15 +30,39 @@ LottieAnimation::LottieAnimation() = default; LottieAnimation::~LottieAnimation() = default; -Result LottieAnimation::override(const char* slot) noexcept +uint32_t LottieAnimation::gen(const char* slot) noexcept +{ + auto loader = PICTURE(pImpl->picture)->loader; + if (!loader) return 0; + + return static_cast(loader)->gen(slot); +} + + +Result LottieAnimation::apply(uint32_t id) noexcept { auto loader = PICTURE(pImpl->picture)->loader; if (!loader) return Result::InsufficientCondition; - if (static_cast(loader)->override(slot)) { + if (static_cast(loader)->apply(id)) { PAINT(pImpl->picture)->mark(RenderUpdateFlag::All); return Result::Success; } + + return Result::InvalidArguments; +} + + +Result LottieAnimation::del(uint32_t id) noexcept +{ + auto loader = PICTURE(pImpl->picture)->loader; + if (!loader) return Result::InsufficientCondition; + + if (static_cast(loader)->del(id)) { + PAINT(pImpl->picture)->mark(RenderUpdateFlag::All); + return Result::Success; + } + return Result::InvalidArguments; } diff --git a/src/loaders/lottie/tvgLottieLoader.cpp b/src/loaders/lottie/tvgLottieLoader.cpp index be4c9b64..5292dda8 100644 --- a/src/loaders/lottie/tvgLottieLoader.cpp +++ b/src/loaders/lottie/tvgLottieLoader.cpp @@ -25,6 +25,7 @@ #include "tvgLottieModel.h" #include "tvgLottieParser.h" #include "tvgLottieBuilder.h" +#include "tvgCompressor.h" /************************************************************************/ /* Internal Class Implementation */ @@ -44,7 +45,9 @@ void LottieLoader::run(unsigned tid) comp = parser.comp; } if (parser.slots) { - override(parser.slots, true); + auto slotcode = gen(parser.slots, true); + apply(slotcode, true); + del(slotcode, true); parser.slots = nullptr; } builder->build(comp); @@ -288,45 +291,98 @@ Paint* LottieLoader::paint() } -bool LottieLoader::override(const char* slots, bool byDefault) +bool LottieLoader::apply(uint32_t slotcode, bool byDefault) { if (!ready() || comp->slots.count == 0) return false; - //override slots - if (slots) { - //Copy the input data because the JSON parser will encode the data immediately. - auto temp = byDefault ? slots : duplicate(slots); - - //parsing slot json - LottieParser parser(temp, dirName, builder->expressions()); - parser.comp = comp; - - auto idx = 0; - auto succeed = false; - while (auto sid = parser.sid(idx == 0)) { - auto applied = false; - ARRAY_FOREACH(p, comp->slots) { - if (strcmp((*p)->sid, sid)) continue; - if (parser.apply(*p, byDefault)) succeed = applied = true; - break; - } - if (!applied) parser.skip(); - ++idx; + bool applied = false; + ARRAY_FOREACH(p, comp->slots) { + // Reset all slots if slotcode is 0 + if (slotcode == 0) { + (*p)->reset(); + applied = true; + continue; + } + + INLIST_FOREACH((*p)->values, q) { + if (q->slotcode != slotcode) continue; + (*p)->apply(q->prop, byDefault); + applied = true; + break; } - tvg::free((char*)temp); - rebuild = succeed; - overridden |= succeed; - return rebuild; - //reset slots - } else if (overridden) { - ARRAY_FOREACH(p, comp->slots) (*p)->reset(); - overridden = false; - rebuild = true; } + + if (!applied) return false; + + overridden = slotcode != 0; + rebuild = true; return true; } +bool LottieLoader::del(uint32_t slotcode, bool byDefault) +{ + if (!ready() || comp->slots.count == 0 || slotcode == 0) return false; + + // Search matching value and remove + ARRAY_FOREACH(p, comp->slots) { + INLIST_FOREACH((*p)->values, q) { + if (q->slotcode != slotcode) continue; + if (!byDefault && (*p)->overridden) { + (*p)->reset(); + rebuild = true; + } + (*p)->values.remove(q); + delete(q); + break; + } + if ((*p)->values.empty()) (*p)->overridden = false; + } + + return true; +} + + +uint32_t LottieLoader::gen(const char* slots, bool byDefault) +{ + if (!slots || !ready() || comp->slots.count == 0) return 0; + + auto slotcode = djb2Encode(slots); + ARRAY_FOREACH(p, comp->slots) { + INLIST_FOREACH((*p)->values, q) { + if (q->slotcode == slotcode) return slotcode; + } + } + + auto temp = byDefault ? slots : duplicate(slots); + + //parsing slot json + LottieParser parser(temp, dirName, builder->expressions()); + parser.comp = comp; + + auto idx = 0; + bool generated = false; + while (auto sid = parser.sid(idx == 0)) { + ARRAY_FOREACH(p, comp->slots) { + if (strcmp((*p)->sid, sid)) continue; + auto prop = parser.parseSlot(*p); + + if (prop) { + (*p)->add(slotcode, prop); + generated = true; + } + break; + } + ++idx; + } + + tvg::free((char*)temp); + + if (!generated) return 0; + return slotcode; +} + + float LottieLoader::shorten(float frameNo) { //This ensures that the target frame number is reached. diff --git a/src/loaders/lottie/tvgLottieLoader.h b/src/loaders/lottie/tvgLottieLoader.h index 1a090c7b..512b17ab 100644 --- a/src/loaders/lottie/tvgLottieLoader.h +++ b/src/loaders/lottie/tvgLottieLoader.h @@ -56,7 +56,11 @@ public: bool resize(Paint* paint, float w, float h) override; bool read() override; Paint* paint() override; - bool override(const char* slot, bool byDefault = false); + + //Slot APIs + uint32_t gen(const char* slot, bool byDefault = false); + bool apply(uint32_t slotcode, bool byDefault = false); + bool del(uint32_t slotcode, bool byDefault = false); //Frame Controls bool frame(float no) override; diff --git a/src/loaders/lottie/tvgLottieModel.cpp b/src/loaders/lottie/tvgLottieModel.cpp index 045c7376..52af2254 100644 --- a/src/loaders/lottie/tvgLottieModel.cpp +++ b/src/loaders/lottie/tvgLottieModel.cpp @@ -203,7 +203,52 @@ void LottieSlot::reset() } -void LottieSlot::assign(LottieObject* target, bool byDefault) +LottieProperty* LottieSlot::property(LottieObject* target) +{ + LottieProperty* prop = nullptr; + //apply slot object to all targets + ARRAY_FOREACH(pair, pairs) { + //backup the original properties before overwriting + switch (type) { + case LottieProperty::Type::Vector: { + return new LottieVector(static_cast(target)->position); + } + case LottieProperty::Type::Scalar: { + return new LottieScalar(static_cast(target)->scale); + } + case LottieProperty::Type::Float: { + return new LottieFloat(static_cast(target)->rotation); + } + case LottieProperty::Type::Opacity: { + return new LottieOpacity(static_cast(target)->opacity); + } + case LottieProperty::Type::Color: { + return new LottieColor(static_cast(target)->color); + } + case LottieProperty::Type::ColorStop: { + return new LottieColorStop(static_cast(target)->colorStops); + } + case LottieProperty::Type::TextDoc: { + return new LottieTextDoc(static_cast(target)->doc); + } + case LottieProperty::Type::Image: { + return new LottieBitmap(static_cast(target)->data); + } + default: break; + } + } + + return prop; +} + + +void LottieSlot::add(uint32_t slotcode, LottieProperty* prop) +{ + values.back(new Value{nullptr, nullptr, slotcode, prop}); +} + + +void LottieSlot::apply(LottieProperty* prop, bool byDefault) { auto copy = !overridden && !byDefault; auto shallow = pairs.count == 1 ? true : false; @@ -214,22 +259,22 @@ void LottieSlot::assign(LottieObject* target, bool byDefault) switch (type) { case LottieProperty::Type::Float: { if (copy) pair->prop = new LottieFloat(static_cast(pair->obj)->rotation); - pair->obj->override(&static_cast(target)->rotation, shallow, !copy); + pair->obj->override(prop, shallow, !copy); break; } case LottieProperty::Type::Scalar: { if (copy) pair->prop = new LottieScalar(static_cast(pair->obj)->scale); - pair->obj->override(&static_cast(target)->scale, shallow, !copy); + pair->obj->override(prop, shallow, !copy); break; } case LottieProperty::Type::Vector: { if (copy) pair->prop = new LottieVector(static_cast(pair->obj)->position); - pair->obj->override(&static_cast(target)->position, shallow, !copy); + pair->obj->override(prop, shallow, !copy); break; } case LottieProperty::Type::Color: { if (copy) pair->prop = new LottieColor(static_cast(pair->obj)->color); - pair->obj->override(&static_cast(target)->color, shallow, !copy); + pair->obj->override(prop, shallow, !copy); break; } case LottieProperty::Type::Opacity: { @@ -237,27 +282,28 @@ void LottieSlot::assign(LottieObject* target, bool byDefault) if (pair->obj->type == LottieObject::Type::Transform) pair->prop = new LottieOpacity(static_cast(pair->obj)->opacity); else pair->prop = new LottieOpacity(static_cast(pair->obj)->opacity); } - pair->obj->override(&static_cast(target)->opacity, shallow, !copy); + pair->obj->override(prop, shallow, !copy); break; } case LottieProperty::Type::ColorStop: { if (copy) pair->prop = new LottieColorStop(static_cast(pair->obj)->colorStops); - pair->obj->override(&static_cast(target)->colorStops, shallow, !copy); + pair->obj->override(prop, shallow, !copy); break; } case LottieProperty::Type::TextDoc: { if (copy) pair->prop = new LottieTextDoc(static_cast(pair->obj)->doc); - pair->obj->override(&static_cast(target)->doc, shallow, !copy); + pair->obj->override(prop, shallow, !copy); break; } case LottieProperty::Type::Image: { if (copy) pair->prop = new LottieBitmap(static_cast(pair->obj)->data); - pair->obj->override(&static_cast(target)->data, shallow, !copy); + pair->obj->override(prop, shallow, !copy); break; } default: break; } } + if (!byDefault) overridden = true; } diff --git a/src/loaders/lottie/tvgLottieModel.h b/src/loaders/lottie/tvgLottieModel.h index 2129b7c0..893f4baa 100644 --- a/src/loaders/lottie/tvgLottieModel.h +++ b/src/loaders/lottie/tvgLottieModel.h @@ -26,6 +26,7 @@ #include "tvgCommon.h" #include "tvgStr.h" #include "tvgCompressor.h" +#include "tvgInlist.h" #include "tvgRender.h" #include "tvgLottieProperty.h" #include "tvgLottieRenderPooler.h" @@ -1058,7 +1059,21 @@ struct LottieSlot LottieProperty* prop; }; - void assign(LottieObject* target, bool byDefault); + struct Value { + INLIST_ITEM(Value); + + uint32_t slotcode; + LottieProperty* prop; + + ~Value() + { + delete(prop); + } + }; + + LottieProperty* property(LottieObject* target); + void add(uint32_t slotcode, LottieProperty* prop); + void apply(LottieProperty* prop, bool byDefault = false); void reset(); LottieSlot(LottieLayer* layer, LottieObject* parent, char* sid, LottieObject* obj, LottieProperty::Type type) : context{layer, parent}, sid(sid), type(type) @@ -1069,6 +1084,7 @@ struct LottieSlot ~LottieSlot() { tvg::free(sid); + values.free(); if (!overridden) return; ARRAY_FOREACH(pair, pairs) delete(pair->prop); } @@ -1079,7 +1095,8 @@ struct LottieSlot } context; char* sid; - Array pairs; + Array pairs; // Object-property pairs that can be overridden by this slot + Inlist values; // Available slot values for property overrides LottieProperty::Type type; bool overridden = false; diff --git a/src/loaders/lottie/tvgLottieParser.cpp b/src/loaders/lottie/tvgLottieParser.cpp index c34e0140..1e649d3d 100644 --- a/src/loaders/lottie/tvgLottieParser.cpp +++ b/src/loaders/lottie/tvgLottieParser.cpp @@ -1539,7 +1539,7 @@ const char* LottieParser::sid(bool first) } -bool LottieParser::apply(LottieSlot* slot, bool byDefault) +LottieProperty* LottieParser::parseSlot(LottieSlot* slot) { enterObject(); @@ -1598,14 +1598,13 @@ bool LottieParser::apply(LottieSlot* slot, bool byDefault) if (!obj || Invalid()) { delete(obj); - return false; + return nullptr; } - slot->assign(obj, byDefault); - + auto prop = slot->property(obj); delete(obj); - return true; + return prop; } diff --git a/src/loaders/lottie/tvgLottieParser.h b/src/loaders/lottie/tvgLottieParser.h index f95b5fa7..6d91632b 100644 --- a/src/loaders/lottie/tvgLottieParser.h +++ b/src/loaders/lottie/tvgLottieParser.h @@ -37,8 +37,8 @@ public: } bool parse(); - bool apply(LottieSlot* slot, bool byDefault); const char* sid(bool first = false); + LottieProperty* parseSlot(LottieSlot* slot); void captureSlots(const char* key); void registerSlot(LottieObject* obj, const char* sid, LottieProperty::Type type);