mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-13 19:44:28 +00:00
lottie: ++scene rendering optimization
Apply LottieRenderPooler to path/rect/ellipse/polystar. This enhances the animation performance: ~10%
This commit is contained in:
parent
d0210a1a72
commit
cf253dd873
10 changed files with 138 additions and 83 deletions
|
@ -99,7 +99,7 @@ struct RenderContext
|
|||
static void _updateChildren(LottieGroup* parent, float frameNo, Inlist<RenderContext>& contexts, LottieExpressions* exps);
|
||||
static void _updateLayer(LottieLayer* root, LottieLayer* layer, float frameNo, LottieExpressions* exps);
|
||||
static bool _buildComposition(LottieComposition* comp, LottieLayer* parent);
|
||||
static bool _draw(LottieGroup* parent, RenderContext* ctx);
|
||||
static bool _draw(LottieGroup* parent, LottieShape* shape, RenderContext* ctx);
|
||||
|
||||
|
||||
static void _rotationXYZ(Matrix* m, float degreeX, float degreeY, float degreeZ)
|
||||
|
@ -275,7 +275,7 @@ static void _updateGroup(LottieGroup* parent, LottieObject** child, float frameN
|
|||
group->reqFragment |= ctx->reqFragment;
|
||||
|
||||
//generate a merging shape to consolidate partial shapes into a single entity
|
||||
if (group->mergeable()) _draw(parent, ctx);
|
||||
if (group->mergeable()) _draw(parent, nullptr, ctx);
|
||||
|
||||
Inlist<RenderContext> contexts;
|
||||
contexts.back(new RenderContext(*ctx, group->mergeable()));
|
||||
|
@ -374,20 +374,24 @@ static void _updateGradientFill(TVG_UNUSED LottieGroup* parent, LottieObject** c
|
|||
}
|
||||
|
||||
|
||||
static bool _draw(LottieGroup* parent, RenderContext* ctx)
|
||||
static bool _draw(LottieGroup* parent, LottieShape* shape, RenderContext* ctx)
|
||||
{
|
||||
if (ctx->merging) return false;
|
||||
|
||||
auto shape = cast<Shape>(ctx->propagator->duplicate());
|
||||
ctx->merging = shape.get();
|
||||
parent->scene->push(std::move(shape));
|
||||
if (shape) {
|
||||
ctx->merging = shape->pooling();
|
||||
PP(ctx->propagator)->duplicate(ctx->merging);
|
||||
} else {
|
||||
ctx->merging = static_cast<Shape*>(ctx->propagator->duplicate());
|
||||
}
|
||||
|
||||
parent->scene->push(cast(ctx->merging));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//OPTIMIZE: path?
|
||||
static void _repeat(LottieGroup* parent, unique_ptr<Shape> path, RenderContext* ctx)
|
||||
static void _repeat(LottieGroup* parent, Shape* path, RenderContext* ctx)
|
||||
{
|
||||
Array<Shape*> propagators;
|
||||
propagators.push(ctx->propagator);
|
||||
|
@ -401,7 +405,7 @@ static void _repeat(LottieGroup* parent, unique_ptr<Shape> path, RenderContext*
|
|||
|
||||
for (auto propagator = propagators.begin(); propagator < propagators.end(); ++propagator) {
|
||||
auto shape = static_cast<Shape*>((*propagator)->duplicate());
|
||||
P(shape)->rs.path = P(path.get())->rs.path;
|
||||
P(shape)->rs.path = P(path)->rs.path;
|
||||
|
||||
auto opacity = repeater->interpOpacity ? mathLerp<uint8_t>(repeater->startOpacity, repeater->endOpacity, static_cast<float>(i + 1) / repeater->cnt) : repeater->startOpacity;
|
||||
shape->opacity(opacity);
|
||||
|
@ -542,11 +546,12 @@ static void _updateRect(LottieGroup* parent, LottieObject** child, float frameNo
|
|||
}
|
||||
|
||||
if (!ctx->repeaters.empty()) {
|
||||
auto path = Shape::gen();
|
||||
_appendRect(path.get(), position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, ctx->transform, rect->clockwise);
|
||||
_repeat(parent, std::move(path), ctx);
|
||||
auto shape = rect->pooling();
|
||||
shape->reset();
|
||||
_appendRect(shape, position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, ctx->transform, rect->clockwise);
|
||||
_repeat(parent, shape, ctx);
|
||||
} else {
|
||||
_draw(parent, ctx);
|
||||
_draw(parent, rect, ctx);
|
||||
_appendRect(ctx->merging, position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, ctx->transform, rect->clockwise);
|
||||
}
|
||||
}
|
||||
|
@ -598,11 +603,12 @@ static void _updateEllipse(LottieGroup* parent, LottieObject** child, float fram
|
|||
auto size = ellipse->size(frameNo, exps);
|
||||
|
||||
if (!ctx->repeaters.empty()) {
|
||||
auto path = Shape::gen();
|
||||
_appendCircle(path.get(), position.x, position.y, size.x * 0.5f, size.y * 0.5f, ctx->transform, ellipse->clockwise);
|
||||
_repeat(parent, std::move(path), ctx);
|
||||
auto shape = ellipse->pooling();
|
||||
shape->reset();
|
||||
_appendCircle(shape, position.x, position.y, size.x * 0.5f, size.y * 0.5f, ctx->transform, ellipse->clockwise);
|
||||
_repeat(parent, shape, ctx);
|
||||
} else {
|
||||
_draw(parent, ctx);
|
||||
_draw(parent, ellipse, ctx);
|
||||
_appendCircle(ctx->merging, position.x, position.y, size.x * 0.5f, size.y * 0.5f, ctx->transform, ellipse->clockwise);
|
||||
}
|
||||
}
|
||||
|
@ -613,11 +619,12 @@ static void _updatePath(LottieGroup* parent, LottieObject** child, float frameNo
|
|||
auto path = static_cast<LottiePath*>(*child);
|
||||
|
||||
if (!ctx->repeaters.empty()) {
|
||||
auto p = Shape::gen();
|
||||
path->pathset(frameNo, P(p)->rs.path.cmds, P(p)->rs.path.pts, ctx->transform, ctx->roundness, exps);
|
||||
_repeat(parent, std::move(p), ctx);
|
||||
auto shape = path->pooling();
|
||||
shape->reset();
|
||||
path->pathset(frameNo, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, ctx->transform, ctx->roundness, exps);
|
||||
_repeat(parent, shape, ctx);
|
||||
} else {
|
||||
_draw(parent, ctx);
|
||||
_draw(parent, path, ctx);
|
||||
if (path->pathset(frameNo, P(ctx->merging)->rs.path.cmds, P(ctx->merging)->rs.path.pts, ctx->transform, ctx->roundness, exps)) {
|
||||
P(ctx->merging)->update(RenderUpdateFlag::Path);
|
||||
}
|
||||
|
@ -708,8 +715,14 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans
|
|||
auto direction = star->clockwise ? 1.0f : -1.0f;
|
||||
auto hasRoundness = false;
|
||||
bool roundedCorner = (roundness > ROUNDNESS_EPSILON) && (mathZero(innerRoundness) || mathZero(outerRoundness));
|
||||
//TODO: we can use PathCommand / PathCoord directly.
|
||||
auto shape = roundedCorner ? Shape::gen().release() : merging;
|
||||
|
||||
Shape* shape;
|
||||
if (roundedCorner) {
|
||||
shape = star->pooling();
|
||||
shape->reset();
|
||||
} else {
|
||||
shape = merging;
|
||||
}
|
||||
|
||||
float x, y;
|
||||
|
||||
|
@ -798,10 +811,7 @@ static void _updateStar(LottieGroup* parent, LottiePolyStar* star, Matrix* trans
|
|||
}
|
||||
shape->close();
|
||||
|
||||
if (roundedCorner) {
|
||||
_applyRoundedCorner(shape, merging, outerRoundness, roundness, hasRoundness);
|
||||
delete(shape);
|
||||
}
|
||||
if (roundedCorner) _applyRoundedCorner(shape, merging, outerRoundness, roundness, hasRoundness);
|
||||
}
|
||||
|
||||
|
||||
|
@ -876,7 +886,7 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr
|
|||
|
||||
static void _updatePolystar(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx, LottieExpressions* exps)
|
||||
{
|
||||
auto star= static_cast<LottiePolyStar*>(*child);
|
||||
auto star = static_cast<LottiePolyStar*>(*child);
|
||||
|
||||
//Optimize: Can we skip the individual coords transform?
|
||||
Matrix matrix;
|
||||
|
@ -890,12 +900,13 @@ static void _updatePolystar(LottieGroup* parent, LottieObject** child, float fra
|
|||
auto identity = mathIdentity((const Matrix*)&matrix);
|
||||
|
||||
if (!ctx->repeaters.empty()) {
|
||||
auto p = Shape::gen();
|
||||
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, ctx->roundness, frameNo, p.get(), exps);
|
||||
else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, p.get(), exps);
|
||||
_repeat(parent, std::move(p), ctx);
|
||||
auto shape = star->pooling();
|
||||
shape->reset();
|
||||
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, ctx->roundness, frameNo, shape, exps);
|
||||
else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, shape, exps);
|
||||
_repeat(parent, shape, ctx);
|
||||
} else {
|
||||
_draw(parent, ctx);
|
||||
_draw(parent, star, ctx);
|
||||
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, ctx->roundness, frameNo, ctx->merging, exps);
|
||||
else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, ctx->merging, exps);
|
||||
P(ctx->merging)->update(RenderUpdateFlag::Path);
|
||||
|
|
|
@ -37,6 +37,16 @@
|
|||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void LottieShape::prepare(LottieObject::Type type)
|
||||
{
|
||||
LottieObject::type = type;
|
||||
|
||||
auto shape = tvg::Shape::gen().release();
|
||||
PP(shape)->ref();
|
||||
pooler.push(shape);
|
||||
}
|
||||
|
||||
|
||||
void LottieSlot::reset()
|
||||
{
|
||||
if (!overriden) return;
|
||||
|
|
|
@ -266,15 +266,18 @@ struct LottieTrimpath : LottieObject
|
|||
};
|
||||
|
||||
|
||||
struct LottieShape : LottieObject
|
||||
struct LottieShape : LottieObject, LottieRenderPooler<tvg::Shape>
|
||||
{
|
||||
virtual ~LottieShape() {}
|
||||
bool clockwise = true; //clockwise or counter-clockwise
|
||||
|
||||
virtual ~LottieShape() {}
|
||||
|
||||
bool mergeable() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void prepare(LottieObject::Type type);
|
||||
};
|
||||
|
||||
|
||||
|
@ -292,7 +295,7 @@ struct LottiePath : LottieShape
|
|||
{
|
||||
void prepare()
|
||||
{
|
||||
LottieObject::type = LottieObject::Path;
|
||||
LottieShape::prepare(LottieObject::Path);
|
||||
}
|
||||
|
||||
LottiePathSet pathset;
|
||||
|
@ -303,7 +306,7 @@ struct LottieRect : LottieShape
|
|||
{
|
||||
void prepare()
|
||||
{
|
||||
LottieObject::type = LottieObject::Rect;
|
||||
LottieShape::prepare(LottieObject::Rect);
|
||||
}
|
||||
|
||||
LottiePosition position = Point{0.0f, 0.0f};
|
||||
|
@ -318,7 +321,7 @@ struct LottiePolyStar : LottieShape
|
|||
|
||||
void prepare()
|
||||
{
|
||||
LottieObject::type = LottieObject::Polystar;
|
||||
LottieShape::prepare(LottieObject::Polystar);
|
||||
}
|
||||
|
||||
LottiePosition position = Point{0.0f, 0.0f};
|
||||
|
@ -336,7 +339,7 @@ struct LottieEllipse : LottieShape
|
|||
{
|
||||
void prepare()
|
||||
{
|
||||
LottieObject::type = LottieObject::Ellipse;
|
||||
LottieShape::prepare(LottieObject::Ellipse);
|
||||
}
|
||||
|
||||
LottiePosition position = Point{0.0f, 0.0f};
|
||||
|
@ -552,7 +555,7 @@ struct LottieGroup : LottieObject
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Scene* scene = nullptr; //tvg render data
|
||||
Scene* scene = nullptr;
|
||||
Array<LottieObject*> children;
|
||||
|
||||
bool reqFragment = false; //requirment to fragment the render context
|
||||
|
|
|
@ -158,16 +158,22 @@ Iterator* Paint::Impl::iterator()
|
|||
}
|
||||
|
||||
|
||||
Paint* Paint::Impl::duplicate()
|
||||
Paint* Paint::Impl::duplicate(Paint* ret)
|
||||
{
|
||||
Paint* ret;
|
||||
PAINT_METHOD(ret, duplicate());
|
||||
if (ret) ret->composite(nullptr, CompositeMethod::None);
|
||||
|
||||
PAINT_METHOD(ret, duplicate(ret));
|
||||
|
||||
//duplicate Transform
|
||||
if (rTransform) {
|
||||
ret->pImpl->rTransform = new RenderTransform();
|
||||
if (!ret->pImpl->rTransform) {
|
||||
ret->pImpl->rTransform = new RenderTransform();
|
||||
}
|
||||
*ret->pImpl->rTransform = *rTransform;
|
||||
ret->pImpl->renderFlag |= RenderUpdateFlag::Transform;
|
||||
} else {
|
||||
delete(ret->pImpl->rTransform);
|
||||
ret->pImpl->rTransform = nullptr;
|
||||
}
|
||||
|
||||
ret->pImpl->opacity = opacity;
|
||||
|
|
|
@ -138,7 +138,7 @@ namespace tvg
|
|||
bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking);
|
||||
RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
|
||||
bool render(RenderMethod* renderer);
|
||||
Paint* duplicate();
|
||||
Paint* duplicate(Paint* ret = nullptr);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -189,19 +189,21 @@ struct Picture::Impl
|
|||
}
|
||||
}
|
||||
|
||||
Paint* duplicate()
|
||||
Paint* duplicate(Paint* ret)
|
||||
{
|
||||
if (ret) TVGERR("RENDERER", "TODO: duplicate()");
|
||||
|
||||
load();
|
||||
|
||||
auto ret = Picture::gen().release();
|
||||
auto dup = ret->pImpl;
|
||||
auto picture = Picture::gen().release();
|
||||
auto dup = picture->pImpl;
|
||||
|
||||
if (paint) dup->paint = paint->duplicate();
|
||||
|
||||
if (loader) {
|
||||
dup->loader = loader;
|
||||
++dup->loader->sharing;
|
||||
PP(ret)->renderFlag |= RenderUpdateFlag::Image;
|
||||
PP(picture)->renderFlag |= RenderUpdateFlag::Image;
|
||||
}
|
||||
|
||||
dup->surface = surface;
|
||||
|
@ -215,7 +217,7 @@ struct Picture::Impl
|
|||
memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return picture;
|
||||
}
|
||||
|
||||
Iterator* iterator()
|
||||
|
|
|
@ -147,6 +147,31 @@ struct RenderStroke
|
|||
bool simultaneous = true;
|
||||
} trim;
|
||||
|
||||
void operator=(const RenderStroke& rhs)
|
||||
{
|
||||
width = rhs.width;
|
||||
|
||||
memcpy(color, rhs.color, sizeof(color));
|
||||
|
||||
delete(fill);
|
||||
if (rhs.fill) fill = rhs.fill->duplicate();
|
||||
else fill = nullptr;
|
||||
|
||||
free(dashPattern);
|
||||
if (rhs.dashCnt > 0) {
|
||||
dashPattern = static_cast<float*>(malloc(sizeof(float) * rhs.dashCnt));
|
||||
memcpy(dashPattern, rhs.dashPattern, sizeof(float) * rhs.dashCnt);
|
||||
} else {
|
||||
dashPattern = nullptr;
|
||||
}
|
||||
dashCnt = rhs.dashCnt;
|
||||
miterlimit = rhs.miterlimit;
|
||||
cap = rhs.cap;
|
||||
join = rhs.join;
|
||||
strokeFirst = rhs.strokeFirst;
|
||||
trim = rhs.trim;
|
||||
}
|
||||
|
||||
~RenderStroke()
|
||||
{
|
||||
free(dashPattern);
|
||||
|
|
|
@ -198,10 +198,12 @@ struct Scene::Impl
|
|||
return true;
|
||||
}
|
||||
|
||||
Paint* duplicate()
|
||||
Paint* duplicate(Paint* ret)
|
||||
{
|
||||
auto ret = Scene::gen().release();
|
||||
auto dup = ret->pImpl;
|
||||
if (ret) TVGERR("RENDERER", "TODO: duplicate()");
|
||||
|
||||
auto scene = Scene::gen().release();
|
||||
auto dup = scene->pImpl;
|
||||
|
||||
for (auto paint : paints) {
|
||||
auto cdup = paint->duplicate();
|
||||
|
@ -209,7 +211,7 @@ struct Scene::Impl
|
|||
dup->paints.push_back(cdup);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return scene;
|
||||
}
|
||||
|
||||
void clear(bool free)
|
||||
|
|
|
@ -34,6 +34,7 @@ struct Shape::Impl
|
|||
RenderData rd = nullptr; //engine data
|
||||
Shape* shape;
|
||||
uint8_t flag = RenderUpdateFlag::None;
|
||||
|
||||
uint8_t opacity; //for composition
|
||||
bool needComp = false; //composite or not
|
||||
|
||||
|
@ -359,47 +360,40 @@ struct Shape::Impl
|
|||
this->flag |= flag;
|
||||
}
|
||||
|
||||
Paint* duplicate()
|
||||
Paint* duplicate(Paint* ret)
|
||||
{
|
||||
auto ret = Shape::gen().release();
|
||||
auto dup = ret->pImpl;
|
||||
auto shape = static_cast<Shape*>(ret);
|
||||
if (shape) shape->reset();
|
||||
else shape = Shape::gen().release();
|
||||
|
||||
auto dup = shape->pImpl;
|
||||
delete(dup->rs.fill);
|
||||
|
||||
//Default Properties
|
||||
dup->flag = RenderUpdateFlag::All;
|
||||
dup->rs.rule = rs.rule;
|
||||
|
||||
//Color
|
||||
memcpy(dup->rs.color, rs.color, sizeof(rs.color));
|
||||
dup->flag = RenderUpdateFlag::Color;
|
||||
|
||||
//Path
|
||||
if (rs.path.cmds.count > 0 && rs.path.pts.count > 0) {
|
||||
dup->rs.path.cmds = rs.path.cmds;
|
||||
dup->rs.path.pts = rs.path.pts;
|
||||
dup->flag |= RenderUpdateFlag::Path;
|
||||
}
|
||||
dup->rs.path.cmds.push(rs.path.cmds);
|
||||
dup->rs.path.pts.push(rs.path.pts);
|
||||
|
||||
//Stroke
|
||||
if (rs.stroke) {
|
||||
dup->rs.stroke = new RenderStroke();
|
||||
if (!dup->rs.stroke) dup->rs.stroke = new RenderStroke;
|
||||
*dup->rs.stroke = *rs.stroke;
|
||||
memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color));
|
||||
if (rs.stroke->dashCnt > 0) {
|
||||
dup->rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * rs.stroke->dashCnt));
|
||||
memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt);
|
||||
}
|
||||
if (rs.stroke->fill) {
|
||||
dup->rs.stroke->fill = rs.stroke->fill->duplicate();
|
||||
dup->flag |= RenderUpdateFlag::GradientStroke;
|
||||
}
|
||||
dup->flag |= RenderUpdateFlag::Stroke;
|
||||
} else {
|
||||
delete(dup->rs.stroke);
|
||||
dup->rs.stroke = nullptr;
|
||||
}
|
||||
|
||||
//Fill
|
||||
if (rs.fill) {
|
||||
dup->rs.fill = rs.fill->duplicate();
|
||||
dup->flag |= RenderUpdateFlag::Gradient;
|
||||
}
|
||||
if (rs.fill) dup->rs.fill = rs.fill->duplicate();
|
||||
else dup->rs.fill = nullptr;
|
||||
|
||||
return ret;
|
||||
return shape;
|
||||
}
|
||||
|
||||
Iterator* iterator()
|
||||
|
|
|
@ -152,12 +152,14 @@ struct Text::Impl
|
|||
return true;
|
||||
}
|
||||
|
||||
Paint* duplicate()
|
||||
Paint* duplicate(Paint* ret)
|
||||
{
|
||||
if (ret) TVGERR("RENDERER", "TODO: duplicate()");
|
||||
|
||||
load();
|
||||
|
||||
auto ret = Text::gen().release();
|
||||
auto dup = ret->pImpl;
|
||||
auto text = Text::gen().release();
|
||||
auto dup = text->pImpl;
|
||||
if (paint) dup->paint = static_cast<Shape*>(paint->duplicate());
|
||||
|
||||
if (loader) {
|
||||
|
@ -169,7 +171,7 @@ struct Text::Impl
|
|||
dup->italic = italic;
|
||||
dup->fontSize = fontSize;
|
||||
|
||||
return ret;
|
||||
return text;
|
||||
}
|
||||
|
||||
Iterator* iterator()
|
||||
|
|
Loading…
Add table
Reference in a new issue