Bug 941585: Better dealing with degenerate beziers. r=jrmuizel

This commit is contained in:
Bas Schouten 2014-01-09 17:04:17 +01:00
parent 2d18c11e26
commit 7bf6b36b0e

View File

@ -219,17 +219,6 @@ SplitBezier(const BezierControlPoints &aControlPoints,
aSecondSegmentControlPoints->mCP1 = cp1aaa;
aSecondSegmentControlPoints->mCP2 = cp2aa;
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
@ -278,9 +267,16 @@ FindInflectionApproximationRange(BezierControlPoints aControlPoints,
Point cp21 = aControlPoints.mCP2 - 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);
*aMax = aT + tf * (1 - aT);
@ -367,6 +363,9 @@ FindInflectionPoints(const BezierControlPoints &aControlPoints,
if (discriminant < 0) {
// No inflection points.
*aCount = 0;
} else if (discriminant == 0) {
*aCount = 1;
*aT1 = -b / (2 * a);
} else {
/* 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
// 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);
}
if (count > 1 && t2 >= 0 && t2 <= 1.0) {
if (count > 1 && t2 >= 0 && t2 < 1.0) {
FindInflectionApproximationRange(aControlPoints, &t2min, &t2max, t2, aTolerance);
}
BezierControlPoints nextCPs = aControlPoints;
@ -430,34 +429,33 @@ FlattenBezier(const BezierControlPoints &aControlPoints,
// Process ranges. [t1min, t1max] and [t2min, t2max] are approximated by line
// segments.
if (t1min < 1.0 && t1max > 0) {
if (t1min > 0) {
// Flatten the Bezier up until the first inflection point's approximation
// point.
SplitBezier(aControlPoints, &prevCPs,
&remainingCP, t1min);
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 (t1min > 0) {
// Flatten the Bezier up until the first inflection point's approximation
// point.
SplitBezier(aControlPoints, &prevCPs,
&remainingCP, t1min);
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 || (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 (t2min > 0 && t2min < t1max) {
// In this case the t2 approximation range starts inside the t1