Skip to content

Commit

Permalink
Add support for partial rendering. This actually allows performing re…
Browse files Browse the repository at this point in the history
…ndering in passes

and does not require a `width * height * 4` bytes buffer anymore. On limited memory MCU,
this will allow rendering a large animation by trading off memory for CPU consumption instead.
  • Loading branch information
X-Ryl669 committed Dec 15, 2021
1 parent 327fb7d commit 870aabd
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 2 deletions.
4 changes: 3 additions & 1 deletion example/lottie2gif.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ class App {
GifBuilder builder(gifName.data(), w, h, bgColor);
for (size_t i = 0; i < frameCount ; i++) {
rlottie::Surface surface(buffer.get(), w, h, w * 4);
player->renderSync(i, surface);
surface.setDrawRegion(0, h/2, w, h/4);
// surface.setDrawRegion(0, 0, w, h);
player->renderPartialSync(i, surface);
builder.addFrame(surface);
}
return result();
Expand Down
14 changes: 13 additions & 1 deletion inc/rlottie.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
19 changes: 19 additions & 0 deletions inc/rlottie_capi.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,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.
*
Expand Down
18 changes: 18 additions & 0 deletions src/binding/c/lottieanimation_capi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,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,
Expand Down
40 changes: 40 additions & 0 deletions src/lottie/lottieanimation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ class AnimationImpl {
public:
void init(std::shared_ptr<model::Composition> composition);
bool update(size_t frameNo, const VSize &size, bool keepAspectRatio);
bool updatePartial(size_t frameNo, const VSize &size, uint 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); }
Surface render(size_t frameNo, const Surface &surface,
bool keepAspectRatio);
Surface renderPartial(size_t frameNo, const Surface &surface);
std::future<Surface> renderAsync(size_t frameNo, Surface &&surface,
bool keepAspectRatio);
const LOTLayerNode * renderTree(size_t frameNo, const VSize &size);
Expand Down Expand Up @@ -105,6 +107,19 @@ bool AnimationImpl::update(size_t frameNo, const VSize &size,
return mRenderer->update(int(frameNo), size, keepAspectRatio);
}

bool AnimationImpl::updatePartial(size_t frameNo, const VSize &size,
uint 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);
}


Surface AnimationImpl::render(size_t frameNo, const Surface &surface,
bool keepAspectRatio)
{
Expand All @@ -125,6 +140,26 @@ Surface AnimationImpl::render(size_t frameNo, const Surface &surface,
return surface;
}

Surface AnimationImpl::renderPartial(size_t frameNo, const Surface &surface)
{
bool renderInProgress = mRenderInProgress.load();
if (renderInProgress) {
vCritical << "Already Rendering Scheduled for this Animation";
return surface;
}

mRenderInProgress.store(true);
updatePartial(
frameNo,
VSize(int(surface.drawRegionWidth()), int(surface.drawRegionHeight())),
uint(surface.drawRegionPosY()));
mRenderer->renderPartial(surface);
mRenderInProgress.store(false);

return surface;
}


void AnimationImpl::init(std::shared_ptr<model::Composition> composition)
{
mModel = composition.get();
Expand Down Expand Up @@ -354,6 +389,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
{
Expand Down
53 changes: 53 additions & 0 deletions src/lottie/lottieitem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,35 @@ bool renderer::Composition::update(int frameNo, const VSize &size,
return true;
}

bool renderer::Composition::updatePartial(int frameNo, const VSize &size,
const uint 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<uchar *>(surface.buffer()),
Expand All @@ -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<uchar *>(surface.buffer()),
uint(surface.width()), uint(surface.drawRegionHeight()),
uint(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)
{
Expand Down
3 changes: 3 additions & 0 deletions src/lottie/lottieitem.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,12 @@ class Composition {
public:
explicit Composition(std::shared_ptr<model::Composition> composition);
bool update(int frameNo, const VSize &size, bool keepAspectRatio);
bool updatePartial(int frameNo, const VSize &size, uint 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:
Expand All @@ -207,6 +209,7 @@ class Composition {
VArenaAlloc mAllocator{2048};
int mCurFrameNo;
bool mKeepAspectRatio{true};
uint mOffset{0};
};

class Layer {
Expand Down

0 comments on commit 870aabd

Please sign in to comment.