From fdd760a34f9f40fcac13ae237995ad68676aa310 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 22 Jan 2025 07:28:44 +0900 Subject: [PATCH] api: revised the animation segment specification Changed the unit of the segment from a normalized value to frame numbers, ensuring alignment with other frame control interfaces. Note that This change may break backward compatibility. issue: https://github.com/thorvg/thorvg/issues/3116 --- examples/Interaction.cpp | 12 ++++----- inc/thorvg.h | 30 +++++++++------------ src/bindings/capi/thorvg_capi.h | 29 +++++++++++++------- src/loaders/lottie/tvgLottieAnimation.cpp | 4 +-- src/loaders/lottie/tvgLottieLoader.cpp | 33 +++++++++++++++-------- src/loaders/lottie/tvgLottieLoader.h | 2 +- src/renderer/tvgAnimation.cpp | 6 +---- src/renderer/tvgFrameModule.h | 9 ++----- test/testAnimation.cpp | 4 +-- 9 files changed, 69 insertions(+), 60 deletions(-) diff --git a/examples/Interaction.cpp b/examples/Interaction.cpp index 2634eb30..97969575 100644 --- a/examples/Interaction.cpp +++ b/examples/Interaction.cpp @@ -48,32 +48,32 @@ struct UserExample : tvgexam::Example //pad1 touch? if (auto paint = picture->paint(tvg::Accessor::id("pad1"))) { - if (hitting(paint, x, y, 0.2222f, 0.3333f)) return true; + if (hitting(paint, x, y, 20.0f, 30.0f)) return true; } //pad3 touch? if (auto paint = picture->paint(tvg::Accessor::id("pad3"))) { - if (hitting(paint, x, y, 0.4444f, 0.5555f)) return true; + if (hitting(paint, x, y, 40.0f, 50.0f)) return true; } //pad5 touch? if (auto paint = picture->paint(tvg::Accessor::id("pad5"))) { - if (hitting(paint, x, y, 0.1111f, 0.2222f)) return true; + if (hitting(paint, x, y, 10.0f, 20.0f)) return true; } //pad7 touch? if (auto paint = picture->paint(tvg::Accessor::id("pad7"))) { - if (hitting(paint, x, y, 0.0000f, 0.1111f)) return true; + if (hitting(paint, x, y, 0.0f, 10.0f)) return true; } //pad9 touch? if (auto paint = picture->paint(tvg::Accessor::id("pad9"))) { - if (hitting(paint, x, y, 0.3333f, 0.4444f)) return true; + if (hitting(paint, x, y, 30.0f, 40.0f)) return true; } //bar touch? if (auto paint = picture->paint(tvg::Accessor::id("bar"))) { - if (hitting(paint, x, y, 0.6666f, 1.0f)) return true; + if (hitting(paint, x, y, 60.0f, 90.0f)) return true; } return false; diff --git a/inc/thorvg.h b/inc/thorvg.h index d8b3e1cd..3782351e 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -1891,7 +1891,6 @@ public: * * @since 0.13 */ - class TVG_API Animation { public: @@ -1910,7 +1909,6 @@ public: * Values less than 0.001 may be disregarded and may not be accurately retained by the Animation. * * @see totalFrame() - * */ Result frame(float no) noexcept; @@ -1924,7 +1922,6 @@ public: * @return A picture instance that is tied to this animation. * * @warning The picture instance is owned by Animation. It should not be deleted manually. - * */ Picture* picture() const noexcept; @@ -1935,9 +1932,8 @@ public: * * @note If the Picture is not properly configured, this function will return 0. * - * @see Animation::frame(float no) + * @see Animation::frame() * @see Animation::totalFrame() - * */ float curFrame() const noexcept; @@ -1948,7 +1944,6 @@ public: * * @note Frame numbering starts from 0. * @note If the Picture is not properly configured, this function will return 0. - * */ float totalFrame() const noexcept; @@ -1958,7 +1953,6 @@ public: * @return The duration of the animation in seconds. * * @note If the Picture is not properly configured, this function will return 0. - * */ float duration() const noexcept; @@ -1970,30 +1964,33 @@ public: * After setting, the number of animation frames and the playback time are calculated * by mapping the playback segment as the entire range. * - * @param[in] begin segment start. - * @param[in] end segment end. + * @param[in] begin segment begin frame. + * @param[in] end segment end frame. * * @retval Result::InsufficientCondition In case the animation is not loaded. + * @retval Result::InvalidArguments If the @p begin is higher than @p end. * @retval Result::NonSupport When it's not animatable. * - * @note Animation allows a range from 0.0 to 1.0. @p end should not be higher than @p begin. + * @note Animation allows a range from 0.0 to the total frame. @p end should not be higher than @p begin. * @note If a marker has been specified, its range will be disregarded. - * @see LottieAnimation::segment(const char* marker) * - * @note Experimental API + * @see LottieAnimation::segment(const char* marker) + * @see Animation::totalFrame() + * + * @since 1.0 */ Result segment(float begin, float end) noexcept; /** - * @brief Gets the current segment. + * @brief Gets the current segment range information. * - * @param[out] begin segment start. - * @param[out] end segment end. + * @param[out] begin segment begin frame. + * @param[out] end segment end frame. * * @retval Result::InsufficientCondition In case the animation is not loaded. * @retval Result::NonSupport When it's not animatable. * - * @note Experimental API + * @since 1.0 */ Result segment(float* begin, float* end = nullptr) noexcept; @@ -2001,7 +1998,6 @@ public: * @brief Creates a new Animation object. * * @return A new Animation object. - * */ static Animation* gen() noexcept; diff --git a/src/bindings/capi/thorvg_capi.h b/src/bindings/capi/thorvg_capi.h index d5deca67..c4dd9c6a 100644 --- a/src/bindings/capi/thorvg_capi.h +++ b/src/bindings/capi/thorvg_capi.h @@ -2373,31 +2373,42 @@ TVG_API Tvg_Result tvg_animation_get_duration(Tvg_Animation* animation, float* d /*! * @brief Specifies the playback segment of the animation. * +* The set segment is designated as the play area of the animation. +* This is useful for playing a specific segment within the entire animation. +* After setting, the number of animation frames and the playback time are calculated +* by mapping the playback segment as the entire range. +* * @param[in] animation The Tvg_Animation pointer to the animation object. -* @param[in] begin segment begin. -* @param[in] end segment end. +* @param[in] begin segment begin frame. +* @param[in] end segment end frame. * * @return Tvg_Result enumeration. * @retval TVG_RESULT_INSUFFICIENT_CONDITION In case the animation is not loaded. -* @retval TVG_RESULT_INVALID_ARGUMENT When the given parameters are out of range. +* @retval TVG_RESULT_INVALID_ARGUMENT If the @p begin is higher than @p end. * -* @note Experimental API +* @note Animation allows a range from 0.0 to the total frame. @p end should not be higher than @p begin. +* @note If a marker has been specified, its range will be disregarded. +* +* @see tvg_lottie_animation_set_marker() +* @see tvg_animation_get_total_frame() + +* @since 1.0 */ TVG_API Tvg_Result tvg_animation_set_segment(Tvg_Animation* animation, float begin, float end); /*! -* @brief Gets the current segment. +* @brief Gets the current segment range information. * * @param[in] animation The Tvg_Animation pointer to the animation object. -* @param[out] begin segment begin. -* @param[out] end segment end. +* @param[out] begin segment begin frame. +* @param[out] end segment end frame. * * @return Tvg_Result enumeration. * @retval TVG_RESULT_INSUFFICIENT_CONDITION In case the animation is not loaded. -* @retval TVG_RESULT_INVALID_ARGUMENT When the given parameters are @c nullptr. +* @retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Animation pointer. * -* @note Experimental API +* @since 1.0 */ TVG_API Tvg_Result tvg_animation_get_segment(Tvg_Animation* animation, float* begin, float* end); diff --git a/src/loaders/lottie/tvgLottieAnimation.cpp b/src/loaders/lottie/tvgLottieAnimation.cpp index 0811bbb6..7b5f8332 100644 --- a/src/loaders/lottie/tvgLottieAnimation.cpp +++ b/src/loaders/lottie/tvgLottieAnimation.cpp @@ -47,14 +47,14 @@ Result LottieAnimation::segment(const char* marker) noexcept if (!loader) return Result::InsufficientCondition; if (!marker) { - static_cast(loader)->segment(0.0f, 1.0f); + static_cast(loader)->segment(0.0f, FLT_MAX); return Result::Success; } float begin, end; if (!static_cast(loader)->segment(marker, begin, end)) return Result::InvalidArguments; - return static_cast(this)->segment(begin, end); + return static_cast(loader)->segment(begin, end); } diff --git a/src/loaders/lottie/tvgLottieLoader.cpp b/src/loaders/lottie/tvgLottieLoader.cpp index 144d92af..66364ba8 100644 --- a/src/loaders/lottie/tvgLottieLoader.cpp +++ b/src/loaders/lottie/tvgLottieLoader.cpp @@ -97,8 +97,7 @@ bool LottieLoader::header() if (comp) { w = static_cast(comp->w); h = static_cast(comp->h); - frameDuration = comp->duration(); - frameCnt = comp->frameCnt(); + segmentEnd = frameCnt = comp->frameCnt(); frameRate = comp->frameRate; return true; } else { @@ -190,10 +189,9 @@ bool LottieLoader::header() return false; } - frameCnt = (endFrame - startFrame); - frameDuration = frameCnt / frameRate; + segmentEnd = frameCnt = (endFrame - startFrame); - TVGLOG("LOTTIE", "info: frame rate = %f, duration = %f size = %f x %f", frameRate, frameDuration, w, h); + TVGLOG("LOTTIE", "info: frame rate = %f, duration = %f size = %f x %f", frameRate, frameCnt / frameRate, w, h); return true; } @@ -355,13 +353,13 @@ bool LottieLoader::frame(float no) float LottieLoader::startFrame() { - return frameCnt * segmentBegin; + return segmentBegin; } float LottieLoader::totalFrame() { - return (segmentEnd - segmentBegin) * frameCnt; + return segmentEnd - segmentBegin; } @@ -373,8 +371,7 @@ float LottieLoader::curFrame() float LottieLoader::duration() { - if (segmentBegin == 0.0f && segmentEnd == 1.0f) return frameDuration; - return frameCnt * (segmentEnd - segmentBegin) / frameRate; + return (segmentEnd - segmentBegin) / frameRate; } @@ -400,14 +397,28 @@ const char* LottieLoader::markers(uint32_t index) } +Result LottieLoader::segment(float begin, float end) +{ + if (begin < 0.0f) begin = 0.0f; + if (end > frameCnt) end = frameCnt; + + if (begin > end) return Result::InvalidArguments; + + segmentBegin = begin; + segmentEnd = end; + + return Result::Success; +} + + bool LottieLoader::segment(const char* marker, float& begin, float& end) { if (!ready() || comp->markers.count == 0) return false; ARRAY_FOREACH(p, comp->markers) { if (!strcmp(marker, (*p)->name)) { - begin = (*p)->time / frameCnt; - end = ((*p)->time + (*p)->duration) / frameCnt; + begin = (*p)->time; + end = (*p)->time + (*p)->duration; return true; } } diff --git a/src/loaders/lottie/tvgLottieLoader.h b/src/loaders/lottie/tvgLottieLoader.h index 0c0f65ea..3b86f0e2 100644 --- a/src/loaders/lottie/tvgLottieLoader.h +++ b/src/loaders/lottie/tvgLottieLoader.h @@ -37,7 +37,6 @@ public: uint32_t size = 0; //lottie data size float frameNo = 0.0f; //current frame number float frameCnt = 0.0f; - float frameDuration = 0.0f; float frameRate = 0.0f; LottieBuilder* builder; @@ -70,6 +69,7 @@ public: uint32_t markersCnt(); const char* markers(uint32_t index); bool segment(const char* marker, float& begin, float& end); + Result segment(float begin, float end) override; private: bool ready(); diff --git a/src/renderer/tvgAnimation.cpp b/src/renderer/tvgAnimation.cpp index 03e9b83c..724b4c4a 100644 --- a/src/renderer/tvgAnimation.cpp +++ b/src/renderer/tvgAnimation.cpp @@ -88,15 +88,11 @@ float Animation::duration() const noexcept Result Animation::segment(float begin, float end) noexcept { - if (begin < 0.0f || end > 1.0f || begin > end) return Result::InvalidArguments; - auto loader = PICTURE(pImpl->picture)->loader; if (!loader) return Result::InsufficientCondition; if (!loader->animatable()) return Result::NonSupport; - static_cast(loader)->segment(begin, end); - - return Result::Success; + return static_cast(loader)->segment(begin, end); } diff --git a/src/renderer/tvgFrameModule.h b/src/renderer/tvgFrameModule.h index e5a68bc1..4060586b 100644 --- a/src/renderer/tvgFrameModule.h +++ b/src/renderer/tvgFrameModule.h @@ -32,7 +32,7 @@ class FrameModule: public ImageLoader { public: float segmentBegin = 0.0f; - float segmentEnd = 1.0f; + float segmentEnd; //Initialize the value with the total frame number FrameModule(FileType type) : ImageLoader(type) {} virtual ~FrameModule() {} @@ -41,6 +41,7 @@ public: 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 Result segment(float begin, float end) = 0; void segment(float* begin, float* end) { @@ -48,12 +49,6 @@ public: if (end) *end = segmentEnd; } - void segment(float begin, float end) - { - segmentBegin = begin; - segmentEnd = end; - } - virtual bool animatable() override { return true; } }; diff --git a/test/testAnimation.cpp b/test/testAnimation.cpp index 12098412..7c25abca 100644 --- a/test/testAnimation.cpp +++ b/test/testAnimation.cpp @@ -301,7 +301,7 @@ TEST_CASE("Animation Segment", "[tvgAnimation]") //Get current segment before segment REQUIRE(animation->segment(&begin, &end) == Result::Success); REQUIRE(begin == 0.0f); - REQUIRE(end == 1.0f); + REQUIRE(end == animation->totalFrame()); //Segment by range REQUIRE(animation->segment(0.25, 0.5) == Result::Success); @@ -320,7 +320,7 @@ TEST_CASE("Animation Segment", "[tvgAnimation]") REQUIRE(end == 0.5f); //Segment by invalid range - REQUIRE(animation->segment(-0.5, 1.5) == Result::InvalidArguments); + REQUIRE(animation->segment(1.5, -0.5) == Result::InvalidArguments); REQUIRE(Initializer::term() == Result::Success); }