diff --git a/examples/LottieExtension.cpp b/examples/LottieExtension.cpp index e569aa8f..c3d1c0d6 100644 --- a/examples/LottieExtension.cpp +++ b/examples/LottieExtension.cpp @@ -41,6 +41,7 @@ struct UserExample : tvgexam::Example unique_ptr slot9; unique_ptr slot10; unique_ptr marker; + uint32_t slotId1, slotId2, slotId3, slotId4, slotId6, slotId7, slotId8, slotId9, slotId10; uint32_t w, h; uint32_t size; @@ -173,7 +174,8 @@ struct UserExample : tvgexam::Example if (!tvgexam::verify(picture->load(EXAMPLE_DIR"/lottie/extensions/slotsample1.json"))) return false; const char* slotJson = R"({"gradient_fill":{"p":{"p":2,"k":{"k":[0,0.1,0.1,0.2,1,1,0.1,0.2,0,0,1,1]}}}})"; - if (!tvgexam::verify(slot1->override(slotJson))) return false; + slotId1 = slot1->gen(slotJson); + if (!tvgexam::verify(slot1->apply(slotId1))) return false; sizing(picture, 1); @@ -187,7 +189,8 @@ struct UserExample : tvgexam::Example if (!tvgexam::verify(picture->load(EXAMPLE_DIR"/lottie/extensions/slotsample2.json"))) return false; const char* slotJson = R"({"ball_color":{"p":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":7,"s":[0,0.176,0.867]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":22,"s":[0.867,0,0.533]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":37,"s":[0.867,0,0.533]},{"t":51,"s":[0,0.867,0.255]}]}}})"; - if (!tvgexam::verify(slot2->override(slotJson))) return false; + slotId2 = slot2->gen(slotJson); + if (!tvgexam::verify(slot2->apply(slotId2))) return false; sizing(picture, 2); @@ -201,7 +204,8 @@ struct UserExample : tvgexam::Example if (!tvgexam::verify(picture->load(EXAMPLE_DIR"/lottie/extensions/slotsample3.json"))) return false; const char* slotJson = R"({"path_img":{"p":{"id":"image_0","w":200,"h":300,"u":"images/","p":"logo.png","e":0}}})"; - if (!tvgexam::verify(slot3->override(slotJson))) return false; + slotId3 = slot3->gen(slotJson); + if (!tvgexam::verify(slot3->apply(slotId3))) return false; sizing(picture, 3); @@ -215,7 +219,8 @@ struct UserExample : tvgexam::Example if (!tvgexam::verify(picture->load(EXAMPLE_DIR"/lottie/extensions/slotsample4.json"))) return false; const char* slotJson = R"({"bg_color":{"p":{"a":0,"k":[1,0.8196,0.2275]}},"check_color":{"p":{"a":0,"k":[0.0078,0.0078,0.0078]}}})"; - if (!tvgexam::verify(slot4->override(slotJson))) return false; + slotId4 = slot4->gen(slotJson); + if (!tvgexam::verify(slot4->apply(slotId4))) return false; sizing(picture, 4); @@ -240,7 +245,8 @@ struct UserExample : tvgexam::Example if (!tvgexam::verify(picture->load(EXAMPLE_DIR"/lottie/extensions/slotsample6.json"))) return false; const char* slotJson = R"({"position_id":{"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"s":[100,100],"t":0},{"s":[200,300],"t":100}]}}})"; - if (!tvgexam::verify(slot6->override(slotJson))) return false; + slotId6 = slot6->gen(slotJson); + if (!tvgexam::verify(slot6->apply(slotId6))) return false; sizing(picture, 6); @@ -254,7 +260,8 @@ struct UserExample : tvgexam::Example if (!tvgexam::verify(picture->load(EXAMPLE_DIR"/lottie/extensions/slotsample7.json"))) return false; const char* slotJson = R"({"scale_id":{"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"s":[0,0],"t":0},{"s":[100,100],"t":100}]}}})"; - if (!tvgexam::verify(slot7->override(slotJson))) return false; + slotId7 = slot7->gen(slotJson); + if (!tvgexam::verify(slot7->apply(slotId7))) return false; sizing(picture, 7); @@ -268,7 +275,8 @@ struct UserExample : tvgexam::Example if (!tvgexam::verify(picture->load(EXAMPLE_DIR"/lottie/extensions/slotsample8.json"))) return false; const char* slotJson = R"({"rotation_id":{"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"s":[0],"t":0},{"s":[180],"t":100}]}}})"; - if (!tvgexam::verify(slot8->override(slotJson))) return false; + slotId8 = slot8->gen(slotJson); + if (!tvgexam::verify(slot8->apply(slotId8))) return false; sizing(picture, 8); @@ -282,7 +290,8 @@ struct UserExample : tvgexam::Example if (!tvgexam::verify(picture->load(EXAMPLE_DIR"/lottie/extensions/slotsample9.json"))) return false; const char* slotJson = R"({"opacity_id":{"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"s":[0],"t":0},{"s":[100],"t":100}]}}})"; - if (!tvgexam::verify(slot9->override(slotJson))) return false; + slotId9 = slot9->gen(slotJson); + if (!tvgexam::verify(slot9->apply(slotId9))) return false; sizing(picture, 9); @@ -296,7 +305,8 @@ struct UserExample : tvgexam::Example if (!tvgexam::verify(picture->load(EXAMPLE_DIR"/lottie/extensions/slotsample10.json"))) return false; const char* slotJson = R"({"rect_rotation":{"p":{"x":"var $bm_rt = time * 360;"}},"rect_scale":{"p":{"x":"var $bm_rt = [];$bm_rt[0] = value[0] + Math.cos(2 * Math.PI * time) * 100;$bm_rt[1] = value[1];"}},"rect_position":{"p":{"x":"var $bm_rt = [];$bm_rt[0] = value[0] + Math.cos(2 * Math.PI * time) * 100;$bm_rt[1] = value[1];"}}})"; - if (!tvgexam::verify(slot10->override(slotJson))) return false; + slotId10 = slot10->gen(slotJson); + if (!tvgexam::verify(slot10->apply(slotId10))) return false; sizing(picture, 10); @@ -317,6 +327,19 @@ struct UserExample : tvgexam::Example return true; } + + ~UserExample() + { + slot1->del(slotId1); + slot2->del(slotId2); + slot3->del(slotId3); + slot4->del(slotId4); + slot6->del(slotId6); + slot7->del(slotId7); + slot8->del(slotId8); + slot9->del(slotId9); + slot10->del(slotId10); + } }; diff --git a/src/bindings/capi/thorvg_capi.h b/src/bindings/capi/thorvg_capi.h index 57b4b4fd..bf02d3f5 100644 --- a/src/bindings/capi/thorvg_capi.h +++ b/src/bindings/capi/thorvg_capi.h @@ -2623,19 +2623,49 @@ TVG_API Tvg_Animation* tvg_lottie_animation_new(void); /*! -* @brief Override the lottie properties through the slot data. +* @brief Generates a new slot from the given slot data. * -* @param[in] animation The Tvg_Animation object to override the property with the slot. -* @param[in] slot The Lottie slot data in json, or @c nullptr to reset. +* @param[in] animation The Tvg_Animation pointer to the Lottie animation object. +* @param[in] slot The Lottie slot data in JSON format. +* +* @return The generated slot ID when successful, 0 otherwise. +* +* @since 1.0 +*/ +TVG_API uint32_t tvg_lottie_animation_gen_slot(Tvg_Animation* animation, const char* slot); + + +/*! +* @brief Applies a previously generated slot to the animation. +* +* @param[in] animation The Tvg_Animation pointer to the Lottie animation object. +* @param[in] id The ID of the slot to apply, or 0 to reset all slots. * * @return Tvg_Result enumeration. -* @retval TVG_RESULT_INSUFFICIENT_CONDITION In case the animation is not loaded. -* @retval TVG_RESULT_INVALID_ARGUMENT When the given @p slot is invalid +* @retval TVG_RESULT_INSUFFICIENT_CONDITION In case the animation is not loaded or the slot ID is invalid. * @retval TVG_RESULT_NOT_SUPPORTED The Lottie Animation is not supported. * * @since 1.0 */ -TVG_API Tvg_Result tvg_lottie_animation_override(Tvg_Animation* animation, const char* slot); +TVG_API Tvg_Result tvg_lottie_animation_apply_slot(Tvg_Animation* animation, uint32_t id); + + +/*! +* @brief Deletes a previously generated slot. +* +* @param[in] animation The Tvg_Animation pointer to the Lottie animation object. +* @param[in] id The ID of the slot to delete. +* +* @return Tvg_Result enumeration. +* @retval TVG_RESULT_INSUFFICIENT_CONDITION In case the animation is not loaded or the slot ID is invalid. +* @retval TVG_RESULT_NOT_SUPPORTED The Lottie Animation is not supported. +* +* @note This function should be paired with gen. +* @see tvg_lottie_animation_gen_slot() +* @since 1.0 +*/ +TVG_API Tvg_Result tvg_lottie_animation_del_slot(Tvg_Animation* animation, uint32_t id); + /*! diff --git a/src/bindings/capi/tvgCapi.cpp b/src/bindings/capi/tvgCapi.cpp index 552534b8..34955345 100644 --- a/src/bindings/capi/tvgCapi.cpp +++ b/src/bindings/capi/tvgCapi.cpp @@ -1035,10 +1035,29 @@ TVG_API Tvg_Animation* tvg_lottie_animation_new() } -TVG_API Tvg_Result tvg_lottie_animation_override(Tvg_Animation* animation, const char* slot) +TVG_API uint32_t tvg_lottie_animation_gen_slot(Tvg_Animation* animation, const char* slot) { #ifdef THORVG_LOTTIE_LOADER_SUPPORT - if (animation) return (Tvg_Result) reinterpret_cast(animation)->override(slot); + if (animation) return reinterpret_cast(animation)->gen(slot); +#endif + return 0; +} + + +TVG_API Tvg_Result tvg_lottie_animation_apply_slot(Tvg_Animation* animation, uint32_t id) +{ +#ifdef THORVG_LOTTIE_LOADER_SUPPORT + if (animation) return (Tvg_Result) reinterpret_cast(animation)->apply(id); + return TVG_RESULT_INVALID_ARGUMENT; +#endif + return TVG_RESULT_NOT_SUPPORTED; +} + + +TVG_API Tvg_Result tvg_lottie_animation_del_slot(Tvg_Animation* animation, uint32_t id) +{ +#ifdef THORVG_LOTTIE_LOADER_SUPPORT + if (animation) return (Tvg_Result) reinterpret_cast(animation)->del(id); return TVG_RESULT_INVALID_ARGUMENT; #endif return TVG_RESULT_NOT_SUPPORTED; 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); diff --git a/test/testLottie.cpp b/test/testLottie.cpp index 54d592b9..b78ba654 100644 --- a/test/testLottie.cpp +++ b/test/testLottie.cpp @@ -46,26 +46,33 @@ TEST_CASE("Lottie Slot", "[tvgLottie]") const char* slotJson = R"({"gradient_fill":{"p":{"p":2,"k":{"a":0,"k":[0,0.1,0.1,0.2,1,1,0.1,0.2,0.1,1]}}}})"; - //Slot override before loaded - REQUIRE(animation->override(slotJson) == Result::InsufficientCondition); + //Slot generation before loaded + REQUIRE(animation->gen(slotJson) == 0); //Animation load REQUIRE(picture->load(TEST_DIR"/lottieslot.json") == Result::Success); + //Slot generation + auto id = animation->gen(slotJson); + REQUIRE(id > 0); + //Slot revert before overriding - REQUIRE(animation->override(nullptr) == Result::Success); + REQUIRE(animation->apply(0) == Result::Success); //Slot override - REQUIRE(animation->override(slotJson) == Result::Success); + REQUIRE(animation->apply(id) == Result::Success); //Slot revert - REQUIRE(animation->override(nullptr) == Result::Success); + REQUIRE(animation->apply(0) == Result::Success); //Slot override after reverting - REQUIRE(animation->override(slotJson) == Result::Success); + REQUIRE(animation->apply(id) == Result::Success); - //Slot override with invalid JSON - REQUIRE(animation->override("") == Result::InvalidArguments); + //Slot generation with invalid JSON + REQUIRE(animation->gen("") == 0); + + //Slot deletion + REQUIRE(animation->del(id) == Result::Success); REQUIRE(Initializer::term() == Result::Success); } @@ -84,14 +91,21 @@ TEST_CASE("Lottie Slot 2", "[tvgLottie]") //Animation load REQUIRE(picture->load(TEST_DIR"/lottieslotkeyframe.json") == Result::Success); + //Slot generation + auto id = animation->gen(slotJson); + REQUIRE(id > 0); + //Slot override - REQUIRE(animation->override(slotJson) == Result::Success); + REQUIRE(animation->apply(id) == Result::Success); //Slot revert - REQUIRE(animation->override(nullptr) == Result::Success); + REQUIRE(animation->apply(0) == Result::Success); //Slot override after reverting - REQUIRE(animation->override(slotJson) == Result::Success); + REQUIRE(animation->apply(id) == Result::Success); + + //Slot deletion + REQUIRE(animation->del(id) == Result::Success); REQUIRE(Initializer::term() == Result::Success); }