lottie: repeater transformation fix

In the case of a transformation (denoted as T1)
appearing after the repeater, but at the same
level of nesting, the objects to which the repeater
is applied should not consider T1. However,
the transformation of the repeater itself should
take T1 into account.
Also, it is possible to have several consecutive
repeaters, and all of them should be taken into
account. Until now, the last one was overwriting
the previous one.
This commit is contained in:
Mira Grudzinska 2024-05-28 13:13:36 +02:00 committed by Hermet Park
parent b812277a06
commit 0e59daafc7

View file

@ -40,6 +40,7 @@
struct RenderRepeater struct RenderRepeater
{ {
int cnt; int cnt;
Matrix transform;
float offset; float offset;
Point position; Point position;
Point anchor; Point anchor;
@ -59,7 +60,7 @@ struct RenderContext
Shape* propagator = nullptr; Shape* propagator = nullptr;
Shape* merging = nullptr; //merging shapes if possible (if shapes have same properties) Shape* merging = nullptr; //merging shapes if possible (if shapes have same properties)
LottieObject** begin = nullptr; //iteration entry point LottieObject** begin = nullptr; //iteration entry point
RenderRepeater* repeater = nullptr; Array<RenderRepeater> repeaters;
Matrix* transform = nullptr; Matrix* transform = nullptr;
float roundness = 0.0f; float roundness = 0.0f;
bool fragmenting = false; //render context has been fragmented by filling bool fragmenting = false; //render context has been fragmented by filling
@ -74,7 +75,6 @@ struct RenderContext
~RenderContext() ~RenderContext()
{ {
if (ownPropagator) delete(propagator); if (ownPropagator) delete(propagator);
delete(repeater);
free(transform); free(transform);
} }
@ -88,9 +88,8 @@ struct RenderContext
propagator = static_cast<Shape*>(rhs.propagator->duplicate()); propagator = static_cast<Shape*>(rhs.propagator->duplicate());
} }
if (rhs.repeater) { for (auto repeater = rhs.repeaters.begin(); repeater < rhs.repeaters.end(); ++repeater) {
repeater = new RenderRepeater(); repeaters.push(*repeater);
*repeater = *rhs.repeater;
} }
roundness = rhs.roundness; roundness = rhs.roundness;
} }
@ -392,46 +391,59 @@ static Shape* _draw(LottieGroup* parent, RenderContext* ctx)
//OPTIMIZE: path? //OPTIMIZE: path?
static void _repeat(LottieGroup* parent, unique_ptr<Shape> path, RenderContext* ctx) static void _repeat(LottieGroup* parent, unique_ptr<Shape> path, RenderContext* ctx)
{ {
auto repeater = ctx->repeater; Array<Shape*> propagators;
propagators.push(ctx->propagator);
Array<Shape*> shapes;
Array<Shape*> shapes(repeater->cnt); for (auto repeater = ctx->repeaters.end() - 1; repeater >= ctx->repeaters.begin(); --repeater) {
shapes.reserve(repeater->cnt);
for (int i = 0; i < repeater->cnt; ++i) { for (int i = 0; i < repeater->cnt; ++i) {
auto multiplier = repeater->offset + static_cast<float>(i); auto multiplier = repeater->offset + static_cast<float>(i);
auto shape = static_cast<Shape*>(ctx->propagator->duplicate()); for (auto propagator = propagators.begin(); propagator < propagators.end(); ++propagator) {
P(shape)->rs.path = P(path.get())->rs.path; auto shape = static_cast<Shape*>((*propagator)->duplicate());
P(shape)->rs.path = P(path.get())->rs.path;
auto opacity = repeater->interpOpacity ? mathLerp<uint8_t>(repeater->startOpacity, repeater->endOpacity, static_cast<float>(i + 1) / repeater->cnt) : repeater->startOpacity; auto opacity = repeater->interpOpacity ? mathLerp<uint8_t>(repeater->startOpacity, repeater->endOpacity, static_cast<float>(i + 1) / repeater->cnt) : repeater->startOpacity;
shape->opacity(opacity); shape->opacity(opacity);
Matrix m; Matrix m;
mathIdentity(&m); mathIdentity(&m);
mathTranslate(&m, repeater->position.x * multiplier + repeater->anchor.x, repeater->position.y * multiplier + repeater->anchor.y); mathTranslate(&m, repeater->position.x * multiplier + repeater->anchor.x, repeater->position.y * multiplier + repeater->anchor.y);
mathScale(&m, powf(repeater->scale.x * 0.01f, multiplier), powf(repeater->scale.y * 0.01f, multiplier)); mathScale(&m, powf(repeater->scale.x * 0.01f, multiplier), powf(repeater->scale.y * 0.01f, multiplier));
mathRotate(&m, repeater->rotation * multiplier); mathRotate(&m, repeater->rotation * multiplier);
mathTranslateR(&m, -repeater->anchor.x, -repeater->anchor.y); mathTranslateR(&m, -repeater->anchor.x, -repeater->anchor.y);
m = repeater->transform * m;
auto pm = PP(shape)->transform(); auto pm = PP(shape)->transform();
shape->transform(pm ? (m * *pm) : m); if (pm) {
Matrix inverse;
mathInverse(&repeater->transform, &inverse);
*pm = inverse * *pm;
}
if (ctx->roundness > 1.0f && P(shape)->rs.stroke) { shape->transform(pm ? m * *pm : m);
TVGERR("LOTTIE", "FIXME: Path roundesss should be applied properly!"); shapes.push(shape);
P(shape)->rs.stroke->join = StrokeJoin::Round; }
} }
shapes.push(shape); propagators.clear();
} propagators.reserve(shapes.count);
//push repeat shapes in order. //push repeat shapes in order.
if (repeater->inorder) { if (repeater->inorder) {
for (auto shape = shapes.begin(); shape < shapes.end(); ++shape) { for (auto shape = shapes.begin(); shape < shapes.end(); ++shape) {
parent->scene->push(cast(*shape)); parent->scene->push(cast(*shape));
} propagators.push(*shape);
} else { }
for (auto shape = shapes.end() - 1; shape >= shapes.begin(); --shape) { } else {
parent->scene->push(cast(*shape)); for (auto shape = shapes.end() - 1; shape >= shapes.begin(); --shape) {
parent->scene->push(cast(*shape));
propagators.push(*shape);
}
} }
shapes.clear();
} }
} }
@ -504,7 +516,7 @@ static void _updateRect(LottieGroup* parent, LottieObject** child, float frameNo
if (roundness > size.y * 0.5f) roundness = size.y * 0.5f; if (roundness > size.y * 0.5f) roundness = size.y * 0.5f;
} }
if (ctx->repeater) { if (!ctx->repeaters.empty()) {
auto path = Shape::gen(); 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); _appendRect(path.get(), position.x - size.x * 0.5f, position.y - size.y * 0.5f, size.x, size.y, roundness, ctx->transform);
_repeat(parent, std::move(path), ctx); _repeat(parent, std::move(path), ctx);
@ -552,7 +564,7 @@ static void _updateEllipse(LottieGroup* parent, LottieObject** child, float fram
auto position = ellipse->position(frameNo, exps); auto position = ellipse->position(frameNo, exps);
auto size = ellipse->size(frameNo, exps); auto size = ellipse->size(frameNo, exps);
if (ctx->repeater) { if (!ctx->repeaters.empty()) {
auto path = Shape::gen(); auto path = Shape::gen();
_appendCircle(path.get(), position.x, position.y, size.x * 0.5f, size.y * 0.5f, ctx->transform); _appendCircle(path.get(), position.x, position.y, size.x * 0.5f, size.y * 0.5f, ctx->transform);
_repeat(parent, std::move(path), ctx); _repeat(parent, std::move(path), ctx);
@ -567,7 +579,7 @@ static void _updatePath(LottieGroup* parent, LottieObject** child, float frameNo
{ {
auto path = static_cast<LottiePath*>(*child); auto path = static_cast<LottiePath*>(*child);
if (ctx->repeater) { if (!ctx->repeaters.empty()) {
auto p = Shape::gen(); auto p = Shape::gen();
path->pathset(frameNo, P(p)->rs.path.cmds, P(p)->rs.path.pts, ctx->transform, ctx->roundness, exps); path->pathset(frameNo, P(p)->rs.path.cmds, P(p)->rs.path.pts, ctx->transform, ctx->roundness, exps);
_repeat(parent, std::move(p), ctx); _repeat(parent, std::move(p), ctx);
@ -923,7 +935,7 @@ static void _updatePolystar(LottieGroup* parent, LottieObject** child, float fra
auto identity = mathIdentity((const Matrix*)&matrix); auto identity = mathIdentity((const Matrix*)&matrix);
if (ctx->repeater) { if (!ctx->repeaters.empty()) {
auto p = Shape::gen(); auto p = Shape::gen();
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, ctx->roundness, frameNo, p.get(), exps); 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); else _updatePolygon(parent, star, identity ? nullptr : &matrix, frameNo, p.get(), exps);
@ -988,17 +1000,20 @@ static void _updateRepeater(TVG_UNUSED LottieGroup* parent, LottieObject** child
{ {
auto repeater= static_cast<LottieRepeater*>(*child); auto repeater= static_cast<LottieRepeater*>(*child);
if (!ctx->repeater) ctx->repeater = new RenderRepeater(); RenderRepeater r;
ctx->repeater->cnt = static_cast<int>(repeater->copies(frameNo, exps)); r.cnt = static_cast<int>(repeater->copies(frameNo, exps));
ctx->repeater->offset = repeater->offset(frameNo, exps); if (auto tr = PP(ctx->propagator)->transform()) r.transform = *tr;
ctx->repeater->position = repeater->position(frameNo, exps); else mathIdentity(&r.transform);
ctx->repeater->anchor = repeater->anchor(frameNo, exps); r.offset = repeater->offset(frameNo, exps);
ctx->repeater->scale = repeater->scale(frameNo, exps); r.position = repeater->position(frameNo, exps);
ctx->repeater->rotation = repeater->rotation(frameNo, exps); r.anchor = repeater->anchor(frameNo, exps);
ctx->repeater->startOpacity = repeater->startOpacity(frameNo, exps); r.scale = repeater->scale(frameNo, exps);
ctx->repeater->endOpacity = repeater->endOpacity(frameNo, exps); r.rotation = repeater->rotation(frameNo, exps);
ctx->repeater->inorder = repeater->inorder; r.startOpacity = repeater->startOpacity(frameNo, exps);
ctx->repeater->interpOpacity = (ctx->repeater->startOpacity == ctx->repeater->endOpacity) ? false : true; r.endOpacity = repeater->endOpacity(frameNo, exps);
r.inorder = repeater->inorder;
r.interpOpacity = (r.startOpacity == r.endOpacity) ? false : true;
ctx->repeaters.push(r);
ctx->merging = nullptr; ctx->merging = nullptr;
} }
@ -1006,7 +1021,7 @@ static void _updateRepeater(TVG_UNUSED LottieGroup* parent, LottieObject** child
static void _updateTrimpath(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx, LottieExpressions* exps) static void _updateTrimpath(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist<RenderContext>& contexts, RenderContext* ctx, LottieExpressions* exps)
{ {
auto trimpath= static_cast<LottieTrimpath*>(*child); auto trimpath = static_cast<LottieTrimpath*>(*child);
float begin, end; float begin, end;
trimpath->segment(frameNo, begin, end, exps); trimpath->segment(frameNo, begin, end, exps);