Revert "Revert "sw_engine: Increasing accuracy for dashed curves""

This reverts commit e49f9125b2.
This commit is contained in:
Hermet Park 2024-02-20 12:33:00 +09:00
parent e49f9125b2
commit 31b4428dd8
4 changed files with 73 additions and 43 deletions

View file

@ -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<typename LengthFunc>
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<typename LengthFunc>
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);
}

View file

@ -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_

View file

@ -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);
}
};

View file

@ -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;
}