diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index 194d4cff..c1ead920 100644 --- a/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/src/loaders/lottie/tvgLottieBuilder.cpp @@ -705,7 +705,7 @@ void LottieBuilder::updateRoundedCorner(TVG_UNUSED LottieGroup* parent, LottieOb auto r = roundedCorner->radius(frameNo, tween, exps); if (r < LottieRoundnessModifier::ROUNDNESS_EPSILON) return; - if (!ctx->roundness) ctx->roundness = new LottieRoundnessModifier(&buffer, r); + if (!ctx->roundness) ctx->roundness = new LottieRoundnessModifier(buffer, r); else if (ctx->roundness->r < r) ctx->roundness->r = r; ctx->update(ctx->roundness); @@ -715,7 +715,7 @@ void LottieBuilder::updateRoundedCorner(TVG_UNUSED LottieGroup* parent, LottieOb void LottieBuilder::updateOffsetPath(TVG_UNUSED LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist& contexts, RenderContext* ctx) { auto offset = static_cast(*child); - if (!ctx->offset) ctx->offset = new LottieOffsetModifier(offset->offset(frameNo, tween, exps), offset->miterLimit(frameNo, tween, exps), offset->join); + if (!ctx->offset) ctx->offset = new LottieOffsetModifier(buffer, offset->offset(frameNo, tween, exps), offset->miterLimit(frameNo, tween, exps), offset->join); ctx->update(ctx->offset); } @@ -1222,7 +1222,7 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo) //Masking with Expansion (Offset) } else { //TODO: Once path direction support is implemented, ensure that the direction is ignored here - auto offset = LottieOffsetModifier(expand); + auto offset = LottieOffsetModifier(buffer, expand); mask->pathset(frameNo, SHAPE(pShape)->rs.path, nullptr, tween, exps, &offset); } diff --git a/src/loaders/lottie/tvgLottieBuilder.h b/src/loaders/lottie/tvgLottieBuilder.h index c600f072..dfbe5233 100644 --- a/src/loaders/lottie/tvgLottieBuilder.h +++ b/src/loaders/lottie/tvgLottieBuilder.h @@ -88,7 +88,7 @@ struct RenderContext update(roundness); } if (rhs.offset) { - offset = new LottieOffsetModifier(rhs.offset->offset, rhs.offset->miterLimit, rhs.offset->join); + offset = new LottieOffsetModifier(rhs.offset->buffer, rhs.offset->offset, rhs.offset->miterLimit, rhs.offset->join); update(offset); } if (rhs.transform) { @@ -97,10 +97,10 @@ struct RenderContext } } - void update(LottieModifier* next) + void update(LottieModifier* prev) { - if (modifier) modifier = modifier->decorate(next); - else modifier = next; + if (modifier) modifier = modifier->decorate(prev); + else modifier = prev; } }; @@ -174,7 +174,7 @@ private: void updateRoundedCorner(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); void updateOffsetPath(LottieGroup* parent, LottieObject** child, float frameNo, Inlist& contexts, RenderContext* ctx); - RenderPath buffer; //resusable path + RenderPath buffer[2]; //resusable path LottieExpressions* exps; Tween tween; }; diff --git a/src/loaders/lottie/tvgLottieModifier.cpp b/src/loaders/lottie/tvgLottieModifier.cpp index e9a54682..f0a8096f 100644 --- a/src/loaders/lottie/tvgLottieModifier.cpp +++ b/src/loaders/lottie/tvgLottieModifier.cpp @@ -180,9 +180,8 @@ void LottieOffsetModifier::line(RenderPath& out, PathCommand* inCmds, uint32_t i bool LottieRoundnessModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) { - buffer->clear(); - - auto& path = (next) ? *buffer : out; + auto& path = next ? (inCmds == buffer[0].cmds.data ? buffer[1] : buffer[0]) : out; + if (next) path.clear(); path.cmds.reserve(inCmdsCnt * 2); path.pts.reserve((uint32_t)(inPtsCnt * 1.5)); @@ -272,9 +271,8 @@ bool LottieRoundnessModifier::modifyPolystar(RenderPath& in, RenderPath& out, fl { constexpr auto ROUNDED_POLYSTAR_MAGIC_NUMBER = 0.47829f; - buffer->clear(); - - auto& path = (next) ? *buffer : out; + auto& path = next ? (&in == &buffer[0] ? buffer[1] : buffer[0]) : out; + if (next) path.clear(); 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; @@ -351,10 +349,11 @@ bool LottieRoundnessModifier::modifyRect(Point& size, float& r) 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?"); + auto& path = next ? (inCmds == buffer[0].cmds.data ? buffer[1] : buffer[0]) : out; + if (next) path.clear(); - out.cmds.reserve(inCmdsCnt * 2); - out.pts.reserve(inPtsCnt * (join == StrokeJoin::Round ? 4 : 2)); + path.cmds.reserve(inCmdsCnt * 2); + path.pts.reserve(inPtsCnt * (join == StrokeJoin::Round ? 4 : 2)); Array stack{5}; State state; @@ -366,12 +365,12 @@ bool LottieOffsetModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, P state.moveto = true; state.movetoInIndex = iPt++; } else if (inCmds[iCmd] == PathCommand::LineTo) { - line(out, inCmds, inCmdsCnt, inPts, iPt, iCmd, state, offset, false); + line(path, inCmds, inCmdsCnt, inPts, iPt, iCmd, state, offset, false); } else if (inCmds[iCmd] == PathCommand::CubicTo) { //cubic degenerated to a line if (tvg::zero(inPts[iPt - 1] - inPts[iPt]) || tvg::zero(inPts[iPt + 1] - inPts[iPt + 2])) { ++iPt; - line(out, inCmds, inCmdsCnt, inPts, iPt, iCmd, state, offset, true); + line(path, inCmds, inCmdsCnt, inPts, iPt, iCmd, state, offset, true); ++iPt; continue; } @@ -394,9 +393,9 @@ bool LottieOffsetModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, P auto line3 = _offset(bezier.ctrl2, bezier.end, offset); if (state.moveto) { - out.cmds.push(PathCommand::MoveTo); - state.movetoOutIndex = out.pts.count; - out.pts.push(line1.pt1); + path.cmds.push(PathCommand::MoveTo); + state.movetoOutIndex = path.pts.count; + path.pts.push(line1.pt1); state.firstLine = line1; state.moveto = false; } @@ -404,23 +403,26 @@ bool LottieOffsetModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, P bool inside{}; Point intersect{}; _intersect(line1, line2, intersect, inside); - out.pts.push(intersect); + path.pts.push(intersect); _intersect(line2, line3, intersect, inside); - out.pts.push(intersect); - out.pts.push(line3.pt2); - out.cmds.push(PathCommand::CubicTo); + path.pts.push(intersect); + path.pts.push(line3.pt2); + path.cmds.push(PathCommand::CubicTo); } iPt += 3; } else { if (!_zero(inPts[iPt - 1], inPts[state.movetoInIndex])) { - out.cmds.push(PathCommand::LineTo); - corner(out, state.line, state.firstLine, state.movetoOutIndex, true); + path.cmds.push(PathCommand::LineTo); + corner(path, state.line, state.firstLine, state.movetoOutIndex, true); } - out.cmds.push(PathCommand::Close); + path.cmds.push(PathCommand::Close); } } + + if (next) return next->modifyPath(path.cmds.data, path.cmds.count, path.pts.data, path.pts.count, transform, out); + return true; } diff --git a/src/loaders/lottie/tvgLottieModifier.h b/src/loaders/lottie/tvgLottieModifier.h index b9367431..7ab1b120 100644 --- a/src/loaders/lottie/tvgLottieModifier.h +++ b/src/loaders/lottie/tvgLottieModifier.h @@ -31,44 +31,33 @@ struct LottieModifier { - enum Type : uint8_t {Roundness = 0, Offset}; - LottieModifier* next = nullptr; - Type type; + RenderPath* buffer; + bool chained = false; + LottieModifier(RenderPath* buffer) : buffer(buffer) {} 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) + LottieModifier* decorate(LottieModifier* prev) { - /* TODO: build the decorative chaining here. - currently we only have roundness and offset. */ + //prevent cyclic decoration: 1) self-decoration: a->decorate(a); 2) mutual decoration: a->decorate(b); b->decorate(a); + if (prev->chained || prev == this) return this; + this->chained = true; - //roundness -> offset - if (next->type == Roundness) { - next->next = this; - return next; - } - - //just in the order. - this->next = next; - return this; + prev->next = this; + return prev; } }; struct LottieRoundnessModifier : LottieModifier { static constexpr float ROUNDNESS_EPSILON = 1.0f; - - RenderPath* buffer; float r; - LottieRoundnessModifier(RenderPath* buffer, float r) : buffer(buffer), r(r) - { - type = Roundness; - } + LottieRoundnessModifier(RenderPath* buffer, float r) : LottieModifier(buffer), r(r) {} 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) override; @@ -82,10 +71,7 @@ struct LottieOffsetModifier : LottieModifier float miterLimit; StrokeJoin join; - LottieOffsetModifier(float offset, float miter = 4.0f, StrokeJoin join = StrokeJoin::Round) : offset(offset), miterLimit(miter), join(join) - { - type = Offset; - } + LottieOffsetModifier(RenderPath* buffer, float offset, float miter = 4.0f, StrokeJoin join = StrokeJoin::Round) : LottieModifier(buffer), offset(offset), miterLimit(miter), join(join) {} 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) override;