Rewriting path writer
authorcaryclark <caryclark@google.com>
Wed, 14 Sep 2016 14:18:20 +0000 (07:18 -0700)
committerCommit bot <commit-bot@chromium.org>
Wed, 14 Sep 2016 14:18:20 +0000 (07:18 -0700)
The path writer takes constructs the output path out of
curves that satisfy the pathop operation.

Curves contain lists of t/point pairs that may not be
comparable to each other. To match up curve ends in the
output path, look for adjacent curves to have a shared
membership rather than comparing point values.

Use path utilities to connect partial curve lists into
closed contours.

Share the angle code that determines if a curve has become
a degenerate line with the path writer.

Clean up some code on the way, and delete some unused
functions.

TBR=reed@google.com
BUG=5188
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2321973005

Review-Url: https://codereview.chromium.org/2321973005

21 files changed:
src/pathops/SkOpAngle.cpp
src/pathops/SkOpAngle.h
src/pathops/SkOpBuilder.cpp
src/pathops/SkOpContour.cpp
src/pathops/SkOpContour.h
src/pathops/SkOpEdgeBuilder.cpp
src/pathops/SkOpEdgeBuilder.h
src/pathops/SkOpSegment.cpp
src/pathops/SkOpSegment.h
src/pathops/SkPathOpsCommon.cpp
src/pathops/SkPathOpsCommon.h
src/pathops/SkPathOpsCurve.cpp
src/pathops/SkPathOpsCurve.h
src/pathops/SkPathOpsDebug.cpp
src/pathops/SkPathOpsDebug.h
src/pathops/SkPathOpsOp.cpp
src/pathops/SkPathOpsSimplify.cpp
src/pathops/SkPathOpsTightBounds.cpp
src/pathops/SkPathWriter.cpp
src/pathops/SkPathWriter.h
tests/PathOpsDebug.cpp

index 6bc510e..db86077 100644 (file)
@@ -62,11 +62,11 @@ bool SkOpAngle::after(SkOpAngle* test) {
     SkOpAngle* lh = test;
     SkOpAngle* rh = lh->fNext;
     SkASSERT(lh != rh);
-    fCurvePart = fOriginalCurvePart;
-    lh->fCurvePart = lh->fOriginalCurvePart;
-    lh->fCurvePart.offset(lh->segment()->verb(), fCurvePart[0] - lh->fCurvePart[0]);
-    rh->fCurvePart = rh->fOriginalCurvePart;
-    rh->fCurvePart.offset(rh->segment()->verb(), fCurvePart[0] - rh->fCurvePart[0]);
+    fPart.fCurve = fOriginalCurvePart;
+    lh->fPart.fCurve = lh->fOriginalCurvePart;
+    lh->fPart.fCurve.offset(lh->segment()->verb(), fPart.fCurve[0] - lh->fPart.fCurve[0]);
+    rh->fPart.fCurve = rh->fOriginalCurvePart;
+    rh->fPart.fCurve.offset(rh->segment()->verb(), fPart.fCurve[0] - rh->fPart.fCurve[0]);
 
 #if DEBUG_ANGLE
     SkString bugOut;
@@ -177,15 +177,15 @@ bool SkOpAngle::after(SkOpAngle* test) {
 // given a line, see if the opposite curve's convex hull is all on one side
 // returns -1=not on one side    0=this CW of test   1=this CCW of test
 int SkOpAngle::allOnOneSide(const SkOpAngle* test) {
-    SkASSERT(!fIsCurve);
-    SkASSERT(test->fIsCurve);
-    SkDPoint origin = fCurvePart[0];
-    SkDVector line = fCurvePart[1] - origin;
+    SkASSERT(!fPart.isCurve());
+    SkASSERT(test->fPart.isCurve());
+    SkDPoint origin = fPart.fCurve[0];
+    SkDVector line = fPart.fCurve[1] - origin;
     double crosses[3];
     SkPath::Verb testVerb = test->segment()->verb();
     int iMax = SkPathOpsVerbToPoints(testVerb);
 //    SkASSERT(origin == test.fCurveHalf[0]);
-    const SkDCurve& testCurve = test->fCurvePart;
+    const SkDCurve& testCurve = test->fPart.fCurve;
     for (int index = 1; index <= iMax; ++index) {
         double xy1 = line.fX * (testCurve[index].fY - origin.fY);
         double xy2 = line.fY * (testCurve[index].fX - origin.fX);
@@ -222,16 +222,16 @@ bool SkOpAngle::checkCrossesZero() const {
 bool SkOpAngle::checkParallel(SkOpAngle* rh) {
     SkDVector scratch[2];
     const SkDVector* sweep, * tweep;
-    if (!this->fUnorderedSweep) {
-        sweep = this->fSweep;
+    if (this->fPart.isOrdered()) {
+        sweep = this->fPart.fSweep;
     } else {
-        scratch[0] = this->fCurvePart[1] - this->fCurvePart[0];
+        scratch[0] = this->fPart.fCurve[1] - this->fPart.fCurve[0];
         sweep = &scratch[0];
     }
-    if (!rh->fUnorderedSweep) {
-        tweep = rh->fSweep;
+    if (rh->fPart.isOrdered()) {
+        tweep = rh->fPart.fSweep;
     } else {
-        scratch[1] = rh->fCurvePart[1] - rh->fCurvePart[0];
+        scratch[1] = rh->fPart.fCurve[1] - rh->fPart.fCurve[0];
         tweep = &scratch[1];
     }
     double s0xt0 = sweep->crossCheck(*tweep);
@@ -256,8 +256,8 @@ bool SkOpAngle::checkParallel(SkOpAngle* rh) {
         return !inside;
     }
     // compute the cross check from the mid T values (last resort)
-    SkDVector m0 = segment()->dPtAtT(this->midT()) - this->fCurvePart[0];
-    SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fCurvePart[0];
+    SkDVector m0 = segment()->dPtAtT(this->midT()) - this->fPart.fCurve[0];
+    SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fPart.fCurve[0];
     double m0xm1 = m0.crossCheck(m1);
     if (m0xm1 == 0) {
         this->fUnorderable = true;
@@ -321,8 +321,8 @@ recomputeSector:
 }
 
 int SkOpAngle::convexHullOverlaps(const SkOpAngle* rh) const {
-    const SkDVector* sweep = this->fSweep;
-    const SkDVector* tweep = rh->fSweep;
+    const SkDVector* sweep = this->fPart.fSweep;
+    const SkDVector* tweep = rh->fPart.fSweep;
     double s0xs1 = sweep[0].crossCheck(sweep[1]);
     double s0xt0 = sweep[0].crossCheck(tweep[0]);
     double s1xt0 = sweep[1].crossCheck(tweep[0]);
@@ -352,8 +352,8 @@ int SkOpAngle::convexHullOverlaps(const SkOpAngle* rh) const {
     // if the outside sweeps are greater than 180 degress:
         // first assume the inital tangents are the ordering
         // if the midpoint direction matches the inital order, that is enough
-    SkDVector m0 = this->segment()->dPtAtT(this->midT()) - this->fCurvePart[0];
-    SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fCurvePart[0];
+    SkDVector m0 = this->segment()->dPtAtT(this->midT()) - this->fPart.fCurve[0];
+    SkDVector m1 = rh->segment()->dPtAtT(rh->midT()) - rh->fPart.fCurve[0];
     double m0xm1 = m0.crossCheck(m1);
     if (s0xt0 > 0 && m0xm1 > 0) {
         return 0;
@@ -392,8 +392,8 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
     SkPath::Verb rVerb = rh->segment()->verb();
     int lPts = SkPathOpsVerbToPoints(lVerb);
     int rPts = SkPathOpsVerbToPoints(rVerb);
-    SkDLine rays[] = {{{this->fCurvePart[0], rh->fCurvePart[rPts]}},
-            {{this->fCurvePart[0], this->fCurvePart[lPts]}}};
+    SkDLine rays[] = {{{this->fPart.fCurve[0], rh->fPart.fCurve[rPts]}},
+            {{this->fPart.fCurve[0], this->fPart.fCurve[lPts]}}};
     if (this->fEnd->contains(rh->fEnd)) {
         return checkParallel(rh);
     }
@@ -464,7 +464,7 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
         double minX, minY, maxX, maxY;
         minX = minY = SK_ScalarInfinity;
         maxX = maxY = -SK_ScalarInfinity;
-        const SkDCurve& curve = index ? rh->fCurvePart : this->fCurvePart;
+        const SkDCurve& curve = index ? rh->fPart.fCurve : this->fPart.fCurve;
         int ptCount = index ? rPts : lPts;
         for (int idx2 = 0; idx2 <= ptCount; ++idx2) {
             minX = SkTMin(minX, curve[idx2].fX);
@@ -482,7 +482,7 @@ bool SkOpAngle::endsIntersect(SkOpAngle* rh) {
         }
     }
     if (useIntersect) {
-        const SkDCurve& curve = sIndex ? rh->fCurvePart : this->fCurvePart;
+        const SkDCurve& curve = sIndex ? rh->fPart.fCurve : this->fPart.fCurve;
         const SkOpSegment& segment = sIndex ? *rh->segment() : *this->segment();
         double tStart = sIndex ? rh->fStart->t() : fStart->t();
         SkDVector mid = segment.dPtAtT(tStart + (sCeptT - tStart) / 2) - curve[0];
@@ -524,7 +524,7 @@ bool SkOpAngle::endToSide(const SkOpAngle* rh, bool* inside) const {
     double minX, minY, maxX, maxY;
     minX = minY = SK_ScalarInfinity;
     maxX = maxY = -SK_ScalarInfinity;
-    const SkDCurve& curve = rh->fCurvePart;
+    const SkDCurve& curve = rh->fPart.fCurve;
     int oppPts = SkPathOpsVerbToPoints(oppVerb);
     for (int idx2 = 0; idx2 <= oppPts; ++idx2) {
         minX = SkTMin(minX, curve[idx2].fX);
@@ -764,8 +764,8 @@ bool SkOpAngle::oppositePlanes(const SkOpAngle* rh) const {
 
 bool SkOpAngle::orderable(SkOpAngle* rh) {
     int result;
-    if (!fIsCurve) {
-        if (!rh->fIsCurve) {
+    if (!fPart.isCurve()) {
+        if (!rh->fPart.isCurve()) {
             double leftX = fTangentHalf.dx();
             double leftY = fTangentHalf.dy();
             double rightX = rh->fTangentHalf.dx();
@@ -787,7 +787,7 @@ bool SkOpAngle::orderable(SkOpAngle* rh) {
         if (fUnorderable || approximately_zero(rh->fSide)) {
             goto unorderable;
         }
-    } else if (!rh->fIsCurve) {
+    } else if (!rh->fPart.isCurve()) {
         if ((result = rh->allOnOneSide(this)) >= 0) {
             return !result;
         }
@@ -832,59 +832,6 @@ void SkOpAngle::set(SkOpSpanBase* start, SkOpSpanBase* end) {
     SkDEBUGCODE(fID = start ? start->globalState()->nextAngleID() : -1);
 }
 
-void SkOpAngle::setCurveHullSweep() {
-    fUnorderedSweep = false;
-    fSweep[0] = fCurvePart[1] - fCurvePart[0];
-    const SkOpSegment* segment = fStart->segment();
-    if (SkPath::kLine_Verb == segment->verb()) {
-        fSweep[1] = fSweep[0];
-        return;
-    }
-    fSweep[1] = fCurvePart[2] - fCurvePart[0];
-    // OPTIMIZE: I do the following float check a lot -- probably need a
-    // central place for this val-is-small-compared-to-curve check
-    double maxVal = 0;
-    for (int index = 0; index < SkPathOpsVerbToPoints(segment->verb()); ++index) {
-        maxVal = SkTMax(maxVal, SkTMax(SkTAbs(fCurvePart[index].fX),
-                SkTAbs(fCurvePart[index].fY)));
-    }
-
-    if (SkPath::kCubic_Verb != segment->verb()) {
-        if (roughly_zero_when_compared_to(fSweep[0].fX, maxVal)
-                && roughly_zero_when_compared_to(fSweep[0].fY, maxVal)) {
-            fSweep[0] = fSweep[1];
-        }
-        return;
-    }
-    SkDVector thirdSweep = fCurvePart[3] - fCurvePart[0];
-    if (fSweep[0].fX == 0 && fSweep[0].fY == 0) {
-        fSweep[0] = fSweep[1];
-        fSweep[1] = thirdSweep;
-        if (roughly_zero_when_compared_to(fSweep[0].fX, maxVal)
-                && roughly_zero_when_compared_to(fSweep[0].fY, maxVal)) {
-            fSweep[0] = fSweep[1];
-            fCurvePart[1] = fCurvePart[3];
-            fIsCurve = false;
-        }
-        return;
-    }
-    double s1x3 = fSweep[0].crossCheck(thirdSweep);
-    double s3x2 = thirdSweep.crossCheck(fSweep[1]);
-    if (s1x3 * s3x2 >= 0) {  // if third vector is on or between first two vectors
-        return;
-    }
-    double s2x1 = fSweep[1].crossCheck(fSweep[0]);
-    // FIXME: If the sweep of the cubic is greater than 180 degrees, we're in trouble
-    // probably such wide sweeps should be artificially subdivided earlier so that never happens
-    SkASSERT(s1x3 * s2x1 < 0 || s1x3 * s3x2 < 0);
-    if (s3x2 * s2x1 < 0) {
-        SkASSERT(s2x1 * s1x3 > 0);
-        fSweep[0] = fSweep[1];
-        fUnorderedSweep = true;
-    }
-    fSweep[1] = thirdSweep;
-}
-
 void SkOpAngle::setSpans() {
     fUnorderable = false;
     fLastMarked = nullptr;
@@ -894,21 +841,20 @@ void SkOpAngle::setSpans() {
     }
     const SkOpSegment* segment = fStart->segment();
     const SkPoint* pts = segment->pts();
-    SkDEBUGCODE(fCurvePart.fVerb = SkPath::kCubic_Verb);
-    SkDEBUGCODE(fCurvePart[2].fX = fCurvePart[2].fY = fCurvePart[3].fX = fCurvePart[3].fY
+    SkDEBUGCODE(fPart.fCurve.fVerb = SkPath::kCubic_Verb);
+    SkDEBUGCODE(fPart.fCurve[2].fX = fPart.fCurve[2].fY = fPart.fCurve[3].fX = fPart.fCurve[3].fY
             = SK_ScalarNaN);
-    SkDEBUGCODE(fCurvePart.fVerb = segment->verb());
-    segment->subDivide(fStart, fEnd, &fCurvePart);
-    fOriginalCurvePart = fCurvePart;
-    setCurveHullSweep();
+    SkDEBUGCODE(fPart.fCurve.fVerb = segment->verb());
+    segment->subDivide(fStart, fEnd, &fPart.fCurve);
+    fOriginalCurvePart = fPart.fCurve;
     const SkPath::Verb verb = segment->verb();
-    if (verb != SkPath::kLine_Verb
-            && !(fIsCurve = fSweep[0].crossCheck(fSweep[1]) != 0)) {
+    fPart.setCurveHullSweep(verb);
+    if (SkPath::kLine_Verb != verb && !fPart.isCurve()) {
         SkDLine lineHalf;
-        fCurvePart[1] = fCurvePart[SkPathOpsVerbToPoints(verb)];
-        fOriginalCurvePart[1] = fCurvePart[1];
-        lineHalf[0].set(fCurvePart[0].asSkPoint());
-        lineHalf[1].set(fCurvePart[1].asSkPoint());
+        fPart.fCurve[1] = fPart.fCurve[SkPathOpsVerbToPoints(verb)];
+        fOriginalCurvePart[1] = fPart.fCurve[1];
+        lineHalf[0].set(fPart.fCurve[0].asSkPoint());
+        lineHalf[1].set(fPart.fCurve[1].asSkPoint());
         fTangentHalf.lineEndPoints(lineHalf);
         fSide = 0;
     }
@@ -921,18 +867,17 @@ void SkOpAngle::setSpans() {
         lineHalf[1].set(cP1);
         fTangentHalf.lineEndPoints(lineHalf);
         fSide = 0;
-        fIsCurve = false;
         } return;
     case SkPath::kQuad_Verb:
     case SkPath::kConic_Verb: {
         SkLineParameters tangentPart;
-        (void) tangentPart.quadEndPoints(fCurvePart.fQuad);
-        fSide = -tangentPart.pointDistance(fCurvePart[2]);  // not normalized -- compare sign only
+        (void) tangentPart.quadEndPoints(fPart.fCurve.fQuad);
+        fSide = -tangentPart.pointDistance(fPart.fCurve[2]);  // not normalized -- compare sign only
         } break;
     case SkPath::kCubic_Verb: {
         SkLineParameters tangentPart;
-        (void) tangentPart.cubicPart(fCurvePart.fCubic);
-        fSide = -tangentPart.pointDistance(fCurvePart[3]);
+        (void) tangentPart.cubicPart(fPart.fCurve.fCubic);
+        fSide = -tangentPart.pointDistance(fPart.fCurve[3]);
         double testTs[4];
         // OPTIMIZATION: keep inflections precomputed with cubic segment?
         int testCount = SkDCubic::FindInflections(pts, testTs);
@@ -964,7 +909,7 @@ void SkOpAngle::setSpans() {
             // OPTIMIZE: could avoid call for t == startT, endT
             SkDPoint pt = dcubic_xy_at_t(pts, segment->weight(), testT);
             SkLineParameters tangentPart;
-            tangentPart.cubicEndPoints(fCurvePart.fCubic);
+            tangentPart.cubicEndPoints(fPart.fCurve.fCubic);
             double testSide = tangentPart.pointDistance(pt);
             if (fabs(bestSide) < fabs(testSide)) {
                 bestSide = testSide;
@@ -984,18 +929,18 @@ void SkOpAngle::setSector() {
     }
     const SkOpSegment* segment = fStart->segment();
     SkPath::Verb verb = segment->verb();
-    fSectorStart = this->findSector(verb, fSweep[0].fX, fSweep[0].fY);
+    fSectorStart = this->findSector(verb, fPart.fSweep[0].fX, fPart.fSweep[0].fY);
     if (fSectorStart < 0) {
         goto deferTilLater;
     }
-    if (!fIsCurve) {  // if it's a line or line-like, note that both sectors are the same
+    if (!fPart.isCurve()) {  // if it's a line or line-like, note that both sectors are the same
         SkASSERT(fSectorStart >= 0);
         fSectorEnd = fSectorStart;
         fSectorMask = 1 << fSectorStart;
         return;
     }
     SkASSERT(SkPath::kLine_Verb != verb);
-    fSectorEnd = this->findSector(verb, fSweep[1].fX, fSweep[1].fY);
+    fSectorEnd = this->findSector(verb, fPart.fSweep[1].fX, fPart.fSweep[1].fY);
     if (fSectorEnd < 0) {
 deferTilLater:
         fSectorStart = fSectorEnd = -1;
@@ -1045,8 +990,8 @@ bool SkOpAngle::tangentsDiverge(const SkOpAngle* rh, double s0xt0) const {
     // - m * (v2.x * v1.x + v2.y * v1.y) == v2.x * v1.y - v2.y * v1.x
     // m = (v2.y * v1.x - v2.x * v1.y) / (v2.x * v1.x + v2.y * v1.y)
     // m = v1.cross(v2) / v1.dot(v2)
-    const SkDVector* sweep = fSweep;
-    const SkDVector* tweep = rh->fSweep;
+    const SkDVector* sweep = fPart.fSweep;
+    const SkDVector* tweep = rh->fPart.fSweep;
     double s0dt0 = sweep[0].dot(tweep[0]);
     if (!s0dt0) {
         return true;
index 4099c4a..cbdadf1 100644 (file)
@@ -107,27 +107,23 @@ private:
     bool midToSide(const SkOpAngle* rh, bool* inside) const;
     bool oppositePlanes(const SkOpAngle* rh) const;
     bool orderable(SkOpAngle* rh);  // false == this < rh ; true == this > rh
-    void setCurveHullSweep();
     void setSector();
     void setSpans();
     bool tangentsDiverge(const SkOpAngle* rh, double s0xt0) const;
 
     SkDCurve fOriginalCurvePart;  // the curve from start to end
-    SkDCurve fCurvePart;  // the curve from start to end offset as needed
+    SkDCurveSweep fPart;  // the curve from start to end offset as needed
     double fSide;
     SkLineParameters fTangentHalf;  // used only to sort a pair of lines or line-like sections
     SkOpAngle* fNext;
     SkOpSpanBase* fLastMarked;
-    SkDVector fSweep[2];
     SkOpSpanBase* fStart;
     SkOpSpanBase* fEnd;
     SkOpSpanBase* fComputedEnd;
     int fSectorMask;
     int8_t fSectorStart;  // in 32nds of a circle
     int8_t fSectorEnd;
-    bool fIsCurve;
     bool fUnorderable;
-    bool fUnorderedSweep;  // set when a cubic's first control point between the sweep vectors
     bool fComputeSector;
     bool fComputedSector;
     bool fCheckCoincidence;
index ede1ed0..d0cb9c1 100644 (file)
@@ -54,6 +54,7 @@ bool FixWinding(SkPath* path) {
         return true;
     }
     SkASSERT(contourHead.next());
+    contourHead.joinAllSegments();
     contourHead.resetReverse();
     bool writePath = false;
     SkOpSpan* topSpan;
index ed53388..3b2318c 100644 (file)
@@ -38,23 +38,21 @@ SkOpSegment* SkOpContour::addCurve(SkPath::Verb verb, const SkPoint pts[4]) {
 }
 
 void SkOpContour::toPath(SkPathWriter* path) const {
-    const SkPoint& pt = fHead.pts()[0];
-    path->deferredMove(pt);
     const SkOpSegment* segment = &fHead;
     do {
         SkAssertResult(segment->addCurveTo(segment->head(), segment->tail(), path));
     } while ((segment = segment->next()));
-    path->close();
+    path->finishContour();
+    path->assemble();
 }
 
 void SkOpContour::toReversePath(SkPathWriter* path) const {
-    const SkPoint& pt = fTail->pts()[0];
-    path->deferredMove(pt);
     const SkOpSegment* segment = fTail;
     do {
         SkAssertResult(segment->addCurveTo(segment->tail(), segment->head(), path));
     } while ((segment = segment->prev()));
-    path->close();
+    path->finishContour();
+    path->assemble();
 }
 
 SkOpSegment* SkOpContour::undoneSegment(SkOpSpanBase** startPtr, SkOpSpanBase** endPtr) {
index 412fecd..acc6744 100644 (file)
@@ -63,18 +63,6 @@ public:
         return *result;
     }
 
-    SkOpContour* appendContour() {
-        SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(this->globalState()->allocator());
-        contour->setNext(nullptr);
-        SkOpContour* prev = this;
-        SkOpContour* next;
-        while ((next = prev->next())) {
-            prev = next;
-        }
-        prev->setNext(contour);
-        return contour;
-    }
-
     const SkPathOpsBounds& bounds() const {
         return fBounds;
     }
@@ -219,6 +207,15 @@ public:
         return fXor;
     }
 
+    void joinSegments() {
+        SkOpSegment* segment = &fHead;
+        SkOpSegment* next;
+        do {
+            next = segment->next();
+            segment->joinEnds(next ? next : &fHead);
+        } while ((segment = next));
+    }
+
     void markAllDone() {
         SkOpSegment* segment = &fHead;
         do {
@@ -289,22 +286,6 @@ public:
 
     void rayCheck(const SkOpRayHit& base, SkOpRayDir dir, SkOpRayHit** hits, SkChunkAlloc* );
 
-    void remove(SkOpContour* contour) {
-        if (contour == this) {
-            SkASSERT(fCount == 0);
-            return;
-        }
-        SkASSERT(contour->fNext == nullptr);
-        SkOpContour* prev = this;
-        SkOpContour* next;
-        while ((next = prev->next()) != contour) {
-            SkASSERT(next);
-            prev = next;
-        }
-        SkASSERT(prev);
-        prev->setNext(nullptr);
-    }
-
     void reset() {
         fTail = nullptr;
         fNext = nullptr;
@@ -416,6 +397,42 @@ private:
 };
 
 class SkOpContourHead : public SkOpContour {
+public:
+    SkOpContour* appendContour() {
+        SkOpContour* contour = SkOpTAllocator<SkOpContour>::New(this->globalState()->allocator());
+        contour->setNext(nullptr);
+        SkOpContour* prev = this;
+        SkOpContour* next;
+        while ((next = prev->next())) {
+            prev = next;
+        }
+        prev->setNext(contour);
+        return contour;
+    }
+
+    void joinAllSegments() {
+        SkOpContour* next = this;
+        do {
+            next->joinSegments();
+        } while ((next = next->next()));
+    }
+
+    void remove(SkOpContour* contour) {
+        if (contour == this) {
+            SkASSERT(this->count() == 0);
+            return;
+        }
+        SkASSERT(contour->next() == nullptr);
+        SkOpContour* prev = this;
+        SkOpContour* next;
+        while ((next = prev->next()) != contour) {
+            SkASSERT(next);
+            prev = next;
+        }
+        SkASSERT(prev);
+        prev->setNext(nullptr);
+    }
+
 };
 
 #endif
index 118f78d..36fc9ed 100644 (file)
@@ -26,16 +26,6 @@ void SkOpEdgeBuilder::addOperand(const SkPath& path) {
     preFetch();
 }
 
-int SkOpEdgeBuilder::count() const {
-    SkOpContour* contour = fContoursHead;
-    int count = 0;
-    while (contour) {
-        count += contour->count() > 0;
-        contour = contour->next();
-    }
-    return count;
-}
-
 bool SkOpEdgeBuilder::finish() {
     fOperand = false;
     if (fUnparseable || !walk()) {
index 1a78a21..c6fc7dc 100644 (file)
@@ -12,7 +12,8 @@
 
 class SkOpEdgeBuilder {
 public:
-    SkOpEdgeBuilder(const SkPathWriter& path, SkOpContour* contours2, SkOpGlobalState* globalState)
+    SkOpEdgeBuilder(const SkPathWriter& path, SkOpContourHead* contours2,
+            SkOpGlobalState* globalState)
         : fGlobalState(globalState)
         , fPath(path.nativePath())
         , fContoursHead(contours2)
@@ -20,7 +21,7 @@ public:
         init();
     }
 
-    SkOpEdgeBuilder(const SkPath& path, SkOpContour* contours2, SkOpGlobalState* globalState)
+    SkOpEdgeBuilder(const SkPath& path, SkOpContourHead* contours2, SkOpGlobalState* globalState)
         : fGlobalState(globalState)
         , fPath(&path)
         , fContoursHead(contours2)
@@ -37,7 +38,6 @@ public:
         }
     }
 
-    int count() const;
     bool finish();
 
     const SkOpContour* head() const {
@@ -60,7 +60,7 @@ private:
     SkTDArray<SkScalar> fWeights;
     SkTDArray<uint8_t> fPathVerbs;
     SkOpContour* fCurrentContour;
-    SkOpContour* fContoursHead;
+    SkOpContourHead* fContoursHead;
     SkPathOpsMask fXorMask[2];
     int fSecondHalf;
     bool fOperand;
index 484ddca..1a965c2 100644 (file)
@@ -162,55 +162,28 @@ bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sum
 bool SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end,
         SkPathWriter* path) const {
     FAIL_IF(start->starter(end)->alreadyAdded());
-    SkOpCurve edge;
-    const SkPoint* ePtr;
-    SkScalar eWeight;
-    if ((start == &fHead && end == &fTail) || (start == &fTail && end == &fHead)) {
-        ePtr = fPts;
-        eWeight = fWeight;
-    } else {
-    // OPTIMIZE? if not active, skip remainder and return xyAtT(end)
-        subDivide(start, end, &edge);
-        ePtr = edge.fPts;
-        eWeight = edge.fWeight;
-    }
-    bool reverse = ePtr == fPts && start != &fHead;
-    if (reverse) {
-        path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
-        switch (fVerb) {
-            case SkPath::kLine_Verb:
-                path->deferredLine(ePtr[0]);
-                break;
-            case SkPath::kQuad_Verb:
-                path->quadTo(ePtr[1], ePtr[0]);
-                break;
-            case SkPath::kConic_Verb:
-                path->conicTo(ePtr[1], ePtr[0], eWeight);
-                break;
-            case SkPath::kCubic_Verb:
-                path->cubicTo(ePtr[2], ePtr[1], ePtr[0]);
-                break;
-            default:
-                SkASSERT(0);
-        }
-    } else {
-        path->deferredMoveLine(ePtr[0]);
-        switch (fVerb) {
-            case SkPath::kLine_Verb:
-                path->deferredLine(ePtr[1]);
-                break;
-            case SkPath::kQuad_Verb:
-                path->quadTo(ePtr[1], ePtr[2]);
-                break;
-            case SkPath::kConic_Verb:
-                path->conicTo(ePtr[1], ePtr[2], eWeight);
-                break;
-            case SkPath::kCubic_Verb:
-                path->cubicTo(ePtr[1], ePtr[2], ePtr[3]);
-                break;
-            default:
-                SkASSERT(0);
-        }
+    SkDCurveSweep curvePart;
+    start->segment()->subDivide(start, end, &curvePart.fCurve);
+    curvePart.setCurveHullSweep(fVerb);
+    SkPath::Verb verb = curvePart.isCurve() ? fVerb : SkPath::kLine_Verb;
+    path->deferredMove(start->ptT());
+    switch (verb) {
+        case SkPath::kLine_Verb:
+            path->deferredLine(end->ptT());
+            break;
+        case SkPath::kQuad_Verb:
+            path->quadTo(curvePart.fCurve.fQuad.fPts[1].asSkPoint(), end->ptT());
+            break;
+        case SkPath::kConic_Verb:
+            path->conicTo(curvePart.fCurve.fConic.fPts[1].asSkPoint(), end->ptT(),
+                    curvePart.fCurve.fConic.fWeight);
+            break;
+        case SkPath::kCubic_Verb:
+            path->cubicTo(curvePart.fCurve.fCubic.fPts[1].asSkPoint(),
+                    curvePart.fCurve.fCubic.fPts[2].asSkPoint(), end->ptT());
+            break;
+        default:
+            SkASSERT(0);
     }
     return true;
 }
index a1b5477..feae838 100644 (file)
@@ -268,6 +268,10 @@ public:
 
     bool isXor() const;
 
+    void joinEnds(SkOpSegment* start) {
+        fTail.ptT()->addOpp(start->fHead.ptT(), start->fHead.ptT());
+    }
+
     const SkPoint& lastPt() const {
         return fPts[SkPathOpsVerbToPoints(fVerb)];
     }
index 34895db..a1ca873 100644 (file)
@@ -198,215 +198,6 @@ bool SortContourList(SkOpContourHead** contourList, bool evenOdd, bool oppEvenOd
     return true;
 }
 
-class DistanceLessThan {
-public:
-    DistanceLessThan(double* distances) : fDistances(distances) { }
-    double* fDistances;
-    bool operator()(const int one, const int two) {
-        return fDistances[one] < fDistances[two];
-    }
-};
-
-    /*
-        check start and end of each contour
-        if not the same, record them
-        match them up
-        connect closest
-        reassemble contour pieces into new path
-    */
-void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
-    SkChunkAlloc allocator(4096);  // FIXME: constant-ize, tune
-    SkOpContourHead contour;
-    SkOpGlobalState globalState(&contour, &allocator  SkDEBUGPARAMS(false)
-                                SkDEBUGPARAMS(nullptr));
-#if DEBUG_SHOW_TEST_NAME
-    SkDebugf("</div>\n");
-#endif
-#if DEBUG_PATH_CONSTRUCTION
-    SkDebugf("%s\n", __FUNCTION__);
-#endif
-    SkOpEdgeBuilder builder(path, &contour, &globalState);
-    builder.finish();
-    SkTDArray<const SkOpContour* > runs;  // indices of partial contours
-    const SkOpContour* eContour = builder.head();
-    do {
-        if (!eContour->count()) {
-            continue;
-        }
-        const SkPoint& eStart = eContour->start();
-        const SkPoint& eEnd = eContour->end();
-#if DEBUG_ASSEMBLE
-        SkDebugf("%s contour", __FUNCTION__);
-        if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
-            SkDebugf("[%d]", runs.count());
-        } else {
-            SkDebugf("   ");
-        }
-        SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
-                eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
-#endif
-        if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
-            eContour->toPath(simple);
-            continue;
-        }
-        *runs.append() = eContour;
-    } while ((eContour = eContour->next()));
-    int count = runs.count();
-    if (count == 0) {
-        return;
-    }
-    SkTDArray<int> sLink, eLink;
-    sLink.append(count);
-    eLink.append(count);
-    int rIndex, iIndex;
-    for (rIndex = 0; rIndex < count; ++rIndex) {
-        sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
-    }
-    const int ends = count * 2;  // all starts and ends
-    const int entries = (ends - 1) * count;  // folded triangle : n * (n - 1) / 2
-    SkTDArray<double> distances;
-    distances.append(entries);
-    for (rIndex = 0; rIndex < ends - 1; ++rIndex) {
-        const SkOpContour* oContour = runs[rIndex >> 1];
-        const SkPoint& oPt = rIndex & 1 ? oContour->end() : oContour->start();
-        const int row = rIndex < count - 1 ? rIndex * ends : (ends - rIndex - 2)
-                * ends - rIndex - 1;
-        for (iIndex = rIndex + 1; iIndex < ends; ++iIndex) {
-            const SkOpContour* iContour = runs[iIndex >> 1];
-            const SkPoint& iPt = iIndex & 1 ? iContour->end() : iContour->start();
-            double dx = iPt.fX - oPt.fX;
-            double dy = iPt.fY - oPt.fY;
-            double dist = dx * dx + dy * dy;
-            distances[row + iIndex] = dist;  // oStart distance from iStart
-        }
-    }
-    SkTDArray<int> sortedDist;
-    sortedDist.append(entries);
-    for (rIndex = 0; rIndex < entries; ++rIndex) {
-        sortedDist[rIndex] = rIndex;
-    }
-    SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin()));
-    int remaining = count;  // number of start/end pairs
-    for (rIndex = 0; rIndex < entries; ++rIndex) {
-        int pair = sortedDist[rIndex];
-        int row = pair / ends;
-        int col = pair - row * ends;
-        int thingOne = row < col ? row : ends - row - 2;
-        int ndxOne = thingOne >> 1;
-        bool endOne = thingOne & 1;
-        int* linkOne = endOne ? eLink.begin() : sLink.begin();
-        if (linkOne[ndxOne] != SK_MaxS32) {
-            continue;
-        }
-        int thingTwo = row < col ? col : ends - row + col - 1;
-        int ndxTwo = thingTwo >> 1;
-        bool endTwo = thingTwo & 1;
-        int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
-        if (linkTwo[ndxTwo] != SK_MaxS32) {
-            continue;
-        }
-        SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
-        bool flip = endOne == endTwo;
-        linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
-        linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
-        if (!--remaining) {
-            break;
-        }
-    }
-    SkASSERT(!remaining);
-#if DEBUG_ASSEMBLE
-    for (rIndex = 0; rIndex < count; ++rIndex) {
-        int s = sLink[rIndex];
-        int e = eLink[rIndex];
-        SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
-                s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
-    }
-#endif
-    rIndex = 0;
-    do {
-        bool forward = true;
-        bool first = true;
-        int sIndex = sLink[rIndex];
-        SkASSERT(sIndex != SK_MaxS32);
-        sLink[rIndex] = SK_MaxS32;
-        int eIndex;
-        if (sIndex < 0) {
-            eIndex = sLink[~sIndex];
-            sLink[~sIndex] = SK_MaxS32;
-        } else {
-            eIndex = eLink[sIndex];
-            eLink[sIndex] = SK_MaxS32;
-        }
-        SkASSERT(eIndex != SK_MaxS32);
-#if DEBUG_ASSEMBLE
-        SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
-                    sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
-                    eIndex < 0 ? ~eIndex : eIndex);
-#endif
-        do {
-            const SkOpContour* contour = runs[rIndex];
-            if (first) {
-                first = false;
-                const SkPoint* startPtr = &contour->start();
-                simple->deferredMove(startPtr[0]);
-            }
-            if (forward) {
-                contour->toPartialForward(simple);
-            } else {
-                contour->toPartialBackward(simple);
-            }
-#if DEBUG_ASSEMBLE
-            SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
-                eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
-                sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
-#endif
-            if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
-                simple->close();
-                break;
-            }
-            if (forward) {
-                eIndex = eLink[rIndex];
-                SkASSERT(eIndex != SK_MaxS32);
-                eLink[rIndex] = SK_MaxS32;
-                if (eIndex >= 0) {
-                    SkASSERT(sLink[eIndex] == rIndex);
-                    sLink[eIndex] = SK_MaxS32;
-                } else {
-                    SkASSERT(eLink[~eIndex] == ~rIndex);
-                    eLink[~eIndex] = SK_MaxS32;
-                }
-            } else {
-                eIndex = sLink[rIndex];
-                SkASSERT(eIndex != SK_MaxS32);
-                sLink[rIndex] = SK_MaxS32;
-                if (eIndex >= 0) {
-                    SkASSERT(eLink[eIndex] == rIndex);
-                    eLink[eIndex] = SK_MaxS32;
-                } else {
-                    SkASSERT(sLink[~eIndex] == ~rIndex);
-                    sLink[~eIndex] = SK_MaxS32;
-                }
-            }
-            rIndex = eIndex;
-            if (rIndex < 0) {
-                forward ^= 1;
-                rIndex = ~rIndex;
-            }
-        } while (true);
-        for (rIndex = 0; rIndex < count; ++rIndex) {
-            if (sLink[rIndex] != SK_MaxS32) {
-                break;
-            }
-        }
-    } while (rIndex < count);
-#if DEBUG_ASSEMBLE
-    for (rIndex = 0; rIndex < count; ++rIndex) {
-       SkASSERT(sLink[rIndex] == SK_MaxS32);
-       SkASSERT(eLink[rIndex] == SK_MaxS32);
-    }
-#endif
-}
-
 static void calcAngles(SkOpContourHead* contourList) {
     SkOpContour* contour = contourList;
     do {
index ed80318..beffb85 100644 (file)
@@ -16,7 +16,6 @@ class SkPathWriter;
 
 const SkOpAngle* AngleWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* windingPtr,
                               bool* sortable);
-void Assemble(const SkPathWriter& path, SkPathWriter* simple);
 SkOpSegment* FindChase(SkTDArray<SkOpSpanBase*>* chase, SkOpSpanBase** startPtr,
                        SkOpSpanBase** endPtr);
 SkOpSpan* FindSortableTop(SkOpContourHead* );
index df67efa..4bc518a 100644 (file)
@@ -88,3 +88,58 @@ void SkDCurve::setQuadBounds(const SkPoint curve[3], SkScalar ,
     bounds->set(SkDoubleToScalar(dRect.fLeft), SkDoubleToScalar(dRect.fTop),
             SkDoubleToScalar(dRect.fRight), SkDoubleToScalar(dRect.fBottom));
 }
+
+void SkDCurveSweep::setCurveHullSweep(SkPath::Verb verb) {
+    fOrdered = true;
+    fSweep[0] = fCurve[1] - fCurve[0];
+    if (SkPath::kLine_Verb == verb) {
+        fSweep[1] = fSweep[0];
+        fIsCurve = false;
+        return;
+    }
+    fSweep[1] = fCurve[2] - fCurve[0];
+    // OPTIMIZE: I do the following float check a lot -- probably need a
+    // central place for this val-is-small-compared-to-curve check
+    double maxVal = 0;
+    for (int index = 0; index < SkPathOpsVerbToPoints(verb); ++index) {
+        maxVal = SkTMax(maxVal, SkTMax(SkTAbs(fCurve[index].fX),
+                SkTAbs(fCurve[index].fY)));
+    }
+    {
+        if (SkPath::kCubic_Verb != verb) {
+            if (roughly_zero_when_compared_to(fSweep[0].fX, maxVal)
+                    && roughly_zero_when_compared_to(fSweep[0].fY, maxVal)) {
+                fSweep[0] = fSweep[1];
+            }
+            goto setIsCurve;
+        }
+        SkDVector thirdSweep = fCurve[3] - fCurve[0];
+        if (fSweep[0].fX == 0 && fSweep[0].fY == 0) {
+            fSweep[0] = fSweep[1];
+            fSweep[1] = thirdSweep;
+            if (roughly_zero_when_compared_to(fSweep[0].fX, maxVal)
+                    && roughly_zero_when_compared_to(fSweep[0].fY, maxVal)) {
+                fSweep[0] = fSweep[1];
+                fCurve[1] = fCurve[3];
+            }
+            goto setIsCurve;
+        }
+        double s1x3 = fSweep[0].crossCheck(thirdSweep);
+        double s3x2 = thirdSweep.crossCheck(fSweep[1]);
+        if (s1x3 * s3x2 >= 0) {  // if third vector is on or between first two vectors
+            goto setIsCurve;
+        }
+        double s2x1 = fSweep[1].crossCheck(fSweep[0]);
+        // FIXME: If the sweep of the cubic is greater than 180 degrees, we're in trouble
+        // probably such wide sweeps should be artificially subdivided earlier so that never happens
+        SkASSERT(s1x3 * s2x1 < 0 || s1x3 * s3x2 < 0);
+        if (s3x2 * s2x1 < 0) {
+            SkASSERT(s2x1 * s1x3 > 0);
+            fSweep[0] = fSweep[1];
+            fOrdered = false;
+        }
+        fSweep[1] = thirdSweep;
+    }
+setIsCurve:
+    fIsCurve = fSweep[0].crossCheck(fSweep[1]) != 0;
+}
index dc9cec9..2b50864 100644 (file)
@@ -82,6 +82,19 @@ struct SkDCurve {
                        double s, double e, SkPathOpsBounds*);
 };
 
+class SkDCurveSweep {
+public:
+    bool isCurve() const { return fIsCurve; }
+    bool isOrdered() const { return fOrdered; }
+    void setCurveHullSweep(SkPath::Verb verb);
+
+    SkDCurve fCurve;
+    SkDVector fSweep[2];
+private:
+    bool fIsCurve;
+    bool fOrdered;  // cleared when a cubic's control point isn't between the sweep vectors
+
+};
 
 extern SkDPoint (SkDCurve::* const Top[])(const SkPoint curve[], SkScalar cWeight,
     double tStart, double tEnd, double* topT);
index 22ea12e..67bcee4 100644 (file)
@@ -1152,20 +1152,20 @@ SkString SkOpAngle::debugPart() const {
     SkString result;
     switch (this->segment()->verb()) {
         case SkPath::kLine_Verb:
-            result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fCurvePart),
+            result.printf(LINE_DEBUG_STR " id=%d", LINE_DEBUG_DATA(fPart.fCurve),
                     this->segment()->debugID());
             break;
         case SkPath::kQuad_Verb:
-            result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fCurvePart),
+            result.printf(QUAD_DEBUG_STR " id=%d", QUAD_DEBUG_DATA(fPart.fCurve),
                     this->segment()->debugID());
             break;
         case SkPath::kConic_Verb:
             result.printf(CONIC_DEBUG_STR " id=%d",
-                    CONIC_DEBUG_DATA(fCurvePart, fCurvePart.fConic.fWeight),
+                    CONIC_DEBUG_DATA(fPart.fCurve, fPart.fCurve.fConic.fWeight),
                     this->segment()->debugID());
             break;
         case SkPath::kCubic_Verb:
-            result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fCurvePart),
+            result.printf(CUBIC_DEBUG_STR " id=%d", CUBIC_DEBUG_DATA(fPart.fCurve),
                     this->segment()->debugID());
             break;
         default:
index ecf2eef..dfe68bb 100644 (file)
@@ -37,7 +37,7 @@
         if (!SkPathOpsDebug::ValidWind(x)) strcpy(x##Str, "?"); \
         else SK_SNPRINTF(x##Str, sizeof(x##Str), "%d", x)
 
-#define DEBUG_UNDER_DEVELOPMENT 1
+#define DEBUG_UNDER_DEVELOPMENT 01
 
 #if FORCE_RELEASE
 
@@ -79,7 +79,7 @@
 #define DEBUG_ASSEMBLE 1
 #define DEBUG_COINCIDENCE 01
 #define DEBUG_COINCIDENCE_ORDER 0  // tight arc quads may generate out-of-order coincdence spans
-#define DEBUG_COINCIDENCE_VERBOSE 01
+#define DEBUG_COINCIDENCE_VERBOSE 0
 #define DEBUG_CUBIC_BINARY_SEARCH 0
 #define DEBUG_CUBIC_SPLIT 1
 #define DEBUG_DUMP_SEGMENTS 1
index b7a48b0..0f4415b 100644 (file)
@@ -152,7 +152,7 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
                         current->markDone(spanStart);
                     }
                 }
-                simple->close();
+                simple->finishContour();
             } else {
                 SkOpSpanBase* last = current->markAndChaseDone(start, end);
                 if (last && !last->chased()) {
@@ -175,7 +175,7 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
             }
         } while (true);
     } while (true);
-    return simple->someAssemblyRequired();
+    return true;
 }
 
 // pretty picture:
@@ -286,7 +286,7 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
     SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
 #endif
     // turn path into list of segments
-    SkOpEdgeBuilder builder(*minuend, &contour, &globalState);
+    SkOpEdgeBuilder builder(*minuend, contourList, &globalState);
     if (builder.unparseable()) {
         return false;
     }
@@ -327,15 +327,10 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
     result->reset();
     result->setFillType(fillType);
     SkPathWriter wrapper(*result);
-    bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper);
-    {  // if some edges could not be resolved, assemble remaining fragments
-        SkPath temp;
-        temp.setFillType(fillType);
-        SkPathWriter assembled(temp);
-        Assemble(wrapper, &assembled);
-        *result = *assembled.nativePath();
-        result->setFillType(fillType);
+    if (!bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper)) {
+        return false;
     }
+    wrapper.assemble();  // if some edges could not be resolved, assemble remaining
 #if DEBUG_T_SECT_LOOP_COUNT
     {
         SkAutoMutexAcquire autoM(debugWorstLoop);
index 2376e1d..8d936bb 100644 (file)
@@ -10,7 +10,7 @@
 #include "SkPathOpsCommon.h"
 #include "SkPathWriter.h"
 
-static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple, bool* closable) {
+static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple) {
     bool unsortable = false;
     do {
         SkOpSpan* span = FindSortableTop(contourList);
@@ -38,7 +38,7 @@ static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple, bo
                                 && !simple->isClosed()) {
                             // FIXME: put in the next two lines to avoid handling already added
                             if (start->starter(end)->checkAlreadyAdded()) {
-                                simple->close();
+                                simple->finishContour();
                             } else if (!current->addCurveTo(start, end, simple)) {
                                 return false;
                             }
@@ -69,7 +69,7 @@ static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple, bo
                         current->markDone(spanStart);
                     }
                 }
-                simple->close();
+                simple->finishContour();
             } else {
                 SkOpSpanBase* last = current->markAndChaseDone(start, end);
                 if (last && !last->chased()) {
@@ -92,17 +92,15 @@ static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple, bo
             }
         } while (true);
     } while (true);
-    *closable = !simple->someAssemblyRequired();
     return true;
 }
 
 // returns true if all edges were processed
-static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple, bool* closable) {
+static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple) {
     SkOpSegment* current;
     SkOpSpanBase* start;
     SkOpSpanBase* end;
     bool unsortable = false;
-    *closable = true;
     while ((current = FindUndone(contourList, &start, &end))) {
         do {
             if (!unsortable && current->done()) {
@@ -146,9 +144,8 @@ static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple, bool*
                 }
                 current->markDone(spanStart);
             }
-            *closable = false;
         }
-        simple->close();
+        simple->finishContour();
         SkPathOpsDebug::ShowActiveSpans(contourList);
     }
     return true;
@@ -186,7 +183,7 @@ bool SimplifyDebug(const SkPath& path, SkPath* result
 #if DEBUG_SORT
     SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
 #endif
-    SkOpEdgeBuilder builder(*workingPath, &contour, &globalState);
+    SkOpEdgeBuilder builder(*workingPath, contourList, &globalState);
     if (!builder.finish()) {
         return false;
     }
@@ -218,21 +215,11 @@ bool SimplifyDebug(const SkPath& path, SkPath* result
     result->reset();
     result->setFillType(fillType);
     SkPathWriter wrapper(*result);
-    bool closable SK_INIT_TO_AVOID_WARNING;
-    if (builder.xorMask() == kWinding_PathOpsMask
-            ? !bridgeWinding(contourList, &wrapper, &closable)
-            : !bridgeXor(contourList, &wrapper, &closable)) {
+    if (builder.xorMask() == kWinding_PathOpsMask ? !bridgeWinding(contourList, &wrapper)
+            : !bridgeXor(contourList, &wrapper)) {
         return false;
     }
-    if (!closable)
-    {  // if some edges could not be resolved, assemble remaining fragments
-        SkPath temp;
-        temp.setFillType(fillType);
-        SkPathWriter assembled(temp);
-        Assemble(wrapper, &assembled);
-        *result = *assembled.nativePath();
-        result->setFillType(fillType);
-    }
+    wrapper.assemble();  // if some edges could not be resolved, assemble remaining
     if (scaleFactor > 1) {
         ScalePath(*result, scaleFactor, result);
     }
index e3a3108..60f18cf 100644 (file)
@@ -23,7 +23,7 @@ bool TightBounds(const SkPath& path, SkRect* result) {
     } else {
         workingPath = &path;
     }
-    SkOpEdgeBuilder builder(*workingPath, &contour, &globalState);
+    SkOpEdgeBuilder builder(*workingPath, contourList, &globalState);
     if (!builder.finish()) {
         return false;
     }
index bd8c721..2c3391b 100644 (file)
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+#include "SkOpSpan.h"
 #include "SkPathOpsPoint.h"
 #include "SkPathWriter.h"
+#include "SkTSort.h"
 
 // wrap path to keep track of whether the contour is initialized and non-empty
 SkPathWriter::SkPathWriter(SkPath& path)
     : fPathPtr(&path)
-    , fCloses(0)
-    , fMoves(0)
 {
     init();
 }
 
 void SkPathWriter::close() {
-    if (!fHasMove) {
+    if (fCurrent.isEmpty()) {
         return;
     }
-    bool callClose = isClosed();
-    lineTo();
-    if (fEmpty) {
-        return;
-    }
-    if (callClose) {
+    SkASSERT(this->isClosed());
 #if DEBUG_PATH_CONSTRUCTION
-        SkDebugf("path.close();\n");
+    SkDebugf("path.close();\n");
 #endif
-        fPathPtr->close();
-        fCloses++;
-    }
+    fCurrent.close();
+    fPathPtr->addPath(fCurrent);
+    fCurrent.reset();
     init();
 }
 
-void SkPathWriter::conicTo(const SkPoint& pt1, const SkPoint& pt2, SkScalar weight) {
-    lineTo();
-    if (fEmpty && AlmostEqualUlps(fDefer[0], pt1) && AlmostEqualUlps(pt1, pt2)) {
-        deferredLine(pt2);
-        return;
-    }
-    moveTo();
-    fDefer[1] = pt2;
-    nudge();
-    fDefer[0] = fDefer[1];
+void SkPathWriter::conicTo(const SkPoint& pt1, const SkOpPtT* pt2, SkScalar weight) {
+    this->update(pt2);
 #if DEBUG_PATH_CONSTRUCTION
     SkDebugf("path.conicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g);\n",
-            pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY, weight);
+            pt1.fX, pt1.fY, pt2->fPt.fX, pt2->fPt.fY, weight);
 #endif
-    fPathPtr->conicTo(pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY, weight);
-    fEmpty = false;
+    fCurrent.conicTo(pt1, pt2->fPt, weight);
 }
 
-void SkPathWriter::cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3) {
-    lineTo();
-    if (fEmpty && AlmostEqualUlps(fDefer[0], pt1) && AlmostEqualUlps(pt1, pt2)
-            && AlmostEqualUlps(pt2, pt3)) {
-        deferredLine(pt3);
-        return;
-    }
-    moveTo();
-    fDefer[1] = pt3;
-    nudge();
-    fDefer[0] = fDefer[1];
+void SkPathWriter::cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkOpPtT* pt3) {
+    this->update(pt3);
 #if DEBUG_PATH_CONSTRUCTION
     SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
-            pt1.fX, pt1.fY, pt2.fX, pt2.fY, fDefer[1].fX, fDefer[1].fY);
+            pt1.fX, pt1.fY, pt2.fX, pt2.fY, pt3->fPt.fX, pt3->fPt.fY);
 #endif
-    fPathPtr->cubicTo(pt1.fX, pt1.fY, pt2.fX, pt2.fY, fDefer[1].fX, fDefer[1].fY);
-    fEmpty = false;
+    fCurrent.cubicTo(pt1, pt2, pt3->fPt);
 }
 
-void SkPathWriter::deferredLine(const SkPoint& pt) {
-    if (pt == fDefer[1]) {
+void SkPathWriter::deferredLine(const SkOpPtT* pt) {
+    SkASSERT(fFirstPtT);
+    SkASSERT(fDefer[0]);
+    if (fDefer[0] == pt) {
+        // FIXME: why we're adding a degenerate line? Caller should have preflighted this.
+        return;
+    }
+    if (pt->contains(fDefer[0])) {
+        // FIXME: why we're adding a degenerate line?
         return;
     }
-    if (changedSlopes(pt)) {
-        lineTo();
+    SkASSERT(!this->matchedLast(pt));
+    if (fDefer[1] && this->changedSlopes(pt)) {
+        this->lineTo();
         fDefer[0] = fDefer[1];
     }
     fDefer[1] = pt;
 }
 
-void SkPathWriter::deferredMove(const SkPoint& pt) {
-    fMoved = true;
-    fHasMove = true;
-    fEmpty = true;
-    fDefer[0] = fDefer[1] = pt;
-}
-
-void SkPathWriter::deferredMoveLine(const SkPoint& pt) {
-    if (!fHasMove) {
-        deferredMove(pt);
+void SkPathWriter::deferredMove(const SkOpPtT* pt) {
+    if (!fDefer[1]) {
+        fFirstPtT = fDefer[0] = pt;
+        return;
+    }
+    SkASSERT(fDefer[0]);
+    if (!this->matchedLast(pt)) {
+        this->finishContour();
+        fFirstPtT = fDefer[0] = pt;
     }
-    deferredLine(pt);
 }
 
-bool SkPathWriter::hasMove() const {
-    return fHasMove;
+void SkPathWriter::finishContour() {
+    if (!this->matchedLast(fDefer[0])) {
+        this->lineTo();
+    }
+    if (fCurrent.isEmpty()) {
+        return;
+    }
+    if (this->isClosed()) {
+        this->close();
+    } else {
+        SkASSERT(fDefer[1]);
+        fEndPtTs.push(fFirstPtT);
+        fEndPtTs.push(fDefer[1]);
+        fPartials.push_back(fCurrent);
+        this->init();
+    }
 }
 
 void SkPathWriter::init() {
-    fEmpty = true;
-    fHasMove = false;
-    fMoved = false;
+    fCurrent.reset();
+    fFirstPtT = fDefer[0] = fDefer[1] = nullptr;
 }
 
 bool SkPathWriter::isClosed() const {
-    return !fEmpty && SkDPoint::ApproximatelyEqual(fFirstPt, fDefer[1]);
+    return this->matchedLast(fFirstPtT);
 }
 
 void SkPathWriter::lineTo() {
-    if (fDefer[0] == fDefer[1]) {
-        return;
+    if (fCurrent.isEmpty()) {
+        this->moveTo();
     }
-    moveTo();
-    nudge();
-    fEmpty = false;
 #if DEBUG_PATH_CONSTRUCTION
-    SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1].fX, fDefer[1].fY);
+    SkDebugf("path.lineTo(%1.9g,%1.9g);\n", fDefer[1]->fPt.fX, fDefer[1]->fPt.fY);
 #endif
-    fPathPtr->lineTo(fDefer[1].fX, fDefer[1].fY);
-    fDefer[0] = fDefer[1];
+    fCurrent.lineTo(fDefer[1]->fPt);
 }
 
-const SkPath* SkPathWriter::nativePath() const {
-    return fPathPtr;
+bool SkPathWriter::matchedLast(const SkOpPtT* test) const {
+    if (test == fDefer[1]) {
+        return true;
+    }
+    if (!test) {
+        return false;
+    }
+    if (!fDefer[1]) {
+        return false;
+    }
+    return test->contains(fDefer[1]);
 }
 
-void SkPathWriter::nudge() {
-    if (fEmpty || !AlmostEqualUlps(fDefer[1].fX, fFirstPt.fX)
-            || !AlmostEqualUlps(fDefer[1].fY, fFirstPt.fY)) {
-        return;
-    }
-    fDefer[1] = fFirstPt;
+void SkPathWriter::moveTo() {
+#if DEBUG_PATH_CONSTRUCTION
+    SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fFirstPtT->fPt.fX, fFirstPtT->fPt.fY);
+#endif
+    fCurrent.moveTo(fFirstPtT->fPt);
 }
 
-void SkPathWriter::quadTo(const SkPoint& pt1, const SkPoint& pt2) {
-    lineTo();
-    if (fEmpty && AlmostEqualUlps(fDefer[0], pt1) && AlmostEqualUlps(pt1, pt2)) {
-        deferredLine(pt2);
-        return;
-    }
-    moveTo();
-    fDefer[1] = pt2;
-    nudge();
-    fDefer[0] = fDefer[1];
+void SkPathWriter::quadTo(const SkPoint& pt1, const SkOpPtT* pt2) {
+    this->update(pt2);
 #if DEBUG_PATH_CONSTRUCTION
     SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
-            pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY);
+            pt1.fX, pt1.fY, pt2->fPt.fX, pt2->fPt.fY);
 #endif
-    fPathPtr->quadTo(pt1.fX, pt1.fY, fDefer[1].fX, fDefer[1].fY);
-    fEmpty = false;
+    fCurrent.quadTo(pt1, pt2->fPt);
 }
 
-bool SkPathWriter::someAssemblyRequired() const {
-    return fCloses < fMoves;
+void SkPathWriter::update(const SkOpPtT* pt) {
+    if (!fDefer[1]) {
+        this->moveTo();
+    } else if (!this->matchedLast(fDefer[0])) {
+        this->lineTo();
+    }
+    fDefer[0] = fDefer[1] = pt;  // set both to know that there is not a pending deferred line
+}
+
+bool SkPathWriter::someAssemblyRequired() {
+    this->finishContour();
+    return fEndPtTs.count() > 0;
 }
 
-bool SkPathWriter::changedSlopes(const SkPoint& pt) const {
-    if (fDefer[0] == fDefer[1]) {
+bool SkPathWriter::changedSlopes(const SkOpPtT* ptT) const {
+    if (matchedLast(fDefer[0])) {
         return false;
     }
-    SkScalar deferDx = fDefer[1].fX - fDefer[0].fX;
-    SkScalar deferDy = fDefer[1].fY - fDefer[0].fY;
-    SkScalar lineDx = pt.fX - fDefer[1].fX;
-    SkScalar lineDy = pt.fY - fDefer[1].fY;
-    return deferDx * lineDy != deferDy * lineDx;
+    SkVector deferDxdy = fDefer[1]->fPt - fDefer[0]->fPt;
+    SkVector lineDxdy = ptT->fPt - fDefer[1]->fPt;
+    return deferDxdy.fX * lineDxdy.fY != deferDxdy.fY * lineDxdy.fX;
 }
 
-void SkPathWriter::moveTo() {
-    if (!fMoved) {
+class DistanceLessThan {
+public:
+    DistanceLessThan(double* distances) : fDistances(distances) { }
+    double* fDistances;
+    bool operator()(const int one, const int two) {
+        return fDistances[one] < fDistances[two];
+    }
+};
+
+    /*
+        check start and end of each contour
+        if not the same, record them
+        match them up
+        connect closest
+        reassemble contour pieces into new path
+    */
+void SkPathWriter::assemble() {
+#if DEBUG_SHOW_TEST_NAME
+    SkDebugf("</div>\n");
+#endif
+    if (!this->someAssemblyRequired()) {
         return;
     }
-    fFirstPt = fDefer[0];
 #if DEBUG_PATH_CONSTRUCTION
-    SkDebugf("path.moveTo(%1.9g,%1.9g);\n", fDefer[0].fX, fDefer[0].fY);
+    SkDebugf("%s\n", __FUNCTION__);
+#endif
+    SkOpPtT const* const* runs = fEndPtTs.begin();  // starts, ends of partial contours
+    int endCount = fEndPtTs.count(); // all starts and ends
+    SkASSERT(endCount > 0);
+    SkASSERT(endCount == fPartials.count() * 2);
+#if DEBUG_ASSEMBLE
+    for (int index = 0; index < endCount; index += 2) {
+        const SkOpPtT* eStart = runs[index];
+        const SkOpPtT* eEnd = runs[index + 1];
+        SkASSERT(eStart != eEnd);
+        SkASSERT(!eStart->contains(eEnd));
+        SkDebugf("%s contour start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n", __FUNCTION__,
+                eStart->fPt.fX, eStart->fPt.fY, eEnd->fPt.fX, eEnd->fPt.fY);
+    }
+#endif
+    SkTDArray<int> sLink, eLink;
+    int linkCount = endCount / 2; // number of partial contours
+    sLink.append(linkCount);
+    eLink.append(linkCount);
+    int rIndex, iIndex;
+    for (rIndex = 0; rIndex < linkCount; ++rIndex) {
+        sLink[rIndex] = eLink[rIndex] = SK_MaxS32;
+    }
+    const int entries = endCount * (endCount - 1) / 2;  // folded triangle
+    SkSTArray<8, double, true> distances(entries);
+    SkSTArray<8, int, true> sortedDist(entries);
+    SkSTArray<8, int, true> distLookup(entries);
+    int rRow = 0;
+    int dIndex = 0;
+    for (rIndex = 0; rIndex < endCount - 1; ++rIndex) {
+        const SkOpPtT* oPtT = runs[rIndex];
+        for (iIndex = rIndex + 1; iIndex < endCount; ++iIndex) {
+            const SkOpPtT* iPtT = runs[iIndex];
+            double dx = iPtT->fPt.fX - oPtT->fPt.fX;
+            double dy = iPtT->fPt.fY - oPtT->fPt.fY;
+            double dist = dx * dx + dy * dy;
+            distLookup.push_back(rRow + iIndex);
+            distances.push_back(dist);  // oStart distance from iStart
+            sortedDist.push_back(dIndex++); 
+        }
+        rRow += endCount;
+    }
+    SkASSERT(dIndex == entries);
+    SkTQSort<int>(sortedDist.begin(), sortedDist.end() - 1, DistanceLessThan(distances.begin()));
+    int remaining = linkCount;  // number of start/end pairs
+    for (rIndex = 0; rIndex < entries; ++rIndex) {
+        int pair = sortedDist[rIndex];
+        pair = distLookup[pair];
+        int row = pair / endCount;
+        int col = pair - row * endCount;
+        int ndxOne = row >> 1;
+        bool endOne = row & 1;
+        int* linkOne = endOne ? eLink.begin() : sLink.begin();
+        if (linkOne[ndxOne] != SK_MaxS32) {
+            continue;
+        }
+        int ndxTwo = col >> 1;
+        bool endTwo = col & 1;
+        int* linkTwo = endTwo ? eLink.begin() : sLink.begin();
+        if (linkTwo[ndxTwo] != SK_MaxS32) {
+            continue;
+        }
+        SkASSERT(&linkOne[ndxOne] != &linkTwo[ndxTwo]);
+        bool flip = endOne == endTwo;
+        linkOne[ndxOne] = flip ? ~ndxTwo : ndxTwo;
+        linkTwo[ndxTwo] = flip ? ~ndxOne : ndxOne;
+        if (!--remaining) {
+            break;
+        }
+    }
+    SkASSERT(!remaining);
+#if DEBUG_ASSEMBLE
+    for (rIndex = 0; rIndex < linkCount; ++rIndex) {
+        int s = sLink[rIndex];
+        int e = eLink[rIndex];
+        SkDebugf("%s %c%d <- s%d - e%d -> %c%d\n", __FUNCTION__, s < 0 ? 's' : 'e',
+                s < 0 ? ~s : s, rIndex, rIndex, e < 0 ? 'e' : 's', e < 0 ? ~e : e);
+    }
+#endif
+    rIndex = 0;
+    do {
+        bool forward = true;
+        bool first = true;
+        int sIndex = sLink[rIndex];
+        SkASSERT(sIndex != SK_MaxS32);
+        sLink[rIndex] = SK_MaxS32;
+        int eIndex;
+        if (sIndex < 0) {
+            eIndex = sLink[~sIndex];
+            sLink[~sIndex] = SK_MaxS32;
+        } else {
+            eIndex = eLink[sIndex];
+            eLink[sIndex] = SK_MaxS32;
+        }
+        SkASSERT(eIndex != SK_MaxS32);
+#if DEBUG_ASSEMBLE
+        SkDebugf("%s sIndex=%c%d eIndex=%c%d\n", __FUNCTION__, sIndex < 0 ? 's' : 'e',
+                    sIndex < 0 ? ~sIndex : sIndex, eIndex < 0 ? 's' : 'e',
+                    eIndex < 0 ? ~eIndex : eIndex);
+#endif
+        do {
+            const SkPath& contour = fPartials[rIndex];
+            if (forward) {
+                fPathPtr->addPath(contour,
+                        first ? SkPath::kAppend_AddPathMode : SkPath::kExtend_AddPathMode);
+            } else {
+                SkASSERT(!first);
+                fPathPtr->reverseAddPath(contour);
+            }
+            if (first) {
+                first = false;
+            }
+#if DEBUG_ASSEMBLE
+            SkDebugf("%s rIndex=%d eIndex=%s%d close=%d\n", __FUNCTION__, rIndex,
+                eIndex < 0 ? "~" : "", eIndex < 0 ? ~eIndex : eIndex,
+                sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex));
+#endif
+            if (sIndex == ((rIndex != eIndex) ^ forward ? eIndex : ~eIndex)) {
+                fPathPtr->close();
+                break;
+            }
+            if (forward) {
+                eIndex = eLink[rIndex];
+                SkASSERT(eIndex != SK_MaxS32);
+                eLink[rIndex] = SK_MaxS32;
+                if (eIndex >= 0) {
+                    SkASSERT(sLink[eIndex] == rIndex);
+                    sLink[eIndex] = SK_MaxS32;
+                } else {
+                    SkASSERT(eLink[~eIndex] == ~rIndex);
+                    eLink[~eIndex] = SK_MaxS32;
+                }
+            } else {
+                eIndex = sLink[rIndex];
+                SkASSERT(eIndex != SK_MaxS32);
+                sLink[rIndex] = SK_MaxS32;
+                if (eIndex >= 0) {
+                    SkASSERT(eLink[eIndex] == rIndex);
+                    eLink[eIndex] = SK_MaxS32;
+                } else {
+                    SkASSERT(sLink[~eIndex] == ~rIndex);
+                    sLink[~eIndex] = SK_MaxS32;
+                }
+            }
+            rIndex = eIndex;
+            if (rIndex < 0) {
+                forward ^= 1;
+                rIndex = ~rIndex;
+            }
+        } while (true);
+        for (rIndex = 0; rIndex < linkCount; ++rIndex) {
+            if (sLink[rIndex] != SK_MaxS32) {
+                break;
+            }
+        }
+    } while (rIndex < linkCount);
+#if DEBUG_ASSEMBLE
+    for (rIndex = 0; rIndex < linkCount; ++rIndex) {
+       SkASSERT(sLink[rIndex] == SK_MaxS32);
+       SkASSERT(eLink[rIndex] == SK_MaxS32);
+    }
 #endif
-    fPathPtr->moveTo(fDefer[0].fX, fDefer[0].fY);
-    fMoved = false;
-    fMoves++;
+    return;
 }
index 820ddc5..bd13c71 100644 (file)
@@ -8,38 +8,47 @@
 #define SkPathWriter_DEFINED
 
 #include "SkPath.h"
+#include "SkTArray.h"
+#include "SkTDArray.h"
+
+class SkOpPtT;
+
+// Construct the path one contour at a time.
+// If the contour is closed, copy it to the final output.
+// Otherwise, keep the partial contour for later assembly.
 
 class SkPathWriter {
 public:
     SkPathWriter(SkPath& path);
-    void close();
-    void conicTo(const SkPoint& pt1, const SkPoint& pt2, SkScalar weight);
-    void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkPoint& pt3);
-    void deferredLine(const SkPoint& pt);
-    void deferredMove(const SkPoint& pt);
-    void deferredMoveLine(const SkPoint& pt);
-    bool hasMove() const;
+    void assemble();
+    void conicTo(const SkPoint& pt1, const SkOpPtT* pt2, SkScalar weight);
+    void cubicTo(const SkPoint& pt1, const SkPoint& pt2, const SkOpPtT* pt3);
+    void deferredLine(const SkOpPtT* pt);
+    void deferredMove(const SkOpPtT* pt);
+    void finishContour();
+    bool hasMove() const { return !fFirstPtT; }
     void init();
     bool isClosed() const;
-    bool isEmpty() const { return fEmpty; }
-    void lineTo();
-    const SkPath* nativePath() const;
-    void nudge();
-    void quadTo(const SkPoint& pt1, const SkPoint& pt2);
-    bool someAssemblyRequired() const;
+    const SkPath* nativePath() const { return fPathPtr; }
+    void quadTo(const SkPoint& pt1, const SkOpPtT* pt2);
 
 private:
-    bool changedSlopes(const SkPoint& pt) const;
+    bool changedSlopes(const SkOpPtT* pt) const;
+    void close();
+    const SkTDArray<const SkOpPtT*>& endPtTs() const { return fEndPtTs; }
+    void lineTo();
+    bool matchedLast(const SkOpPtT*) const;
     void moveTo();
+    const SkTArray<SkPath>& partials() const { return fPartials; }
+    bool someAssemblyRequired();
+    void update(const SkOpPtT* pt);
 
-    SkPath* fPathPtr;
-    SkPoint fDefer[2];
-    SkPoint fFirstPt;
-    int fCloses;
-    int fMoves;
-    bool fEmpty;
-    bool fHasMove;
-    bool fMoved;
+    SkPath fCurrent;  // contour under construction 
+    SkTArray<SkPath> fPartials;   // contours with mismatched starts and ends
+    SkTDArray<const SkOpPtT*> fEndPtTs;  // possible pt values for partial starts and ends
+    SkPath* fPathPtr;  // closed contours are written here
+    const SkOpPtT* fDefer[2];  // [0] deferred move, [1] deferred line
+    const SkOpPtT* fFirstPtT;  // first in current contour
 };
 
 #endif /* defined(__PathOps__SkPathWriter__) */
index 164f9ae..c9635ea 100755 (executable)
@@ -896,7 +896,7 @@ void SkOpAngle::dumpCurves() const {
     const SkOpAngle* first = this;
     const SkOpAngle* next = this;
     do {
-        next->fCurvePart.dumpID(next->segment()->debugID());
+        next->fPart.fCurve.dumpID(next->segment()->debugID());
         next = next->fNext;
     } while (next && next != first);
 }