From 0832a188fedda482ef49e5175dd12919e130583b Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Mon, 23 Oct 2023 18:25:57 +0900 Subject: [PATCH] animation/lottie: updated the frame count unit. replace the frame count unit from the int32_t to float since animations could smoothly interpolate key-frames. This notificably improve the animation smoothness in Lottie Beta API changes: Result Animation::frame(uint32_t no) -> Result Animation::frame(float no) uint32_t Animation::curFrame() const -> float Animation::curFrame() const uint32_t Animation::totalFrame() const -> float Animation::totalFrame() const --- inc/thorvg.h | 8 +-- src/bindings/capi/thorvg_capi.h | 6 +- src/bindings/capi/tvgCapi.cpp | 6 +- src/bindings/wasm/tvgWasm.cpp | 3 +- src/examples/Animation.cpp | 5 +- src/examples/Capi.cpp | 13 ++--- src/examples/Lottie.cpp | 12 ++-- src/loaders/lottie/tvgLottieBuilder.cpp | 76 ++++++++++++------------- src/loaders/lottie/tvgLottieBuilder.h | 2 +- src/loaders/lottie/tvgLottieLoader.cpp | 24 ++++---- src/loaders/lottie/tvgLottieLoader.h | 11 ++-- src/loaders/lottie/tvgLottieModel.cpp | 8 +-- src/loaders/lottie/tvgLottieModel.h | 24 ++++---- src/loaders/lottie/tvgLottieParser.cpp | 12 ++-- src/loaders/lottie/tvgLottieProperty.h | 40 ++++++------- src/renderer/tvgAnimation.cpp | 6 +- src/renderer/tvgFrameModule.h | 7 +-- test/capi/capiAnimation.cpp | 12 ++-- test/testAnimation.cpp | 8 +-- 19 files changed, 137 insertions(+), 146 deletions(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index 0dabc0dd..b2027467 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -1708,7 +1708,7 @@ public: * * @BETA_API */ - Result frame(uint32_t no) noexcept; + Result frame(float no) noexcept; /** * @brief Retrieves a picture instance associated with this animation instance. @@ -1732,12 +1732,12 @@ public: * * @note If the Picture is not properly configured, this function will return 0. * - * @see Animation::frame(uint32_t no) + * @see Animation::frame(float no) * @see Animation::totalFrame() * * @BETA_API */ - uint32_t curFrame() const noexcept; + float curFrame() const noexcept; /** * @brief Retrieves the total number of frames in the animation. @@ -1749,7 +1749,7 @@ public: * * @BETA_API */ - uint32_t totalFrame() const noexcept; + float totalFrame() const noexcept; /** * @brief Retrieves the duration of the animation in seconds. diff --git a/src/bindings/capi/thorvg_capi.h b/src/bindings/capi/thorvg_capi.h index b380c4e4..d79cb42e 100644 --- a/src/bindings/capi/thorvg_capi.h +++ b/src/bindings/capi/thorvg_capi.h @@ -2234,7 +2234,7 @@ TVG_API Tvg_Animation* tvg_animation_new(); * * \see tvg_animation_get_total_frame() */ -TVG_API Tvg_Result tvg_animation_set_frame(Tvg_Animation* animation, uint32_t no); +TVG_API Tvg_Result tvg_animation_set_frame(Tvg_Animation* animation, float no); /*! @@ -2266,7 +2266,7 @@ TVG_API Tvg_Paint* tvg_animation_get_picture(Tvg_Animation* animation); * \see tvg_animation_get_total_frame() * \see tvg_animation_set_frame() */ -TVG_API Tvg_Result tvg_animation_get_frame(Tvg_Animation* animation, uint32_t* no); +TVG_API Tvg_Result tvg_animation_get_frame(Tvg_Animation* animation, float* no); /*! @@ -2282,7 +2282,7 @@ TVG_API Tvg_Result tvg_animation_get_frame(Tvg_Animation* animation, uint32_t* n * \note Frame numbering starts from 0. * \note If the Picture is not properly configured, this function will return 0. */ -TVG_API Tvg_Result tvg_animation_get_total_frame(Tvg_Animation* animation, uint32_t* cnt); +TVG_API Tvg_Result tvg_animation_get_total_frame(Tvg_Animation* animation, float* cnt); /*! diff --git a/src/bindings/capi/tvgCapi.cpp b/src/bindings/capi/tvgCapi.cpp index 9adbd1ef..7d602e4f 100644 --- a/src/bindings/capi/tvgCapi.cpp +++ b/src/bindings/capi/tvgCapi.cpp @@ -728,14 +728,14 @@ TVG_API Tvg_Animation* tvg_animation_new() } -TVG_API Tvg_Result tvg_animation_set_frame(Tvg_Animation* animation, uint32_t no) +TVG_API Tvg_Result tvg_animation_set_frame(Tvg_Animation* animation, float no) { if (!animation) return TVG_RESULT_INVALID_ARGUMENT; return (Tvg_Result) reinterpret_cast(animation)->frame(no); } -TVG_API Tvg_Result tvg_animation_get_frame(Tvg_Animation* animation, uint32_t* no) +TVG_API Tvg_Result tvg_animation_get_frame(Tvg_Animation* animation, float* no) { if (!animation || !no) return TVG_RESULT_INVALID_ARGUMENT; *no = reinterpret_cast(animation)->curFrame(); @@ -743,7 +743,7 @@ TVG_API Tvg_Result tvg_animation_get_frame(Tvg_Animation* animation, uint32_t* n } -TVG_API Tvg_Result tvg_animation_get_total_frame(Tvg_Animation* animation, uint32_t* cnt) +TVG_API Tvg_Result tvg_animation_get_total_frame(Tvg_Animation* animation, float* cnt) { if (!animation || !cnt) return TVG_RESULT_INVALID_ARGUMENT; *cnt = reinterpret_cast(animation)->totalFrame(); diff --git a/src/bindings/wasm/tvgWasm.cpp b/src/bindings/wasm/tvgWasm.cpp index 9b849005..a5fe71be 100644 --- a/src/bindings/wasm/tvgWasm.cpp +++ b/src/bindings/wasm/tvgWasm.cpp @@ -149,10 +149,9 @@ public: return val(animation->totalFrame()); } - bool frame(uint32_t no) + bool frame(float no) { if (!canvas || !animation) return false; - if (animation->curFrame() == no) return true; if (animation->frame(no) != Result::Success) { errorMsg = "frame() fail"; return false; diff --git a/src/examples/Animation.cpp b/src/examples/Animation.cpp index ce5ad58e..38cd8079 100644 --- a/src/examples/Animation.cpp +++ b/src/examples/Animation.cpp @@ -34,10 +34,7 @@ void tvgUpdateCmds(tvg::Canvas* canvas, tvg::Animation* animation, float progres if (!canvas) return; //Update animation frame only when it's changed - auto frame = lroundf(animation->totalFrame() * progress); - - if (frame != animation->curFrame()) { - animation->frame(frame); + if (animation->frame(animation->totalFrame() * progress) == tvg::Result::Success) { canvas->update(animation->picture()); } } diff --git a/src/examples/Capi.cpp b/src/examples/Capi.cpp index 0dd78cd9..0e46b927 100644 --- a/src/examples/Capi.cpp +++ b/src/examples/Capi.cpp @@ -257,19 +257,18 @@ void transitCb(Elm_Transit_Effect *effect, Elm_Transit* transit, double progress { if (!canvas) return; - uint32_t total_frame = 0; + float total_frame = 0.0f; tvg_animation_get_total_frame(animation, &total_frame); - uint32_t new_frame = lroundf(total_frame * progress); + float new_frame = total_frame * progress; - uint32_t cur_frame = 0; + float cur_frame = 0.0f; tvg_animation_get_frame(animation, &cur_frame); //Update animation frame only when it's changed - if (new_frame == cur_frame) return; - - tvg_animation_set_frame(animation, new_frame); - tvg_canvas_update_paint(canvas, tvg_animation_get_picture(animation)); + if (tvg_animation_set_frame(animation, new_frame) == TVG_RESULT_SUCCESS) { + tvg_canvas_update_paint(canvas, tvg_animation_get_picture(animation)); + } //Draw the canvas tvg_canvas_draw(canvas); diff --git a/src/examples/Lottie.cpp b/src/examples/Lottie.cpp index cf46d0cd..6e2383d2 100644 --- a/src/examples/Lottie.cpp +++ b/src/examples/Lottie.cpp @@ -93,14 +93,10 @@ void tvgUpdateCmds(Elm_Transit_Effect *effect, Elm_Transit* transit, double prog auto animation = static_cast(effect); //Update animation frame only when it's changed - auto frame = lroundf(animation->totalFrame() * progress); - - if (frame != animation->curFrame()) { - auto before = ecore_time_get(); - animation->frame(frame); - auto after = ecore_time_get(); - updateTime += after - before; - } + auto before = ecore_time_get(); + animation->frame(animation->totalFrame() * progress); + auto after = ecore_time_get(); + updateTime += after - before; } void tvgDrawCmds(tvg::Canvas* canvas) diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index f36cd6e5..ee81e8b3 100644 --- a/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/src/loaders/lottie/tvgLottieBuilder.cpp @@ -83,8 +83,8 @@ struct RenderContext }; -static void _updateChildren(LottieGroup* parent, int32_t frameNo, queue& contexts); -static void _updateLayer(LottieLayer* root, LottieLayer* layer, int32_t frameNo); +static void _updateChildren(LottieGroup* parent, float frameNo, queue& contexts); +static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo); static bool _buildPrecomp(LottieComposition* comp, LottieGroup* parent); static void _rotateX(Matrix* m, float degree) @@ -114,7 +114,7 @@ static void _rotationZ(Matrix* m, float degree) } -static bool _updateTransform(LottieTransform* transform, int32_t frameNo, bool autoOrient, Matrix& matrix, uint8_t& opacity) +static bool _updateTransform(LottieTransform* transform, float frameNo, bool autoOrient, Matrix& matrix, uint8_t& opacity) { mathIdentity(&matrix); @@ -154,9 +154,9 @@ static bool _updateTransform(LottieTransform* transform, int32_t frameNo, bool a } -static void _updateTransform(LottieLayer* layer, int32_t frameNo) +static void _updateTransform(LottieLayer* layer, float frameNo) { - if (!layer || layer->cache.frameNo == frameNo) return; + if (!layer || mathEqual(layer->cache.frameNo, frameNo)) return; auto transform = layer->transform; auto parent = layer->parent; @@ -177,7 +177,7 @@ static void _updateTransform(LottieLayer* layer, int32_t frameNo) } -static void _updateTransform(LottieTransform* transform, int32_t frameNo, RenderContext& ctx) +static void _updateTransform(LottieTransform* transform, float frameNo, RenderContext& ctx) { if (!transform) return; @@ -198,7 +198,7 @@ static void _updateTransform(LottieTransform* transform, int32_t frameNo, Render } -static void _updateGroup(LottieGroup* parent, LottieGroup* group, int32_t frameNo, RenderContext& ctx) +static void _updateGroup(LottieGroup* parent, LottieGroup* group, float frameNo, RenderContext& ctx) { if (group->children.empty()) return; @@ -212,7 +212,7 @@ static void _updateGroup(LottieGroup* parent, LottieGroup* group, int32_t frameN } -static void _updateStroke(LottieStroke* stroke, int32_t frameNo, RenderContext& ctx) +static void _updateStroke(LottieStroke* stroke, float frameNo, RenderContext& ctx) { ctx.propagator->stroke(stroke->width(frameNo)); ctx.propagator->stroke(stroke->cap); @@ -245,7 +245,7 @@ static bool _fragmentedStroking(LottieObject** child, queue& cont } -static void _updateSolidStroke(LottieObject** child, int32_t frameNo, queue& contexts, RenderContext& ctx) +static void _updateSolidStroke(LottieObject** child, float frameNo, queue& contexts, RenderContext& ctx) { if (_fragmentedStroking(child, contexts, ctx)) return; @@ -259,7 +259,7 @@ static void _updateSolidStroke(LottieObject** child, int32_t frameNo, queue& contexts, RenderContext& ctx) +static void _updateGradientStroke(LottieObject** child, float frameNo, queue& contexts, RenderContext& ctx) { if (_fragmentedStroking(child, contexts, ctx)) return; @@ -272,7 +272,7 @@ static void _updateGradientStroke(LottieObject** child, int32_t frameNo, queue path, RenderContext& ctx) +static void _repeat(LottieGroup* parent, unique_ptr path, RenderContext& ctx) { auto repeater = ctx.repeater; @@ -363,7 +363,7 @@ static void _repeat(LottieGroup* parent, int32_t frameNo, unique_ptr path } -static void _updateRect(LottieGroup* parent, LottieRect* rect, int32_t frameNo, RenderContext& ctx) +static void _updateRect(LottieGroup* parent, LottieRect* rect, float frameNo, RenderContext& ctx) { auto position = rect->position(frameNo); auto size = rect->size(frameNo); @@ -378,15 +378,15 @@ static void _updateRect(LottieGroup* parent, LottieRect* rect, int32_t frameNo, if (ctx.repeater) { auto path = Shape::gen(); path->appendRect(position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, roundness); - _repeat(parent, frameNo, std::move(path), ctx); + _repeat(parent, std::move(path), ctx); } else { - auto merging = _draw(parent, frameNo, ctx); + auto merging = _draw(parent, ctx); merging->appendRect(position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, roundness); } } -static void _updateEllipse(LottieGroup* parent, LottieEllipse* ellipse, int32_t frameNo, RenderContext& ctx) +static void _updateEllipse(LottieGroup* parent, LottieEllipse* ellipse, float frameNo, RenderContext& ctx) { auto position = ellipse->position(frameNo); auto size = ellipse->size(frameNo); @@ -394,22 +394,22 @@ static void _updateEllipse(LottieGroup* parent, LottieEllipse* ellipse, int32_t if (ctx.repeater) { auto path = Shape::gen(); path->appendCircle(position.x, position.y, size.x * 0.5f, size.y * 0.5f); - _repeat(parent, frameNo, std::move(path), ctx); + _repeat(parent, std::move(path), ctx); } else { - auto merging = _draw(parent, frameNo, ctx); + auto merging = _draw(parent, ctx); merging->appendCircle(position.x, position.y, size.x * 0.5f, size.y * 0.5f); } } -static void _updatePath(LottieGroup* parent, LottiePath* path, int32_t frameNo, RenderContext& ctx) +static void _updatePath(LottieGroup* parent, LottiePath* path, float frameNo, RenderContext& ctx) { if (ctx.repeater) { auto p = Shape::gen(); path->pathset(frameNo, P(p)->rs.path.cmds, P(p)->rs.path.pts); - _repeat(parent, frameNo, std::move(p), ctx); + _repeat(parent, std::move(p), ctx); } else { - auto merging = _draw(parent, frameNo, ctx); + auto merging = _draw(parent, ctx); if (path->pathset(frameNo, P(merging)->rs.path.cmds, P(merging)->rs.path.pts)) { P(merging)->update(RenderUpdateFlag::Path); @@ -423,7 +423,7 @@ static void _updatePath(LottieGroup* parent, LottiePath* path, int32_t frameNo, } -static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* transform, int32_t frameNo, Shape* merging) +static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* transform, float frameNo, Shape* merging) { static constexpr auto POLYSTAR_MAGIC_NUMBER = 0.47829f / 0.28f; @@ -532,7 +532,7 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans } -static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* transform, int32_t frameNo, Shape* merging) +static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* transform, float frameNo, Shape* merging) { static constexpr auto POLYGON_MAGIC_NUMBER = 0.25f; @@ -601,7 +601,7 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr } -static void _updatePolystar(LottieGroup* parent, LottiePolyStar* star, int32_t frameNo, RenderContext& ctx) +static void _updatePolystar(LottieGroup* parent, LottiePolyStar* star, float frameNo, RenderContext& ctx) { //Optimize: Can we skip the individual coords transform? Matrix matrix; @@ -616,9 +616,9 @@ static void _updatePolystar(LottieGroup* parent, LottiePolyStar* star, int32_t f auto p = Shape::gen(); if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, frameNo, p.get()); else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, p.get()); - _repeat(parent, frameNo, std::move(p), ctx); + _repeat(parent, std::move(p), ctx); } else { - auto merging = _draw(parent, frameNo, ctx); + auto merging = _draw(parent, ctx); if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, frameNo, merging); else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, merging); P(merging)->update(RenderUpdateFlag::Path); @@ -626,7 +626,7 @@ static void _updatePolystar(LottieGroup* parent, LottiePolyStar* star, int32_t f } -static void _updateImage(LottieGroup* parent, LottieImage* image, int32_t frameNo, RenderContext& ctx) +static void _updateImage(LottieGroup* parent, LottieImage* image, float frameNo, RenderContext& ctx) { auto picture = image->picture; @@ -665,14 +665,14 @@ static void _updateImage(LottieGroup* parent, LottieImage* image, int32_t frameN } -static void _updateRoundedCorner(LottieRoundedCorner* roundedCorner, int32_t frameNo, RenderContext& ctx) +static void _updateRoundedCorner(LottieRoundedCorner* roundedCorner, float frameNo, RenderContext& ctx) { auto roundness = roundedCorner->radius(frameNo); if (ctx.roundness < roundness) ctx.roundness = roundness; } -static void _updateRepeater(LottieRepeater* repeater, int32_t frameNo, RenderContext& ctx) +static void _updateRepeater(LottieRepeater* repeater, float frameNo, RenderContext& ctx) { if (!ctx.repeater) ctx.repeater = new RenderRepeater(); ctx.repeater->cnt = static_cast(repeater->copies(frameNo)); @@ -690,7 +690,7 @@ static void _updateRepeater(LottieRepeater* repeater, int32_t frameNo, RenderCon } -static void _updateTrimpath(LottieTrimpath* trimpath, int32_t frameNo, RenderContext& ctx) +static void _updateTrimpath(LottieTrimpath* trimpath, float frameNo, RenderContext& ctx) { float begin, end; trimpath->segment(frameNo, begin, end); @@ -709,7 +709,7 @@ static void _updateTrimpath(LottieTrimpath* trimpath, int32_t frameNo, RenderCon } -static void _updateChildren(LottieGroup* parent, int32_t frameNo, queue& contexts) +static void _updateChildren(LottieGroup* parent, float frameNo, queue& contexts) { contexts.front().begin = parent->children.end() - 1; @@ -783,7 +783,7 @@ static void _updateChildren(LottieGroup* parent, int32_t frameNo, queuechildren.count == 0) return; @@ -808,7 +808,7 @@ static void _updatePrecomp(LottieLayer* precomp, int32_t frameNo) } -static void _updateSolid(LottieLayer* layer, int32_t frameNo) +static void _updateSolid(LottieLayer* layer, float frameNo) { auto shape = Shape::gen(); shape->appendRect(0, 0, static_cast(layer->w), static_cast(layer->h)); @@ -817,7 +817,7 @@ static void _updateSolid(LottieLayer* layer, int32_t frameNo) } -static void _updateMaskings(LottieLayer* layer, int32_t frameNo) +static void _updateMaskings(LottieLayer* layer, float frameNo) { if (layer->masks.count == 0) return; @@ -855,7 +855,7 @@ static void _updateMaskings(LottieLayer* layer, int32_t frameNo) } -static void _updateLayer(LottieLayer* root, LottieLayer* layer, int32_t frameNo) +static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo) { layer->scene = nullptr; @@ -1021,7 +1021,7 @@ static bool _buildPrecomp(LottieComposition* comp, LottieGroup* parent) /* External Class Implementation */ /************************************************************************/ -bool LottieBuilder::update(LottieComposition* comp, int32_t frameNo) +bool LottieBuilder::update(LottieComposition* comp, float frameNo) { frameNo += comp->startFrame; if (frameNo < comp->startFrame) frameNo = comp->startFrame; diff --git a/src/loaders/lottie/tvgLottieBuilder.h b/src/loaders/lottie/tvgLottieBuilder.h index 9e26c080..0edba534 100644 --- a/src/loaders/lottie/tvgLottieBuilder.h +++ b/src/loaders/lottie/tvgLottieBuilder.h @@ -29,7 +29,7 @@ struct LottieComposition; struct LottieBuilder { - bool update(LottieComposition* comp, int32_t frameNo); + bool update(LottieComposition* comp, float progress); void build(LottieComposition* comp); }; diff --git a/src/loaders/lottie/tvgLottieLoader.cpp b/src/loaders/lottie/tvgLottieLoader.cpp index 7ea1e58f..dcfdd53d 100644 --- a/src/loaders/lottie/tvgLottieLoader.cpp +++ b/src/loaders/lottie/tvgLottieLoader.cpp @@ -191,7 +191,8 @@ bool LottieLoader::header() return false; } - frameDuration = (endFrame - startFrame) / frameRate; + frameCnt = (endFrame - startFrame); + frameDuration = frameCnt / frameRate; TVGLOG("LOTTIE", "info: frame rate = %f, duration = %f size = %d x %d", frameRate, frameDuration, (int)w, (int)h); @@ -286,6 +287,10 @@ bool LottieLoader::read() TaskScheduler::request(this); + if (comp && TaskScheduler::threads() == 0) { + frameCnt = comp->frameCnt(); + } + return true; } @@ -309,15 +314,13 @@ unique_ptr LottieLoader::paint() } -bool LottieLoader::frame(uint32_t frameNo) +bool LottieLoader::frame(float no) { - if (this->frameNo == frameNo) return true; + if (mathEqual(this->frameNo, no)) return false; this->done(); - if (!comp || frameNo >= comp->frameCnt()) return false; - - this->frameNo = frameNo; + this->frameNo = no; TaskScheduler::request(this); @@ -325,16 +328,13 @@ bool LottieLoader::frame(uint32_t frameNo) } -uint32_t LottieLoader::totalFrame() +float LottieLoader::totalFrame() { - this->done(); - - if (!comp) return 0; - return comp->frameCnt(); + return frameCnt; } -uint32_t LottieLoader::curFrame() +float LottieLoader::curFrame() { return frameNo; } diff --git a/src/loaders/lottie/tvgLottieLoader.h b/src/loaders/lottie/tvgLottieLoader.h index 012743d9..7a195120 100644 --- a/src/loaders/lottie/tvgLottieLoader.h +++ b/src/loaders/lottie/tvgLottieLoader.h @@ -35,8 +35,9 @@ class LottieLoader : public FrameModule, public Task public: const char* content = nullptr; //lottie file data uint32_t size = 0; //lottie data size - uint32_t frameNo = 0; //current frame number - float frameDuration; + float frameNo = 0.0f; //current frame number + float frameCnt = 0.0f; + float frameDuration = 0.0f; LottieBuilder* builder = nullptr; LottieComposition* comp = nullptr; @@ -57,9 +58,9 @@ public: unique_ptr paint() override; //Frame Controls - bool frame(uint32_t frameNo) override; - uint32_t totalFrame() override; - uint32_t curFrame() override; + bool frame(float no) override; + float totalFrame() override; + float curFrame() override; float duration() override; void sync() override; diff --git a/src/loaders/lottie/tvgLottieModel.cpp b/src/loaders/lottie/tvgLottieModel.cpp index 2632a5f6..023b20fd 100644 --- a/src/loaders/lottie/tvgLottieModel.cpp +++ b/src/loaders/lottie/tvgLottieModel.cpp @@ -34,7 +34,7 @@ /* External Class Implementation */ /************************************************************************/ -void LottieTrimpath::segment(int32_t frameNo, float& start, float& end) +void LottieTrimpath::segment(float frameNo, float& start, float& end) { auto s = this->start(frameNo) * 0.01f; auto e = this->end(frameNo) * 0.01f; @@ -77,7 +77,7 @@ void LottieTrimpath::segment(int32_t frameNo, float& start, float& end) } -Fill* LottieGradient::fill(int32_t frameNo) +Fill* LottieGradient::fill(float frameNo) { Fill* fill = nullptr; @@ -145,14 +145,14 @@ void LottieLayer::prepare() } -int32_t LottieLayer::remap(int32_t frameNo) +float LottieLayer::remap(float frameNo) { if (timeRemap.frames || timeRemap.value) { frameNo = comp->frameAtTime(timeRemap(frameNo)); } else { frameNo -= startFrame; } - return (int32_t)(frameNo / timeStretch); + return (frameNo / timeStretch); } diff --git a/src/loaders/lottie/tvgLottieModel.h b/src/loaders/lottie/tvgLottieModel.h index 3a27664a..ce8ca0ce 100644 --- a/src/loaders/lottie/tvgLottieModel.h +++ b/src/loaders/lottie/tvgLottieModel.h @@ -49,17 +49,17 @@ struct LottieStroke return dashattr->value[no]; } - float dashOffset(int32_t frameNo) + float dashOffset(float frameNo) { return dash(0)(frameNo); } - float dashGap(int32_t frameNo) + float dashGap(float frameNo) { return dash(2)(frameNo); } - float dashSize(int32_t frameNo) + float dashSize(float frameNo) { auto d = dash(1)(frameNo); if (d == 0.0f) return 0.1f; @@ -177,7 +177,7 @@ struct LottieGradient return false; } - Fill* fill(int32_t frameNo); + Fill* fill(float frameNo); LottiePoint start = Point{0.0f, 0.0f}; LottiePoint end = Point{0.0f, 0.0f}; @@ -248,7 +248,7 @@ struct LottieTrimpath : LottieObject if (start.frames || end.frames || offset.frames) statical = false; } - void segment(int32_t frameNo, float& start, float& end); + void segment(float frameNo, float& start, float& end); LottieFloat start = 0.0f; LottieFloat end = 0.0f; @@ -507,7 +507,7 @@ struct LottieLayer : LottieGroup delete(transform); } - uint8_t opacity(int32_t frameNo) + uint8_t opacity(float frameNo) { //return zero if the visibility is false. if (type == Null) return 255; @@ -515,7 +515,7 @@ struct LottieLayer : LottieGroup } void prepare(); - int32_t remap(int32_t frameNo); + float remap(float frameNo); //Optimize: compact data?? RGB24 color; @@ -534,16 +534,16 @@ struct LottieLayer : LottieGroup float timeStretch = 1.0f; uint32_t w = 0, h = 0; - int32_t inFrame = 0; - int32_t outFrame = 0; - uint32_t startFrame = 0; + float inFrame = 0.0f; + float outFrame = 0.0f; + float startFrame = 0.0f; char* refId = nullptr; //pre-composition reference. int16_t pid = -1; //id of the parent layer. int16_t id = -1; //id of the current layer. //cached data struct { - int32_t frameNo = -1; + float frameNo = -1.0f; Matrix matrix; uint8_t opacity; } cache; @@ -583,7 +583,7 @@ struct LottieComposition char* version = nullptr; char* name = nullptr; uint32_t w, h; - int32_t startFrame, endFrame; + float startFrame, endFrame; float frameRate; Array assets; Array interpolators; diff --git a/src/loaders/lottie/tvgLottieParser.cpp b/src/loaders/lottie/tvgLottieParser.cpp index c7ee345c..95d74f28 100644 --- a/src/loaders/lottie/tvgLottieParser.cpp +++ b/src/loaders/lottie/tvgLottieParser.cpp @@ -382,7 +382,7 @@ void LottieParser::parseKeyFrame(T& prop) } } } else if (!strcmp(key, "t")) { - frame.no = lroundf(getFloat()); + frame.no = getFloat(); } else if (!strcmp(key, "s")) { getValue(frame.value); } else if (!strcmp(key, "e")) { @@ -1003,9 +1003,9 @@ LottieLayer* LottieParser::parseLayer() } else if (!strcmp(key, "ao")) layer->autoOrient = getInt(); else if (!strcmp(key, "shapes")) parseShapes(layer); - else if (!strcmp(key, "ip")) layer->inFrame = lroundf(getFloat()); - else if (!strcmp(key, "op")) layer->outFrame = lroundf(getFloat()); - else if (!strcmp(key, "st")) layer->startFrame = lroundf(getFloat()); + else if (!strcmp(key, "ip")) layer->inFrame = getFloat(); + else if (!strcmp(key, "op")) layer->outFrame = getFloat(); + else if (!strcmp(key, "st")) layer->startFrame = getFloat(); else if (!strcmp(key, "bm")) layer->blendMethod = getBlendMethod(); else if (!strcmp(key, "parent")) layer->pid = getInt(); else if (!strcmp(key, "tm")) parseTimeRemap(layer); @@ -1091,8 +1091,8 @@ bool LottieParser::parse() while (auto key = nextObjectKey()) { if (!strcmp(key, "v")) comp->version = getStringCopy(); else if (!strcmp(key, "fr")) comp->frameRate = getFloat(); - else if (!strcmp(key, "ip")) comp->startFrame = lroundf(getFloat()); - else if (!strcmp(key, "op")) comp->endFrame = lroundf(getFloat()); + else if (!strcmp(key, "ip")) comp->startFrame = getFloat(); + else if (!strcmp(key, "op")) comp->endFrame = getFloat(); else if (!strcmp(key, "w")) comp->w = getInt(); else if (!strcmp(key, "h")) comp->h = getInt(); else if (!strcmp(key, "nm")) comp->name = getStringCopy(); diff --git a/src/loaders/lottie/tvgLottieProperty.h b/src/loaders/lottie/tvgLottieProperty.h index e31b504d..ddca6e03 100644 --- a/src/loaders/lottie/tvgLottieProperty.h +++ b/src/loaders/lottie/tvgLottieProperty.h @@ -93,13 +93,13 @@ template struct LottieScalarFrame { T value; //keyframe value - int32_t no; //frame number + float no; //frame number LottieInterpolator* interpolator; bool hold = false; //do not interpolate. - T interpolate(LottieScalarFrame* next, int32_t frameNo) + T interpolate(LottieScalarFrame* next, float frameNo) { - auto t = float(frameNo - no) / float(next->no - no); + auto t = (frameNo - no) / (next->no - no); if (interpolator) t = interpolator->progress(t); if (hold) { @@ -115,16 +115,16 @@ template struct LottieVectorFrame { T value; //keyframe value - int32_t no; //frame number + float no; //frame number LottieInterpolator* interpolator; T outTangent, inTangent; float length; bool hasTangent = false; bool hold = false; - T interpolate(LottieVectorFrame* next, int32_t frameNo) + T interpolate(LottieVectorFrame* next, float frameNo) { - auto t = float(frameNo - no) / float(next->no - no); + auto t = (frameNo - no) / (next->no - no); if (interpolator) t = interpolator->progress(t); if (hold) { @@ -141,10 +141,10 @@ struct LottieVectorFrame } } - float angle(LottieVectorFrame* next, int32_t frameNo) + float angle(LottieVectorFrame* next, float frameNo) { if (!hasTangent) return 0; - auto t = float(frameNo - no) / float(next->no - no); + auto t = (frameNo - no) / (next->no - no); if (interpolator) t = interpolator->progress(t); Bezier bz = {value, value + outTangent, next->value + inTangent, next->value}; t = bezAt(bz, t * length, length); @@ -190,7 +190,7 @@ struct LottieProperty return (*frames)[frames->count]; } - T operator()(int32_t frameNo) + T operator()(float frameNo) { if (!frames) return value; if (frames->count == 1 || frameNo <= frames->first().no) return frames->first().value; @@ -202,7 +202,7 @@ struct LottieProperty while (low <= high) { auto mid = low + (high - low) / 2; auto frame = frames->data + mid; - if (frameNo == frame->no) return frame->value; + if (mathEqual(frameNo, frame->no)) return frame->value; else if (frameNo > frame->no) low = mid + 1; else high = mid - 1; } @@ -211,7 +211,7 @@ struct LottieProperty return (frame - 1)->interpolate(frame, frameNo); } - float angle(int32_t frameNo) { return 0; } + float angle(float frameNo) { return 0; } void prepare() {} }; @@ -258,7 +258,7 @@ struct LottiePathSet return (*frames)[frames->count]; } - bool operator()(int32_t frameNo, Array& cmds, Array& pts) + bool operator()(float frameNo, Array& cmds, Array& pts) { if (!frames) { copy(value, cmds); @@ -284,7 +284,7 @@ struct LottiePathSet while (low <= high) { auto mid = low + (high - low) / 2; auto frame = frames->data + mid; - if (frameNo == frame->no) { + if (mathEqual(frameNo, frame->no)) { copy(frame->value, cmds); copy(frame->value, pts); return true; @@ -300,7 +300,7 @@ struct LottiePathSet auto pframe = frame - 1; copy(pframe->value, cmds); - auto t = float(frameNo - pframe->no) / float(frame->no - pframe->no); + auto t = (frameNo - pframe->no) / (frame->no - pframe->no); if (pframe->interpolator) t = pframe->interpolator->progress(t); if (pframe->hold) { @@ -358,7 +358,7 @@ struct LottieColorStop return (*frames)[frames->count]; } - void operator()(int32_t frameNo, Fill* fill) + void operator()(float frameNo, Fill* fill) { if (!frames) { fill->colorStops(value.data, count); @@ -381,7 +381,7 @@ struct LottieColorStop while (low <= high) { auto mid = low + (high - low) / 2; auto frame = frames->data + mid; - if (frameNo == frame->no) { + if (mathEqual(frameNo, frame->no)) { fill->colorStops(frame->value.data, count); return; } else if (frameNo > frame->no) { @@ -394,7 +394,7 @@ struct LottieColorStop //interpolate auto frame = frames->data + low; auto pframe = frame - 1; - auto t = float(frameNo - pframe->no) / float(frame->no - pframe->no); + auto t = (frameNo - pframe->no) / (frame->no - pframe->no); if (pframe->interpolator) t = pframe->interpolator->progress(t); if (pframe->hold) { @@ -453,7 +453,7 @@ struct LottiePosition return (*frames)[frames->count]; } - Point operator()(int32_t frameNo) + Point operator()(float frameNo) { if (!frames) return value; if (frames->count == 1 || frameNo <= frames->first().no) return frames->first().value; @@ -465,7 +465,7 @@ struct LottiePosition while (low <= high) { auto mid = low + (high - low) / 2; auto frame = frames->data + mid; - if (frameNo == frame->no) return frame->value; + if (mathEqual(frameNo, frame->no)) return frame->value; else if (frameNo > frame->no) low = mid + 1; else high = mid - 1; } @@ -474,7 +474,7 @@ struct LottiePosition return (frame - 1)->interpolate(frame, frameNo); } - float angle(int32_t frameNo) + float angle(float frameNo) { if (!frames) return 0; if (frames->count == 1 || frameNo <= frames->first().no) return 0; diff --git a/src/renderer/tvgAnimation.cpp b/src/renderer/tvgAnimation.cpp index 7a29ecf9..b4ea534b 100644 --- a/src/renderer/tvgAnimation.cpp +++ b/src/renderer/tvgAnimation.cpp @@ -62,7 +62,7 @@ Animation::Animation() : pImpl(new Impl) } -Result Animation::frame(uint32_t no) noexcept +Result Animation::frame(float no) noexcept { auto loader = pImpl->picture->pImpl->loader.get(); @@ -80,7 +80,7 @@ Picture* Animation::picture() const noexcept } -uint32_t Animation::curFrame() const noexcept +float Animation::curFrame() const noexcept { auto loader = pImpl->picture->pImpl->loader.get(); @@ -91,7 +91,7 @@ uint32_t Animation::curFrame() const noexcept } -uint32_t Animation::totalFrame() const noexcept +float Animation::totalFrame() const noexcept { auto loader = pImpl->picture->pImpl->loader.get(); diff --git a/src/renderer/tvgFrameModule.h b/src/renderer/tvgFrameModule.h index 857c6cae..332cca30 100644 --- a/src/renderer/tvgFrameModule.h +++ b/src/renderer/tvgFrameModule.h @@ -33,10 +33,9 @@ class FrameModule: public LoadModule public: virtual ~FrameModule() {} - virtual bool frame(uint32_t frameNo) = 0; //set the current frame number - - virtual uint32_t totalFrame() = 0; //return the total frame count - virtual uint32_t curFrame() = 0; //return the current frame number + virtual bool frame(float no) = 0; //set the current frame number + virtual float totalFrame() = 0; //return the total frame count + virtual float curFrame() = 0; //return the current frame number virtual float duration() = 0; //return the animation duration in seconds virtual bool animatable() override { return true; } diff --git a/test/capi/capiAnimation.cpp b/test/capi/capiAnimation.cpp index 45bc10d7..d52fbaf1 100644 --- a/test/capi/capiAnimation.cpp +++ b/test/capi/capiAnimation.cpp @@ -40,16 +40,16 @@ TEST_CASE("Animation Basic", "[capiAnimation]") //Negative cases REQUIRE(tvg_animation_set_frame(animation, 0) == TVG_RESULT_INSUFFICIENT_CONDITION); - uint32_t frame = 0; + auto frame = 0.0f; REQUIRE(tvg_animation_set_frame(animation, frame) == TVG_RESULT_INSUFFICIENT_CONDITION); REQUIRE(tvg_animation_get_frame(animation, nullptr) == TVG_RESULT_INVALID_ARGUMENT); REQUIRE(tvg_animation_get_frame(animation, &frame) == TVG_RESULT_SUCCESS); - REQUIRE(frame == 0); + REQUIRE(frame == 0.0f); REQUIRE(tvg_animation_get_total_frame(animation, nullptr) == TVG_RESULT_INVALID_ARGUMENT); REQUIRE(tvg_animation_get_total_frame(animation, &frame) == TVG_RESULT_SUCCESS); - REQUIRE(frame == 0); + REQUIRE(frame == 0.0f); REQUIRE(tvg_animation_get_duration(animation, nullptr) == TVG_RESULT_INVALID_ARGUMENT); float duration = 0.0f; @@ -79,13 +79,13 @@ TEST_CASE("Animation Lottie", "[capiAnimation]") REQUIRE(tvg_picture_load(picture, TEST_DIR"/invalid.json") == TVG_RESULT_INVALID_ARGUMENT); REQUIRE(tvg_picture_load(picture, TEST_DIR"/test.json") == TVG_RESULT_SUCCESS); - uint32_t frame; + float frame; REQUIRE(tvg_animation_get_total_frame(animation, &frame) == TVG_RESULT_SUCCESS); - REQUIRE(frame == 120); + REQUIRE(frame == Approx(120).margin(004004)); REQUIRE(tvg_animation_set_frame(animation, frame - 1) == TVG_RESULT_SUCCESS); REQUIRE(tvg_animation_get_frame(animation, &frame) == TVG_RESULT_SUCCESS); - REQUIRE(frame == 119); + REQUIRE(frame == Approx(119).margin(004004)); float duration; REQUIRE(tvg_animation_get_duration(animation, &duration) == TVG_RESULT_SUCCESS); diff --git a/test/testAnimation.cpp b/test/testAnimation.cpp index d110f17f..7f396b35 100644 --- a/test/testAnimation.cpp +++ b/test/testAnimation.cpp @@ -39,9 +39,9 @@ TEST_CASE("Animation Basic", "[tvgAnimation]") REQUIRE(picture->identifier == Picture::identifier); //Negative cases - REQUIRE(animation->frame(0) == Result::InsufficientCondition); - REQUIRE(animation->curFrame() == 0); - REQUIRE(animation->totalFrame() == 0); + REQUIRE(animation->frame(0.0f) == Result::InsufficientCondition); + REQUIRE(animation->curFrame() == 0.0f); + REQUIRE(animation->totalFrame() == 0.0f); REQUIRE(animation->duration() == 0.0f); } @@ -60,7 +60,7 @@ TEST_CASE("Animation Lottie", "[tvgAnimation]") REQUIRE(picture->load(TEST_DIR"/invalid.json") == Result::InvalidArguments); REQUIRE(picture->load(TEST_DIR"/test.json") == Result::Success); - REQUIRE(animation->totalFrame() == 120); + REQUIRE(animation->totalFrame() == Approx(120).margin(004004)); REQUIRE(animation->curFrame() == 0); REQUIRE(animation->duration() == Approx(4).margin(004004)); REQUIRE(animation->frame(20) == Result::Success);