From 269e6c4d10145541622d78683134da973cec50db Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Thu, 17 Oct 2024 18:30:36 +0700 Subject: [PATCH] lottie: offset does not depend on shape direction According to tests in AE, the offset direction should not depend on the direction of the shape. A positive offset value expands the shape, while a negative value contracts it. Fixed. --- src/common/tvgMath.h | 6 +++++ src/loaders/lottie/tvgLottieBuilder.cpp | 14 ++++++------ src/loaders/lottie/tvgLottieModifier.cpp | 28 ++++++++++++++++++------ src/loaders/lottie/tvgLottieModifier.h | 6 ++--- src/loaders/lottie/tvgLottieProperty.h | 8 +++---- 5 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/common/tvgMath.h b/src/common/tvgMath.h index 353cccd0..a9179982 100644 --- a/src/common/tvgMath.h +++ b/src/common/tvgMath.h @@ -174,6 +174,12 @@ void operator*=(Point& pt, const Matrix& m); Point operator*(const Point& pt, const Matrix& m); Point normal(const Point& p1, const Point& p2); +static inline float cross(const Point& lhs, const Point& rhs) +{ + return lhs.x * rhs.y - rhs.x * lhs.y; +} + + static inline bool zero(const Point& p) { return tvg::zero(p.x) && tvg::zero(p.y); diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index ce66a1bb..4b43ff0d 100644 --- a/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/src/loaders/lottie/tvgLottieBuilder.cpp @@ -407,7 +407,7 @@ static void _appendRect(Shape* shape, float x, float y, float w, float h, float } } - if (offsetPath) offsetPath->modifyRect(commands, 5, points, 4, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, clockwise); + if (offsetPath) offsetPath->modifyRect(commands, 5, points, 4, P(shape)->rs.path.cmds, P(shape)->rs.path.pts); else shape->appendPath(commands, 5, points, 4); //round rect } else { @@ -460,7 +460,7 @@ static void _appendRect(Shape* shape, float x, float y, float w, float h, float } } - if (offsetPath) offsetPath->modifyRect(commands, cmdCnt, points, ptsCnt, P(shape)->rs.path.cmds, P(shape)->rs.path.pts, clockwise); + if (offsetPath) offsetPath->modifyRect(commands, cmdCnt, points, ptsCnt, P(shape)->rs.path.cmds, P(shape)->rs.path.pts); else shape->appendPath(commands, cmdCnt, points, ptsCnt); } } @@ -555,7 +555,7 @@ void LottieBuilder::updateEllipse(LottieGroup* parent, LottieObject** child, flo void LottieBuilder::updatePath(LottieGroup* parent, LottieObject** child, float frameNo, TVG_UNUSED Inlist& contexts, RenderContext* ctx) { auto path = static_cast(*child); - //TODO: use direction for paths' offsetPath + if (!ctx->repeaters.empty()) { auto shape = path->pooling(); shape->reset(); @@ -690,11 +690,11 @@ static void _updateStar(TVG_UNUSED LottieGroup* parent, LottiePolyStar* star, Ma if (offsetPath) { auto intermediate = Shape::gen(); roundness->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, outerRoundness, hasRoundness); - offsetPath->modifyPolystar(P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, star->clockwise); + offsetPath->modifyPolystar(P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts); } else { roundness->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, outerRoundness, hasRoundness); } - } else if (offsetPath) offsetPath->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, star->clockwise); + } else if (offsetPath) offsetPath->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts); } @@ -776,11 +776,11 @@ static void _updatePolygon(LottieGroup* parent, LottiePolyStar* star, Matrix* tr if (offsetPath) { auto intermediate = Shape::gen(); roundness->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, 0.0f, false); - offsetPath->modifyPolystar(P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, star->clockwise); + offsetPath->modifyPolystar(P(intermediate)->rs.path.cmds, P(intermediate)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts); } else { roundness->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, 0.0f, false); } - } else if (offsetPath) offsetPath->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts, star->clockwise); + } else if (offsetPath) offsetPath->modifyPolystar(P(shape)->rs.path.cmds, P(shape)->rs.path.pts, P(merging)->rs.path.cmds, P(merging)->rs.path.pts); } diff --git a/src/loaders/lottie/tvgLottieModifier.cpp b/src/loaders/lottie/tvgLottieModifier.cpp index 1378887f..33dab786 100644 --- a/src/loaders/lottie/tvgLottieModifier.cpp +++ b/src/loaders/lottie/tvgLottieModifier.cpp @@ -79,7 +79,20 @@ static bool _intersect(const Line& line1, const Line& line2, Point& intersection static Line _offset(const Point& p1, const Point& p2, float offset) { auto scaledNormal = normal(p1, p2) * offset; - return {p1 - scaledNormal, p2 - scaledNormal}; + return {p1 + scaledNormal, p2 + scaledNormal}; +} + + +static bool _clockwise(const Point* pts, uint32_t n) +{ + auto area = 0.0f; + + for (uint32_t i = 0; i < n - 1; i++) { + area += cross(pts[i], pts[i + 1]); + } + area += cross(pts[n - 1], pts[0]);; + + return area < 0.0f; } @@ -113,6 +126,7 @@ void LottieOffsetModifier::corner(const Line& line, const Line& nextLine, uint32 } else outPts.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 { if (tvg::zero(inPts[currentPt - 1] - inPts[currentPt])) { @@ -288,14 +302,14 @@ bool LottieRoundnessModifier::modifyRect(const Point& size, float& r) const } -bool LottieOffsetModifier::modifyPath(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t inPtsCnt, Array& outCmds, Array& outPts, bool clockwise) const +bool LottieOffsetModifier::modifyPath(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t inPtsCnt, Array& outCmds, Array& outPts) const { outCmds.reserve(inCmdsCnt * 2); outPts.reserve(inPtsCnt * (join == StrokeJoin::Round ? 4 : 2)); Array stack{5}; State state; - auto offset = clockwise ? this->offset : -this->offset; + auto offset = _clockwise(inPts, inPtsCnt) ? this->offset : -this->offset; auto threshold = 1.0f / fabsf(offset) + 1.0f; for (uint32_t iCmd = 0, iPt = 0; iCmd < inCmdsCnt; ++iCmd) { @@ -362,14 +376,14 @@ bool LottieOffsetModifier::modifyPath(const PathCommand* inCmds, uint32_t inCmds } -bool LottieOffsetModifier::modifyPolystar(const Array& inCmds, const Array& inPts, Array& outCmds, Array& outPts, bool clockwise) const { - return modifyPath(inCmds.data, inCmds.count, inPts.data, inPts.count, outCmds, outPts, clockwise); +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::modifyRect(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t inPtsCnt, Array& outCmds, Array& outPts, bool clockwise) const +bool LottieOffsetModifier::modifyRect(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t inPtsCnt, Array& outCmds, Array& outPts) const { - return modifyPath(inCmds, inCmdsCnt, inPts, inPtsCnt, outCmds, outPts, clockwise); + return modifyPath(inCmds, inCmdsCnt, inPts, inPtsCnt, outCmds, outPts); } diff --git a/src/loaders/lottie/tvgLottieModifier.h b/src/loaders/lottie/tvgLottieModifier.h index e3cab1c0..cc3f77e6 100644 --- a/src/loaders/lottie/tvgLottieModifier.h +++ b/src/loaders/lottie/tvgLottieModifier.h @@ -49,9 +49,9 @@ struct LottieOffsetModifier 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, bool clockwise) const; - bool modifyPolystar(const Array& inCmds, const Array& inPts, Array& outCmds, Array& outPts, bool clockwise) const; - bool modifyRect(const PathCommand* inCmds, uint32_t inCmdsCnt, const Point* inPts, uint32_t inPtsCnt, Array& outCmds, Array& outPts, bool clockwise) const; + 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 modifyEllipse(float& rx, float& ry) const; private: diff --git a/src/loaders/lottie/tvgLottieProperty.h b/src/loaders/lottie/tvgLottieProperty.h index 91a879ac..55522476 100644 --- a/src/loaders/lottie/tvgLottieProperty.h +++ b/src/loaders/lottie/tvgLottieProperty.h @@ -438,11 +438,11 @@ struct LottiePathSet : LottieProperty Array cmds1(path->cmdsCnt); Array pts1(path->ptsCnt); roundness->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, cmds1, pts1, transform); - return offsetPath->modifyPath(cmds1.data, cmds1.count, pts1.data, pts1.count, cmds, pts, true); + return offsetPath->modifyPath(cmds1.data, cmds1.count, pts1.data, pts1.count, cmds, pts); } return roundness->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, cmds, pts, transform); } - if (offsetPath) return offsetPath->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, cmds, pts, true); + if (offsetPath) return offsetPath->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, cmds, pts); _copy(path, cmds); _copy(path, pts, transform); @@ -474,9 +474,9 @@ struct LottiePathSet : LottieProperty Array cmds1; Array pts1; roundness->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, cmds1, pts1, nullptr); - offsetPath->modifyPath(cmds1.data, cmds1.count, pts1.data, pts1.count, cmds, pts, true); + offsetPath->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 (offsetPath) offsetPath->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, cmds, pts, true); + } else if (offsetPath) offsetPath->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, cmds, pts); free(interpPts);