Index: src/pathops/SkOpEdgeBuilder.cpp |
diff --git a/src/pathops/SkOpEdgeBuilder.cpp b/src/pathops/SkOpEdgeBuilder.cpp |
index 36fc9ed1613ccf026705a3fa39381fc35f286540..d67ed44ddcc29d89a911e7e2f00834fdbf958dd8 100644 |
--- a/src/pathops/SkOpEdgeBuilder.cpp |
+++ b/src/pathops/SkOpEdgeBuilder.cpp |
@@ -17,6 +17,26 @@ void SkOpEdgeBuilder::init() { |
fSecondHalf = preFetch(); |
} |
+// very tiny points cause numerical instability : don't allow them |
+static void force_small_to_zero(SkPoint* pt) { |
+ if (SkScalarAbs(pt->fX) < FLT_EPSILON_ORDERABLE_ERR) { |
+ pt->fX = 0; |
+ } |
+ if (SkScalarAbs(pt->fY) < FLT_EPSILON_ORDERABLE_ERR) { |
+ pt->fY = 0; |
+ } |
+} |
+ |
+static bool can_add_curve(SkPath::Verb verb, SkPoint* curve) { |
+ if (SkPath::kMove_Verb == verb) { |
+ return false; |
+ } |
+ for (int index = 0; index < SkPathOpsVerbToPoints(verb); ++index) { |
+ force_small_to_zero(&curve[index]); |
+ } |
+ return SkPath::kLine_Verb != verb || !SkDPoint::ApproximatelyEqual(curve[0], curve[1]); |
+} |
+ |
void SkOpEdgeBuilder::addOperand(const SkPath& path) { |
SkASSERT(fPathVerbs.count() > 0 && fPathVerbs.end()[-1] == SkPath::kDone_Verb); |
fPathVerbs.pop(); |
@@ -48,16 +68,6 @@ void SkOpEdgeBuilder::closeContour(const SkPoint& curveEnd, const SkPoint& curve |
*fPathVerbs.append() = SkPath::kClose_Verb; |
} |
-// very tiny points cause numerical instability : don't allow them |
-static void force_small_to_zero(SkPoint* pt) { |
- if (SkScalarAbs(pt->fX) < FLT_EPSILON_ORDERABLE_ERR) { |
- pt->fX = 0; |
- } |
- if (SkScalarAbs(pt->fY) < FLT_EPSILON_ORDERABLE_ERR) { |
- pt->fY = 0; |
- } |
-} |
- |
int SkOpEdgeBuilder::preFetch() { |
if (!fPath->isFinite()) { |
fUnparseable = true; |
@@ -107,8 +117,10 @@ int SkOpEdgeBuilder::preFetch() { |
force_small_to_zero(&pts[2]); |
curve[1] = pts[1]; |
curve[2] = pts[2]; |
- verb = SkReduceOrder::Conic(curve, iter.conicWeight(), pts); |
- if (verb == SkPath::kMove_Verb) { |
+ verb = SkReduceOrder::Quad(curve, pts); |
+ if (SkPath::kQuad_Verb == verb && 1 != iter.conicWeight()) { |
+ verb = SkPath::kConic_Verb; |
+ } else if (verb == SkPath::kMove_Verb) { |
continue; // skip degenerate points |
} |
break; |
@@ -183,49 +195,83 @@ bool SkOpEdgeBuilder::walk() { |
fCurrentContour->addLine(pointsPtr); |
break; |
case SkPath::kQuad_Verb: |
- fCurrentContour->addQuad(pointsPtr); |
- break; |
- case SkPath::kConic_Verb: |
- fCurrentContour->addConic(pointsPtr, *weightPtr++); |
- break; |
- case SkPath::kCubic_Verb: { |
- // Split complex cubics (such as self-intersecting curves or |
- // ones with difficult curvature) in two before proceeding. |
- // This can be required for intersection to succeed. |
- SkScalar splitT; |
- if (SkDCubic::ComplexBreak(pointsPtr, &splitT)) { |
- SkPoint cubicPair[7]; |
- SkChopCubicAt(pointsPtr, cubicPair, splitT); |
- if (!SkScalarsAreFinite(&cubicPair[0].fX, SK_ARRAY_COUNT(cubicPair) * 2)) { |
- return false; |
- } |
- SkPoint cStorage[2][4]; |
- SkPath::Verb v1 = SkReduceOrder::Cubic(&cubicPair[0], cStorage[0]); |
- SkPath::Verb v2 = SkReduceOrder::Cubic(&cubicPair[3], cStorage[1]); |
- if (v1 != SkPath::kMove_Verb && v2 != SkPath::kMove_Verb) { |
- SkPoint* curve1 = v1 == SkPath::kCubic_Verb ? &cubicPair[0] : cStorage[0]; |
- SkPoint* curve2 = v2 == SkPath::kCubic_Verb ? &cubicPair[3] : cStorage[1]; |
- for (int index = 0; index < SkPathOpsVerbToPoints(v1); ++index) { |
- force_small_to_zero(&curve1[index]); |
+ { |
+ SkVector v1 = pointsPtr[1] - pointsPtr[0]; |
+ SkVector v2 = pointsPtr[2] - pointsPtr[1]; |
+ if (v1.dot(v2) < 0) { |
+ SkPoint pair[5]; |
+ if (SkChopQuadAtMaxCurvature(pointsPtr, pair) == 1) { |
+ goto addOneQuad; |
} |
- for (int index = 0; index < SkPathOpsVerbToPoints(v2); ++index) { |
- force_small_to_zero(&curve2[index]); |
+ if (!SkScalarsAreFinite(&pair[0].fX, SK_ARRAY_COUNT(pair) * 2)) { |
+ return false; |
} |
- if (SkPath::kLine_Verb != v1 || |
- !SkDPoint::ApproximatelyEqual(curve1[0], curve1[1])) { |
+ SkPoint cStorage[2][2]; |
+ SkPath::Verb v1 = SkReduceOrder::Quad(&pair[0], cStorage[0]); |
+ SkPath::Verb v2 = SkReduceOrder::Quad(&pair[2], cStorage[1]); |
+ SkPoint* curve1 = v1 == SkPath::kQuad_Verb ? &pair[0] : cStorage[0]; |
+ SkPoint* curve2 = v2 == SkPath::kQuad_Verb ? &pair[2] : cStorage[1]; |
+ if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { |
fCurrentContour->addCurve(v1, curve1); |
- } |
- if (SkPath::kLine_Verb != v2 || |
- !SkDPoint::ApproximatelyEqual(curve2[0], curve2[1])) { |
fCurrentContour->addCurve(v2, curve2); |
+ break; |
} |
- } else { |
- fCurrentContour->addCubic(pointsPtr); |
} |
- } else { |
- fCurrentContour->addCubic(pointsPtr); |
} |
+ addOneQuad: |
+ fCurrentContour->addQuad(pointsPtr); |
+ break; |
+ case SkPath::kConic_Verb: { |
+ SkVector v1 = pointsPtr[1] - pointsPtr[0]; |
+ SkVector v2 = pointsPtr[2] - pointsPtr[1]; |
+ SkScalar weight = *weightPtr++; |
+ if (v1.dot(v2) < 0) { |
+ // FIXME: max curvature for conics hasn't been implemented; use placeholder |
+ SkScalar maxCurvature = SkFindQuadMaxCurvature(pointsPtr); |
+ if (maxCurvature > 0) { |
+ SkConic conic(pointsPtr, weight); |
+ SkConic pair[2]; |
+ conic.chopAt(maxCurvature, pair); |
+ SkPoint cStorage[2][3]; |
+ SkPath::Verb v1 = SkReduceOrder::Conic(pair[0], cStorage[0]); |
+ SkPath::Verb v2 = SkReduceOrder::Conic(pair[1], cStorage[1]); |
+ SkPoint* curve1 = v1 == SkPath::kConic_Verb ? pair[0].fPts : cStorage[0]; |
+ SkPoint* curve2 = v2 == SkPath::kConic_Verb ? pair[1].fPts : cStorage[1]; |
+ if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { |
+ fCurrentContour->addCurve(v1, curve1, pair[0].fW); |
+ fCurrentContour->addCurve(v2, curve2, pair[1].fW); |
+ break; |
+ } |
+ } |
+ } |
+ fCurrentContour->addConic(pointsPtr, weight); |
} break; |
+ case SkPath::kCubic_Verb: |
+ { |
+ // Split complex cubics (such as self-intersecting curves or |
+ // ones with difficult curvature) in two before proceeding. |
+ // This can be required for intersection to succeed. |
+ SkScalar splitT; |
+ if (SkDCubic::ComplexBreak(pointsPtr, &splitT)) { |
+ SkPoint pair[7]; |
+ SkChopCubicAt(pointsPtr, pair, splitT); |
+ if (!SkScalarsAreFinite(&pair[0].fX, SK_ARRAY_COUNT(pair) * 2)) { |
+ return false; |
+ } |
+ SkPoint cStorage[2][4]; |
+ SkPath::Verb v1 = SkReduceOrder::Cubic(&pair[0], cStorage[0]); |
+ SkPath::Verb v2 = SkReduceOrder::Cubic(&pair[3], cStorage[1]); |
+ SkPoint* curve1 = v1 == SkPath::kCubic_Verb ? &pair[0] : cStorage[0]; |
+ SkPoint* curve2 = v2 == SkPath::kCubic_Verb ? &pair[3] : cStorage[1]; |
+ if (can_add_curve(v1, curve1) && can_add_curve(v2, curve2)) { |
+ fCurrentContour->addCurve(v1, curve1); |
+ fCurrentContour->addCurve(v2, curve2); |
+ break; |
+ } |
+ } |
+ } |
+ fCurrentContour->addCubic(pointsPtr); |
+ break; |
case SkPath::kClose_Verb: |
SkASSERT(fCurrentContour); |
if (!close()) { |