diff --git a/src/loaders/lottie/thorvg_lottie.h b/src/loaders/lottie/thorvg_lottie.h index b4607939..a07290d8 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,39 @@ public: */ Result assign(const char* layer, uint32_t ix, const char* var, float val); + /** + * @brief Generates a new slot from the given slot data. + * + * @param[in] slot The Lottie slot data in JSON format. + * + * @retval The generated slot ID when successful, 0 otherwise. + * + * @since 1.0 + */ + uint32_t genSlot(const char* slot) noexcept; + + /** + * @brief Applies a previously generated slot to the animation. + * + * @param[in] slotId The ID of the slot to apply, or 0 to reset all slots. + * + * @retval Result::InsufficientCondition In case the animation is not loaded or the slot ID is invalid. + * + * @since 1.0 + */ + Result applySlot(uint32_t slotId) noexcept; + + /** + * @brief Deletes a previously generated slot. + * + * @param[in] slotId The ID of the slot to delete, or 0 to delete all slots. + * + * @retval Result::InsufficientCondition In case the animation is not loaded or the slot ID is invalid. + * + * @since 1.0 + */ + Result delSlot(uint32_t slotId) noexcept; + /** * @brief Creates a new LottieAnimation object. * diff --git a/src/loaders/lottie/tvgLottieAnimation.cpp b/src/loaders/lottie/tvgLottieAnimation.cpp index 1ddb3889..8a9f57a3 100644 --- a/src/loaders/lottie/tvgLottieAnimation.cpp +++ b/src/loaders/lottie/tvgLottieAnimation.cpp @@ -30,12 +30,32 @@ LottieAnimation::LottieAnimation() = default; LottieAnimation::~LottieAnimation() = default; -Result LottieAnimation::override(const char* slot) noexcept +uint32_t LottieAnimation::genSlot(const char* slot) noexcept +{ + auto loader = PICTURE(pImpl->picture)->loader; + if (!loader) return 0; + + return static_cast(loader)->genSlot(slot); +} + + +Result LottieAnimation::applySlot(uint32_t slotId) noexcept { auto loader = PICTURE(pImpl->picture)->loader; if (!loader) return Result::InsufficientCondition; - if (static_cast(loader)->override(slot)) return Result::Success; + if (static_cast(loader)->applySlot(slotId)) return Result::Success; + + return Result::InvalidArguments; +} + + +Result LottieAnimation::delSlot(uint32_t slotId) noexcept +{ + auto loader = PICTURE(pImpl->picture)->loader; + if (!loader) return Result::InsufficientCondition; + + if (static_cast(loader)->delSlot(slotId)) return Result::Success; return Result::InvalidArguments; } diff --git a/src/loaders/lottie/tvgLottieLoader.cpp b/src/loaders/lottie/tvgLottieLoader.cpp index f56f61d8..31b6dc65 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 slotId = genSlot(parser.slots); + applySlot(slotId, true); + delSlot(slotId, true); parser.slots = nullptr; } builder->build(comp); @@ -288,45 +291,133 @@ Paint* LottieLoader::paint() } -bool LottieLoader::override(const char* slots, bool byDefault) +bool LottieLoader::applySlot(uint32_t slotId, bool byDefault) { - if (!ready() || comp->slots.count == 0) return false; + if (!ready() || comp->slots.count == 0 || comp->slotDatas.empty()) 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; + if (slotId == 0) { + ARRAY_FOREACH(p, comp->slots) { + (*p)->reset(); } - 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; + return true; } + + LottieSlotData* slotData = nullptr; + + INLIST_FOREACH(comp->slotDatas, p) { + if (p->id == slotId) { + slotData = p; + break; + } + } + + if (!slotData) return false; + + ARRAY_FOREACH(p, slotData->datas) { + ARRAY_FOREACH(q, comp->slots) { + if (strcmp((*q)->sid, p->sid)) continue; + (*q)->apply(p->prop, byDefault); + break; + } + } + + overridden = true; + rebuild = true; return true; } +bool LottieLoader::delSlot(uint32_t slotId, bool byDefault) +{ + if (!ready() || comp->slots.count == 0 || comp->slotDatas.empty()) return false; + + if (slotId == 0) { + if (!byDefault) { + ARRAY_FOREACH(p, comp->slots) { + (*p)->reset(); + } + rebuild = true; + } + + comp->slotDatas.free(); + overridden = false; + return true; + } + + LottieSlotData* slotData = nullptr; + + INLIST_FOREACH(comp->slotDatas, p) { + if (p->id == slotId) { + slotData = p; + break; + } + } + + if (!slotData) return false; + + ARRAY_FOREACH(p, slotData->datas) { + ARRAY_FOREACH(q, comp->slots) { + if (strcmp((*q)->sid, p->sid)) continue; + if (!byDefault && (*q)->overridden) { + (*q)->reset(); + rebuild = true; + } + break; + } + } + + comp->slotDatas.remove(slotData); + delete(slotData); + return true; +} + + +uint32_t LottieLoader::genSlot(const char* slots) +{ + if (!slots || !ready() || comp->slots.count == 0) return 0; + + auto slotId = djb2Encode(slots); + INLIST_FOREACH(comp->slotDatas, p) { + if (p->id == slotId) return slotId; + } + + auto temp = duplicate(slots); + + //parsing slot json + LottieParser parser(temp, dirName, builder->expressions()); + parser.comp = comp; + + auto idx = 0; + bool generated = false; + LottieSlotData* slotData = new LottieSlotData(slotId); + while (auto sid = parser.sid(idx == 0)) { + ARRAY_FOREACH(p, comp->slots) { + if (strcmp((*p)->sid, sid)) continue; + auto prop = parser.slotData(*p); + + if (prop) { + slotData->datas.push({duplicate(sid), prop}); + generated = true; + } + break; + } + ++idx; + } + + tvg::free((char*)temp); + + if (generated) comp->slotDatas.back(slotData); + else { + delete(slotData); + return 0; + } + + return slotData->id; +} + + 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..04a0953a 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 genSlot(const char* slot); + bool applySlot(uint32_t slotId, bool byDefault = false); + bool delSlot(uint32_t slotId, 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 5fb1b89b..5b10b34e 100644 --- a/src/loaders/lottie/tvgLottieModel.cpp +++ b/src/loaders/lottie/tvgLottieModel.cpp @@ -174,7 +174,62 @@ void LottieSlot::reset() } -void LottieSlot::assign(LottieObject* target, bool byDefault) +LottieProperty* LottieSlot::data(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: { + prop = new LottieVector; + static_cast(prop)->copy(static_cast(target)->position, false); + break; + } + case LottieProperty::Type::Scalar: { + prop = new LottieScalar; + static_cast(prop)->copy(static_cast(target)->scale, false); + break; + } + case LottieProperty::Type::Float: { + prop = new LottieFloat; + static_cast(prop)->copy(static_cast(target)->rotation, false); + break; + } + case LottieProperty::Type::Opacity: { + prop = new LottieOpacity; + static_cast(prop)->copy(static_cast(target)->opacity, false); + break; + } + case LottieProperty::Type::Color: { + prop = new LottieColor; + static_cast(prop)->copy(static_cast(target)->color, false); + break; + } + case LottieProperty::Type::ColorStop: { + prop = new LottieColorStop; + static_cast(prop)->copy(static_cast(target)->colorStops, false); + break; + } + case LottieProperty::Type::TextDoc: { + prop = new LottieTextDoc; + static_cast(prop)->copy(static_cast(target)->doc, false); + break; + } + case LottieProperty::Type::Image: { + prop = new LottieBitmap; + static_cast(prop)->copy(static_cast(target)->data, false); + break; + } + default: break; + } + } + + return prop; +} + + +void LottieSlot::apply(LottieProperty* prop, bool byDefault) { auto copy = !overridden && !byDefault; auto shallow = pairs.count == 1 ? true : false; @@ -185,22 +240,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: { @@ -208,27 +263,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; } @@ -704,4 +760,5 @@ LottieComposition::~LottieComposition() ARRAY_FOREACH(p, fonts) delete(*p); ARRAY_FOREACH(p, slots) delete(*p); ARRAY_FOREACH(p, markers) delete(*p); + slotDatas.free(); } diff --git a/src/loaders/lottie/tvgLottieModel.h b/src/loaders/lottie/tvgLottieModel.h index 9769a773..13077a96 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" @@ -1044,6 +1045,30 @@ struct LottieLayer : LottieGroup }; +struct LottieSlotData +{ + INLIST_ITEM(LottieSlotData); + + struct SlotData { + char* sid; + LottieProperty* prop; + }; + + LottieSlotData(uint32_t id) : id(id) {} + + ~LottieSlotData() + { + ARRAY_FOREACH(p, datas) { + delete(p->prop); + tvg::free(p->sid); + } + } + + uint32_t id; + Array datas; +}; + + struct LottieSlot { struct Pair { @@ -1051,7 +1076,8 @@ struct LottieSlot LottieProperty* prop; }; - void assign(LottieObject* target, bool byDefault); + LottieProperty* data(LottieObject* target); + 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) @@ -1131,6 +1157,7 @@ struct LottieComposition Array fonts; Array slots; Array markers; + Inlist slotDatas; bool expressions = false; bool initiated = false; }; diff --git a/src/loaders/lottie/tvgLottieParser.cpp b/src/loaders/lottie/tvgLottieParser.cpp index 04e06f23..7c31d858 100644 --- a/src/loaders/lottie/tvgLottieParser.cpp +++ b/src/loaders/lottie/tvgLottieParser.cpp @@ -1536,7 +1536,7 @@ const char* LottieParser::sid(bool first) } -bool LottieParser::apply(LottieSlot* slot, bool byDefault) +LottieProperty* LottieParser::slotData(LottieSlot* slot) { enterObject(); @@ -1595,14 +1595,13 @@ bool LottieParser::apply(LottieSlot* slot, bool byDefault) if (!obj || Invalid()) { delete(obj); - return false; + return nullptr; } - slot->assign(obj, byDefault); - + auto prop = slot->data(obj); delete(obj); - return true; + return prop; } diff --git a/src/loaders/lottie/tvgLottieParser.h b/src/loaders/lottie/tvgLottieParser.h index 66a96290..04a3bc2d 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* slotData(LottieSlot* slot); void captureSlots(const char* key); void registerSlot(LottieObject* obj, const char* sid, LottieProperty::Type type);