diff --git a/inc/rlottie.h b/inc/rlottie.h index 67565691..ce3d114b 100644 --- a/inc/rlottie.h +++ b/inc/rlottie.h @@ -142,7 +142,7 @@ class RLOTTIE_API Surface { * @brief Sets the Draw Area available on the Surface. * * Lottie will use the draw region size to generate frame image - * and will update only the draw rgion of the surface. + * and will update only the draw region of the surface. * * @param[in] x region area x position. * @param[in] y region area y position. @@ -421,6 +421,18 @@ class RLOTTIE_API Animation { */ void renderSync(size_t frameNo, Surface surface, bool keepAspectRatio=true); + /** + * @brief Renders the content to partial surface synchronously. + * for performance use the async rendering @see render + * + * @param[in] frameNo Content corresponds to the @p frameNo needs to be drawn + * @param[in] surface Surface in which content will be drawn + * + * @internal + */ + void renderPartialSync(size_t frameNo, Surface surface); + + /** * @brief Returns root layer of the composition updated with * content of the Lottie resource at frame number @p frameNo. diff --git a/inc/rlottie_capi.h b/inc/rlottie_capi.h index 80907ae6..afe324da 100644 --- a/inc/rlottie_capi.h +++ b/inc/rlottie_capi.h @@ -231,6 +231,25 @@ RLOTTIE_API size_t lottie_animation_get_frame_at_pos(const Lottie_Animation *ani */ RLOTTIE_API void lottie_animation_render(Lottie_Animation *animation, size_t frame_num, uint32_t *buffer, size_t width, size_t height, size_t bytes_per_line); +/** + * @brief Request to render the content of the frame @p frame_num to buffer @p buffer. + * + * @param[in] animation Animation object. + * @param[in] frame_num the frame number needs to be rendered. + * @param[in] buffer surface buffer use for rendering. + * @param[in] width width of the surface + * @param[in] height height of the surface + * @param[in] top offset to render from + * @param[in] bottom offset to render from + * @param[in] bytes_per_line stride of the surface in bytes. + * + * + * @ingroup Lottie_Animation + * @internal + */ +RLOTTIE_API void lottie_animation_render_partial(Lottie_Animation *animation, size_t frame_num, uint32_t *buffer, size_t width, size_t height, size_t top, size_t bottom, size_t bytes_per_line); + + /** * @brief Request to render the content of the frame @p frame_num to buffer @p buffer asynchronously. * diff --git a/src/binding/c/lottieanimation_capi.cpp b/src/binding/c/lottieanimation_capi.cpp index 53ca7bda..4a34fe91 100644 --- a/src/binding/c/lottieanimation_capi.cpp +++ b/src/binding/c/lottieanimation_capi.cpp @@ -168,6 +168,24 @@ lottie_animation_render(Lottie_Animation_S *animation, animation->mAnimation->renderSync(frame_number, surface); } +RLOTTIE_API void +lottie_animation_render_partial(Lottie_Animation_S *animation, + size_t frame_number, + uint32_t *buffer, + size_t width, + size_t height, + size_t top, + size_t bottom, + size_t bytes_per_line) +{ + if (!animation) return; + + rlottie::Surface surface(buffer, width, height, bytes_per_line); + surface.setDrawRegion(0, top, width, bottom - top); + animation->mAnimation->renderPartialSync(frame_number, surface); +} + + RLOTTIE_API void lottie_animation_render_async(Lottie_Animation_S *animation, size_t frame_number, diff --git a/src/lottie/lottieanimation.cpp b/src/lottie/lottieanimation.cpp index afcc400b..c4ff39e1 100644 --- a/src/lottie/lottieanimation.cpp +++ b/src/lottie/lottieanimation.cpp @@ -49,13 +49,16 @@ class AnimationImpl { public: void init(std::shared_ptr composition); bool update(size_t frameNo, const VSize &size, bool keepAspectRatio); + bool updatePartial(size_t frameNo, const VSize &size, uint32_t offset); VSize size() const { return mModel->size(); } double duration() const { return mModel->duration(); } double frameRate() const { return mModel->frameRate(); } size_t totalFrame() const { return mModel->totalFrame(); } size_t frameAtPos(double pos) const { return mModel->frameAtPos(pos); } + bool checkRender(); Surface render(size_t frameNo, const Surface &surface, bool keepAspectRatio); + Surface renderPartial(size_t frameNo, const Surface &surface); std::future renderAsync(size_t frameNo, Surface &&surface, bool keepAspectRatio); const LOTLayerNode * renderTree(size_t frameNo, const VSize &size); @@ -105,16 +108,34 @@ bool AnimationImpl::update(size_t frameNo, const VSize &size, return mRenderer->update(int(frameNo), size, keepAspectRatio); } -Surface AnimationImpl::render(size_t frameNo, const Surface &surface, - bool keepAspectRatio) +bool AnimationImpl::updatePartial(size_t frameNo, const VSize &size, uint32_t offset) +{ + frameNo += mModel->startFrame(); + + if (frameNo > mModel->endFrame()) frameNo = mModel->endFrame(); + + if (frameNo < mModel->startFrame()) frameNo = mModel->startFrame(); + + return mRenderer->updatePartial(int(frameNo), size, offset); +} + + +bool AnimationImpl::checkRender() { bool renderInProgress = mRenderInProgress.load(); if (renderInProgress) { vCritical << "Already Rendering Scheduled for this Animation"; - return surface; + return false; } - mRenderInProgress.store(true); + return true; +} + +Surface AnimationImpl::render(size_t frameNo, const Surface &surface, + bool keepAspectRatio) +{ + if (!checkRender()) return surface; + update( frameNo, VSize(int(surface.drawRegionWidth()), int(surface.drawRegionHeight())), @@ -125,6 +146,21 @@ Surface AnimationImpl::render(size_t frameNo, const Surface &surface, return surface; } +Surface AnimationImpl::renderPartial(size_t frameNo, const Surface &surface) +{ + if (!checkRender()) return surface; + + updatePartial( + frameNo, + VSize(int(surface.drawRegionWidth()), int(surface.drawRegionHeight())), + uint32_t(surface.drawRegionPosY())); + mRenderer->renderPartial(surface); + mRenderInProgress.store(false); + + return surface; +} + + void AnimationImpl::init(std::shared_ptr composition) { mModel = composition.get(); @@ -370,6 +406,11 @@ void Animation::renderSync(size_t frameNo, Surface surface, { d->render(frameNo, surface, keepAspectRatio); } +void Animation::renderPartialSync(size_t frameNo, Surface surface) +{ + d->renderPartial(frameNo, surface); +} + const LayerInfoList &Animation::layers() const { diff --git a/src/lottie/lottieitem.cpp b/src/lottie/lottieitem.cpp index cb1bc2cd..4f6f4e0a 100644 --- a/src/lottie/lottieitem.cpp +++ b/src/lottie/lottieitem.cpp @@ -147,6 +147,35 @@ bool renderer::Composition::update(int frameNo, const VSize &size, return true; } +bool renderer::Composition::updatePartial(int frameNo, const VSize &size, + unsigned int offset) +{ + // check if cached frame is same as requested frame. + if ((mViewSize.width() == size.width()) && (mCurFrameNo == frameNo) && + (mOffset == offset)) + return false; + + mViewSize = size; + mCurFrameNo = frameNo; + mKeepAspectRatio = false; + mOffset = offset; + + /* + * if viewbox dosen't scale exactly to the viewport + * we scale the viewbox keeping AspectRatioPreserved and then align the + * viewbox to the viewport using AlignCenter rule. + */ + VMatrix m; + VSize viewPort = mViewSize; + VSize viewBox = mModel->size(); + float sx = float(viewPort.width()) / viewBox.width(); + float ty = offset; + m.translate(0, -ty).scale(sx, sx); + + mRootLayer->update(frameNo, m, 1.0); + return true; +} + bool renderer::Composition::render(const rlottie::Surface &surface) { mSurface.reset(reinterpret_cast(surface.buffer()), @@ -170,6 +199,30 @@ bool renderer::Composition::render(const rlottie::Surface &surface) return true; } +bool renderer::Composition::renderPartial(const rlottie::Surface &surface) +{ + mSurface.reset(reinterpret_cast(surface.buffer()), + uint32_t(surface.width()), uint32_t(surface.drawRegionHeight()), + uint32_t(surface.bytesPerLine()), + VBitmap::Format::ARGB32_Premultiplied); + + /* schedule all preprocess task for this frame at once. + */ + VRect clip(0, 0, int(surface.drawRegionWidth()), + int(surface.drawRegionHeight())); + mRootLayer->preprocess(clip); + + VPainter painter(&mSurface); + // set sub surface area for drawing. + painter.setDrawRegion( + VRect(int(surface.drawRegionPosX()), 0, + int(surface.drawRegionWidth()), int(surface.drawRegionHeight()))); + mRootLayer->render(&painter, {}, {}, mSurfaceCache); + painter.end(); + return true; +} + + void renderer::Mask::update(int frameNo, const VMatrix &parentMatrix, float /*parentAlpha*/, const DirtyFlag &flag) { diff --git a/src/lottie/lottieitem.h b/src/lottie/lottieitem.h index 001ce667..b9dc2c5b 100644 --- a/src/lottie/lottieitem.h +++ b/src/lottie/lottieitem.h @@ -191,10 +191,12 @@ class Composition { public: explicit Composition(std::shared_ptr composition); bool update(int frameNo, const VSize &size, bool keepAspectRatio); + bool updatePartial(int frameNo, const VSize &size, unsigned int offset); VSize size() const { return mViewSize; } void buildRenderTree(); const LOTLayerNode *renderTree() const; bool render(const rlottie::Surface &surface); + bool renderPartial(const rlottie::Surface &surface); void setValue(const std::string &keypath, LOTVariant &value); private: @@ -207,6 +209,7 @@ class Composition { VArenaAlloc mAllocator{2048}; int mCurFrameNo; bool mKeepAspectRatio{true}; + unsigned int mOffset{0}; }; class Layer {