diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index 36130379..32993498 100644 --- a/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/src/loaders/lottie/tvgLottieBuilder.cpp @@ -233,73 +233,100 @@ static void _updateStroke(LottieStroke* stroke, float frameNo, RenderContext* ct } -bool LottieBuilder::fragmented(LottieGroup* parent, LottieObject** child, Inlist& contexts, RenderContext* ctx) +bool LottieBuilder::fragmented(LottieGroup* parent, LottieObject** child, Inlist& contexts, RenderContext* ctx, RenderFragment fragment) { - if (!ctx->reqFragment) return false; - if (ctx->fragmenting) return true; + if (ctx->fragment) return true; - contexts.back(new RenderContext(*ctx, static_cast(PAINT(ctx->propagator)->duplicate(parent->pooling())))); - auto fragment = contexts.tail; - fragment->begin = child - 1; - ctx->fragmenting = true; + if (!ctx->reqFragment || ctx->fragment == RenderFragment::ByNone) return false; + + contexts.back(new RenderContext(*ctx, (Shape*)(PAINT(ctx->propagator)->duplicate(parent->pooling())))); + + contexts.tail->begin = child - 1; + ctx->fragment = fragment; return false; } -void LottieBuilder::updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) +bool LottieBuilder::updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) { - if (fragmented(parent, child, contexts, ctx)) return; - auto stroke = static_cast(*child); + auto opacity = stroke->opacity(frameNo, tween, exps); + + //interrupted by fully opaque, stop the current rendering + if (ctx->fragment == RenderFragment::ByStroke && opacity == 255) return true; + + if (fragmented(parent, child, contexts, ctx, RenderFragment::ByStroke)) return false; + if (opacity == 0) return false; ctx->merging = nullptr; auto color = stroke->color(frameNo, tween, exps); - ctx->propagator->strokeFill(color.rgb[0], color.rgb[1], color.rgb[2], stroke->opacity(frameNo, tween, exps)); + ctx->propagator->strokeFill(color.rgb[0], color.rgb[1], color.rgb[2], opacity); _updateStroke(static_cast(stroke), frameNo, ctx, tween, exps); + + return false; } -void LottieBuilder::updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) +bool LottieBuilder::updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) { - if (fragmented(parent, child, contexts, ctx)) return; - auto stroke = static_cast(*child); + auto opacity = stroke->opacity(frameNo, tween, exps); + + //interrupted by fully opaque, stop the current rendering + if (ctx->fragment == RenderFragment::ByStroke && stroke->opaque && opacity == 255) return true; + + if (fragmented(parent, child, contexts, ctx, RenderFragment::ByStroke)) return false; ctx->merging = nullptr; - ctx->propagator->strokeFill(stroke->fill(frameNo, tween, exps)); + if (auto val = stroke->fill(frameNo, opacity, tween, exps)) ctx->propagator->strokeFill(val); _updateStroke(static_cast(stroke), frameNo, ctx, tween, exps); + + return false; } -void LottieBuilder::updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) +bool LottieBuilder::updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) { - if (fragmented(parent, child, contexts, ctx)) return; - auto fill = static_cast(*child); + auto opacity = fill->opacity(frameNo, tween, exps); + + //interrupted by fully opaque, stop the current rendering + if (ctx->fragment == RenderFragment::ByFill && opacity == 255) return true; + + if (fragmented(parent, child, contexts, ctx, RenderFragment::ByFill)) return false; + if (opacity == 0) return false; ctx->merging = nullptr; auto color = fill->color(frameNo, tween, exps); - ctx->propagator->fill(color.rgb[0], color.rgb[1], color.rgb[2], fill->opacity(frameNo, tween, exps)); + ctx->propagator->fill(color.rgb[0], color.rgb[1], color.rgb[2], opacity); ctx->propagator->fill(fill->rule); if (ctx->propagator->strokeWidth() > 0) ctx->propagator->order(true); + + return false; } -void LottieBuilder::updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) +bool LottieBuilder::updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx) { - if (fragmented(parent, child, contexts, ctx)) return; - auto fill = static_cast(*child); + auto opacity = fill->opacity(frameNo, tween, exps); + + //interrupted by fully opaque, stop the current rendering + if (ctx->fragment == RenderFragment::ByFill && fill->opaque && opacity == 255) return true; + + if (fragmented(parent, child, contexts, ctx, RenderFragment::ByFill)) return false; ctx->merging = nullptr; - //TODO: reuse the fill instance? - ctx->propagator->fill(fill->fill(frameNo, tween, exps)); + + if (auto val = fill->fill(frameNo, opacity, tween, exps)) ctx->propagator->fill(val); ctx->propagator->fill(fill->rule); if (ctx->propagator->strokeWidth() > 0) ctx->propagator->order(true); + + return false; } @@ -769,6 +796,7 @@ void LottieBuilder::updateChildren(LottieGroup* parent, float frameNo, InlistreqFragment = parent->reqFragment; + auto stop = false; for (auto child = ctx->begin; child >= parent->children.data; --child) { //Here switch-case statements are more performant than virtual methods. switch ((*child)->type) { @@ -781,19 +809,19 @@ void LottieBuilder::updateChildren(LottieGroup* parent, float frameNo, Inlistpropagator->opacity() == skip) break; + if (stop || ctx->propagator->opacity() == skip) break; } delete(ctx); } diff --git a/src/loaders/lottie/tvgLottieBuilder.h b/src/loaders/lottie/tvgLottieBuilder.h index 7deb8901..bd995523 100644 --- a/src/loaders/lottie/tvgLottieBuilder.h +++ b/src/loaders/lottie/tvgLottieBuilder.h @@ -46,6 +46,8 @@ struct RenderRepeater bool inorder; }; +enum RenderFragment : uint8_t {ByNone = 0, ByFill, ByStroke}; + struct RenderContext { INLIST_ITEM(RenderContext); @@ -58,7 +60,7 @@ struct RenderContext LottieRoundnessModifier* roundness = nullptr; LottieOffsetModifier* offset = nullptr; LottieModifier* modifier = nullptr; - bool fragmenting = false; //render context has been fragmented by filling + RenderFragment fragment = ByNone; //render context has been fragmented bool reqFragment = false; //requirement to fragment the render context RenderContext(Shape* propagator) @@ -76,12 +78,12 @@ struct RenderContext delete(offset); } - RenderContext(const RenderContext& rhs, Shape* propagator, bool mergeable = false) + RenderContext(const RenderContext& rhs, Shape* propagator, bool mergeable = false) : propagator(propagator) { if (mergeable) merging = rhs.merging; propagator->ref(); - this->propagator = propagator; repeaters = rhs.repeaters; + fragment = rhs.fragment; if (rhs.roundness) { roundness = new LottieRoundnessModifier(rhs.roundness->buffer, rhs.roundness->r); update(roundness); @@ -138,7 +140,7 @@ struct LottieBuilder private: void appendRect(Shape* shape, Point& pos, Point& size, float r, bool clockwise, RenderContext* ctx); - bool fragmented(LottieGroup* parent, LottieObject** child, Inlist& contexts, RenderContext* ctx); + bool fragmented(LottieGroup* parent, LottieObject** child, Inlist& contexts, RenderContext* ctx, RenderFragment fragment); void updateStrokeEffect(LottieLayer* layer, LottieFxStroke* effect, float frameNo); void updateEffect(LottieLayer* layer, float frameNo); @@ -154,10 +156,10 @@ private: void updateChildren(LottieGroup* parent, float frameNo, Inlist& contexts, uint8_t skip); void updateGroup(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& pcontexts, RenderContext* ctx, uint8_t skip); void updateTransform(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); - void updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); - void updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); - void updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); - void updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); + bool updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); + bool updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); + bool updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); + bool updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); void updateRect(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); void updateEllipse(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); void updatePath(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); diff --git a/src/loaders/lottie/tvgLottieModel.cpp b/src/loaders/lottie/tvgLottieModel.cpp index f4e7c666..bca2379e 100644 --- a/src/loaders/lottie/tvgLottieModel.cpp +++ b/src/loaders/lottie/tvgLottieModel.cpp @@ -311,6 +311,7 @@ uint32_t LottieGradient::populate(ColorStop& color, size_t count) } aidx += 2; } + if (cs.a < 255) opaque = false; output.push(cs); } @@ -321,6 +322,7 @@ uint32_t LottieGradient::populate(ColorStop& color, size_t count) cs.g = (uint8_t)nearbyint((*color.input)[cidx + 2] * 255.0f); cs.b = (uint8_t)nearbyint((*color.input)[cidx + 3] * 255.0f); cs.a = (output.count > 0) ? output.last().a : 255; + if (cs.a < 255) opaque = false; output.push(cs); cidx += 4; } @@ -329,6 +331,7 @@ uint32_t LottieGradient::populate(ColorStop& color, size_t count) while (aidx < color.input->count) { cs.offset = (*color.input)[aidx]; cs.a = (uint8_t)nearbyint((*color.input)[aidx + 1] * 255.0f); + if (cs.a < 255) opaque = false; if (output.count > 0) { cs.r = output.last().r; cs.g = output.last().g; @@ -348,12 +351,11 @@ uint32_t LottieGradient::populate(ColorStop& color, size_t count) } -Fill* LottieGradient::fill(float frameNo, Tween& tween, LottieExpressions* exps) +Fill* LottieGradient::fill(float frameNo, uint8_t opacity, Tween& tween, LottieExpressions* exps) { - auto opacity = this->opacity(frameNo, tween, exps); if (opacity == 0) return nullptr; - Fill* fill = nullptr; + Fill* fill; auto s = start(frameNo, tween, exps); auto e = end(frameNo, tween, exps); @@ -361,11 +363,9 @@ Fill* LottieGradient::fill(float frameNo, Tween& tween, LottieExpressions* exps) if (id == 1) { fill = LinearGradient::gen(); static_cast(fill)->linear(s.x, s.y, e.x, e.y); - } //Radial Gradient - if (id == 2) { + } else { fill = RadialGradient::gen(); - auto w = fabsf(e.x - s.x); auto h = fabsf(e.y - s.y); auto r = (w > h) ? (w + 0.375f * h) : (h + 0.375f * w); @@ -384,8 +384,6 @@ Fill* LottieGradient::fill(float frameNo, Tween& tween, LottieExpressions* exps) } } - if (!fill) return nullptr; - colorStops(frameNo, fill, tween, exps); //multiply the current opacity with the fill diff --git a/src/loaders/lottie/tvgLottieModel.h b/src/loaders/lottie/tvgLottieModel.h index e8837aac..f2bea8ea 100644 --- a/src/loaders/lottie/tvgLottieModel.h +++ b/src/loaders/lottie/tvgLottieModel.h @@ -701,7 +701,7 @@ struct LottieGradient : LottieObject } uint32_t populate(ColorStop& color, size_t count); - Fill* fill(float frameNo, Tween& tween, LottieExpressions* exps); + Fill* fill(float frameNo, uint8_t opacity, Tween& tween, LottieExpressions* exps); LottieScalar start = Point{0.0f, 0.0f}; LottieScalar end = Point{0.0f, 0.0f}; @@ -709,7 +709,8 @@ struct LottieGradient : LottieObject LottieFloat angle = 0.0f; LottieOpacity opacity = 255; LottieColorStop colorStops; - uint8_t id = 0; //1: linear, 2: radial + uint8_t id = 0; //1: linear, 2: radial + bool opaque = true; //fully opaque or not };