lottie: revised the shape modifiers

- use a decoration style for path-modification chaining.
- use a reusable buffer for intermediate path generation.
This commit is contained in:
Hermet Park 2025-02-06 01:34:39 +09:00 committed by Hermet Park
parent f4a2c922be
commit 2a4dfcd10e
6 changed files with 160 additions and 115 deletions

View file

@ -233,7 +233,7 @@ static void _updateStroke(LottieStroke* stroke, float frameNo, RenderContext* ct
} }
static bool _fragmented(LottieGroup* parent, LottieObject** child, Inlist<RenderContext>& contexts, RenderContext* ctx) bool LottieBuilder::fragmented(LottieGroup* parent, LottieObject** child, Inlist<RenderContext>& contexts, RenderContext* ctx)
{ {
if (!ctx->reqFragment) return false; if (!ctx->reqFragment) return false;
if (ctx->fragmenting) return true; if (ctx->fragmenting) return true;
@ -249,7 +249,7 @@ static bool _fragmented(LottieGroup* parent, LottieObject** child, Inlist<Render
void LottieBuilder::updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx) void LottieBuilder::updateSolidStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
{ {
if (_fragmented(parent, child, contexts, ctx)) return; if (fragmented(parent, child, contexts, ctx)) return;
auto stroke = static_cast<LottieSolidStroke*>(*child); auto stroke = static_cast<LottieSolidStroke*>(*child);
@ -262,7 +262,7 @@ void LottieBuilder::updateSolidStroke(LottieGroup* parent, LottieObject** child,
void LottieBuilder::updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx) void LottieBuilder::updateGradientStroke(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
{ {
if (_fragmented(parent, child, contexts, ctx)) return; if (fragmented(parent, child, contexts, ctx)) return;
auto stroke = static_cast<LottieGradientStroke*>(*child); auto stroke = static_cast<LottieGradientStroke*>(*child);
@ -274,7 +274,7 @@ void LottieBuilder::updateGradientStroke(LottieGroup* parent, LottieObject** chi
void LottieBuilder::updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx) void LottieBuilder::updateSolidFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
{ {
if (_fragmented(parent, child, contexts, ctx)) return; if (fragmented(parent, child, contexts, ctx)) return;
auto fill = static_cast<LottieSolidFill*>(*child); auto fill = static_cast<LottieSolidFill*>(*child);
@ -289,7 +289,7 @@ void LottieBuilder::updateSolidFill(LottieGroup* parent, LottieObject** child, f
void LottieBuilder::updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx) void LottieBuilder::updateGradientFill(LottieGroup* parent, LottieObject** child, float frameNo, Inlist<RenderContext>& contexts, RenderContext* ctx)
{ {
if (_fragmented(parent, child, contexts, ctx)) return; if (fragmented(parent, child, contexts, ctx)) return;
auto fill = static_cast<LottieGradientFill*>(*child); auto fill = static_cast<LottieGradientFill*>(*child);
@ -547,11 +547,11 @@ void LottieBuilder::updatePath(LottieGroup* parent, LottieObject** child, float
if (!ctx->repeaters.empty()) { if (!ctx->repeaters.empty()) {
auto shape = path->pooling(); auto shape = path->pooling();
shape->reset(); shape->reset();
path->pathset(frameNo, SHAPE(shape)->rs.path, ctx->transform, exps, ctx->roundness, ctx->offset); path->pathset(frameNo, SHAPE(shape)->rs.path, ctx->transform, exps, ctx->modifier);
_repeat(parent, shape, ctx); _repeat(parent, shape, ctx);
} else { } else {
_draw(parent, path, ctx); _draw(parent, path, ctx);
if (path->pathset(frameNo, SHAPE(ctx->merging)->rs.path, ctx->transform, exps, ctx->roundness, ctx->offset)) { if (path->pathset(frameNo, SHAPE(ctx->merging)->rs.path, ctx->transform, exps, ctx->modifier)) {
PAINT(ctx->merging)->update(RenderUpdateFlag::Path); PAINT(ctx->merging)->update(RenderUpdateFlag::Path);
} }
} }
@ -674,13 +674,7 @@ void LottieBuilder::updateStar(LottiePolyStar* star, float frameNo, Matrix* tran
} }
shape->close(); shape->close();
if (roundedCorner) { if (ctx->modifier) ctx->modifier->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path, outerRoundness, hasRoundness);
if (ctx->offset) {
buffer.clear();
ctx->roundness->modifyPolystar(SHAPE(shape)->rs.path, buffer, outerRoundness, hasRoundness);
ctx->offset->modifyPolystar(buffer, SHAPE(merging)->rs.path);
} else ctx->roundness->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path, outerRoundness, hasRoundness);
} else if (ctx->offset) ctx->offset->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path);
} }
@ -758,13 +752,7 @@ void LottieBuilder::updatePolygon(LottieGroup* parent, LottiePolyStar* star, flo
} }
shape->close(); shape->close();
if (roundedCorner) { if (ctx->modifier) ctx->modifier->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path, 0.0f, false);
if (ctx->offset) {
buffer.clear();
ctx->roundness->modifyPolystar(SHAPE(shape)->rs.path, buffer, 0.0f, false);
ctx->offset->modifyPolystar(buffer, SHAPE(merging)->rs.path);
} else ctx->roundness->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path, 0.0f, false);
} else if (ctx->offset) ctx->offset->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path);
} }
@ -803,8 +791,10 @@ void LottieBuilder::updateRoundedCorner(TVG_UNUSED LottieGroup* parent, LottieOb
auto r = roundedCorner->radius(frameNo, exps); auto r = roundedCorner->radius(frameNo, exps);
if (r < LottieRoundnessModifier::ROUNDNESS_EPSILON) return; if (r < LottieRoundnessModifier::ROUNDNESS_EPSILON) return;
if (!ctx->roundness) ctx->roundness = new LottieRoundnessModifier(r); if (!ctx->roundness) ctx->roundness = new LottieRoundnessModifier(&buffer, r);
else if (ctx->roundness->r < r) ctx->roundness->r = r; else if (ctx->roundness->r < r) ctx->roundness->r = r;
ctx->update(ctx->roundness);
} }
@ -812,6 +802,8 @@ void LottieBuilder::updateOffsetPath(TVG_UNUSED LottieGroup* parent, LottieObjec
{ {
auto offset = static_cast<LottieOffsetPath*>(*child); auto offset = static_cast<LottieOffsetPath*>(*child);
if (!ctx->offset) ctx->offset = new LottieOffsetModifier(offset->offset(frameNo, exps), offset->miterLimit(frameNo, exps), offset->join); if (!ctx->offset) ctx->offset = new LottieOffsetModifier(offset->offset(frameNo, exps), offset->miterLimit(frameNo, exps), offset->join);
ctx->update(ctx->offset);
} }
@ -1261,7 +1253,7 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
} else { } else {
//TODO: Once path direction support is implemented, ensure that the direction is ignored here //TODO: Once path direction support is implemented, ensure that the direction is ignored here
auto offset = LottieOffsetModifier(expand); auto offset = LottieOffsetModifier(expand);
mask->pathset(frameNo, SHAPE(pShape)->rs.path, nullptr, exps, nullptr, &offset); mask->pathset(frameNo, SHAPE(pShape)->rs.path, nullptr, exps, &offset);
} }
if (fastTrack) return; if (fastTrack) return;
@ -1309,7 +1301,7 @@ void LottieBuilder::updateStrokeEffect(LottieLayer* layer, LottieFxStroke* effec
auto idx = static_cast<uint32_t>(effect->mask(frameNo) - 1); auto idx = static_cast<uint32_t>(effect->mask(frameNo) - 1);
if (idx < 0 || idx >= layer->masks.count) return; if (idx < 0 || idx >= layer->masks.count) return;
auto mask = layer->masks[idx]; auto mask = layer->masks[idx];
mask->pathset(frameNo, SHAPE(shape)->rs.path, nullptr, exps, nullptr, nullptr); mask->pathset(frameNo, SHAPE(shape)->rs.path, nullptr, exps);
} }
shape->transform(layer->cache.matrix); shape->transform(layer->cache.matrix);

View file

@ -57,6 +57,7 @@ struct RenderContext
Matrix* transform = nullptr; Matrix* transform = nullptr;
LottieRoundnessModifier* roundness = nullptr; LottieRoundnessModifier* roundness = nullptr;
LottieOffsetModifier* offset = nullptr; LottieOffsetModifier* offset = nullptr;
LottieModifier* modifier = nullptr;
bool fragmenting = false; //render context has been fragmented by filling bool fragmenting = false; //render context has been fragmented by filling
bool reqFragment = false; //requirement to fragment the render context bool reqFragment = false; //requirement to fragment the render context
@ -81,8 +82,20 @@ struct RenderContext
propagator->ref(); propagator->ref();
this->propagator = propagator; this->propagator = propagator;
repeaters = rhs.repeaters; repeaters = rhs.repeaters;
if (rhs.roundness) roundness = new LottieRoundnessModifier(rhs.roundness->r); if (rhs.roundness) {
if (rhs.offset) offset = new LottieOffsetModifier(rhs.offset->offset, rhs.offset->miterLimit, rhs.offset->join); roundness = new LottieRoundnessModifier(rhs.roundness->buffer, rhs.roundness->r);
update(roundness);
}
if (rhs.offset) {
offset = new LottieOffsetModifier(rhs.offset->offset, rhs.offset->miterLimit, rhs.offset->join);
update(offset);
}
}
void update(LottieModifier* next)
{
if (modifier) modifier = modifier->decorate(next);
else modifier = next;
} }
}; };
@ -108,6 +121,7 @@ struct LottieBuilder
private: private:
void appendRect(Shape* shape, Point& pos, Point& size, float r, bool clockwise, RenderContext* ctx); void appendRect(Shape* shape, Point& pos, Point& size, float r, bool clockwise, RenderContext* ctx);
bool fragmented(LottieGroup* parent, LottieObject** child, Inlist<RenderContext>& contexts, RenderContext* ctx);
void updateStrokeEffect(LottieLayer* layer, LottieFxStroke* effect, float frameNo); void updateStrokeEffect(LottieLayer* layer, LottieFxStroke* effect, float frameNo);
void updateEffect(LottieLayer* layer, float frameNo); void updateEffect(LottieLayer* layer, float frameNo);

View file

@ -29,8 +29,7 @@
struct LottieExpression; struct LottieExpression;
struct LottieComposition; struct LottieComposition;
struct LottieLayer; struct LottieLayer;
struct LottieRoundnessModifier; struct LottieModifier;
struct LottieOffsetModifier;
#ifdef THORVG_LOTTIE_EXPRESSIONS_SUPPORT #ifdef THORVG_LOTTIE_EXPRESSIONS_SUPPORT
@ -112,13 +111,13 @@ public:
} }
template<typename Property> template<typename Property>
bool result(float frameNo, RenderPath& out, Matrix* transform, LottieRoundnessModifier* roundness, LottieOffsetModifier* offset, LottieExpression* exp) bool result(float frameNo, RenderPath& out, Matrix* transform, LottieModifier* modifier, LottieExpression* exp)
{ {
auto bm_rt = evaluate(frameNo, exp); auto bm_rt = evaluate(frameNo, exp);
if (jerry_value_is_undefined(bm_rt)) return false; if (jerry_value_is_undefined(bm_rt)) return false;
if (auto pathset = static_cast<Property*>(jerry_object_get_native_ptr(bm_rt, nullptr))) { if (auto pathset = static_cast<Property*>(jerry_object_get_native_ptr(bm_rt, nullptr))) {
(*pathset)(frameNo, out, transform, nullptr, roundness, offset); (*pathset)(frameNo, out, transform, nullptr, modifier);
} }
jerry_value_free(bm_rt); jerry_value_free(bm_rt);
return true; return true;
@ -157,7 +156,7 @@ struct LottieExpressions
template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED Point&, LottieExpression*) { return false; } template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED Point&, LottieExpression*) { return false; }
template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED RGB24&, TVG_UNUSED LottieExpression*) { return false; } template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED RGB24&, TVG_UNUSED LottieExpression*) { return false; }
template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED Fill*, TVG_UNUSED LottieExpression*) { return false; } template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED Fill*, TVG_UNUSED LottieExpression*) { return false; }
template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED RenderPath&, TVG_UNUSED Matrix*, TVG_UNUSED LottieRoundnessModifier*, TVG_UNUSED LottieOffsetModifier*, TVG_UNUSED LottieExpression*) { return false; } template<typename Property> bool result(TVG_UNUSED float, TVG_UNUSED RenderPath&, TVG_UNUSED Matrix*, TVG_UNUSED LottieModifier*, TVG_UNUSED LottieExpression*) { return false; }
void update(TVG_UNUSED float) {} void update(TVG_UNUSED float) {}
static LottieExpressions* instance() { return nullptr; } static LottieExpressions* instance() { return nullptr; }
static void retrieve(TVG_UNUSED LottieExpressions* instance) {} static void retrieve(TVG_UNUSED LottieExpressions* instance) {}

View file

@ -96,7 +96,7 @@ static bool _clockwise(Point* pts, uint32_t n)
} }
void LottieOffsetModifier::corner(RenderPath& out, Line& line, Line& nextLine, uint32_t movetoOutIndex, bool nextClose) const void LottieOffsetModifier::corner(RenderPath& out, Line& line, Line& nextLine, uint32_t movetoOutIndex, bool nextClose)
{ {
bool inside{}; bool inside{};
Point intersect{}; Point intersect{};
@ -127,7 +127,7 @@ void LottieOffsetModifier::corner(RenderPath& out, Line& line, Line& nextLine, u
} }
void LottieOffsetModifier::line(RenderPath& out, PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t& curPt, uint32_t curCmd, State& state, float offset, bool degenerated) const void LottieOffsetModifier::line(RenderPath& out, PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t& curPt, uint32_t curCmd, State& state, float offset, bool degenerated)
{ {
if (tvg::zero(inPts[curPt - 1] - inPts[curPt])) { if (tvg::zero(inPts[curPt - 1] - inPts[curPt])) {
++curPt; ++curPt;
@ -172,19 +172,23 @@ void LottieOffsetModifier::line(RenderPath& out, PathCommand* inCmds, uint32_t i
/* External Class Implementation */ /* External Class Implementation */
/************************************************************************/ /************************************************************************/
bool LottieRoundnessModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) const bool LottieRoundnessModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out)
{ {
out.cmds.reserve(inCmdsCnt * 2); buffer->clear();
out.pts.reserve((uint32_t)(inPtsCnt * 1.5));
auto ptsCnt = out.pts.count; auto& path = (next) ? *buffer : out;
path.cmds.reserve(inCmdsCnt * 2);
path.pts.reserve((uint32_t)(inPtsCnt * 1.5));
auto pivot = path.pts.count;
uint32_t startIndex = 0; uint32_t startIndex = 0;
for (uint32_t iCmds = 0, iPts = 0; iCmds < inCmdsCnt; ++iCmds) { for (uint32_t iCmds = 0, iPts = 0; iCmds < inCmdsCnt; ++iCmds) {
switch (inCmds[iCmds]) { switch (inCmds[iCmds]) {
case PathCommand::MoveTo: { case PathCommand::MoveTo: {
startIndex = out.pts.count; startIndex = path.pts.count;
out.cmds.push(PathCommand::MoveTo); path.cmds.push(PathCommand::MoveTo);
out.pts.push(inPts[iPts++]); path.pts.push(inPts[iPts++]);
break; break;
} }
case PathCommand::CubicTo: { case PathCommand::CubicTo: {
@ -196,52 +200,59 @@ bool LottieRoundnessModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt
if (inCmds[iCmds + 1] == PathCommand::CubicTo && if (inCmds[iCmds + 1] == PathCommand::CubicTo &&
tvg::zero(inPts[iPts + 2] - inPts[iPts + 3]) && tvg::zero(inPts[iPts + 2] - inPts[iPts + 3]) &&
tvg::zero(inPts[iPts + 4] - inPts[iPts + 5])) { tvg::zero(inPts[iPts + 4] - inPts[iPts + 5])) {
_roundCorner(out.cmds, out.pts, prev, curr, inPts[iPts + 5], r); _roundCorner(path.cmds, path.pts, prev, curr, inPts[iPts + 5], r);
iPts += 3; iPts += 3;
break; break;
} else if (inCmds[iCmds + 1] == PathCommand::Close) { } else if (inCmds[iCmds + 1] == PathCommand::Close) {
_roundCorner(out.cmds, out.pts, prev, curr, inPts[2], r); _roundCorner(path.cmds, path.pts, prev, curr, inPts[2], r);
out.pts[startIndex] = out.pts.last(); path.pts[startIndex] = path.pts.last();
iPts += 3; iPts += 3;
break; break;
} }
} }
out.cmds.push(PathCommand::CubicTo); path.cmds.push(PathCommand::CubicTo);
out.pts.push(inPts[iPts++]); path.pts.push(inPts[iPts++]);
out.pts.push(inPts[iPts++]); path.pts.push(inPts[iPts++]);
out.pts.push(inPts[iPts++]); path.pts.push(inPts[iPts++]);
break; break;
} }
case PathCommand::Close: { case PathCommand::Close: {
out.cmds.push(PathCommand::Close); path.cmds.push(PathCommand::Close);
break; break;
} }
default: break; default: break;
} }
} }
if (transform) { if (transform) {
for (auto i = ptsCnt; i < out.pts.count; ++i) { for (auto i = pivot; i < path.pts.count; ++i) {
out.pts[i] *= *transform; path.pts[i] *= *transform;
} }
} }
if (next) return next->modifyPath(path.cmds.data, path.cmds.count, path.pts.data, path.pts.count, transform, out);
return true; return true;
} }
bool LottieRoundnessModifier::modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) const bool LottieRoundnessModifier::modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness)
{ {
static constexpr auto ROUNDED_POLYSTAR_MAGIC_NUMBER = 0.47829f; constexpr auto ROUNDED_POLYSTAR_MAGIC_NUMBER = 0.47829f;
buffer->clear();
auto& path = (next) ? *buffer : out;
auto len = length(in.pts[1] - in.pts[2]); auto len = length(in.pts[1] - in.pts[2]);
auto r = len > 0.0f ? ROUNDED_POLYSTAR_MAGIC_NUMBER * std::min(len * 0.5f, this->r) / len : 0.0f; auto r = len > 0.0f ? ROUNDED_POLYSTAR_MAGIC_NUMBER * std::min(len * 0.5f, this->r) / len : 0.0f;
if (hasRoundness) { if (hasRoundness) {
out.cmds.grow((uint32_t)(1.5 * in.cmds.count)); path.cmds.grow((uint32_t)(1.5 * in.cmds.count));
out.pts.grow((uint32_t)(4.5 * in.cmds.count)); path.pts.grow((uint32_t)(4.5 * in.cmds.count));
int start = 3 * tvg::zero(outerRoundness); int start = 3 * tvg::zero(outerRoundness);
out.cmds.push(PathCommand::MoveTo); path.cmds.push(PathCommand::MoveTo);
out.pts.push(in.pts[start]); path.pts.push(in.pts[start]);
for (uint32_t i = 1 + start; i < in.pts.count; i += 6) { for (uint32_t i = 1 + start; i < in.pts.count; i += 6) {
auto& prev = in.pts[i]; auto& prev = in.pts[i];
@ -256,21 +267,21 @@ bool LottieRoundnessModifier::modifyPolystar(RenderPath& in, RenderPath& out, fl
auto p2 = curr - dNext; auto p2 = curr - dNext;
auto p3 = curr - 2.0f * dNext; auto p3 = curr - 2.0f * dNext;
out.cmds.push(PathCommand::CubicTo); path.cmds.push(PathCommand::CubicTo);
out.pts.push(prev); out.pts.push(p0); out.pts.push(p0); path.pts.push(prev); path.pts.push(p0); path.pts.push(p0);
out.cmds.push(PathCommand::CubicTo); path.cmds.push(PathCommand::CubicTo);
out.pts.push(p1); out.pts.push(p2); out.pts.push(p3); path.pts.push(p1); path.pts.push(p2); path.pts.push(p3);
out.cmds.push(PathCommand::CubicTo); path.cmds.push(PathCommand::CubicTo);
out.pts.push(p3); out.pts.push(next); out.pts.push(nextCtrl); path.pts.push(p3); path.pts.push(next); path.pts.push(nextCtrl);
} }
} else { } else {
out.cmds.grow(2 * in.cmds.count); path.cmds.grow(2 * in.cmds.count);
out.pts.grow(4 * in.cmds.count); path.pts.grow(4 * in.cmds.count);
auto dPrev = r * (in.pts[1] - in.pts[0]); auto dPrev = r * (in.pts[1] - in.pts[0]);
auto p = in.pts[0] + 2.0f * dPrev; auto p = in.pts[0] + 2.0f * dPrev;
out.cmds.push(PathCommand::MoveTo); path.cmds.push(PathCommand::MoveTo);
out.pts.push(p); path.pts.push(p);
for (uint32_t i = 1; i < in.pts.count; ++i) { for (uint32_t i = 1; i < in.pts.count; ++i) {
auto& curr = in.pts[i]; auto& curr = in.pts[i];
@ -282,28 +293,33 @@ bool LottieRoundnessModifier::modifyPolystar(RenderPath& in, RenderPath& out, fl
auto p2 = curr - dNext; auto p2 = curr - dNext;
auto p3 = curr - 2.0f * dNext; auto p3 = curr - 2.0f * dNext;
out.cmds.push(PathCommand::LineTo); path.cmds.push(PathCommand::LineTo);
out.pts.push(p0); path.pts.push(p0);
out.cmds.push(PathCommand::CubicTo); path.cmds.push(PathCommand::CubicTo);
out.pts.push(p1); out.pts.push(p2); out.pts.push(p3); path.pts.push(p1); path.pts.push(p2); path.pts.push(p3);
dPrev = -1.0f * dNext; dPrev = -1.0f * dNext;
} }
} }
out.cmds.push(PathCommand::Close); path.cmds.push(PathCommand::Close);
if (next) return next->modifyPolystar(path, out, outerRoundness, hasRoundness);
return true; return true;
} }
bool LottieRoundnessModifier::modifyRect(Point& size, float& r) const bool LottieRoundnessModifier::modifyRect(Point& size, float& r)
{ {
r = std::min(this->r, std::max(size.x, size.y) * 0.5f); r = std::min(this->r, std::max(size.x, size.y) * 0.5f);
return true; return true;
} }
bool LottieOffsetModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, RenderPath& out) const bool LottieOffsetModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, TVG_UNUSED Matrix* transform, RenderPath& out)
{ {
if (next) TVGERR("LOTTIE", "Offset has a next modifier?");
out.cmds.reserve(inCmdsCnt * 2); out.cmds.reserve(inCmdsCnt * 2);
out.pts.reserve(inPtsCnt * (join == StrokeJoin::Round ? 4 : 2)); out.pts.reserve(inPtsCnt * (join == StrokeJoin::Round ? 4 : 2));
@ -376,18 +392,19 @@ bool LottieOffsetModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, P
} }
bool LottieOffsetModifier::modifyPolystar(RenderPath& in, RenderPath& out) const { bool LottieOffsetModifier::modifyPolystar(RenderPath& in, RenderPath& out, TVG_UNUSED float, TVG_UNUSED bool)
return modifyPath(in.cmds.data, in.cmds.count, in.pts.data, in.pts.count, out);
}
bool LottieOffsetModifier::modifyRect(RenderPath& in, RenderPath& out) const
{ {
return modifyPath(in.cmds.data, in.cmds.count, in.pts.data, in.pts.count, out); return modifyPath(in.cmds.data, in.cmds.count, in.pts.data, in.pts.count, nullptr, out);
} }
bool LottieOffsetModifier::modifyEllipse(Point& radius) const bool LottieOffsetModifier::modifyRect(RenderPath& in, RenderPath& out)
{
return modifyPath(in.cmds.data, in.cmds.count, in.pts.data, in.pts.count, nullptr, out);
}
bool LottieOffsetModifier::modifyEllipse(Point& radius)
{ {
radius.x += offset; radius.x += offset;
radius.y += offset; radius.y += offset;

View file

@ -28,31 +28,69 @@
#include "tvgMath.h" #include "tvgMath.h"
#include "tvgRender.h" #include "tvgRender.h"
struct LottieRoundnessModifier
struct LottieModifier
{
enum Type : uint8_t {Roundness = 0, Offset};
LottieModifier* next = nullptr;
Type type;
virtual ~LottieModifier() {}
virtual bool modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) = 0;
virtual bool modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) = 0;
LottieModifier* decorate(LottieModifier* next)
{
/* TODO: build the decorative chaining here.
currently we only have roundness and offset. */
//roundness -> offset
if (next->type == Roundness) {
next->next = this;
return next;
}
//just in the order.
this->next = next;
return this;
}
};
struct LottieRoundnessModifier : LottieModifier
{ {
static constexpr float ROUNDNESS_EPSILON = 1.0f; static constexpr float ROUNDNESS_EPSILON = 1.0f;
RenderPath* buffer;
float r; float r;
LottieRoundnessModifier(float r) : r(r) {}; LottieRoundnessModifier(RenderPath* buffer, float r) : buffer(buffer), r(r)
{
type = Roundness;
}
bool modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) const; bool modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) override;
bool modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) const; bool modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) override;
bool modifyRect(Point& size, float& r) const; bool modifyRect(Point& size, float& r);
}; };
struct LottieOffsetModifier struct LottieOffsetModifier : LottieModifier
{ {
float offset; float offset;
float miterLimit; float miterLimit;
StrokeJoin join; StrokeJoin join;
LottieOffsetModifier(float offset, float miter = 4.0f, StrokeJoin join = StrokeJoin::Round) : offset(offset), miterLimit(miter), join(join) {} LottieOffsetModifier(float offset, float miter = 4.0f, StrokeJoin join = StrokeJoin::Round) : offset(offset), miterLimit(miter), join(join)
{
type = Offset;
}
bool modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, RenderPath& out) const; bool modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) override;
bool modifyPolystar(RenderPath& in, RenderPath& out) const; bool modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) override;
bool modifyRect(RenderPath& in, RenderPath& out) const; bool modifyRect(RenderPath& in, RenderPath& out);
bool modifyEllipse(Point& radius) const; bool modifyEllipse(Point& radius);
private: private:
struct State struct State
@ -64,8 +102,8 @@ private:
uint32_t movetoInIndex = 0; uint32_t movetoInIndex = 0;
}; };
void line(RenderPath& out, PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t& curPt, uint32_t curCmd, State& state, float offset, bool degenerated) const; void line(RenderPath& out, PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t& curPt, uint32_t curCmd, State& state, float offset, bool degenerated);
void corner(RenderPath& out, Line& line, Line& nextLine, uint32_t movetoIndex, bool nextClose) const; void corner(RenderPath& out, Line& line, Line& nextLine, uint32_t movetoIndex, bool nextClose);
}; };
#endif #endif

View file

@ -467,7 +467,7 @@ struct LottiePathSet : LottieProperty
return true; return true;
} }
bool modifiedPath(float frameNo, RenderPath& out, Matrix* transform, const LottieRoundnessModifier* roundness, const LottieOffsetModifier* offset) bool modifiedPath(float frameNo, RenderPath& out, Matrix* transform, LottieModifier* modifier)
{ {
PathSet* path; PathSet* path;
LottieScalarFrame<PathSet>* frame; LottieScalarFrame<PathSet>* frame;
@ -475,17 +475,7 @@ struct LottiePathSet : LottieProperty
float t; float t;
if (dispatch(frameNo, path, frame, t)) { if (dispatch(frameNo, path, frame, t)) {
if (roundness) { if (modifier) return modifier->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, transform, out);
if (offset) {
temp.cmds = Array<PathCommand>(path->cmdsCnt);
temp.pts = Array<Point>(path->ptsCnt);
roundness->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, transform, temp);
return offset->modifyPath(temp.cmds.data, temp.cmds.count, temp.pts.data, temp.pts.count, out);
}
return roundness->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, transform, out);
}
if (offset) return offset->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, out);
_copy(path, out.cmds); _copy(path, out.cmds);
_copy(path, out.pts, transform); _copy(path, out.pts, transform);
return true; return true;
@ -502,12 +492,7 @@ struct LottiePathSet : LottieProperty
if (transform) *p *= *transform; if (transform) *p *= *transform;
} }
if (roundness) { if (modifier) modifier->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, nullptr, out);
if (offset) {
roundness->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, nullptr, temp);
offset->modifyPath(temp.cmds.data, temp.cmds.count, temp.pts.data, temp.pts.count, out);
} else roundness->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, nullptr, out);
} else if (offset) offset->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, out);
free(interpPts); free(interpPts);
@ -539,15 +524,15 @@ struct LottiePathSet : LottieProperty
return true; return true;
} }
bool operator()(float frameNo, RenderPath& out, Matrix* transform, LottieExpressions* exps, LottieRoundnessModifier* roundness = nullptr, LottieOffsetModifier* offset = nullptr) bool operator()(float frameNo, RenderPath& out, Matrix* transform, LottieExpressions* exps, LottieModifier* modifier = nullptr)
{ {
//overriding with expressions //overriding with expressions
if (exps && exp) { if (exps && exp) {
frameNo = _loop(frames, frameNo, exp); frameNo = _loop(frames, frameNo, exp);
if (exps->result<LottiePathSet>(frameNo, out, transform, roundness, offset, exp)) return true; if (exps->result<LottiePathSet>(frameNo, out, transform, modifier, exp)) return true;
} }
if (roundness || offset) return modifiedPath(frameNo, out, transform, roundness, offset); if (modifier) return modifiedPath(frameNo, out, transform, modifier);
else return defaultPath(frameNo, out, transform); else return defaultPath(frameNo, out, transform);
} }