mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-14 12:04:29 +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
e69cc2b936
commit
bf29289b3c
1 changed files with 65 additions and 50 deletions
|
@ -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);
|
||||||
|
|
Loading…
Add table
Reference in a new issue