lottie: Properly handle the TrimPath mode.

This correction addresses the drawing order of
objects when a group contains a TrimPath and
is merging shapes.

Additionally, the TrimPath method is can applied
to the engine to control the drawing behavior by
7f3dc541d6b7abcdc03facd884489f37c327fd98

issue: https://github.com/thorvg/thorvg/issues/2047
This commit is contained in:
Hermet Park 2024-04-02 23:45:22 +09:00 committed by Hermet Park
parent c74cd42363
commit cbed261ba2
3 changed files with 32 additions and 11 deletions

View file

@ -198,7 +198,7 @@ static void _updateTransform(LottieGroup* parent, LottieObject** child, float fr
uint8_t opacity; uint8_t opacity;
if (parent->mergeable) { if (parent->mergeable()) {
if (!ctx->transform) ctx->transform = (Matrix*)malloc(sizeof(Matrix)); if (!ctx->transform) ctx->transform = (Matrix*)malloc(sizeof(Matrix));
_updateTransform(transform, frameNo, false, *ctx->transform, opacity); _updateTransform(transform, frameNo, false, *ctx->transform, opacity);
return; return;
@ -232,10 +232,10 @@ static void _updateGroup(LottieGroup* parent, LottieObject** child, float frameN
group->reqFragment |= ctx->reqFragment; group->reqFragment |= ctx->reqFragment;
//generate a merging shape to consolidate partial shapes into a single entity //generate a merging shape to consolidate partial shapes into a single entity
if (group->mergeable) _draw(parent, ctx); if (group->mergeable()) _draw(parent, ctx);
Inlist<RenderContext> contexts; Inlist<RenderContext> contexts;
contexts.back(new RenderContext(*ctx, group->mergeable)); contexts.back(new RenderContext(*ctx, group->mergeable()));
_updateChildren(group, frameNo, contexts); _updateChildren(group, frameNo, contexts);
@ -897,9 +897,7 @@ static void _updateTrimpath(TVG_UNUSED LottieGroup* parent, LottieObject** child
end = (length * end) + pbegin; end = (length * end) + pbegin;
} }
P(ctx->propagator)->strokeTrim(begin, end, false); P(ctx->propagator)->strokeTrim(begin, end, trimpath->type == LottieTrimpath::Type::Individual ? true : false);
//TODO: individual or simultaenous mode
} }

View file

@ -142,20 +142,20 @@ void LottieGroup::prepare(LottieObject::Type type)
size_t fillCnt = 0; size_t fillCnt = 0;
for (auto c = children.end() - 1; c >= children.begin(); --c) { for (auto c = children.end() - 1; c >= children.begin(); --c) {
if (!mergeable && reqFragment) break;
auto child = static_cast<LottieObject*>(*c); auto child = static_cast<LottieObject*>(*c);
if (child->type == LottieObject::Type::Trimpath) trimpath = true;
/* Figure out if this group is a simple path drawing. /* Figure out if this group is a simple path drawing.
In that case, the rendering context can be sharable with the parent's. */ In that case, the rendering context can be sharable with the parent's. */
if (mergeable && !child->mergeable()) mergeable = false; if (allowMerge && (child->type == LottieObject::Group || !child->mergeable())) allowMerge = false;
if (reqFragment) continue; if (reqFragment) continue;
/* Figure out if the rendering context should be fragmented. /* Figure out if the rendering context should be fragmented.
Multiple stroking or grouping with a stroking would occur this. Multiple stroking or grouping with a stroking would occur this.
This fragment resolves the overlapped stroke outlines. */ This fragment resolves the overlapped stroke outlines. */
if (child->type == LottieObject::Group && !static_cast<LottieGroup*>(child)->mergeable) { if (child->type == LottieObject::Group && !child->mergeable()) {
if (strokeCnt > 0 || fillCnt > 0) reqFragment = true; if (strokeCnt > 0 || fillCnt > 0) reqFragment = true;
} else if (child->type == LottieObject::SolidStroke || child->type == LottieObject::GradientStroke) { } else if (child->type == LottieObject::SolidStroke || child->type == LottieObject::GradientStroke) {
if (strokeCnt > 0) reqFragment = true; if (strokeCnt > 0) reqFragment = true;
@ -165,6 +165,25 @@ void LottieGroup::prepare(LottieObject::Type type)
else ++fillCnt; else ++fillCnt;
} }
} }
//Reverse the drawing order if this group has a trimpath.
if (!trimpath) return;
for (uint32_t i = 0; i < children.count - 1; ) {
auto child2 = children[i + 1];
if (!child2->mergeable() || child2->type == LottieObject::Transform) {
i += 2;
continue;
}
auto child = children[i];
if (!child->mergeable() || child->type == LottieObject::Transform) {
i++;
continue;
}
children[i] = child2;
children[i + 1] = child;
i++;
}
} }

View file

@ -572,13 +572,15 @@ struct LottieGroup : LottieObject
} }
void prepare(LottieObject::Type type = LottieObject::Group); void prepare(LottieObject::Type type = LottieObject::Group);
bool mergeable() override { return allowMerge; }
Scene* scene = nullptr; //tvg render data Scene* scene = nullptr; //tvg render data
Array<LottieObject*> children; Array<LottieObject*> children;
bool reqFragment = false; //requirment to fragment the render context bool reqFragment = false; //requirment to fragment the render context
bool buildDone = false; //completed in building the composition. bool buildDone = false; //completed in building the composition.
bool mergeable = true; //if this group is consisted of simple (transformed) shapes. bool allowMerge = true; //if this group is consisted of simple (transformed) shapes.
bool trimpath = false; //this group has a trimpath.
}; };
@ -595,6 +597,8 @@ struct LottieLayer : LottieGroup
return transform->opacity(frameNo); return transform->opacity(frameNo);
} }
bool mergeable() override { return false; }
void prepare(); void prepare();
float remap(float frameNo); float remap(float frameNo);