diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index b5d9ef7d..5139e754 100644 --- a/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/src/loaders/lottie/tvgLottieBuilder.cpp @@ -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 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(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 path, RenderContext* ctx) { - auto repeater = ctx->repeater; + Array propagators; + propagators.push(ctx->propagator); + Array shapes; - Array 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(i); + for (int i = 0; i < repeater->cnt; ++i) { + auto multiplier = repeater->offset + static_cast(i); - auto shape = static_cast(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((*propagator)->duplicate()); + P(shape)->rs.path = P(path.get())->rs.path; - auto opacity = repeater->interpOpacity ? mathLerp(repeater->startOpacity, repeater->endOpacity, static_cast(i + 1) / repeater->cnt) : repeater->startOpacity; - shape->opacity(opacity); + auto opacity = repeater->interpOpacity ? mathLerp(repeater->startOpacity, repeater->endOpacity, static_cast(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(*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(*child); - if (!ctx->repeater) ctx->repeater = new RenderRepeater(); - ctx->repeater->cnt = static_cast(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(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& contexts, RenderContext* ctx, LottieExpressions* exps) { - auto trimpath= static_cast(*child); + auto trimpath = static_cast(*child); float begin, end; trimpath->segment(frameNo, begin, end, exps);