mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 941585: Better dealing with degenerate beziers. r=jrmuizel
This commit is contained in:
parent
2d18c11e26
commit
7bf6b36b0e
@ -219,17 +219,6 @@ SplitBezier(const BezierControlPoints &aControlPoints,
|
|||||||
aSecondSegmentControlPoints->mCP1 = cp1aaa;
|
aSecondSegmentControlPoints->mCP1 = cp1aaa;
|
||||||
aSecondSegmentControlPoints->mCP2 = cp2aa;
|
aSecondSegmentControlPoints->mCP2 = cp2aa;
|
||||||
aSecondSegmentControlPoints->mCP3 = cp3a;
|
aSecondSegmentControlPoints->mCP3 = cp3a;
|
||||||
|
|
||||||
// Ensure the result is a nice cubic bezier.
|
|
||||||
if (aSecondSegmentControlPoints->mCP1 == aSecondSegmentControlPoints->mCP2 ||
|
|
||||||
aSecondSegmentControlPoints->mCP2 == aSecondSegmentControlPoints->mCP3) {
|
|
||||||
aSecondSegmentControlPoints->mCP2 = aSecondSegmentControlPoints->mCP1 +
|
|
||||||
(aSecondSegmentControlPoints->mCP3 - aSecondSegmentControlPoints->mCP1) *
|
|
||||||
Float(2. / 3.);
|
|
||||||
aSecondSegmentControlPoints->mCP3 = aControlPoints.mCP4 +
|
|
||||||
(aSecondSegmentControlPoints->mCP3 - aControlPoints.mCP4) *
|
|
||||||
Float(2. / 3.);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -278,9 +267,16 @@ FindInflectionApproximationRange(BezierControlPoints aControlPoints,
|
|||||||
Point cp21 = aControlPoints.mCP2 - aControlPoints.mCP1;
|
Point cp21 = aControlPoints.mCP2 - aControlPoints.mCP1;
|
||||||
Point cp41 = aControlPoints.mCP4 - aControlPoints.mCP1;
|
Point cp41 = aControlPoints.mCP4 - aControlPoints.mCP1;
|
||||||
|
|
||||||
Float s4 = (cp41.x * cp21.y - cp41.y * cp21.x) / hypotf(cp21.x, cp21.y);
|
if (!cp21.x && !cp21.y) {
|
||||||
|
// In this case s3 becomes lim[n->0] (cp41.x * n) / n - (cp41.y * n) / n = cp41.x - cp41.y.
|
||||||
|
*aMin = aT - pow(aTolerance / (cp41.x - cp41.y), Float(1. / 3.));
|
||||||
|
*aMax = aT + pow(aTolerance / (cp41.x - cp41.y), Float(1. / 3.));;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Float tf = pow(abs(aTolerance / s4), Float(1. / 3.));
|
Float s3 = (cp41.x * cp21.y - cp41.y * cp21.x) / hypotf(cp21.x, cp21.y);
|
||||||
|
|
||||||
|
Float tf = pow(abs(aTolerance / s3), Float(1. / 3.));
|
||||||
|
|
||||||
*aMin = aT - tf * (1 - aT);
|
*aMin = aT - tf * (1 - aT);
|
||||||
*aMax = aT + tf * (1 - aT);
|
*aMax = aT + tf * (1 - aT);
|
||||||
@ -367,6 +363,9 @@ FindInflectionPoints(const BezierControlPoints &aControlPoints,
|
|||||||
if (discriminant < 0) {
|
if (discriminant < 0) {
|
||||||
// No inflection points.
|
// No inflection points.
|
||||||
*aCount = 0;
|
*aCount = 0;
|
||||||
|
} else if (discriminant == 0) {
|
||||||
|
*aCount = 1;
|
||||||
|
*aT1 = -b / (2 * a);
|
||||||
} else {
|
} else {
|
||||||
/* Use the following formula for computing the roots:
|
/* Use the following formula for computing the roots:
|
||||||
*
|
*
|
||||||
@ -419,10 +418,10 @@ FlattenBezier(const BezierControlPoints &aControlPoints,
|
|||||||
|
|
||||||
// For both inflection points, calulate the range where they can be linearly
|
// For both inflection points, calulate the range where they can be linearly
|
||||||
// approximated if they are positioned within [0,1]
|
// approximated if they are positioned within [0,1]
|
||||||
if (count > 0 && t1 >= 0 && t1 <= 1.0) {
|
if (count > 0 && t1 >= 0 && t1 < 1.0) {
|
||||||
FindInflectionApproximationRange(aControlPoints, &t1min, &t1max, t1, aTolerance);
|
FindInflectionApproximationRange(aControlPoints, &t1min, &t1max, t1, aTolerance);
|
||||||
}
|
}
|
||||||
if (count > 1 && t2 >= 0 && t2 <= 1.0) {
|
if (count > 1 && t2 >= 0 && t2 < 1.0) {
|
||||||
FindInflectionApproximationRange(aControlPoints, &t2min, &t2max, t2, aTolerance);
|
FindInflectionApproximationRange(aControlPoints, &t2min, &t2max, t2, aTolerance);
|
||||||
}
|
}
|
||||||
BezierControlPoints nextCPs = aControlPoints;
|
BezierControlPoints nextCPs = aControlPoints;
|
||||||
@ -430,34 +429,33 @@ FlattenBezier(const BezierControlPoints &aControlPoints,
|
|||||||
|
|
||||||
// Process ranges. [t1min, t1max] and [t2min, t2max] are approximated by line
|
// Process ranges. [t1min, t1max] and [t2min, t2max] are approximated by line
|
||||||
// segments.
|
// segments.
|
||||||
if (t1min < 1.0 && t1max > 0) {
|
if (t1min > 0) {
|
||||||
if (t1min > 0) {
|
// Flatten the Bezier up until the first inflection point's approximation
|
||||||
// Flatten the Bezier up until the first inflection point's approximation
|
// point.
|
||||||
// point.
|
SplitBezier(aControlPoints, &prevCPs,
|
||||||
SplitBezier(aControlPoints, &prevCPs,
|
&remainingCP, t1min);
|
||||||
&remainingCP, t1min);
|
FlattenBezierCurveSegment(prevCPs, aSink, aTolerance);
|
||||||
FlattenBezierCurveSegment(prevCPs, aSink, aTolerance);
|
|
||||||
}
|
|
||||||
if (t1max < 1.0 && (count == 1 || t2min > t1max)) {
|
|
||||||
// The second inflection point's approximation range begins after the end
|
|
||||||
// of the first, approximate the first inflection point by a line and
|
|
||||||
// subsequently flatten up until the end or the next inflection point.
|
|
||||||
SplitBezier(aControlPoints, nullptr, &nextCPs, t1max);
|
|
||||||
|
|
||||||
aSink->LineTo(nextCPs.mCP1);
|
|
||||||
|
|
||||||
if (count > 1 && t2min > 1.0) {
|
|
||||||
// No more inflection points to deal with, flatten the rest of the curve.
|
|
||||||
FlattenBezierCurveSegment(nextCPs, aSink, aTolerance);
|
|
||||||
}
|
|
||||||
} else if (count > 1 && t2min > 1.0) {
|
|
||||||
// We've already concluded t2min <= t1max, so if this is true the
|
|
||||||
// approximation range for the first inflection point runs past the
|
|
||||||
// end of the curve, draw a line to the end and we're done.
|
|
||||||
aSink->LineTo(aControlPoints.mCP4);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (t1max < 1.0 && (count == 1 || t2min > t1max)) {
|
||||||
|
// The second inflection point's approximation range begins after the end
|
||||||
|
// of the first, approximate the first inflection point by a line and
|
||||||
|
// subsequently flatten up until the end or the next inflection point.
|
||||||
|
SplitBezier(aControlPoints, nullptr, &nextCPs, t1max);
|
||||||
|
|
||||||
|
aSink->LineTo(nextCPs.mCP1);
|
||||||
|
|
||||||
|
if (count == 1 || (count > 1 && t2min >= 1.0)) {
|
||||||
|
// No more inflection points to deal with, flatten the rest of the curve.
|
||||||
|
FlattenBezierCurveSegment(nextCPs, aSink, aTolerance);
|
||||||
|
}
|
||||||
|
} else if (count > 1 && t2min > 1.0) {
|
||||||
|
// We've already concluded t2min <= t1max, so if this is true the
|
||||||
|
// approximation range for the first inflection point runs past the
|
||||||
|
// end of the curve, draw a line to the end and we're done.
|
||||||
|
aSink->LineTo(aControlPoints.mCP4);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (count > 1 && t2min < 1.0 && t2max > 0) {
|
if (count > 1 && t2min < 1.0 && t2max > 0) {
|
||||||
if (t2min > 0 && t2min < t1max) {
|
if (t2min > 0 && t2min < t1max) {
|
||||||
// In this case the t2 approximation range starts inside the t1
|
// In this case the t2 approximation range starts inside the t1
|
||||||
|
Loading…
Reference in New Issue
Block a user