From 4edaf311c6aa00867e83ee85d049ea00aeb01777 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 5 Feb 2025 19:48:45 +0900 Subject: [PATCH] lottie: ++compact & neat code introduced RenderPath to minimize the path parameters. --- src/loaders/lottie/tvgLottieBuilder.cpp | 42 ++--- src/loaders/lottie/tvgLottieExpressions.h | 6 +- src/loaders/lottie/tvgLottieModifier.cpp | 208 +++++++++++----------- src/loaders/lottie/tvgLottieModifier.h | 20 +-- src/loaders/lottie/tvgLottieProperty.h | 47 +++-- src/renderer/tvgRender.h | 13 +- 6 files changed, 165 insertions(+), 171 deletions(-) diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index 25cd171c..dc5cf331 100644 --- a/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/src/loaders/lottie/tvgLottieBuilder.cpp @@ -450,7 +450,7 @@ static void _appendRect(Shape* shape, Point& pos, Point& size, float r, bool clo } } - if (ctx->offset) ctx->offset->modifyRect(cmds, cmdsCnt, pts, ptsCnt, SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts); + if (ctx->offset) ctx->offset->modifyRect(cmds, cmdsCnt, pts, ptsCnt, SHAPE(shape)->rs.path); else shape->appendPath(cmds, cmdsCnt, pts, ptsCnt); } @@ -539,11 +539,11 @@ void LottieBuilder::updatePath(LottieGroup* parent, LottieObject** child, float if (!ctx->repeaters.empty()) { auto shape = path->pooling(); shape->reset(); - path->pathset(frameNo, SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, ctx->transform, ctx->roundness, ctx->offset, exps); + path->pathset(frameNo, SHAPE(shape)->rs.path, ctx->transform, exps, ctx->roundness, ctx->offset); _repeat(parent, shape, ctx); } else { _draw(parent, path, ctx); - if (path->pathset(frameNo, SHAPE(ctx->merging)->rs.path.cmds, SHAPE(ctx->merging)->rs.path.pts, ctx->transform, ctx->roundness, ctx->offset, exps)) { + if (path->pathset(frameNo, SHAPE(ctx->merging)->rs.path, ctx->transform, exps, ctx->roundness, ctx->offset)) { PAINT(ctx->merging)->update(RenderUpdateFlag::Path); } } @@ -668,14 +668,11 @@ static void _updateStar(TVG_UNUSED LottieGroup* parent, LottiePolyStar* star, Ma if (roundedCorner) { if (offset) { - auto intermediate = Shape::gen(); - roundness->modifyPolystar(SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, SHAPE(intermediate)->rs.path.cmds, SHAPE(intermediate)->rs.path.pts, outerRoundness, hasRoundness); - offset->modifyPolystar(SHAPE(intermediate)->rs.path.cmds, SHAPE(intermediate)->rs.path.pts, SHAPE(merging)->rs.path.cmds, SHAPE(merging)->rs.path.pts); - delete(intermediate); - } else { - roundness->modifyPolystar(SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, SHAPE(merging)->rs.path.cmds, SHAPE(merging)->rs.path.pts, outerRoundness, hasRoundness); - } - } else if (offset) offset->modifyPolystar(SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, SHAPE(merging)->rs.path.cmds, SHAPE(merging)->rs.path.pts); + RenderPath temp; + roundness->modifyPolystar(SHAPE(shape)->rs.path, temp, outerRoundness, hasRoundness); + offset->modifyPolystar(temp, SHAPE(merging)->rs.path); + } else roundness->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path, outerRoundness, hasRoundness); + } else if (offset) offset->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path); } @@ -755,14 +752,11 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr if (roundedCorner) { if (offset) { - auto intermediate = Shape::gen(); - roundness->modifyPolystar(SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, SHAPE(intermediate)->rs.path.cmds, SHAPE(intermediate)->rs.path.pts, 0.0f, false); - offset->modifyPolystar(SHAPE(intermediate)->rs.path.cmds, SHAPE(intermediate)->rs.path.pts, SHAPE(merging)->rs.path.cmds, SHAPE(merging)->rs.path.pts); - delete(intermediate); - } else { - roundness->modifyPolystar(SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, SHAPE(merging)->rs.path.cmds, SHAPE(merging)->rs.path.pts, 0.0f, false); - } - } else if (offset) offset->modifyPolystar(SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, SHAPE(merging)->rs.path.cmds, SHAPE(merging)->rs.path.pts); + RenderPath temp; + roundness->modifyPolystar(SHAPE(shape)->rs.path, temp, 0.0f, false); + offset->modifyPolystar(temp, SHAPE(merging)->rs.path); + } else roundness->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path, 0.0f, false); + } else if (offset) offset->modifyPolystar(SHAPE(shape)->rs.path, SHAPE(merging)->rs.path); } @@ -1064,7 +1058,7 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo) ARRAY_FOREACH(p, glyph->children) { auto group = static_cast(*p); ARRAY_FOREACH(p, group->children) { - if (static_cast(*p)->pathset(frameNo, SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, nullptr, nullptr, nullptr, exps)) { + if (static_cast(*p)->pathset(frameNo, SHAPE(shape)->rs.path, nullptr, exps)) { PAINT(shape)->update(RenderUpdateFlag::Path); } } @@ -1254,12 +1248,12 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo) //Default Masking if (expand == 0.0f) { - mask->pathset(frameNo, SHAPE(pShape)->rs.path.cmds, SHAPE(pShape)->rs.path.pts, nullptr, nullptr, nullptr, exps); + mask->pathset(frameNo, SHAPE(pShape)->rs.path, nullptr, exps); //Masking with Expansion (Offset) } else { //TODO: Once path direction support is implemented, ensure that the direction is ignored here auto offset = LottieOffsetModifier(expand); - mask->pathset(frameNo, SHAPE(pShape)->rs.path.cmds, SHAPE(pShape)->rs.path.pts, nullptr, nullptr, &offset, exps); + mask->pathset(frameNo, SHAPE(pShape)->rs.path, nullptr, exps, nullptr, &offset); } if (fastTrack) return; @@ -1300,14 +1294,14 @@ void LottieBuilder::updateStrokeEffect(LottieLayer* layer, LottieFxStroke* effec if (effect->allMask(frameNo)) { ARRAY_FOREACH(p, layer->masks) { auto mask = *p; - mask->pathset(frameNo, SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, nullptr, nullptr, nullptr, exps); + mask->pathset(frameNo, SHAPE(shape)->rs.path, nullptr, exps); } //A specific mask } else { auto idx = static_cast(effect->mask(frameNo) - 1); if (idx < 0 || idx >= layer->masks.count) return; auto mask = layer->masks[idx]; - mask->pathset(frameNo, SHAPE(shape)->rs.path.cmds, SHAPE(shape)->rs.path.pts, nullptr, nullptr, nullptr, exps); + mask->pathset(frameNo, SHAPE(shape)->rs.path, nullptr, exps, nullptr, nullptr); } shape->transform(layer->cache.matrix); diff --git a/src/loaders/lottie/tvgLottieExpressions.h b/src/loaders/lottie/tvgLottieExpressions.h index f14b652c..2da72281 100644 --- a/src/loaders/lottie/tvgLottieExpressions.h +++ b/src/loaders/lottie/tvgLottieExpressions.h @@ -112,13 +112,13 @@ public: } template - bool result(float frameNo, Array& cmds, Array& pts, Matrix* transform, const LottieRoundnessModifier* roundness, const LottieOffsetModifier* offset, LottieExpression* exp) + bool result(float frameNo, RenderPath& out, Matrix* transform, LottieRoundnessModifier* roundness, LottieOffsetModifier* offset, LottieExpression* exp) { auto bm_rt = evaluate(frameNo, exp); if (jerry_value_is_undefined(bm_rt)) return false; if (auto pathset = static_cast(jerry_object_get_native_ptr(bm_rt, nullptr))) { - (*pathset)(frameNo, cmds, pts, transform, roundness, offset); + (*pathset)(frameNo, out, transform, nullptr, roundness, offset); } jerry_value_free(bm_rt); return true; @@ -157,7 +157,7 @@ struct LottieExpressions template bool result(TVG_UNUSED float, TVG_UNUSED Point&, LottieExpression*) { return false; } template bool result(TVG_UNUSED float, TVG_UNUSED RGB24&, TVG_UNUSED LottieExpression*) { return false; } template bool result(TVG_UNUSED float, TVG_UNUSED Fill*, TVG_UNUSED LottieExpression*) { return false; } - template bool result(TVG_UNUSED float, TVG_UNUSED Array&, TVG_UNUSED Array&, TVG_UNUSED Matrix* transform, TVG_UNUSED const LottieRoundnessModifier*, TVG_UNUSED const LottieOffsetModifier*, TVG_UNUSED LottieExpression*) { return false; } + template bool result(TVG_UNUSED float, TVG_UNUSED RenderPath&, TVG_UNUSED Matrix*, TVG_UNUSED LottieRoundnessModifier*, TVG_UNUSED LottieOffsetModifier*, TVG_UNUSED LottieExpression*) { return false; } void update(TVG_UNUSED float) {} static LottieExpressions* instance() { return nullptr; } static void retrieve(TVG_UNUSED LottieExpressions* instance) {} diff --git a/src/loaders/lottie/tvgLottieModifier.cpp b/src/loaders/lottie/tvgLottieModifier.cpp index 7a5f265e..d698a2f0 100644 --- a/src/loaders/lottie/tvgLottieModifier.cpp +++ b/src/loaders/lottie/tvgLottieModifier.cpp @@ -27,7 +27,7 @@ /* Internal Class Implementation */ /************************************************************************/ -static void _roundCorner(Array& cmds, Array& pts, const Point& prev, const Point& curr, const Point& next, float r) +static void _roundCorner(Array& cmds, Array& pts, Point& prev, Point& curr, Point& next, float r) { auto lenPrev = length(prev - curr); auto rPrev = lenPrev > 0.0f ? 0.5f * std::min(lenPrev * 0.5f, r) / lenPrev : 0.0f; @@ -46,14 +46,14 @@ static void _roundCorner(Array& cmds, Array& pts, const Poin } -static bool _zero(const Point& p1, const Point& p2) +static bool _zero(Point& p1, Point& p2) { constexpr float epsilon = 1e-3f; return fabsf(p1.x / p2.x - 1.0f) < epsilon && fabsf(p1.y / p2.y - 1.0f) < epsilon; } -static bool _intersect(const Line& line1, const Line& line2, Point& intersection, bool& inside) +static bool _intersect(Line& line1, Line& line2, Point& intersection, bool& inside) { if (_zero(line1.pt2, line2.pt1)) { intersection = line1.pt2; @@ -76,14 +76,14 @@ static bool _intersect(const Line& line1, const Line& line2, Point& intersection } -static Line _offset(const Point& p1, const Point& p2, float offset) +static Line _offset(Point& p1, Point& p2, float offset) { auto scaledNormal = normal(p1, p2) * offset; return {p1 + scaledNormal, p2 + scaledNormal}; } -static bool _clockwise(const Point* pts, uint32_t n) +static bool _clockwise(Point* pts, uint32_t n) { auto area = 0.0f; @@ -96,50 +96,50 @@ static bool _clockwise(const Point* pts, uint32_t n) } -void LottieOffsetModifier::corner(const Line& line, const Line& nextLine, uint32_t movetoOutIndex, bool nextClose, Array& outCmds, Array& outPts) const +void LottieOffsetModifier::corner(RenderPath& out, Line& line, Line& nextLine, uint32_t movetoOutIndex, bool nextClose) const { bool inside{}; Point intersect{}; if (_intersect(line, nextLine, intersect, inside)) { if (inside) { - if (nextClose) outPts[movetoOutIndex] = intersect; - outPts.push(intersect); + if (nextClose) out.pts[movetoOutIndex] = intersect; + out.pts.push(intersect); } else { - outPts.push(line.pt2); + out.pts.push(line.pt2); if (join == StrokeJoin::Round) { - outCmds.push(PathCommand::CubicTo); - outPts.push((line.pt2 + intersect) * 0.5f); - outPts.push((nextLine.pt1 + intersect) * 0.5f); - outPts.push(nextLine.pt1); + out.cmds.push(PathCommand::CubicTo); + out.pts.push((line.pt2 + intersect) * 0.5f); + out.pts.push((nextLine.pt1 + intersect) * 0.5f); + out.pts.push(nextLine.pt1); } else if (join == StrokeJoin::Miter) { auto norm = normal(line.pt1, line.pt2); auto nextNorm = normal(nextLine.pt1, nextLine.pt2); auto miterDirection = (norm + nextNorm) / length(norm + nextNorm); - outCmds.push(PathCommand::LineTo); - if (1.0f <= miterLimit * fabsf(miterDirection.x * norm.x + miterDirection.y * norm.y)) outPts.push(intersect); - else outPts.push(nextLine.pt1); + out.cmds.push(PathCommand::LineTo); + if (1.0f <= miterLimit * fabsf(miterDirection.x * norm.x + miterDirection.y * norm.y)) out.pts.push(intersect); + else out.pts.push(nextLine.pt1); } else { - outCmds.push(PathCommand::LineTo); - outPts.push(nextLine.pt1); + out.cmds.push(PathCommand::LineTo); + out.pts.push(nextLine.pt1); } } - } else outPts.push(line.pt2); + } else out.pts.push(line.pt2); } -void LottieOffsetModifier::line(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t& currentPt, uint32_t currentCmd, State& state, bool degenerated, Array& outCmds, Array& outPts, float offset) 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) const { - if (tvg::zero(inPts[currentPt - 1] - inPts[currentPt])) { - ++currentPt; + if (tvg::zero(inPts[curPt - 1] - inPts[curPt])) { + ++curPt; return; } - if (inCmds[currentCmd - 1] != PathCommand::LineTo) state.line = _offset(inPts[currentPt - 1], inPts[currentPt], offset); + if (inCmds[curCmd - 1] != PathCommand::LineTo) state.line = _offset(inPts[curPt - 1], inPts[curPt], offset); if (state.moveto) { - outCmds.push(PathCommand::MoveTo); - state.movetoOutIndex = outPts.count; - outPts.push(state.line.pt1); + out.cmds.push(PathCommand::MoveTo); + state.movetoOutIndex = out.pts.count; + out.pts.push(state.line.pt1); state.firstLine = state.line; state.moveto = false; } @@ -148,43 +148,43 @@ void LottieOffsetModifier::line(const PathCommand* inCmds, uint32_t inCmdsCnt, c return inCmds[cmd] == PathCommand::CubicTo && !tvg::zero(inPts[pt] - inPts[pt + 1]) && !tvg::zero(inPts[pt + 2] - inPts[pt + 3]); }; - outCmds.push(PathCommand::LineTo); + out.cmds.push(PathCommand::LineTo); - if (currentCmd + 1 == inCmdsCnt || inCmds[currentCmd + 1] == PathCommand::MoveTo || nonDegeneratedCubic(currentCmd + 1, currentPt + degenerated)) { - outPts.push(state.line.pt2); - ++currentPt; + if (curCmd + 1 == inCmdsCnt || inCmds[curCmd + 1] == PathCommand::MoveTo || nonDegeneratedCubic(curCmd + 1, curPt + degenerated)) { + out.pts.push(state.line.pt2); + ++curPt; return; } Line nextLine = state.firstLine; - if (inCmds[currentCmd + 1] == PathCommand::LineTo) nextLine = _offset(inPts[currentPt + degenerated], inPts[currentPt + 1 + degenerated], offset); - else if (inCmds[currentCmd + 1] == PathCommand::CubicTo) nextLine = _offset(inPts[currentPt + 1 + degenerated], inPts[currentPt + 2 + degenerated], offset); - else if (inCmds[currentCmd + 1] == PathCommand::Close && !_zero(inPts[currentPt + degenerated], inPts[state.movetoInIndex + degenerated])) - nextLine = _offset(inPts[currentPt + degenerated], inPts[state.movetoInIndex + degenerated], offset); + if (inCmds[curCmd + 1] == PathCommand::LineTo) nextLine = _offset(inPts[curPt + degenerated], inPts[curPt + 1 + degenerated], offset); + else if (inCmds[curCmd + 1] == PathCommand::CubicTo) nextLine = _offset(inPts[curPt + 1 + degenerated], inPts[curPt + 2 + degenerated], offset); + else if (inCmds[curCmd + 1] == PathCommand::Close && !_zero(inPts[curPt + degenerated], inPts[state.movetoInIndex + degenerated])) + nextLine = _offset(inPts[curPt + degenerated], inPts[state.movetoInIndex + degenerated], offset); - corner(state.line, nextLine, state.movetoOutIndex, inCmds[currentCmd + 1] == PathCommand::Close, outCmds, outPts); + corner(out, state.line, nextLine, state.movetoOutIndex, inCmds[curCmd + 1] == PathCommand::Close); state.line = nextLine; - ++currentPt; + ++curPt; } /************************************************************************/ /* External Class Implementation */ /************************************************************************/ -bool LottieRoundnessModifier::modifyPath(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t inPtsCnt, Array& outCmds, Array& outPts, Matrix* transform) const +bool LottieRoundnessModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) const { - outCmds.reserve(inCmdsCnt * 2); - outPts.reserve((uint32_t)(inPtsCnt * 1.5)); - auto ptsCnt = outPts.count; + out.cmds.reserve(inCmdsCnt * 2); + out.pts.reserve((uint32_t)(inPtsCnt * 1.5)); + auto ptsCnt = out.pts.count; uint32_t startIndex = 0; for (uint32_t iCmds = 0, iPts = 0; iCmds < inCmdsCnt; ++iCmds) { switch (inCmds[iCmds]) { case PathCommand::MoveTo: { - startIndex = outPts.count; - outCmds.push(PathCommand::MoveTo); - outPts.push(inPts[iPts++]); + startIndex = out.pts.count; + out.cmds.push(PathCommand::MoveTo); + out.pts.push(inPts[iPts++]); break; } case PathCommand::CubicTo: { @@ -196,58 +196,58 @@ bool LottieRoundnessModifier::modifyPath(const PathCommand* inCmds, uint32_t inC if (inCmds[iCmds + 1] == PathCommand::CubicTo && tvg::zero(inPts[iPts + 2] - inPts[iPts + 3]) && tvg::zero(inPts[iPts + 4] - inPts[iPts + 5])) { - _roundCorner(outCmds, outPts, prev, curr, inPts[iPts + 5], r); + _roundCorner(out.cmds, out.pts, prev, curr, inPts[iPts + 5], r); iPts += 3; break; } else if (inCmds[iCmds + 1] == PathCommand::Close) { - _roundCorner(outCmds, outPts, prev, curr, inPts[2], r); - outPts[startIndex] = outPts.last(); + _roundCorner(out.cmds, out.pts, prev, curr, inPts[2], r); + out.pts[startIndex] = out.pts.last(); iPts += 3; break; } } - outCmds.push(PathCommand::CubicTo); - outPts.push(inPts[iPts++]); - outPts.push(inPts[iPts++]); - outPts.push(inPts[iPts++]); + out.cmds.push(PathCommand::CubicTo); + out.pts.push(inPts[iPts++]); + out.pts.push(inPts[iPts++]); + out.pts.push(inPts[iPts++]); break; } case PathCommand::Close: { - outCmds.push(PathCommand::Close); + out.cmds.push(PathCommand::Close); break; } default: break; } } if (transform) { - for (auto i = ptsCnt; i < outPts.count; ++i) { - outPts[i] *= *transform; + for (auto i = ptsCnt; i < out.pts.count; ++i) { + out.pts[i] *= *transform; } } return true; } -bool LottieRoundnessModifier::modifyPolystar(TVG_UNUSED const Array& inCmds, const Array& inPts, Array& outCmds, Array& outPts, float outerRoundness, bool hasRoundness) const +bool LottieRoundnessModifier::modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) const { static constexpr auto ROUNDED_POLYSTAR_MAGIC_NUMBER = 0.47829f; - auto len = length(inPts[1] - inPts[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; if (hasRoundness) { - outCmds.grow((uint32_t)(1.5 * inCmds.count)); - outPts.grow((uint32_t)(4.5 * inCmds.count)); + out.cmds.grow((uint32_t)(1.5 * in.cmds.count)); + out.pts.grow((uint32_t)(4.5 * in.cmds.count)); int start = 3 * tvg::zero(outerRoundness); - outCmds.push(PathCommand::MoveTo); - outPts.push(inPts[start]); + out.cmds.push(PathCommand::MoveTo); + out.pts.push(in.pts[start]); - for (uint32_t i = 1 + start; i < inPts.count; i += 6) { - auto& prev = inPts[i]; - auto& curr = inPts[i + 2]; - auto& next = (i < inPts.count - start) ? inPts[i + 4] : inPts[2]; - auto& nextCtrl = (i < inPts.count - start) ? inPts[i + 5] : inPts[3]; + for (uint32_t i = 1 + start; i < in.pts.count; i += 6) { + auto& prev = in.pts[i]; + auto& curr = in.pts[i + 2]; + auto& next = (i < in.pts.count - start) ? in.pts[i + 4] : in.pts[2]; + auto& nextCtrl = (i < in.pts.count - start) ? in.pts[i + 5] : in.pts[3]; auto dNext = r * (curr - next); auto dPrev = r * (curr - prev); @@ -256,25 +256,25 @@ bool LottieRoundnessModifier::modifyPolystar(TVG_UNUSED const Array auto p2 = curr - dNext; auto p3 = curr - 2.0f * dNext; - outCmds.push(PathCommand::CubicTo); - outPts.push(prev); outPts.push(p0); outPts.push(p0); - outCmds.push(PathCommand::CubicTo); - outPts.push(p1); outPts.push(p2); outPts.push(p3); - outCmds.push(PathCommand::CubicTo); - outPts.push(p3); outPts.push(next); outPts.push(nextCtrl); + out.cmds.push(PathCommand::CubicTo); + out.pts.push(prev); out.pts.push(p0); out.pts.push(p0); + out.cmds.push(PathCommand::CubicTo); + out.pts.push(p1); out.pts.push(p2); out.pts.push(p3); + out.cmds.push(PathCommand::CubicTo); + out.pts.push(p3); out.pts.push(next); out.pts.push(nextCtrl); } } else { - outCmds.grow(2 * inCmds.count); - outPts.grow(4 * inCmds.count); + out.cmds.grow(2 * in.cmds.count); + out.pts.grow(4 * in.cmds.count); - auto dPrev = r * (inPts[1] - inPts[0]); - auto p = inPts[0] + 2.0f * dPrev; - outCmds.push(PathCommand::MoveTo); - outPts.push(p); + auto dPrev = r * (in.pts[1] - in.pts[0]); + auto p = in.pts[0] + 2.0f * dPrev; + out.cmds.push(PathCommand::MoveTo); + out.pts.push(p); - for (uint32_t i = 1; i < inPts.count; ++i) { - auto& curr = inPts[i]; - auto& next = (i == inPts.count - 1) ? inPts[1] : inPts[i + 1]; + for (uint32_t i = 1; i < in.pts.count; ++i) { + auto& curr = in.pts[i]; + auto& next = (i == in.pts.count - 1) ? in.pts[1] : in.pts[i + 1]; auto dNext = r * (curr - next); auto p0 = curr - 2.0f * dPrev; @@ -282,30 +282,30 @@ bool LottieRoundnessModifier::modifyPolystar(TVG_UNUSED const Array auto p2 = curr - dNext; auto p3 = curr - 2.0f * dNext; - outCmds.push(PathCommand::LineTo); - outPts.push(p0); - outCmds.push(PathCommand::CubicTo); - outPts.push(p1); outPts.push(p2); outPts.push(p3); + out.cmds.push(PathCommand::LineTo); + out.pts.push(p0); + out.cmds.push(PathCommand::CubicTo); + out.pts.push(p1); out.pts.push(p2); out.pts.push(p3); dPrev = -1.0f * dNext; } } - outCmds.push(PathCommand::Close); + out.cmds.push(PathCommand::Close); return true; } -bool LottieRoundnessModifier::modifyRect(const Point& size, float& r) const +bool LottieRoundnessModifier::modifyRect(Point& size, float& r) const { r = std::min(this->r, std::max(size.x, size.y) * 0.5f); return true; } -bool LottieOffsetModifier::modifyPath(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t inPtsCnt, Array& outCmds, Array& outPts) const +bool LottieOffsetModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, RenderPath& out) const { - outCmds.reserve(inCmdsCnt * 2); - outPts.reserve(inPtsCnt * (join == StrokeJoin::Round ? 4 : 2)); + out.cmds.reserve(inCmdsCnt * 2); + out.pts.reserve(inPtsCnt * (join == StrokeJoin::Round ? 4 : 2)); Array stack{5}; State state; @@ -317,12 +317,12 @@ bool LottieOffsetModifier::modifyPath(const PathCommand* inCmds, uint32_t inCmds state.moveto = true; state.movetoInIndex = iPt++; } else if (inCmds[iCmd] == PathCommand::LineTo) { - line(inCmds, inCmdsCnt, inPts, iPt, iCmd, state, false, outCmds, outPts, offset); + line(out, 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(inCmds, inCmdsCnt, inPts, iPt, iCmd, state, true, outCmds, outPts, offset); + line(out, inCmds, inCmdsCnt, inPts, iPt, iCmd, state, offset, true); ++iPt; continue; } @@ -345,9 +345,9 @@ bool LottieOffsetModifier::modifyPath(const PathCommand* inCmds, uint32_t inCmds auto line3 = _offset(bezier.ctrl2, bezier.end, offset); if (state.moveto) { - outCmds.push(PathCommand::MoveTo); - state.movetoOutIndex = outPts.count; - outPts.push(line1.pt1); + out.cmds.push(PathCommand::MoveTo); + state.movetoOutIndex = out.pts.count; + out.pts.push(line1.pt1); state.firstLine = line1; state.moveto = false; } @@ -355,35 +355,35 @@ bool LottieOffsetModifier::modifyPath(const PathCommand* inCmds, uint32_t inCmds bool inside{}; Point intersect{}; _intersect(line1, line2, intersect, inside); - outPts.push(intersect); + out.pts.push(intersect); _intersect(line2, line3, intersect, inside); - outPts.push(intersect); - outPts.push(line3.pt2); - outCmds.push(PathCommand::CubicTo); + out.pts.push(intersect); + out.pts.push(line3.pt2); + out.cmds.push(PathCommand::CubicTo); } iPt += 3; } else { if (!_zero(inPts[iPt - 1], inPts[state.movetoInIndex])) { - outCmds.push(PathCommand::LineTo); - corner(state.line, state.firstLine, state.movetoOutIndex, true, outCmds, outPts); + out.cmds.push(PathCommand::LineTo); + corner(out, state.line, state.firstLine, state.movetoOutIndex, true); } - outCmds.push(PathCommand::Close); + out.cmds.push(PathCommand::Close); } } return true; } -bool LottieOffsetModifier::modifyPolystar(const Array& inCmds, const Array& inPts, Array& outCmds, Array& outPts) const { - return modifyPath(inCmds.data, inCmds.count, inPts.data, inPts.count, outCmds, outPts); +bool LottieOffsetModifier::modifyPolystar(RenderPath& in, RenderPath& out) const { + return modifyPath(in.cmds.data, in.cmds.count, in.pts.data, in.pts.count, out); } -bool LottieOffsetModifier::modifyRect(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t inPtsCnt, Array& outCmds, Array& outPts) const +bool LottieOffsetModifier::modifyRect(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, RenderPath& out) const { - return modifyPath(inCmds, inCmdsCnt, inPts, inPtsCnt, outCmds, outPts); + return modifyPath(inCmds, inCmdsCnt, inPts, inPtsCnt, out); } diff --git a/src/loaders/lottie/tvgLottieModifier.h b/src/loaders/lottie/tvgLottieModifier.h index 444ca836..e30fbc27 100644 --- a/src/loaders/lottie/tvgLottieModifier.h +++ b/src/loaders/lottie/tvgLottieModifier.h @@ -26,7 +26,7 @@ #include "tvgCommon.h" #include "tvgArray.h" #include "tvgMath.h" - +#include "tvgRender.h" struct LottieRoundnessModifier { @@ -35,9 +35,9 @@ struct LottieRoundnessModifier LottieRoundnessModifier(float r) : r(r) {}; - bool modifyPath(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t inPtsCnt, Array& outCmds, Array& outPts, Matrix* transform) const; - bool modifyPolystar(const Array& inCmds, const Array& inPts, Array& outCmds, Array& outPts, float outerRoundness, bool hasRoundness) const; - bool modifyRect(const Point& size, float& r) const; + bool modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, Matrix* transform, RenderPath& out) const; + bool modifyPolystar(RenderPath& in, RenderPath& out, float outerRoundness, bool hasRoundness) const; + bool modifyRect(Point& size, float& r) const; }; @@ -47,11 +47,11 @@ struct LottieOffsetModifier float miterLimit; 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) {} - bool modifyPath(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t inPtsCnt, Array& outCmds, Array& outPts) const; - bool modifyPolystar(const Array& inCmds, const Array& inPts, Array& outCmds, Array& outPts) const; - bool modifyRect(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t inPtsCnt, Array& outCmds, Array& outPts) const; + bool modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, RenderPath& out) const; + bool modifyPolystar(RenderPath& in, RenderPath& out) const; + bool modifyRect(PathCommand* inCmds, uint32_t inCmdsCnt, Point* inPts, uint32_t inPtsCnt, RenderPath& out) const; bool modifyEllipse(Point& radius) const; private: @@ -64,8 +64,8 @@ private: uint32_t movetoInIndex = 0; }; - void line(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t& currentPt, uint32_t currentCmd, State& state, bool degenerated, Array& cmds, Array& pts, float offset) const; - void corner(const Line& line, const Line& nextLine, uint32_t movetoIndex, bool nextClose, Array& cmds, Array& pts) const; + 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 corner(RenderPath& out, Line& line, Line& nextLine, uint32_t movetoIndex, bool nextClose) const; }; #endif \ No newline at end of file diff --git a/src/loaders/lottie/tvgLottieProperty.h b/src/loaders/lottie/tvgLottieProperty.h index c4a323ab..159c4a39 100644 --- a/src/loaders/lottie/tvgLottieProperty.h +++ b/src/loaders/lottie/tvgLottieProperty.h @@ -467,28 +467,27 @@ struct LottiePathSet : LottieProperty return true; } - bool modifiedPath(float frameNo, Array& cmds, Array& pts, Matrix* transform, const LottieRoundnessModifier* roundness, const LottieOffsetModifier* offset) + bool modifiedPath(float frameNo, RenderPath& out, Matrix* transform, const LottieRoundnessModifier* roundness, const LottieOffsetModifier* offset) { PathSet* path; LottieScalarFrame* frame; - Array cmds1; - Array pts1; + RenderPath temp; float t; if (dispatch(frameNo, path, frame, t)) { if (roundness) { if (offset) { - cmds1 = Array(path->cmdsCnt); - pts1 = Array(path->ptsCnt); - roundness->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, cmds1, pts1, transform); - return offset->modifyPath(cmds1.data, cmds1.count, pts1.data, pts1.count, cmds, pts); + temp.cmds = Array(path->cmdsCnt); + temp.pts = Array(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, cmds, pts, transform); + 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, cmds, pts); + if (offset) return offset->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, out); - _copy(path, cmds); - _copy(path, pts, transform); + _copy(path, out.cmds); + _copy(path, out.pts, transform); return true; } @@ -505,25 +504,25 @@ struct LottiePathSet : LottieProperty if (roundness) { if (offset) { - roundness->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, cmds1, pts1, nullptr); - offset->modifyPath(cmds1.data, cmds1.count, pts1.data, pts1.count, cmds, pts); - } else roundness->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, cmds, pts, nullptr); - } else if (offset) offset->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, cmds, pts); + 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); return true; } - bool defaultPath(float frameNo, Array& cmds, Array& pts, Matrix* transform) + bool defaultPath(float frameNo, RenderPath& out, Matrix* transform) { PathSet* path; LottieScalarFrame* frame; float t; if (dispatch(frameNo, path, frame, t)) { - _copy(path, cmds); - _copy(path, pts, transform); + _copy(path, out.cmds); + _copy(path, out.pts, transform); return true; } @@ -534,22 +533,22 @@ struct LottiePathSet : LottieProperty for (auto i = 0; i < frame->value.ptsCnt; ++i, ++s, ++e) { auto pt = lerp(*s, *e, t); if (transform) pt *= *transform; - pts.push(pt); + out.pts.push(pt); } - _copy(&frame->value, cmds); + _copy(&frame->value, out.cmds); return true; } - bool operator()(float frameNo, Array& cmds, Array& pts, Matrix* transform, const LottieRoundnessModifier* roundness, const LottieOffsetModifier* offset, LottieExpressions* exps = nullptr) + bool operator()(float frameNo, RenderPath& out, Matrix* transform, LottieExpressions* exps, LottieRoundnessModifier* roundness = nullptr, LottieOffsetModifier* offset = nullptr) { //overriding with expressions if (exps && exp) { frameNo = _loop(frames, frameNo, exp); - if (exps->result(frameNo, cmds, pts, transform, roundness, offset, exp)) return true; + if (exps->result(frameNo, out, transform, roundness, offset, exp)) return true; } - if (roundness || offset) return modifiedPath(frameNo, cmds, pts, transform, roundness, offset); - else return defaultPath(frameNo, cmds, pts, transform); + if (roundness || offset) return modifiedPath(frameNo, out, transform, roundness, offset); + else return defaultPath(frameNo, out, transform); } void prepare() {} diff --git a/src/renderer/tvgRender.h b/src/renderer/tvgRender.h index b691e108..7748b5be 100644 --- a/src/renderer/tvgRender.h +++ b/src/renderer/tvgRender.h @@ -170,14 +170,15 @@ struct RenderStroke } }; +struct RenderPath +{ + Array cmds; + Array pts; +}; + struct RenderShape { - struct - { - Array cmds; - Array pts; - } path; - + RenderPath path; Fill *fill = nullptr; RenderColor color{}; RenderStroke *stroke = nullptr;