mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-13 19:44:28 +00:00
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:
parent
1ee79a6c2a
commit
7b9bebe1de
1 changed files with 65 additions and 50 deletions
|
@ -40,6 +40,7 @@
|
|||
struct RenderRepeater
|
||||
{
|
||||
int cnt;
|
||||
Matrix transform;
|
||||
float offset;
|
||||
Point position;
|
||||
Point anchor;
|
||||
|
@ -59,7 +60,7 @@ struct RenderContext
|
|||
Shape* propagator = nullptr;
|
||||
Shape* merging = nullptr; //merging shapes if possible (if shapes have same properties)
|
||||
LottieObject** begin = nullptr; //iteration entry point
|
||||
RenderRepeater* repeater = nullptr;
|
||||
Array<RenderRepeater> repeaters;
|
||||
Matrix* transform = nullptr;
|
||||
float roundness = 0.0f;
|
||||
bool fragmenting = false; //render context has been fragmented by filling
|
||||
|
@ -74,7 +75,6 @@ struct RenderContext
|
|||
~RenderContext()
|
||||
{
|
||||
if (ownPropagator) delete(propagator);
|
||||
delete(repeater);
|
||||
free(transform);
|
||||
}
|
||||
|
||||
|
@ -88,9 +88,8 @@ struct RenderContext
|
|||
propagator = static_cast<Shape*>(rhs.propagator->duplicate());
|
||||
}
|
||||
|
||||
if (rhs.repeater) {
|
||||
repeater = new RenderRepeater();
|
||||
*repeater = *rhs.repeater;
|
||||
for (auto repeater = rhs.repeaters.begin(); repeater < rhs.repeaters.end(); ++repeater) {
|
||||
repeaters.push(*repeater);
|
||||
}
|
||||
roundness = rhs.roundness;
|
||||
}
|
||||
|
@ -392,46 +391,59 @@ static Shape* _draw(LottieGroup* parent, RenderContext* ctx)
|
|||
//OPTIMIZE: path?
|
||||
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) {
|
||||
auto multiplier = repeater->offset + static_cast<float>(i);
|
||||
for (int i = 0; i < repeater->cnt; ++i) {
|
||||
auto multiplier = repeater->offset + static_cast<float>(i);
|
||||
|
||||
auto shape = static_cast<Shape*>(ctx->propagator->duplicate());
|
||||
P(shape)->rs.path = P(path.get())->rs.path;
|
||||
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;
|
||||
|
||||
auto opacity = repeater->interpOpacity ? mathLerp<uint8_t>(repeater->startOpacity, repeater->endOpacity, static_cast<float>(i + 1) / repeater->cnt) : repeater->startOpacity;
|
||||
shape->opacity(opacity);
|
||||
auto opacity = repeater->interpOpacity ? mathLerp<uint8_t>(repeater->startOpacity, repeater->endOpacity, static_cast<float>(i + 1) / repeater->cnt) : repeater->startOpacity;
|
||||
shape->opacity(opacity);
|
||||
|
||||
Matrix m;
|
||||
mathIdentity(&m);
|
||||
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));
|
||||
mathRotate(&m, repeater->rotation * multiplier);
|
||||
mathTranslateR(&m, -repeater->anchor.x, -repeater->anchor.y);
|
||||
Matrix m;
|
||||
mathIdentity(&m);
|
||||
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));
|
||||
mathRotate(&m, repeater->rotation * multiplier);
|
||||
mathTranslateR(&m, -repeater->anchor.x, -repeater->anchor.y);
|
||||
m = repeater->transform * m;
|
||||
|
||||
auto pm = PP(shape)->transform();
|
||||
shape->transform(pm ? (m * *pm) : m);
|
||||
auto pm = PP(shape)->transform();
|
||||
if (pm) {
|
||||
Matrix inverse;
|
||||
mathInverse(&repeater->transform, &inverse);
|
||||
*pm = inverse * *pm;
|
||||
}
|
||||
|
||||
if (ctx->roundness > 1.0f && P(shape)->rs.stroke) {
|
||||
TVGERR("LOTTIE", "FIXME: Path roundesss should be applied properly!");
|
||||
P(shape)->rs.stroke->join = StrokeJoin::Round;
|
||||
shape->transform(pm ? m * *pm : m);
|
||||
shapes.push(shape);
|
||||
}
|
||||
}
|
||||
|
||||
shapes.push(shape);
|
||||
}
|
||||
propagators.clear();
|
||||
propagators.reserve(shapes.count);
|
||||
|
||||
//push repeat shapes in order.
|
||||
if (repeater->inorder) {
|
||||
for (auto shape = shapes.begin(); shape < shapes.end(); ++shape) {
|
||||
parent->scene->push(cast(*shape));
|
||||
}
|
||||
} else {
|
||||
for (auto shape = shapes.end() - 1; shape >= shapes.begin(); --shape) {
|
||||
parent->scene->push(cast(*shape));
|
||||
//push repeat shapes in order.
|
||||
if (repeater->inorder) {
|
||||
for (auto shape = shapes.begin(); shape < shapes.end(); ++shape) {
|
||||
parent->scene->push(cast(*shape));
|
||||
propagators.push(*shape);
|
||||
}
|
||||
} else {
|
||||
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 (ctx->repeater) {
|
||||
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);
|
||||
_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 size = ellipse->size(frameNo, exps);
|
||||
|
||||
if (ctx->repeater) {
|
||||
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);
|
||||
_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);
|
||||
|
||||
if (ctx->repeater) {
|
||||
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);
|
||||
|
@ -923,7 +935,7 @@ static void _updatePolystar(LottieGroup* parent, LottieObject** child, float fra
|
|||
|
||||
auto identity = mathIdentity((const Matrix*)&matrix);
|
||||
|
||||
if (ctx->repeater) {
|
||||
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);
|
||||
|
@ -988,17 +1000,20 @@ static void _updateRepeater(TVG_UNUSED LottieGroup* parent, LottieObject** child
|
|||
{
|
||||
auto repeater= static_cast<LottieRepeater*>(*child);
|
||||
|
||||
if (!ctx->repeater) ctx->repeater = new RenderRepeater();
|
||||
ctx->repeater->cnt = static_cast<int>(repeater->copies(frameNo, exps));
|
||||
ctx->repeater->offset = repeater->offset(frameNo, exps);
|
||||
ctx->repeater->position = repeater->position(frameNo, exps);
|
||||
ctx->repeater->anchor = repeater->anchor(frameNo, exps);
|
||||
ctx->repeater->scale = repeater->scale(frameNo, exps);
|
||||
ctx->repeater->rotation = repeater->rotation(frameNo, exps);
|
||||
ctx->repeater->startOpacity = repeater->startOpacity(frameNo, exps);
|
||||
ctx->repeater->endOpacity = repeater->endOpacity(frameNo, exps);
|
||||
ctx->repeater->inorder = repeater->inorder;
|
||||
ctx->repeater->interpOpacity = (ctx->repeater->startOpacity == ctx->repeater->endOpacity) ? false : true;
|
||||
RenderRepeater r;
|
||||
r.cnt = static_cast<int>(repeater->copies(frameNo, exps));
|
||||
if (auto tr = PP(ctx->propagator)->transform()) r.transform = *tr;
|
||||
else mathIdentity(&r.transform);
|
||||
r.offset = repeater->offset(frameNo, exps);
|
||||
r.position = repeater->position(frameNo, exps);
|
||||
r.anchor = repeater->anchor(frameNo, exps);
|
||||
r.scale = repeater->scale(frameNo, exps);
|
||||
r.rotation = repeater->rotation(frameNo, exps);
|
||||
r.startOpacity = repeater->startOpacity(frameNo, exps);
|
||||
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;
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
auto trimpath= static_cast<LottieTrimpath*>(*child);
|
||||
auto trimpath = static_cast<LottieTrimpath*>(*child);
|
||||
|
||||
float begin, end;
|
||||
trimpath->segment(frameNo, begin, end, exps);
|
||||
|
|
Loading…
Add table
Reference in a new issue