mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
Lottie: Fixed handling of multiple strokes in one layer.
Revised the rendering logic of Lottie by creating a new rendering context using a queue when multiple strokes are requested. Issue: https://github.com/thorvg/thorvg/issues/1642
This commit is contained in:
parent
32b825c6d7
commit
57038df21f
1 changed files with 137 additions and 84 deletions
|
@ -20,6 +20,8 @@
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
#include "tvgCommon.h"
|
#include "tvgCommon.h"
|
||||||
#include "tvgPaint.h"
|
#include "tvgPaint.h"
|
||||||
#include "tvgShape.h"
|
#include "tvgShape.h"
|
||||||
|
@ -36,6 +38,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
|
||||||
float roundness = 0.0f;
|
float roundness = 0.0f;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
@ -52,6 +55,8 @@ struct RenderContext
|
||||||
bool valid = false;
|
bool valid = false;
|
||||||
} repeater;
|
} repeater;
|
||||||
|
|
||||||
|
bool fragmented = false; //context has been separated. allow adding visual properties(fill/stroking) if it is false.
|
||||||
|
|
||||||
RenderContext()
|
RenderContext()
|
||||||
{
|
{
|
||||||
propagator = Shape::gen().release();
|
propagator = Shape::gen().release();
|
||||||
|
@ -64,14 +69,15 @@ struct RenderContext
|
||||||
|
|
||||||
RenderContext(const RenderContext& rhs)
|
RenderContext(const RenderContext& rhs)
|
||||||
{
|
{
|
||||||
propagator = rhs.propagator ? static_cast<Shape*>(rhs.propagator->duplicate()) : Shape::gen().release();
|
propagator = static_cast<Shape*>(rhs.propagator->duplicate());
|
||||||
repeater = rhs.repeater;
|
repeater = rhs.repeater;
|
||||||
roundness = rhs.roundness;
|
roundness = rhs.roundness;
|
||||||
|
fragmented = rhs.fragmented;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static void _updateChildren(LottieGroup* parent, int32_t frameNo, RenderContext& ctx);
|
static void _updateChildren(LottieGroup* parent, int32_t frameNo, queue<RenderContext>& contexts);
|
||||||
static void _updateLayer(LottieLayer* root, LottieLayer* layer, int32_t frameNo);
|
static void _updateLayer(LottieLayer* root, LottieLayer* layer, int32_t frameNo);
|
||||||
static bool _buildPrecomp(LottieComposition* comp, LottieGroup* parent);
|
static bool _buildPrecomp(LottieComposition* comp, LottieGroup* parent);
|
||||||
|
|
||||||
|
@ -180,23 +186,15 @@ static void _updateTransform(LottieTransform* transform, int32_t frameNo, Render
|
||||||
|
|
||||||
static void _updateGroup(LottieGroup* parent, LottieGroup* group, int32_t frameNo, RenderContext& ctx)
|
static void _updateGroup(LottieGroup* parent, LottieGroup* group, int32_t frameNo, RenderContext& ctx)
|
||||||
{
|
{
|
||||||
|
if (group->children.empty()) return;
|
||||||
|
|
||||||
//Prepare render data
|
//Prepare render data
|
||||||
group->scene = parent->scene;
|
group->scene = parent->scene;
|
||||||
auto ctx2 = ctx;
|
|
||||||
_updateChildren(group, frameNo, ctx2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
queue<RenderContext> contexts;
|
||||||
|
contexts.push(ctx);
|
||||||
|
|
||||||
static void _updateFill(LottieSolidFill* fill, int32_t frameNo, RenderContext& ctx)
|
_updateChildren(group, frameNo, contexts);
|
||||||
{
|
|
||||||
//TODO: Skip if property has not been modified actually.
|
|
||||||
ctx.merging = nullptr;
|
|
||||||
|
|
||||||
auto color = fill->color(frameNo);
|
|
||||||
ctx.propagator->fill(color.rgb[0], color.rgb[1], color.rgb[2], fill->opacity(frameNo));
|
|
||||||
ctx.propagator->fill(fill->rule);
|
|
||||||
|
|
||||||
if (ctx.propagator->strokeWidth() > 0) ctx.propagator->order(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -218,25 +216,73 @@ static void _updateStroke(LottieStroke* stroke, int32_t frameNo, RenderContext&
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _updateStroke(LottieSolidStroke* stroke, int32_t frameNo, RenderContext& ctx)
|
static void _updateSolidStroke(LottieObject** child, int32_t frameNo, queue<RenderContext>& contexts, RenderContext& ctx)
|
||||||
{
|
{
|
||||||
|
//the rendering context is fragmented
|
||||||
|
if (ctx.propagator->strokeWidth() > 0.0f) {
|
||||||
|
contexts.push(ctx);
|
||||||
|
auto& fragment = contexts.back();
|
||||||
|
fragment.propagator->stroke(0.0f);
|
||||||
|
fragment.begin = child - 1;
|
||||||
|
_updateSolidStroke(child, frameNo, contexts, fragment);
|
||||||
|
ctx.fragmented = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.fragmented) return;
|
||||||
|
|
||||||
|
auto stroke = static_cast<LottieSolidStroke*>(*child);
|
||||||
|
|
||||||
ctx.merging = nullptr;
|
ctx.merging = nullptr;
|
||||||
auto color = stroke->color(frameNo);
|
auto color = stroke->color(frameNo);
|
||||||
ctx.propagator->stroke(color.rgb[0], color.rgb[1], color.rgb[2], stroke->opacity(frameNo));
|
ctx.propagator->stroke(color.rgb[0], color.rgb[1], color.rgb[2], stroke->opacity(frameNo));
|
||||||
|
|
||||||
_updateStroke(static_cast<LottieStroke*>(stroke), frameNo, ctx);
|
_updateStroke(static_cast<LottieStroke*>(stroke), frameNo, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _updateStroke(LottieGradientStroke* stroke, int32_t frameNo, RenderContext& ctx)
|
static void _updateGradientStroke(LottieObject** child, int32_t frameNo, queue<RenderContext>& contexts, RenderContext& ctx)
|
||||||
{
|
{
|
||||||
|
//the rendering context is fragmented
|
||||||
|
if (ctx.propagator->strokeWidth() > 0.0f) {
|
||||||
|
contexts.push(ctx);
|
||||||
|
auto& fragment = contexts.back();
|
||||||
|
fragment.propagator->stroke(0.0f);
|
||||||
|
fragment.begin = child - 1;
|
||||||
|
_updateGradientStroke(child, frameNo, contexts, fragment);
|
||||||
|
ctx.fragmented = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.fragmented) return;
|
||||||
|
|
||||||
|
auto stroke = static_cast<LottieGradientStroke*>(*child);
|
||||||
|
|
||||||
ctx.merging = nullptr;
|
ctx.merging = nullptr;
|
||||||
ctx.propagator->stroke(unique_ptr<Fill>(stroke->fill(frameNo)));
|
ctx.propagator->stroke(unique_ptr<Fill>(stroke->fill(frameNo)));
|
||||||
|
|
||||||
_updateStroke(static_cast<LottieStroke*>(stroke), frameNo, ctx);
|
_updateStroke(static_cast<LottieStroke*>(stroke), frameNo, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void _updateFill(LottieSolidFill* fill, int32_t frameNo, RenderContext& ctx)
|
||||||
|
{
|
||||||
|
if (ctx.fragmented) return;
|
||||||
|
|
||||||
|
ctx.merging = nullptr;
|
||||||
|
|
||||||
|
auto color = fill->color(frameNo);
|
||||||
|
ctx.propagator->fill(color.rgb[0], color.rgb[1], color.rgb[2], fill->opacity(frameNo));
|
||||||
|
ctx.propagator->fill(fill->rule);
|
||||||
|
|
||||||
|
if (ctx.propagator->strokeWidth() > 0) ctx.propagator->order(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static Shape* _updateFill(LottieGradientFill* fill, int32_t frameNo, RenderContext& ctx)
|
static Shape* _updateFill(LottieGradientFill* fill, int32_t frameNo, RenderContext& ctx)
|
||||||
{
|
{
|
||||||
|
if (ctx.fragmented) return nullptr;
|
||||||
|
|
||||||
ctx.merging = nullptr;
|
ctx.merging = nullptr;
|
||||||
|
|
||||||
//TODO: reuse the fill instance?
|
//TODO: reuse the fill instance?
|
||||||
|
@ -559,7 +605,6 @@ static void _updatePolystar(LottieGroup* parent, LottiePolyStar* star, int32_t f
|
||||||
|
|
||||||
auto identity = mathIdentity((const Matrix*)&matrix);
|
auto identity = mathIdentity((const Matrix*)&matrix);
|
||||||
|
|
||||||
|
|
||||||
if (ctx.repeater.valid) {
|
if (ctx.repeater.valid) {
|
||||||
auto p = Shape::gen();
|
auto p = Shape::gen();
|
||||||
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, frameNo, p.get());
|
if (star->type == LottiePolyStar::Star) _updateStar(parent, star, identity ? nullptr : &matrix, frameNo, p.get());
|
||||||
|
@ -659,12 +704,13 @@ static void _updateTrimpath(LottieTrimpath* trimpath, int32_t frameNo, RenderCon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _updateChildren(LottieGroup* parent, int32_t frameNo, RenderContext& ctx)
|
static void _updateChildren(LottieGroup* parent, int32_t frameNo, queue<RenderContext>& contexts)
|
||||||
{
|
{
|
||||||
if (parent->children.empty()) return;
|
contexts.front().begin = parent->children.end() - 1;
|
||||||
|
|
||||||
//Draw the parent shapes first
|
while (contexts.size() > 0) {
|
||||||
for (auto child = parent->children.end() - 1; child >= parent->children.data; --child) {
|
auto& ctx = contexts.front();
|
||||||
|
for (auto child = ctx.begin; child >= parent->children.data; --child) {
|
||||||
//TODO: Polymorphsim?
|
//TODO: Polymorphsim?
|
||||||
switch ((*child)->type) {
|
switch ((*child)->type) {
|
||||||
case LottieObject::Group: {
|
case LottieObject::Group: {
|
||||||
|
@ -680,7 +726,7 @@ static void _updateChildren(LottieGroup* parent, int32_t frameNo, RenderContext&
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::SolidStroke: {
|
case LottieObject::SolidStroke: {
|
||||||
_updateStroke(static_cast<LottieSolidStroke*>(*child), frameNo, ctx);
|
_updateSolidStroke(child, frameNo, contexts, ctx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::GradientFill: {
|
case LottieObject::GradientFill: {
|
||||||
|
@ -688,7 +734,7 @@ static void _updateChildren(LottieGroup* parent, int32_t frameNo, RenderContext&
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::GradientStroke: {
|
case LottieObject::GradientStroke: {
|
||||||
_updateStroke(static_cast<LottieGradientStroke*>(*child), frameNo, ctx);
|
_updateGradientStroke(child, frameNo, contexts, ctx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieObject::Rect: {
|
case LottieObject::Rect: {
|
||||||
|
@ -726,6 +772,8 @@ static void _updateChildren(LottieGroup* parent, int32_t frameNo, RenderContext&
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
contexts.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -839,7 +887,9 @@ static void _updateLayer(LottieLayer* root, LottieLayer* layer, int32_t frameNo)
|
||||||
|
|
||||||
switch (layer->type) {
|
switch (layer->type) {
|
||||||
case LottieLayer::Precomp: {
|
case LottieLayer::Precomp: {
|
||||||
|
if (!layer->children.empty()) {
|
||||||
_updatePrecomp(layer, frameNo);
|
_updatePrecomp(layer, frameNo);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LottieLayer::Solid: {
|
case LottieLayer::Solid: {
|
||||||
|
@ -847,8 +897,11 @@ static void _updateLayer(LottieLayer* root, LottieLayer* layer, int32_t frameNo)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
RenderContext ctx;
|
if (!layer->children.empty()) {
|
||||||
_updateChildren(layer, frameNo, ctx);
|
queue<RenderContext> contexts;
|
||||||
|
contexts.emplace();
|
||||||
|
_updateChildren(layer, frameNo, contexts);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue