From 31b4428dd8110dc76b81cc324e48b3901a2ab768 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Tue, 20 Feb 2024 12:33:00 +0900 Subject: [PATCH] Revert "Revert "sw_engine: Increasing accuracy for dashed curves"" This reverts commit e49f9125b259b33e5fe86144390509be09d0e390. --- src/common/tvgBezier.cpp | 99 +++++++++++++++++--------- src/common/tvgBezier.h | 2 + src/loaders/lottie/tvgLottieProperty.h | 6 +- src/renderer/sw_engine/tvgSwShape.cpp | 9 +-- 4 files changed, 73 insertions(+), 43 deletions(-) diff --git a/src/common/tvgBezier.cpp b/src/common/tvgBezier.cpp index db518537..068004bc 100644 --- a/src/common/tvgBezier.cpp +++ b/src/common/tvgBezier.cpp @@ -29,7 +29,7 @@ /* Internal Class Implementation */ /************************************************************************/ -static float _lineLength(const Point& pt1, const Point& pt2) +static float _lineLengthApprox(const Point& pt1, const Point& pt2) { /* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm. With alpha = 1, beta = 3/8, giving results with the largest error less @@ -41,6 +41,59 @@ static float _lineLength(const Point& pt1, const Point& pt2) } +static float _lineLength(const Point& pt1, const Point& pt2) +{ + Point diff = {pt2.x - pt1.x, pt2.y - pt1.y}; + return sqrtf(diff.x * diff.x + diff.y * diff.y); +} + + +template +float _bezLength(const Bezier& cur, LengthFunc lineLengthFunc) +{ + Bezier left, right; + auto len = lineLengthFunc(cur.start, cur.ctrl1) + lineLengthFunc(cur.ctrl1, cur.ctrl2) + lineLengthFunc(cur.ctrl2, cur.end); + auto chord = lineLengthFunc(cur.start, cur.end); + + if (fabsf(len - chord) > BEZIER_EPSILON) { + tvg::bezSplit(cur, left, right); + return _bezLength(left, lineLengthFunc) + _bezLength(right, lineLengthFunc); + } + return len; +} + + +template +float _bezAt(const Bezier& bz, float at, float length, LengthFunc lineLengthFunc) +{ + auto biggest = 1.0f; + auto smallest = 0.0f; + auto t = 0.5f; + + //just in case to prevent an infinite loop + if (at <= 0) return 0.0f; + if (at >= length) return 1.0f; + + while (true) { + auto right = bz; + Bezier left; + bezSplitLeft(right, t, left); + length = _bezLength(left, lineLengthFunc); + if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) { + break; + } + if (length < at) { + smallest = t; + t = (t + biggest) * 0.5f; + } else { + biggest = t; + t = (smallest + t) * 0.5f; + } + } + return t; +} + + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ @@ -48,7 +101,7 @@ static float _lineLength(const Point& pt1, const Point& pt2) namespace tvg { -void bezSplit(const Bezier&cur, Bezier& left, Bezier& right) +void bezSplit(const Bezier& cur, Bezier& left, Bezier& right) { auto c = (cur.ctrl1.x + cur.ctrl2.x) * 0.5f; left.ctrl1.x = (cur.start.x + cur.ctrl1.x) * 0.5f; @@ -72,15 +125,13 @@ void bezSplit(const Bezier&cur, Bezier& left, Bezier& right) float bezLength(const Bezier& cur) { - Bezier left, right; - auto len = _lineLength(cur.start, cur.ctrl1) + _lineLength(cur.ctrl1, cur.ctrl2) + _lineLength(cur.ctrl2, cur.end); - auto chord = _lineLength(cur.start, cur.end); + return _bezLength(cur, _lineLength); +} - if (fabsf(len - chord) > BEZIER_EPSILON) { - bezSplit(cur, left, right); - return bezLength(left) + bezLength(right); - } - return len; + +float bezLengthApprox(const Bezier& cur) +{ + return _bezLength(cur, _lineLengthApprox); } @@ -110,31 +161,13 @@ void bezSplitLeft(Bezier& cur, float at, Bezier& left) float bezAt(const Bezier& bz, float at, float length) { - auto biggest = 1.0f; - auto smallest = 0.0f; - auto t = 0.5f; + return _bezAt(bz, at, length, _lineLength); +} - //just in case to prevent an infinite loop - if (at <= 0) return 0.0f; - if (at >= length) return 1.0f; - while (true) { - auto right = bz; - Bezier left; - bezSplitLeft(right, t, left); - length = bezLength(left); - if (fabsf(length - at) < BEZIER_EPSILON || fabsf(smallest - biggest) < BEZIER_EPSILON) { - break; - } - if (length < at) { - smallest = t; - t = (t + biggest) * 0.5f; - } else { - biggest = t; - t = (smallest + t) * 0.5f; - } - } - return t; +float bezAtApprox(const Bezier& bz, float at, float length) +{ + return _bezAt(bz, at, length, _lineLengthApprox); } diff --git a/src/common/tvgBezier.h b/src/common/tvgBezier.h index cb2766c5..80a19925 100644 --- a/src/common/tvgBezier.h +++ b/src/common/tvgBezier.h @@ -44,6 +44,8 @@ void bezSplitAt(const Bezier& cur, float at, Bezier& left, Bezier& right); Point bezPointAt(const Bezier& bz, float t); float bezAngleAt(const Bezier& bz, float t); +float bezLengthApprox(const Bezier& cur); +float bezAtApprox(const Bezier& bz, float at, float length); } #endif //_TVG_BEZIER_H_ diff --git a/src/loaders/lottie/tvgLottieProperty.h b/src/loaders/lottie/tvgLottieProperty.h index 0a7f4bc6..b0e3fd7c 100644 --- a/src/loaders/lottie/tvgLottieProperty.h +++ b/src/loaders/lottie/tvgLottieProperty.h @@ -158,7 +158,7 @@ struct LottieVectorFrame if (hasTangent) { Bezier bz = {value, value + outTangent, next->value + inTangent, next->value}; - t = bezAt(bz, t * length, length); + t = bezAtApprox(bz, t * length, length); return bezPointAt(bz, t); } else { return mathLerp(value, next->value, t); @@ -171,14 +171,14 @@ struct LottieVectorFrame auto t = (frameNo - no) / (next->no - no); if (interpolator) t = interpolator->progress(t); Bezier bz = {value, value + outTangent, next->value + inTangent, next->value}; - t = bezAt(bz, t * length, length); + t = bezAtApprox(bz, t * length, length); return -bezAngleAt(bz, t); } void prepare(LottieVectorFrame* next) { Bezier bz = {value, value + outTangent, next->value + inTangent, next->value}; - length = bezLength(bz); + length = bezLengthApprox(bz); } }; diff --git a/src/renderer/sw_engine/tvgSwShape.cpp b/src/renderer/sw_engine/tvgSwShape.cpp index d3b715ea..8cad462f 100644 --- a/src/renderer/sw_engine/tvgSwShape.cpp +++ b/src/renderer/sw_engine/tvgSwShape.cpp @@ -37,13 +37,8 @@ struct Line static float _lineLength(const Point& pt1, const Point& pt2) { - /* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm. - With alpha = 1, beta = 3/8, giving results with the largest error less - than 7% compared to the exact value. */ Point diff = {pt2.x - pt1.x, pt2.y - pt1.y}; - if (diff.x < 0) diff.x = -diff.x; - if (diff.y < 0) diff.y = -diff.y; - return (diff.x > diff.y) ? (diff.x + diff.y * 0.375f) : (diff.y + diff.x * 0.375f); + return sqrtf(diff.x * diff.x + diff.y * diff.y); } @@ -388,7 +383,7 @@ static float _outlineLength(const RenderShape* rshape) break; } case PathCommand::CubicTo: { - length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)}); + length += bezLengthApprox({*(pts - 1), *pts, *(pts + 1), *(pts + 2)}); pts += 3; break; }