From 5de098f128c79832f56eb6ea1d7252c1a7cd1045 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Fri, 28 Feb 2025 00:36:55 +0900 Subject: [PATCH] lottie: suppport writable expressions This function sets the value of a specified expression variable within a particular layer. It is useful for dynamically changing the properties of a layer at runtime. Experimental API - Result LottieAnimation::assign(const char* layer, uint32_t ix, const char* variable, float value) - Tvg_Result tvg_lottie_animation_assign(Tvg_Animation* animation, const char* layer, uint32_t ix, const char* var, float val) --- src/bindings/capi/thorvg_capi.h | 18 +++++++++++ src/bindings/capi/tvgCapi.cpp | 10 ++++++ src/loaders/lottie/thorvg_lottie.h | 19 +++++++++++ src/loaders/lottie/tvgLottieAnimation.cpp | 12 +++++++ src/loaders/lottie/tvgLottieExpressions.cpp | 16 ++++++++- src/loaders/lottie/tvgLottieExpressions.h | 1 + src/loaders/lottie/tvgLottieLoader.cpp | 9 ++++++ src/loaders/lottie/tvgLottieLoader.h | 1 + src/loaders/lottie/tvgLottieModel.cpp | 36 +++++++++++++++++++++ src/loaders/lottie/tvgLottieModel.h | 3 ++ src/loaders/lottie/tvgLottieParser.cpp | 1 + src/loaders/lottie/tvgLottieProperty.h | 23 +++++++++++++ 12 files changed, 148 insertions(+), 1 deletion(-) diff --git a/src/bindings/capi/thorvg_capi.h b/src/bindings/capi/thorvg_capi.h index e61041e0..fe3e798f 100644 --- a/src/bindings/capi/thorvg_capi.h +++ b/src/bindings/capi/thorvg_capi.h @@ -2569,6 +2569,24 @@ TVG_API Tvg_Result tvg_lottie_animation_get_marker(Tvg_Animation* animation, uin TVG_API Tvg_Result tvg_lottie_animation_tween(Tvg_Animation* animation, float from, float to, float progress); +/*! +* \brief Updates the value of an expression variable for a specific layer. +* +* \param[in] animation The Tvg_Animation pointer to the Lottie animation object. +* \param[in] layer The name of the layer containing the variable to be updated. +* \param[in] ix The property index of the variable within the layer. +* \param[in] var The name of the variable to be updated. +* \param[in] val The new value to assign to the variable. +* +* \return Tvg_Result enumeration. +* \retval TVG_RESULT_INSUFFICIENT_CONDITION If the animation is not loaded. +* \retval TVG_RESULT_INVALID_ARGUMENT When the given parameter is invalid. +* \retval TVG_RESULT_NOT_SUPPORTED When neither the layer nor the property is found in the current animation. +* +* \note Experimental API +*/ +TVG_API Tvg_Result tvg_lottie_animation_assign(Tvg_Animation* animation, const char* layer, uint32_t ix, const char* var, float val); + /** \} */ // end addtogroup ThorVGCapi_LottieAnimation diff --git a/src/bindings/capi/tvgCapi.cpp b/src/bindings/capi/tvgCapi.cpp index b8dd1891..a927c34b 100644 --- a/src/bindings/capi/tvgCapi.cpp +++ b/src/bindings/capi/tvgCapi.cpp @@ -974,6 +974,16 @@ TVG_API Tvg_Result tvg_lottie_animation_tween(Tvg_Animation* animation, float fr return TVG_RESULT_NOT_SUPPORTED; } + +TVG_API Tvg_Result tvg_lottie_animation_assign(Tvg_Animation* animation, const char* layer, uint32_t ix, const char* var, float val) +{ +#ifdef THORVG_LOTTIE_LOADER_SUPPORT + if (!animation) return TVG_RESULT_INVALID_ARGUMENT; + return (Tvg_Result) reinterpret_cast(animation)->assign(layer, ix, var, val); +#endif + return TVG_RESULT_NOT_SUPPORTED; +} + #ifdef __cplusplus } #endif diff --git a/src/loaders/lottie/thorvg_lottie.h b/src/loaders/lottie/thorvg_lottie.h index 501aac2d..0ee3d221 100644 --- a/src/loaders/lottie/thorvg_lottie.h +++ b/src/loaders/lottie/thorvg_lottie.h @@ -89,6 +89,25 @@ public: */ const char* marker(uint32_t idx) noexcept; + /** + * @brief Updates the value of an expression variable for a specific layer. + * + * This function sets the value of a specified expression variable within a particular layer. + * It is useful for dynamically changing the properties of a layer at runtime. + * + * @param[in] layer The name of the layer containing the variable to be updated. + * @param[in] ix The property index of the variable within the layer. + * @param[in] var The name of the variable to be updated. + * @param[in] val The new value to assign to the variable. + * + * @retval Result::InsufficientCondition If the animation is not loaded. + * @retval Result::InvalidArguments When the given parameter is invalid. + * @retval Result::NonSupport When neither the layer nor the property is found in the current animation. + * + * @note Experimental API + */ + Result assign(const char* layer, uint32_t ix, const char* var, float val); + /** * @brief Creates a new LottieAnimation object. * diff --git a/src/loaders/lottie/tvgLottieAnimation.cpp b/src/loaders/lottie/tvgLottieAnimation.cpp index b2b77f80..07c49d12 100644 --- a/src/loaders/lottie/tvgLottieAnimation.cpp +++ b/src/loaders/lottie/tvgLottieAnimation.cpp @@ -84,6 +84,18 @@ const char* LottieAnimation::marker(uint32_t idx) noexcept } +Result LottieAnimation::assign(const char* layer, uint32_t ix, const char* var, float val) +{ + if (!layer || !var) return Result::InvalidArguments; + + auto loader = PICTURE(pImpl->picture)->loader; + if (!loader) return Result::InsufficientCondition; + if (static_cast(loader)->assign(layer, ix, var, val)) return Result::Success; + + return Result::NonSupport; +} + + LottieAnimation* LottieAnimation::gen() noexcept { return new LottieAnimation; diff --git a/src/loaders/lottie/tvgLottieExpressions.cpp b/src/loaders/lottie/tvgLottieExpressions.cpp index 8c221a0c..84dfcc7a 100644 --- a/src/loaders/lottie/tvgLottieExpressions.cpp +++ b/src/loaders/lottie/tvgLottieExpressions.cpp @@ -1326,9 +1326,20 @@ jerry_value_t LottieExpressions::buildGlobal() } +void LottieExpressions::buildWritables(LottieExpression* exp) +{ + if (exp->writables.empty()) return; + ARRAY_FOREACH(p, exp->writables) { + auto writable = jerry_number(p->val); + jerry_object_set_sz(global, p->var, writable); + jerry_value_free(writable); + } +} + + jerry_value_t LottieExpressions::evaluate(float frameNo, LottieExpression* exp) { - if (exp->disabled) return jerry_undefined(); + if (exp->disabled && exp->writables.empty()) return jerry_undefined(); buildGlobal(exp); @@ -1352,6 +1363,9 @@ jerry_value_t LottieExpressions::evaluate(float frameNo, LottieExpression* exp) //expansions per object type if (exp->object->type == LottieObject::Transform) _buildTransform(global, frameNo, static_cast(exp->object)); + //update writable values + buildWritables(exp); + //evaluate the code auto eval = jerry_eval((jerry_char_t *) exp->code, strlen(exp->code), JERRY_PARSE_NO_OPTS); diff --git a/src/loaders/lottie/tvgLottieExpressions.h b/src/loaders/lottie/tvgLottieExpressions.h index 6b96679a..c7e759ef 100644 --- a/src/loaders/lottie/tvgLottieExpressions.h +++ b/src/loaders/lottie/tvgLottieExpressions.h @@ -139,6 +139,7 @@ private: void buildComp(LottieComposition* comp, float frameNo, LottieExpression* exp); void buildComp(jerry_value_t context, float frameNo, LottieLayer* comp, LottieExpression* exp); void buildGlobal(LottieExpression* exp); + void buildWritables(LottieExpression* exp); //global object, attributes, methods jerry_value_t global; diff --git a/src/loaders/lottie/tvgLottieLoader.cpp b/src/loaders/lottie/tvgLottieLoader.cpp index e78c05b5..1138cef7 100644 --- a/src/loaders/lottie/tvgLottieLoader.cpp +++ b/src/loaders/lottie/tvgLottieLoader.cpp @@ -456,5 +456,14 @@ bool LottieLoader::tween(float from, float to, float progress) TaskScheduler::request(this); + return true; +} + + +bool LottieLoader::assign(const char* layer, uint32_t ix, const char* var, float val) +{ + if (!ready() || !comp->expressions) return false; + comp->root->assign(layer, ix, var, val); + return true; } \ No newline at end of file diff --git a/src/loaders/lottie/tvgLottieLoader.h b/src/loaders/lottie/tvgLottieLoader.h index 9758e6ba..1a090c7b 100644 --- a/src/loaders/lottie/tvgLottieLoader.h +++ b/src/loaders/lottie/tvgLottieLoader.h @@ -73,6 +73,7 @@ public: float shorten(float frameNo); //Reduce the accuracy for performance bool tween(float from, float to, float progress); + bool assign(const char* layer, uint32_t ix, const char* var, float val); private: bool ready(); diff --git a/src/loaders/lottie/tvgLottieModel.cpp b/src/loaders/lottie/tvgLottieModel.cpp index d090c586..b7a0a007 100644 --- a/src/loaders/lottie/tvgLottieModel.cpp +++ b/src/loaders/lottie/tvgLottieModel.cpp @@ -23,6 +23,7 @@ #include "tvgMath.h" #include "tvgTaskScheduler.h" #include "tvgLottieModel.h" +#include "tvgCompressor.h" /************************************************************************/ @@ -531,6 +532,17 @@ LottieGroup::LottieGroup() } +LottieProperty* LottieGroup::property(uint16_t ix) +{ + ARRAY_FOREACH(p, children) { + auto child = static_cast(*p); + if (auto property = child->property(ix)) return property; + } + + return nullptr; +} + + void LottieGroup::prepare(LottieObject::Type type) { LottieObject::type = type; @@ -617,6 +629,16 @@ LottieLayer::~LottieLayer() } +LottieProperty* LottieLayer::property(uint16_t ix) +{ + if (transform) { + if (auto property = transform->property(ix)) return property; + } + + return LottieGroup::property(ix); +} + + void LottieLayer::prepare(RGB24* color) { /* if layer is hidden, only useful data is its transform matrix. @@ -658,6 +680,20 @@ float LottieLayer::remap(LottieComposition* comp, float frameNo, LottieExpressio } +bool LottieLayer::assign(const char* layer, uint32_t ix, const char* var, float val) +{ + //find the target layer by name + auto target = layerById(djb2Encode(layer)); + if (!target) return false; + + //find the target property by ix + auto property = target->property(ix); + if (property && property->exp) return property->exp->assign(var, val); + + return false; +} + + LottieComposition::~LottieComposition() { if (!initiated && root) delete(root->scene); diff --git a/src/loaders/lottie/tvgLottieModel.h b/src/loaders/lottie/tvgLottieModel.h index fda5c8f2..887b1ed7 100644 --- a/src/loaders/lottie/tvgLottieModel.h +++ b/src/loaders/lottie/tvgLottieModel.h @@ -845,6 +845,7 @@ struct LottieGroup : LottieObject, LottieRenderPooler void prepare(LottieObject::Type type = LottieObject::Group); bool mergeable() override { return allowMerge; } + LottieProperty* property(uint16_t ix); LottieObject* content(unsigned long id) { @@ -880,6 +881,8 @@ struct LottieLayer : LottieGroup bool mergeable() override { return false; } void prepare(RGB24* color = nullptr); float remap(LottieComposition* comp, float frameNo, LottieExpressions* exp); + LottieProperty* property(uint16_t ix); + bool assign(const char* layer, uint32_t ix, const char* var, float val); char* name = nullptr; LottieLayer* parent = nullptr; diff --git a/src/loaders/lottie/tvgLottieParser.cpp b/src/loaders/lottie/tvgLottieParser.cpp index d97e8b92..86947268 100644 --- a/src/loaders/lottie/tvgLottieParser.cpp +++ b/src/loaders/lottie/tvgLottieParser.cpp @@ -573,6 +573,7 @@ LottieTransform* LottieParser::parseTransform(bool ddd) else if (transform->coords && KEY_AS("y")) parseProperty(transform->coords->y); else if (KEY_AS("x") && expressions) transform->position.exp = getExpression(getStringCopy(), comp, context.layer, context.parent, &transform->position); else if (KEY_AS("sid")) registerSlot(transform, getString()); + else if (KEY_AS("ix")) transform->position.ix = getInt(); else skip(); } transform->position.type = LottieProperty::Type::Position; diff --git a/src/loaders/lottie/tvgLottieProperty.h b/src/loaders/lottie/tvgLottieProperty.h index a9cf456c..01e8df1f 100644 --- a/src/loaders/lottie/tvgLottieProperty.h +++ b/src/loaders/lottie/tvgLottieProperty.h @@ -128,11 +128,18 @@ struct LottieExpression { enum LoopMode : uint8_t { None = 0, InCycle = 1, InPingPong, InOffset, InContinue, OutCycle, OutPingPong, OutOffset, OutContinue }; + //writable expressions variable name and value. + struct Writable { + char* var; + float val; + }; + char* code; LottieComposition* comp; LottieLayer* layer; LottieObject* object; LottieProperty* property; + Array writables; bool disabled = false; struct { @@ -155,8 +162,24 @@ struct LottieExpression ~LottieExpression() { + ARRAY_FOREACH(p, writables) { + tvg::free(p->var); + } tvg::free(code); } + + bool assign(const char* var, float val) + { + //overwrite the existing value + ARRAY_FOREACH(p, writables) { + if (tvg::equal(var, p->var)) { + p->val = val; + return true; + } + } + writables.push({tvg::duplicate(var), val}); + return true; + } };