| 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()) {
|
|
|