From 6f065fc6f1cde0e7a942a12e0c50acfb04c87309 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Thu, 17 Apr 2025 16:59:27 +0900 Subject: [PATCH 1/4] lottie/slot: Introduce reusable Slot API Introduce Slot APIs for efficient slot data reuse New APIs: - uint32_t LottieAnimation::genSlot(const char* slot) - Result LottieAnimation::applySlot(uint32_t slotId) - Result LottieAnimation::delSlot(uint32_t slotId) Deprecated API: - Result override(const char* slot) --- src/loaders/lottie/thorvg_lottie.h | 44 +++++-- src/loaders/lottie/tvgLottieAnimation.cpp | 24 +++- src/loaders/lottie/tvgLottieLoader.cpp | 151 +++++++++++++++++----- src/loaders/lottie/tvgLottieLoader.h | 6 +- src/loaders/lottie/tvgLottieModel.cpp | 75 +++++++++-- src/loaders/lottie/tvgLottieModel.h | 29 ++++- src/loaders/lottie/tvgLottieParser.cpp | 9 +- src/loaders/lottie/tvgLottieParser.h | 2 +- 8 files changed, 280 insertions(+), 60 deletions(-) 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); From 89fb7919dac42c9ac595182eea07149004ff2704 Mon Sep 17 00:00:00 2001 From: Jinny You Date: Mon, 17 Mar 2025 13:12:13 +0900 Subject: [PATCH 2/4] examples: update LottieExtension with Slot API --- examples/LottieExtension.cpp | 40 ++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/examples/LottieExtension.cpp b/examples/LottieExtension.cpp index c5b582c0..92bac203 100644 --- a/examples/LottieExtension.cpp +++ b/examples/LottieExtension.cpp @@ -177,7 +177,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; + auto slotId = slot1->genSlot(slotJson); + if (!tvgexam::verify(slot1->applySlot(slotId))) return false; sizing(picture, 1); @@ -191,7 +192,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; + auto slotId = slot2->genSlot(slotJson); + if (!tvgexam::verify(slot2->applySlot(slotId))) return false; sizing(picture, 2); @@ -205,7 +207,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; + auto slotId = slot3->genSlot(slotJson); + if (!tvgexam::verify(slot3->applySlot(slotId))) return false; sizing(picture, 3); @@ -219,7 +222,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; + auto slotId = slot4->genSlot(slotJson); + if (!tvgexam::verify(slot4->applySlot(slotId))) return false; sizing(picture, 4); @@ -244,7 +248,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; + auto slotId = slot6->genSlot(slotJson); + if (!tvgexam::verify(slot6->applySlot(slotId))) return false; sizing(picture, 6); @@ -258,7 +263,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; + auto slotId = slot7->genSlot(slotJson); + if (!tvgexam::verify(slot7->applySlot(slotId))) return false; sizing(picture, 7); @@ -272,7 +278,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; + auto slotId = slot8->genSlot(slotJson); + if (!tvgexam::verify(slot8->applySlot(slotId))) return false; sizing(picture, 8); @@ -286,7 +293,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; + auto slotId = slot9->genSlot(slotJson); + if (!tvgexam::verify(slot9->applySlot(slotId))) return false; sizing(picture, 9); @@ -300,7 +308,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; + auto slotId = slot10->genSlot(slotJson); + if (!tvgexam::verify(slot10->applySlot(slotId))) return false; sizing(picture, 10); @@ -321,6 +330,19 @@ struct UserExample : tvgexam::Example return true; } + + ~UserExample() + { + slot1->delSlot(0); + slot2->delSlot(0); + slot3->delSlot(0); + slot4->delSlot(0); + slot6->delSlot(0); + slot7->delSlot(0); + slot8->delSlot(0); + slot9->delSlot(0); + slot10->delSlot(0); + } }; From 473ec4bb1aa94d555b756d6ffa14226d95c6f76e Mon Sep 17 00:00:00 2001 From: Jinny You Date: Mon, 17 Mar 2025 18:05:38 +0900 Subject: [PATCH 3/4] capi: update with Slot API --- src/bindings/capi/thorvg_capi.h | 40 ++++++++++++++++++++++++++++----- src/bindings/capi/tvgCapi.cpp | 24 ++++++++++++++++++-- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/bindings/capi/thorvg_capi.h b/src/bindings/capi/thorvg_capi.h index 7aca0a36..536dab73 100644 --- a/src/bindings/capi/thorvg_capi.h +++ b/src/bindings/capi/thorvg_capi.h @@ -2537,19 +2537,47 @@ 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, or 0 to delete all slots. +* +* @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. +* +* @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 cd1c7905..54abccb0 100644 --- a/src/bindings/capi/tvgCapi.cpp +++ b/src/bindings/capi/tvgCapi.cpp @@ -944,11 +944,31 @@ 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 0; + return reinterpret_cast(animation)->genSlot(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_INVALID_ARGUMENT; - return (Tvg_Result) reinterpret_cast(animation)->override(slot); + return (Tvg_Result) reinterpret_cast(animation)->applySlot(id); +#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_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(animation)->delSlot(id); #endif return TVG_RESULT_NOT_SUPPORTED; } From 192e0c5627048b7c48e463e87ec95e561d97d462 Mon Sep 17 00:00:00 2001 From: Jinny You Date: Mon, 17 Mar 2025 18:17:52 +0900 Subject: [PATCH 4/4] test: update Lottie slot test code --- test/testLottie.cpp | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/test/testLottie.cpp b/test/testLottie.cpp index 54d592b9..ffeac15a 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->genSlot(slotJson) == 0); //Animation load REQUIRE(picture->load(TEST_DIR"/lottieslot.json") == Result::Success); + //Slot generation + auto slotId = animation->genSlot(slotJson); + REQUIRE(slotId > 0); + //Slot revert before overriding - REQUIRE(animation->override(nullptr) == Result::Success); + REQUIRE(animation->applySlot(0) == Result::Success); //Slot override - REQUIRE(animation->override(slotJson) == Result::Success); + REQUIRE(animation->applySlot(slotId) == Result::Success); //Slot revert - REQUIRE(animation->override(nullptr) == Result::Success); + REQUIRE(animation->applySlot(0) == Result::Success); //Slot override after reverting - REQUIRE(animation->override(slotJson) == Result::Success); + REQUIRE(animation->applySlot(slotId) == Result::Success); - //Slot override with invalid JSON - REQUIRE(animation->override("") == Result::InvalidArguments); + //Slot generation with invalid JSON + REQUIRE(animation->genSlot("") == 0); + + //Slot deletion + REQUIRE(animation->delSlot(slotId) == 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 slotId = animation->genSlot(slotJson); + REQUIRE(slotId > 0); + //Slot override - REQUIRE(animation->override(slotJson) == Result::Success); + REQUIRE(animation->applySlot(slotId) == Result::Success); //Slot revert - REQUIRE(animation->override(nullptr) == Result::Success); + REQUIRE(animation->applySlot(0) == Result::Success); //Slot override after reverting - REQUIRE(animation->override(slotJson) == Result::Success); + REQUIRE(animation->applySlot(slotId) == Result::Success); + + //Slot deletion + REQUIRE(animation->delSlot(slotId) == Result::Success); REQUIRE(Initializer::term() == Result::Success); }