From 4c0765b7617d831e4a9ec79a1228559b04a45c02 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Thu, 17 Oct 2024 14:29:19 +0700 Subject: [PATCH] lottie: fix offset For long Bezier curves compared to the offset value, the offsetting algorithm caused deformation. The problem became evident after adding mask extension, as the simple shapes defined there are based on Bezier curves rather than as shapes like a circle/rect/etc, which is the case for the offset. Now fixed. --- src/loaders/lottie/tvgLottieModifier.cpp | 52 ++++++++++++++++-------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/src/loaders/lottie/tvgLottieModifier.cpp b/src/loaders/lottie/tvgLottieModifier.cpp index 532c5d65..1378887f 100644 --- a/src/loaders/lottie/tvgLottieModifier.cpp +++ b/src/loaders/lottie/tvgLottieModifier.cpp @@ -293,8 +293,10 @@ bool LottieOffsetModifier::modifyPath(const PathCommand* inCmds, uint32_t inCmds 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 threshold = 1.0f / fabsf(offset) + 1.0f; for (uint32_t iCmd = 0, iPt = 0; iCmd < inCmdsCnt; ++iCmd) { if (inCmds[iCmd] == PathCommand::MoveTo) { @@ -311,27 +313,41 @@ bool LottieOffsetModifier::modifyPath(const PathCommand* inCmds, uint32_t inCmds continue; } - auto line1 = _offset(inPts[iPt - 1], inPts[iPt], offset); - auto line2 = _offset(inPts[iPt], inPts[iPt + 1], offset); - auto line3 = _offset(inPts[iPt + 1], inPts[iPt + 2], offset); + stack.push({inPts[iPt - 1], inPts[iPt], inPts[iPt + 1], inPts[iPt + 2]}); + while (!stack.empty()) { + auto& bezier = stack.last(); + auto len = tvg::length(bezier.start - bezier.ctrl1) + tvg::length(bezier.ctrl1 - bezier.ctrl2) + tvg::length(bezier.ctrl2 - bezier.end); - if (state.moveto) { - outCmds.push(PathCommand::MoveTo); - state.movetoOutIndex = outPts.count; - outPts.push(line1.pt1); - state.firstLine = line1; - state.moveto = false; + if (len > threshold * bezier.length()) { + Bezier next; + bezier.split(0.5f, next); + stack.push(next); + continue; + } + stack.pop(); + + auto line1 = _offset(bezier.start, bezier.ctrl1, offset); + auto line2 = _offset(bezier.ctrl1, bezier.ctrl2, offset); + auto line3 = _offset(bezier.ctrl2, bezier.end, offset); + + if (state.moveto) { + outCmds.push(PathCommand::MoveTo); + state.movetoOutIndex = outPts.count; + outPts.push(line1.pt1); + state.firstLine = line1; + state.moveto = false; + } + + bool inside{}; + Point intersect{}; + _intersect(line1, line2, intersect, inside); + outPts.push(intersect); + _intersect(line2, line3, intersect, inside); + outPts.push(intersect); + outPts.push(line3.pt2); + outCmds.push(PathCommand::CubicTo); } - bool inside{}; - Point intersect{}; - _intersect(line1, line2, intersect, inside); - outPts.push(intersect); - _intersect(line2, line3, intersect, inside); - outPts.push(intersect); - outPts.push(line3.pt2); - outCmds.push(PathCommand::CubicTo); - iPt += 3; } else {