Upstream version 10.38.220.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / pathops / SkOpSegment.cpp
index 0e48b3f..5304e4b 100644 (file)
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 #include "SkIntersections.h"
+#include "SkOpContour.h"
 #include "SkOpSegment.h"
 #include "SkPathWriter.h"
 #include "SkTSort.h"
@@ -187,7 +188,8 @@ bool SkOpSegment::activeOp(int xorMiMask, int xorSuMask, int index, int endIndex
     }
     bool result = gActiveEdge[op][miFrom][miTo][suFrom][suTo];
 #if DEBUG_ACTIVE_OP
-    SkDebugf("%s op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n", __FUNCTION__,
+    SkDebugf("%s id=%d t=%1.9g tEnd=%1.9g op=%s miFrom=%d miTo=%d suFrom=%d suTo=%d result=%d\n",
+            __FUNCTION__, debugID(), span(index).fT, span(endIndex).fT,
             SkPathOpsDebug::kPathOpStr[op], miFrom, miTo, suFrom, suTo, result);
 #endif
     return result;
@@ -403,26 +405,27 @@ void SkOpSegment::addCurveTo(int start, int end, SkPathWriter* path, bool active
 }
 
 void SkOpSegment::addEndSpan(int endIndex) {
+    SkASSERT(span(endIndex).fT == 1 || (span(endIndex).fTiny
+            && approximately_greater_than_one(span(endIndex).fT)));
     int spanCount = fTs.count();
-    int angleIndex = fAngles.count();
     int startIndex = endIndex - 1;
     while (fTs[startIndex].fT == 1 || fTs[startIndex].fTiny) {
-        ++startIndex;
-        SkASSERT(startIndex < spanCount - 1);
-        ++endIndex;
+        --startIndex;
+        SkASSERT(startIndex > 0);
+        --endIndex;
     }
-    fAngles.push_back().set(this, spanCount - 1, startIndex);
+    SkOpAngle& angle = fAngles.push_back();
+    angle.set(this, spanCount - 1, startIndex);
 #if DEBUG_ANGLE
-    fAngles.back().setID(angleIndex);
     debugCheckPointsEqualish(endIndex, spanCount);
 #endif
-    setFromAngleIndex(endIndex, angleIndex);
+    setFromAngle(endIndex, &angle);
 }
 
-void SkOpSegment::setFromAngleIndex(int endIndex, int angleIndex) {
+void SkOpSegment::setFromAngle(int endIndex, SkOpAngle* angle) {
     int spanCount = fTs.count();
     do {
-        fTs[endIndex].fFromAngleIndex = angleIndex;
+        fTs[endIndex].fFromAngle = angle;
     } while (++endIndex < spanCount);
 }
 
@@ -448,89 +451,92 @@ void SkOpSegment::addQuad(const SkPoint pts[3], bool operand, bool evenOdd) {
     fBounds.setQuadBounds(pts);
 }
 
-int SkOpSegment::addSingletonAngleDown(int endIndex, SkOpSegment** otherPtr) {
-    int startIndex = findEndSpan(endIndex);
-    SkASSERT(startIndex > 0);
-    int spanIndex = endIndex - 1;
-    fSingletonAngles.push_back().set(this, spanIndex, startIndex - 1);
-    setFromAngleIndex(startIndex, fAngles.count() + fSingletonAngles.count() - 1);
+SkOpAngle* SkOpSegment::addSingletonAngleDown(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
+    int spanIndex = count() - 1;
+    int startIndex = nextExactSpan(spanIndex, -1);
+    SkASSERT(startIndex >= 0);
+    SkOpAngle& angle = fAngles.push_back();
+    *anglePtr = &angle;
+    angle.set(this, spanIndex, startIndex);
+    setFromAngle(spanIndex, &angle);
     SkOpSegment* other;
+    int oStartIndex, oEndIndex;
     do {
-        other = fTs[spanIndex].fOther;
-        if (other->fTs[0].fWindValue) {
+        const SkOpSpan& span = fTs[spanIndex];
+        SkASSERT(span.fT > 0);
+        other = span.fOther;
+        oStartIndex = span.fOtherIndex;
+        oEndIndex = other->nextExactSpan(oStartIndex, 1);
+        if (oEndIndex > 0 && other->span(oStartIndex).fWindValue) {
             break;
         }
+        oEndIndex = oStartIndex;
+        oStartIndex = other->nextExactSpan(oEndIndex, -1);
         --spanIndex;
-        SkASSERT(fTs[spanIndex].fT == 1);
-    } while (true);
-    int oEndIndex = other->findStartSpan(0);
-    SkASSERT(oEndIndex > 0);
-    int otherIndex = other->fSingletonAngles.count();
-    other->fSingletonAngles.push_back().set(other, 0, oEndIndex);
-    other->setToAngleIndex(oEndIndex, other->fAngles.count() + otherIndex);
+    } while (oStartIndex < 0 || !other->span(oStartIndex).fWindSum);
+    SkOpAngle& oAngle = other->fAngles.push_back();
+    oAngle.set(other, oStartIndex, oEndIndex);
+    other->setToAngle(oEndIndex, &oAngle);
     *otherPtr = other;
-    return otherIndex;
+    return &oAngle;
 }
 
-int SkOpSegment::addSingletonAngleUp(int start, SkOpSegment** otherPtr) {
-    int endIndex = findStartSpan(start);
+SkOpAngle* SkOpSegment::addSingletonAngleUp(SkOpSegment** otherPtr, SkOpAngle** anglePtr) {
+    int endIndex = nextExactSpan(0, 1);
     SkASSERT(endIndex > 0);
-    int thisIndex = fSingletonAngles.count();
-    fSingletonAngles.push_back().set(this, start, endIndex);
-    setToAngleIndex(endIndex, fAngles.count() + thisIndex);
-    int spanIndex = start;
+    SkOpAngle& angle = fAngles.push_back();
+    *anglePtr = &angle;
+    angle.set(this, 0, endIndex);
+    setToAngle(endIndex, &angle);
+    int spanIndex = 0;
     SkOpSegment* other;
-    int oCount, oStartIndex;
+    int oStartIndex, oEndIndex;
     do {
-        other = fTs[spanIndex].fOther;
-        oCount = other->count();
-        oStartIndex = other->findEndSpan(oCount);
-        SkASSERT(oStartIndex > 0);
-        if (other->fTs[oStartIndex - 1].fWindValue) {
+        const SkOpSpan& span = fTs[spanIndex];
+        SkASSERT(span.fT < 1);
+        other = span.fOther;
+        oEndIndex = span.fOtherIndex;
+        oStartIndex = other->nextExactSpan(oEndIndex, -1);
+        if (oStartIndex >= 0 && other->span(oStartIndex).fWindValue) {
             break;
         }
+        oStartIndex = oEndIndex;
+        oEndIndex = other->nextExactSpan(oStartIndex, 1);
         ++spanIndex;
-        SkASSERT(fTs[spanIndex].fT == 0);
-    } while (true);
-    int otherIndex = other->fSingletonAngles.count();
-    other->fSingletonAngles.push_back().set(other, oCount - 1, oStartIndex - 1);
-    other->setFromAngleIndex(oStartIndex, other->fAngles.count() + otherIndex);
+    } while (oEndIndex < 0 || !other->span(oStartIndex).fWindValue);
+    SkOpAngle& oAngle = other->fAngles.push_back();
+    oAngle.set(other, oEndIndex, oStartIndex);
+    other->setFromAngle(oEndIndex, &oAngle);
     *otherPtr = other;
-    return otherIndex;
+    return &oAngle;
 }
 
-SkOpAngle* SkOpSegment::addSingletonAngles(int start, int step) {
-    int thisIndex = fSingletonAngles.count();
+SkOpAngle* SkOpSegment::addSingletonAngles(int step) {
     SkOpSegment* other;
-    int otherIndex;
+    SkOpAngle* angle, * otherAngle;
     if (step > 0) {
-        otherIndex = addSingletonAngleUp(start, &other);
+        otherAngle = addSingletonAngleUp(&other, &angle);
     } else {
-        otherIndex = addSingletonAngleDown(start + 1, &other);
+        otherAngle = addSingletonAngleDown(&other, &angle);
     }
-    fSingletonAngles[thisIndex].insert(&other->fSingletonAngles[otherIndex]);
-#if DEBUG_ANGLE
-    fSingletonAngles[thisIndex].setID(fAngles.count() + thisIndex);
-    other->fSingletonAngles[otherIndex].setID(other->fAngles.count() + otherIndex);
-#endif
-    return &fSingletonAngles[thisIndex];
+    angle->insert(otherAngle);
+    return angle;
 }
 
 void SkOpSegment::addStartSpan(int endIndex) {
-    int angleIndex = fAngles.count();
     int index = 0;
-    fAngles.push_back().set(this, index, endIndex);
+    SkOpAngle& angle = fAngles.push_back();
+    angle.set(this, index, endIndex);
 #if DEBUG_ANGLE
-    fAngles.back().setID(angleIndex);
     debugCheckPointsEqualish(index, endIndex);
 #endif
-    setToAngleIndex(endIndex, angleIndex);
+    setToAngle(endIndex, &angle);
 }
 
-void SkOpSegment::setToAngleIndex(int endIndex, int angleIndex) {
+void SkOpSegment::setToAngle(int endIndex, SkOpAngle* angle) {
     int index = 0;
     do {
-        fTs[index].fToAngleIndex = angleIndex;
+        fTs[index].fToAngle = angle;
     } while (++index < endIndex);
 }
 
@@ -546,17 +552,14 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
  #if 0  // this needs an even rougher association to be useful
     SkASSERT(SkDPoint::RoughlyEqual(ptAtT(newT), pt));
  #endif
-    if (precisely_zero(newT)) {
-        newT = 0;
-    } else if (precisely_equal(newT, 1)) {
-        newT = 1;
-    }
+    const SkPoint& firstPt = fPts[0];
+    const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
+    SkASSERT(newT == 0 || !precisely_zero(newT));
+    SkASSERT(newT == 1 || !precisely_equal(newT, 1));
     // FIXME: in the pathological case where there is a ton of intercepts,
     //  binary search?
     int insertedAt = -1;
     int tCount = fTs.count();
-    const SkPoint& firstPt = fPts[0];
-    const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
     for (int index = 0; index < tCount; ++index) {
         // OPTIMIZATION: if there are three or more identical Ts, then
         // the fourth and following could be further insertion-sorted so
@@ -588,6 +591,7 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
         span = fTs.append();
     }
     span->fT = newT;
+    span->fOtherT = -1;
     span->fOther = other;
     span->fPt = pt;
 #if 0
@@ -595,20 +599,24 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT) {
     SkASSERT(approximately_equal(xyAtT(newT).fX, pt.fX)
             && approximately_equal(xyAtT(newT).fY, pt.fY));
 #endif
-    span->fFromAngleIndex = -1;
-    span->fToAngleIndex = -1;
+    span->fFromAngle = NULL;
+    span->fToAngle = NULL;
     span->fWindSum = SK_MinS32;
     span->fOppSum = SK_MinS32;
     span->fWindValue = 1;
     span->fOppValue = 0;
     span->fChased = false;
-    if ((span->fDone = newT == 1)) {
-        ++fDoneSpans;
-    }
+    span->fCoincident = false;
     span->fLoop = false;
+    span->fNear = false;
+    span->fMultiple = false;
     span->fSmall = false;
     span->fTiny = false;
+    if ((span->fDone = newT == 1)) {
+        ++fDoneSpans;
+    }
     int less = -1;
+// FIXME: note that this relies on spans being a continguous array
 // find range of spans with nearly the same point as this one
     while (&span[less + 1] - fTs.begin() > 0 && AlmostEqualUlps(span[less].fPt, pt)) {
         if (fVerb == SkPath::kCubic_Verb) {
@@ -687,6 +695,7 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
     while (index > 0 && precisely_equal(fTs[index].fT, fTs[index - 1].fT)) {
         --index;
     }
+    bool oFoundEnd = false;
     int oIndex = other->fTs.count();
     while (startPt != other->fTs[--oIndex].fPt) {  // look for startPt match
         SkASSERT(oIndex > 0);
@@ -700,15 +709,19 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
     SkOpSpan* oTest = &other->fTs[oIndex];
     SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
     SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
+    bool decrement, track, bigger;
+    int originalWindValue;
+    const SkPoint* testPt;
+    const SkPoint* oTestPt;
     do {
         SkASSERT(test->fT < 1);
         SkASSERT(oTest->fT < 1);
-        bool decrement = test->fWindValue && oTest->fWindValue;
-        bool track = test->fWindValue || oTest->fWindValue;
-        bool bigger = test->fWindValue >= oTest->fWindValue;
-        const SkPoint& testPt = test->fPt;
+        decrement = test->fWindValue && oTest->fWindValue;
+        track = test->fWindValue || oTest->fWindValue;
+        bigger = test->fWindValue >= oTest->fWindValue;
+        testPt = &test->fPt;
         double testT = test->fT;
-        const SkPoint& oTestPt = oTest->fPt;
+        oTestPt = &oTest->fPt;
         double oTestT = oTest->fT;
         do {
             if (decrement) {
@@ -718,12 +731,12 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
                     decrementSpan(test);
                 }
             } else if (track) {
-                TrackOutsidePair(&outsidePts, testPt, oTestPt);
+                TrackOutsidePair(&outsidePts, *testPt, *oTestPt);
             }
             SkASSERT(index < fTs.count() - 1);
             test = &fTs[++index];
-        } while (testPt == test->fPt || precisely_equal(testT, test->fT));
-        SkDEBUGCODE(int originalWindValue = oTest->fWindValue);
+        } while (*testPt == test->fPt || precisely_equal(testT, test->fT));
+        originalWindValue = oTest->fWindValue;
         do {
             SkASSERT(oTest->fT < 1);
             SkASSERT(originalWindValue == oTest->fWindValue);
@@ -734,15 +747,48 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
                     other->decrementSpan(oTest);
                 }
             } else if (track) {
-                TrackOutsidePair(&oOutsidePts, oTestPt, testPt);
+                TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
             }
             if (!oIndex) {
                 break;
             }
+            oFoundEnd |= endPt == oTest->fPt;
             oTest = &other->fTs[--oIndex];
-        } while (oTestPt == oTest->fPt || precisely_equal(oTestT, oTest->fT));
+        } while (*oTestPt == oTest->fPt || precisely_equal(oTestT, oTest->fT));
     } while (endPt != test->fPt && test->fT < 1);
     // FIXME: determine if canceled edges need outside ts added
+    if (!oFoundEnd) {
+        for (int oIdx2 = oIndex; oIdx2 >= 0; --oIdx2) {
+            SkOpSpan* oTst2 = &other->fTs[oIdx2];            
+            if (originalWindValue != oTst2->fWindValue) {
+                goto skipAdvanceOtherCancel;
+            }
+            if (!oTst2->fWindValue) {
+                goto skipAdvanceOtherCancel;
+            }
+            if (endPt == other->fTs[oIdx2].fPt) {
+                break;
+            }
+        }
+        do {
+            SkASSERT(originalWindValue == oTest->fWindValue);
+            if (decrement) {
+                if (binary && !bigger) {
+                    oTest->fOppValue--;
+                } else {
+                    other->decrementSpan(oTest);
+                }
+            } else if (track) {
+                TrackOutsidePair(&oOutsidePts, *oTestPt, *testPt);
+            }
+            if (!oIndex) {
+                break;
+            }
+            oTest = &other->fTs[--oIndex];
+            oFoundEnd |= endPt == oTest->fPt;
+        } while (!oFoundEnd || endPt == oTest->fPt);
+    }
+skipAdvanceOtherCancel:
     int outCount = outsidePts.count();
     if (!done() && outCount) {
         addCancelOutsides(outsidePts[0], outsidePts[1], other);
@@ -753,6 +799,8 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
     if (!other->done() && oOutsidePts.count()) {
         other->addCancelOutsides(oOutsidePts[0], oOutsidePts[1], this);
     }
+    setCoincidentRange(startPt, endPt, other);
+    other->setCoincidentRange(startPt, endPt, this);
 }
 
 int SkOpSegment::addSelfT(const SkPoint& pt, double newT) {
@@ -763,48 +811,178 @@ int SkOpSegment::addSelfT(const SkPoint& pt, double newT) {
     return result;
 }
 
+// find the starting or ending span with an existing loop of angles
+// FIXME? replicate for all identical starting/ending spans?
+// OPTIMIZE? remove the spans pointing to windValue==0 here or earlier?
+// FIXME? assert that only one other span has a valid windValue or oppValue
 void SkOpSegment::addSimpleAngle(int index) {
-    if (index == 0) {
-        SkASSERT(!fTs[index].fTiny && fTs[index + 1].fT > 0);
-        addStartSpan(1);
+    SkOpSpan* span = &fTs[index];
+    int idx;
+    int start, end;
+    if (span->fT == 0) {
+        idx = 0;
+        span = &fTs[0];
+        do {
+            if (span->fToAngle) {
+                SkASSERT(span->fToAngle->loopCount() == 2);
+                SkASSERT(!span->fFromAngle);
+                span->fFromAngle = span->fToAngle->next();
+                return;
+            }
+            span = &fTs[++idx];
+        } while (span->fT == 0);
+        SkASSERT(!fTs[0].fTiny && fTs[idx].fT > 0);
+        addStartSpan(idx);
+        start = 0;
+        end = idx;
     } else {
-        SkASSERT(!fTs[index - 1].fTiny && fTs[index - 1].fT < 1);
-        addEndSpan(index);
+        idx = count() - 1;
+        span = &fTs[idx];
+        do {
+            if (span->fFromAngle) {
+                SkASSERT(span->fFromAngle->loopCount() == 2);
+                SkASSERT(!span->fToAngle);
+                span->fToAngle = span->fFromAngle->next();
+                return;
+            }
+            span = &fTs[--idx];
+        } while (span->fT == 1);
+        SkASSERT(!fTs[idx].fTiny && fTs[idx].fT < 1);
+        addEndSpan(++idx);
+        start = idx;
+        end = count();
     }
-    SkOpSpan& span = fTs[index];
-    SkOpSegment* other = span.fOther;
-    SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
+    SkOpSegment* other;
+    SkOpSpan* oSpan;
+    index = start;
+    do {
+        span = &fTs[index];
+        other = span->fOther;
+        int oFrom = span->fOtherIndex;
+        oSpan = &other->fTs[oFrom];
+        if (oSpan->fT < 1 && oSpan->fWindValue) {
+            break;
+        }
+        if (oSpan->fT == 0) {
+            continue;
+        }
+        oFrom = other->nextExactSpan(oFrom, -1);
+        SkOpSpan* oFromSpan = &other->fTs[oFrom];
+        SkASSERT(oFromSpan->fT < 1);
+        if (oFromSpan->fWindValue) {
+            break;
+        }
+    } while (++index < end);
     SkOpAngle* angle, * oAngle;
-    if (index == 0) {
-        SkASSERT(span.fOtherIndex - 1 >= 0);
-        SkASSERT(span.fOtherT == 1);
-        SkDEBUGCODE(SkOpSpan& oPrior = other->fTs[span.fOtherIndex - 1]);
+    if (span->fT == 0) {
+        SkASSERT(span->fOtherIndex - 1 >= 0);
+        SkASSERT(span->fOtherT == 1);
+        SkDEBUGCODE(int oPriorIndex = other->nextExactSpan(span->fOtherIndex, -1));
+        SkDEBUGCODE(const SkOpSpan& oPrior = other->span(oPriorIndex));
         SkASSERT(!oPrior.fTiny && oPrior.fT < 1);
-        other->addEndSpan(span.fOtherIndex);
-        angle = &this->angle(span.fToAngleIndex);
-        oAngle = &other->angle(oSpan.fFromAngleIndex);
+        other->addEndSpan(span->fOtherIndex);
+        angle = span->fToAngle;
+        oAngle = oSpan->fFromAngle;
     } else {
-        SkASSERT(span.fOtherIndex + 1 < other->count());
-        SkASSERT(span.fOtherT == 0);
-        SkASSERT(!oSpan.fTiny && (other->fTs[span.fOtherIndex + 1].fT > 0
-                || (other->fTs[span.fOtherIndex + 1].fFromAngleIndex < 0
-                && other->fTs[span.fOtherIndex + 1].fToAngleIndex < 0)));
+        SkASSERT(span->fOtherIndex + 1 < other->count());
+        SkASSERT(span->fOtherT == 0);
+        SkASSERT(!oSpan->fTiny && (other->fTs[span->fOtherIndex + 1].fT > 0
+                || (other->fTs[span->fOtherIndex + 1].fFromAngle == NULL
+                && other->fTs[span->fOtherIndex + 1].fToAngle == NULL)));
         int oIndex = 1;
         do {
             const SkOpSpan& osSpan = other->span(oIndex);
-            if (osSpan.fFromAngleIndex >= 0 || osSpan.fT > 0) {
+            if (osSpan.fFromAngle || osSpan.fT > 0) {
                 break;
             }
             ++oIndex;
             SkASSERT(oIndex < other->count());
         } while (true);
         other->addStartSpan(oIndex);
-        angle = &this->angle(span.fFromAngleIndex);
-        oAngle = &other->angle(oSpan.fToAngleIndex);
+        angle = span->fFromAngle;
+        oAngle = oSpan->fToAngle;
     }
     angle->insert(oAngle);
 }
 
+void SkOpSegment::alignMultiples(SkTDArray<AlignedSpan>* alignedArray) {
+    debugValidate();
+    int count = this->count();
+    for (int index = 0; index < count; ++index) {
+        SkOpSpan& span = fTs[index];
+        if (!span.fMultiple) {
+            continue;
+        }
+        int end = nextExactSpan(index, 1);
+        SkASSERT(end > index + 1);
+        const SkPoint& thisPt = span.fPt;
+        while (index < end - 1) {
+            SkOpSegment* other1 = span.fOther;
+            int oCnt = other1->count();
+            for (int idx2 = index + 1; idx2 < end; ++idx2) {
+                SkOpSpan& span2 = fTs[idx2];
+                SkOpSegment* other2 = span2.fOther;
+                for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
+                    SkOpSpan& oSpan = other1->fTs[oIdx];
+                    if (oSpan.fOther != other2) {
+                        continue;
+                    }
+                    if (oSpan.fPt == thisPt) {
+                        goto skipExactMatches;
+                    }
+                }
+                for (int oIdx = 0; oIdx < oCnt; ++oIdx) {
+                    SkOpSpan& oSpan = other1->fTs[oIdx];
+                    if (oSpan.fOther != other2) {
+                        continue;
+                    }
+                    if (SkDPoint::RoughlyEqual(oSpan.fPt, thisPt)) {
+                        SkOpSpan& oSpan2 = other2->fTs[oSpan.fOtherIndex];
+                        if (zero_or_one(span.fOtherT) || zero_or_one(oSpan.fT)
+                                || zero_or_one(span2.fOtherT) || zero_or_one(oSpan2.fT)) {
+                            return;
+                        }
+                        if (!way_roughly_equal(span.fOtherT, oSpan.fT)
+                                || !way_roughly_equal(span2.fOtherT, oSpan2.fT)
+                                || !way_roughly_equal(span2.fOtherT, oSpan.fOtherT)
+                                || !way_roughly_equal(span.fOtherT, oSpan2.fOtherT)) {
+                            return;
+                        }
+                        alignSpan(thisPt, span.fOtherT, other1, span2.fOtherT,
+                                other2, &oSpan, alignedArray);
+                        alignSpan(thisPt, span2.fOtherT, other2, span.fOtherT, 
+                                other1, &oSpan2, alignedArray);
+                        break;
+                    }
+                }
+        skipExactMatches:
+                ;
+            }
+            ++index;
+        }
+    }
+    debugValidate();
+}
+
+void SkOpSegment::alignSpan(const SkPoint& newPt, double newT, const SkOpSegment* other,
+        double otherT, const SkOpSegment* other2, SkOpSpan* oSpan,
+        SkTDArray<AlignedSpan>* alignedArray) {
+    AlignedSpan* aligned = alignedArray->append();
+    aligned->fOldPt = oSpan->fPt;
+    aligned->fPt = newPt;
+    aligned->fOldT = oSpan->fT;
+    aligned->fT = newT;
+    aligned->fSegment = this;  // OPTIMIZE: may be unused, can remove
+    aligned->fOther1 = other;
+    aligned->fOther2 = other2;
+    SkASSERT(SkDPoint::RoughlyEqual(oSpan->fPt, newPt));
+    oSpan->fPt = newPt;
+//    SkASSERT(way_roughly_equal(oSpan->fT, newT));
+    oSpan->fT = newT;
+//    SkASSERT(way_roughly_equal(oSpan->fOtherT, otherT));
+    oSpan->fOtherT = otherT;
+}
+
 bool SkOpSegment::alignSpan(int index, double thisT, const SkPoint& thisPt) {
     bool aligned = false;
     SkOpSpan* span = &fTs[index];
@@ -877,6 +1055,156 @@ void SkOpSegment::alignSpanState(int start, int end) {
     }
 }
 
+void SkOpSegment::blindCancel(const SkCoincidence& coincidence, SkOpSegment* other) {
+    bool binary = fOperand != other->fOperand;
+    int index = 0;
+    int last = this->count();
+    do {
+        SkOpSpan& span = this->fTs[--last];
+        if (span.fT != 1 && !span.fSmall) {
+            break;
+        }
+        span.fCoincident = true;
+    } while (true);
+    int oIndex = other->count();
+    do {
+        SkOpSpan& oSpan = other->fTs[--oIndex];
+        if (oSpan.fT != 1 && !oSpan.fSmall) {
+            break;
+        }
+        oSpan.fCoincident = true;
+    } while (true);
+    do {
+        SkOpSpan* test = &this->fTs[index];
+        int baseWind = test->fWindValue;
+        int baseOpp = test->fOppValue;
+        int endIndex = index;
+        while (++endIndex <= last) {
+            SkOpSpan* endSpan = &this->fTs[endIndex];
+            SkASSERT(endSpan->fT < 1);
+            if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
+                break;
+            }
+            endSpan->fCoincident = true;
+        }
+        SkOpSpan* oTest = &other->fTs[oIndex];
+        int oBaseWind = oTest->fWindValue;
+        int oBaseOpp = oTest->fOppValue;
+        int oStartIndex = oIndex;
+        while (--oStartIndex >= 0) {
+            SkOpSpan* oStartSpan = &other->fTs[oStartIndex];
+            if (oStartSpan->fWindValue != oBaseWind || oStartSpan->fOppValue != oBaseOpp) {
+                break;
+            }
+            oStartSpan->fCoincident = true;
+        }
+        bool decrement = baseWind && oBaseWind;
+        bool bigger = baseWind >= oBaseWind;
+        do {
+            SkASSERT(test->fT < 1);
+            if (decrement) {
+                if (binary && bigger) {
+                    test->fOppValue--;
+                } else {
+                    decrementSpan(test);
+                }
+            }
+            test->fCoincident = true;
+            test = &fTs[++index];
+        } while (index < endIndex);
+        do {
+            SkASSERT(oTest->fT < 1);
+            if (decrement) {
+                if (binary && !bigger) {
+                    oTest->fOppValue--;
+                } else {
+                    other->decrementSpan(oTest);
+                }
+            }
+            oTest->fCoincident = true;
+            oTest = &other->fTs[--oIndex];
+        } while (oIndex > oStartIndex);
+    } while (index <= last && oIndex >= 0);
+    SkASSERT(index > last);
+    SkASSERT(oIndex < 0);
+}
+
+void SkOpSegment::blindCoincident(const SkCoincidence& coincidence, SkOpSegment* other) {
+    bool binary = fOperand != other->fOperand;
+    int index = 0;
+    int last = this->count();
+    do {
+        SkOpSpan& span = this->fTs[--last];
+        if (span.fT != 1 && !span.fSmall) {
+            break;
+        }
+        span.fCoincident = true;
+    } while (true);
+    int oIndex = 0;
+    int oLast = other->count();
+    do {
+        SkOpSpan& oSpan = other->fTs[--oLast];
+        if (oSpan.fT != 1 && !oSpan.fSmall) {
+            break;
+        }
+        oSpan.fCoincident = true;
+    } while (true);
+    do {
+        SkOpSpan* test = &this->fTs[index];
+        int baseWind = test->fWindValue;
+        int baseOpp = test->fOppValue;
+        int endIndex = index;
+        SkOpSpan* endSpan;
+        while (++endIndex <= last) {
+            endSpan = &this->fTs[endIndex];
+            SkASSERT(endSpan->fT < 1);
+            if (endSpan->fWindValue != baseWind || endSpan->fOppValue != baseOpp) {
+                break;
+            }
+            endSpan->fCoincident = true;
+        }
+        SkOpSpan* oTest = &other->fTs[oIndex];
+        int oBaseWind = oTest->fWindValue;
+        int oBaseOpp = oTest->fOppValue;
+        int oEndIndex = oIndex;
+        SkOpSpan* oEndSpan;
+        while (++oEndIndex <= oLast) {
+            oEndSpan = &this->fTs[oEndIndex];
+            SkASSERT(oEndSpan->fT < 1);
+            if (oEndSpan->fWindValue != oBaseWind || oEndSpan->fOppValue != oBaseOpp) {
+                break;
+            }
+            oEndSpan->fCoincident = true;
+        }
+        // consolidate the winding count even if done
+        if ((test->fWindValue || test->fOppValue) && (oTest->fWindValue || oTest->fOppValue)) {
+            if (!binary || test->fWindValue + oTest->fOppValue >= 0) {
+                bumpCoincidentBlind(binary, index, endIndex);
+                other->bumpCoincidentOBlind(oIndex, oEndIndex);
+            } else {
+                other->bumpCoincidentBlind(binary, oIndex, oEndIndex);
+                bumpCoincidentOBlind(index, endIndex);
+            }
+        }
+        index = endIndex;
+        oIndex = oEndIndex;
+    } while (index <= last && oIndex <= oLast);
+    SkASSERT(index > last);
+    SkASSERT(oIndex > oLast);
+}
+
+void SkOpSegment::bumpCoincidentBlind(bool binary, int index, int endIndex) {
+    const SkOpSpan& oTest = fTs[index];
+    int oWindValue = oTest.fWindValue;
+    int oOppValue = oTest.fOppValue;
+    if (binary) {
+        SkTSwap<int>(oWindValue, oOppValue);
+    }
+    do {
+        (void) bumpSpan(&fTs[index], oWindValue, oOppValue);
+    } while (++index < endIndex);
+}
+
 void SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* indexPtr,
         SkTArray<SkPoint, true>* outsideTs) {
     int index = *indexPtr;
@@ -897,6 +1225,12 @@ void SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* in
     *indexPtr = index;
 }
 
+void SkOpSegment::bumpCoincidentOBlind(int index, int endIndex) {
+    do {
+        zeroSpan(&fTs[index]);
+    } while (++index < endIndex);
+}
+
 // because of the order in which coincidences are resolved, this and other
 // may not have the same intermediate points. Compute the corresponding
 // intermediate T values (using this as the master, other as the follower)
@@ -931,7 +1265,7 @@ void SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
 
 // set spans from start to end to increment the greater by one and decrement
 // the lesser
-void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
+bool SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
         SkOpSegment* other) {
     bool binary = fOperand != other->fOperand;
     int index = 0;
@@ -959,10 +1293,14 @@ void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d
     double testT = test->fT;
     SkOpSpan* oTest = &other->fTs[oIndex];
     const SkPoint* oTestPt = &oTest->fPt;
-    SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
+    // paths with extreme data will fail this test and eject out of pathops altogether later on
+    // SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
     do {
         SkASSERT(test->fT < 1);
-        SkASSERT(oTest->fT < 1);
+        if (oTest->fT == 1) {
+            // paths with extreme data may be so mismatched that we fail here
+            return false;
+        }
 
         // consolidate the winding count even if done
         if ((test->fWindValue == 0 && test->fOppValue == 0)
@@ -1025,13 +1363,13 @@ void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d
         int oPeekIndex = oIndex;
         bool success = true;
         SkOpSpan* oPeek;
+        int oCount = other->count();
         do {
             oPeek = &other->fTs[oPeekIndex];
-            if (oPeek->fT == 1) {
+            if (++oPeekIndex == oCount) {
                 success = false;
                 break;
             }
-            ++oPeekIndex;
         } while (endPt != oPeek->fPt);
         if (success) {
             // make sure the matching point completes the coincidence span
@@ -1041,7 +1379,10 @@ void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d
                     success = true;
                     break;
                 }
-                oPeek = &other->fTs[++oPeekIndex];
+                if (++oPeekIndex == oCount) {
+                    break;
+                }
+                oPeek = &other->fTs[oPeekIndex];
             } while (endPt == oPeek->fPt);
         }
         if (success) {
@@ -1063,12 +1404,15 @@ void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, d
     if (!other->done() && oOutsidePts.count()) {
         other->addCoinOutsides(oOutsidePts[0], endPt, this);
     }
+    setCoincidentRange(startPt, endPt, other);
+    other->setCoincidentRange(startPt, endPt, this);
+    return true;
 }
 
 // FIXME: this doesn't prevent the same span from being added twice
 // fix in caller, SkASSERT here?
 const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
-                           const SkPoint& pt) {
+        const SkPoint& pt, const SkPoint& pt2) {
     int tCount = fTs.count();
     for (int tIndex = 0; tIndex < tCount; ++tIndex) {
         const SkOpSpan& span = fTs[tIndex];
@@ -1089,24 +1433,23 @@ const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double other
             __FUNCTION__, fID, t, other->fID, otherT);
 #endif
     int insertedAt = addT(other, pt, t);
-    int otherInsertedAt = other->addT(this, pt, otherT);
+    int otherInsertedAt = other->addT(this, pt2, otherT);
     addOtherT(insertedAt, otherT, otherInsertedAt);
     other->addOtherT(otherInsertedAt, t, insertedAt);
     matchWindingValue(insertedAt, t, borrowWind);
     other->matchWindingValue(otherInsertedAt, otherT, borrowWind);
-    return &span(insertedAt);
+    SkOpSpan& span = this->fTs[insertedAt];
+    if (pt != pt2) {
+        span.fNear = true;
+        SkOpSpan& oSpan = other->fTs[otherInsertedAt];
+        oSpan.fNear = true;
+    }
+    return &span;
 }
 
-const SkOpAngle& SkOpSegment::angle(int index) const {
-    SkASSERT(index >= 0);
-    int count = fAngles.count();
-    if (index < count) {
-        return fAngles[index];
-    }
-    index -= count;
-    count = fSingletonAngles.count();
-    SkASSERT(index < count);
-    return fSingletonAngles[index];
+const SkOpSpan* SkOpSegment::addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
+                           const SkPoint& pt) {
+    return addTPair(t, other, otherT, borrowWind, pt, pt);
 }
 
 bool SkOpSegment::betweenPoints(double midT, const SkPoint& pt1, const SkPoint& pt2) const {
@@ -1138,9 +1481,10 @@ bool SkOpSegment::calcAngles() {
     const SkOpSpan* span = &fTs[0];
     if (firstSpan->fT == 0 || span->fTiny || span->fOtherT != 1 || span->fOther->multipleEnds()) {
         index = findStartSpan(0);  // curve start intersects
-        if (index < 0) {
+        if (fTs[index].fT == 0) {
             return false;
         }
+        SkASSERT(index > 0);
         if (activePrior >= 0) {
             addStartSpan(index);
         }
@@ -1150,9 +1494,7 @@ bool SkOpSegment::calcAngles() {
     span = &fTs[endIndex - 1];
     if ((addEnd = span->fT == 1 || span->fTiny)) {  // if curve end intersects
         endIndex = findEndSpan(endIndex);
-        if (endIndex < 0) {
-            return false;
-        }
+        SkASSERT(endIndex > 0);
     } else {
         addEnd = fTs[endIndex].fOtherT != 0 || fTs[endIndex].fOther->multipleStarts();
     }
@@ -1174,24 +1516,19 @@ bool SkOpSegment::calcAngles() {
                 return false;
             }
         } while (true);
-        int angleIndex = fAngles.count();
-        int priorAngleIndex;
+        SkOpAngle* angle = NULL;
+        SkOpAngle* priorAngle;
         if (activePrior >= 0) {
             int pActive = firstActive(prior);
             SkASSERT(pActive < start);
-            fAngles.push_back().set(this, start, pActive);
-            priorAngleIndex = angleIndex++;
-    #if DEBUG_ANGLE
-            fAngles.back().setID(priorAngleIndex);
-    #endif
+            priorAngle = &fAngles.push_back();
+            priorAngle->set(this, start, pActive);
         }
         int active = checkSetAngle(start);
         if (active >= 0) {
             SkASSERT(active < index);
-            fAngles.push_back().set(this, active, index);
-    #if DEBUG_ANGLE
-            fAngles.back().setID(angleIndex);
-    #endif
+            angle = &fAngles.push_back();
+            angle->set(this, active, index);
         }
     #if DEBUG_ANGLE
         debugCheckPointsEqualish(start, index);
@@ -1199,20 +1536,20 @@ bool SkOpSegment::calcAngles() {
         prior = start;
         do {
             const SkOpSpan* startSpan = &fTs[start - 1];
-            if (!startSpan->fSmall || startSpan->fFromAngleIndex >= 0
-                    || startSpan->fToAngleIndex >= 0) {
+            if (!startSpan->fSmall || isCanceled(start - 1) || startSpan->fFromAngle
+                    || startSpan->fToAngle) {
                 break;
             }
             --start;
         } while (start > 0);
         do {
             if (activePrior >= 0) {
-                SkASSERT(fTs[start].fFromAngleIndex == -1);
-                fTs[start].fFromAngleIndex = priorAngleIndex;
+                SkASSERT(fTs[start].fFromAngle == NULL);
+                fTs[start].fFromAngle = priorAngle;
             }
             if (active >= 0) {
-                SkASSERT(fTs[start].fToAngleIndex == -1);
-                fTs[start].fToAngleIndex = angleIndex;
+                SkASSERT(fTs[start].fToAngle == NULL);
+                fTs[start].fToAngle = angle;
             }
         } while (++start < index);
         activePrior = active;
@@ -1231,7 +1568,7 @@ int SkOpSegment::checkSetAngle(int tIndex) const {
     return isCanceled(tIndex) ? -1 : tIndex;
 }
 
-// at this point, the span is already ordered, or unorderable, or unsortable
+// at this point, the span is already ordered, or unorderable
 int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType includeType) {
     SkASSERT(includeType != SkOpAngle::kUnaryXor);
     SkOpAngle* firstAngle = spanToAngle(endIndex, startIndex);
@@ -1242,50 +1579,61 @@ int SkOpSegment::computeSum(int startIndex, int endIndex, SkOpAngle::IncludeType
     //  or if no adjacent angles are orderable,
     //  or if adjacent orderable angles have no computed winding,
     //  there's nothing to do
-    // if two orderable angles are adjacent, and one has winding computed, transfer to the other
+    // if two orderable angles are adjacent, and both are next to orderable angles,
+    //  and one has winding computed, transfer to the other
     SkOpAngle* baseAngle = NULL;
     bool tryReverse = false;
     // look for counterclockwise transfers
-    SkOpAngle* angle = firstAngle;
+    SkOpAngle* angle = firstAngle->previous();
+    SkOpAngle* next = angle->next();
+    firstAngle = next;
     do {
-        int testWinding = angle->segment()->windSum(angle);
-        if (SK_MinS32 != testWinding && !angle->unorderable()) {
-            baseAngle = angle;
+        SkOpAngle* prior = angle;
+        angle = next;
+        next = angle->next();
+        SkASSERT(prior->next() == angle);
+        SkASSERT(angle->next() == next);
+        if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
+            baseAngle = NULL;
             continue;
         }
-        if (angle->unorderable()) {
-            baseAngle = NULL;
+        int testWinding = angle->segment()->windSum(angle);
+        if (SK_MinS32 != testWinding) {
+            baseAngle = angle;
             tryReverse = true;
             continue;
         }
         if (baseAngle) {
             ComputeOneSum(baseAngle, angle, includeType);
             baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
-            tryReverse |= !baseAngle;
         }
-    } while ((angle = angle->next()) != firstAngle);
-    if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(angle)) {
+    } while (next != firstAngle);
+    if (baseAngle && SK_MinS32 == firstAngle->segment()->windSum(firstAngle)) {
         firstAngle = baseAngle;
         tryReverse = true;
     }
     if (tryReverse) {
         baseAngle = NULL;
-        angle = firstAngle;
+        SkOpAngle* prior = firstAngle;
         do {
+            angle = prior;
+            prior = angle->previous();
+            SkASSERT(prior->next() == angle);
+            next = angle->next();
+            if (prior->unorderable() || angle->unorderable() || next->unorderable()) {
+                baseAngle = NULL;
+                continue;
+            }
             int testWinding = angle->segment()->windSum(angle);
             if (SK_MinS32 != testWinding) {
                 baseAngle = angle;
                 continue;
             }
-            if (angle->unorderable()) {
-                baseAngle = NULL;
-                continue;
-            }
             if (baseAngle) {
                 ComputeOneSumReverse(baseAngle, angle, includeType);
                 baseAngle = SK_MinS32 != angle->segment()->windSum(angle) ? angle : NULL;
             }
-        } while ((angle = angle->previous()) != firstAngle);
+        } while (prior != firstAngle);
     }
     int minIndex = SkMin32(startIndex, endIndex);
     return windSum(minIndex);
@@ -1362,6 +1710,31 @@ bool SkOpSegment::containsPt(const SkPoint& pt, int index, int endIndex) const {
     return false;
 }
 
+bool SkOpSegment::containsT(double t, const SkOpSegment* other, double otherT) const {
+    int count = this->count();
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = fTs[index];
+        if (t < span.fT) {
+            return false;
+        }
+        if (t == span.fT) {
+            if (other != span.fOther) {
+                continue;
+            }
+            if (other->fVerb != SkPath::kCubic_Verb) {
+                return true;
+            }
+            if (!other->fLoop) {
+                return true;
+            }
+            double otherMidT = (otherT + span.fOtherT) / 2;
+            SkPoint otherPt = other->ptAtT(otherMidT);
+            return SkDPoint::ApproximatelyEqual(span.fPt, otherPt);
+        }
+    }
+    return false;
+}
+
 int SkOpSegment::crossedSpanY(const SkPoint& basePt, SkScalar* bestY, double* hitT,
                               bool* hitSomething, double mid, bool opp, bool current) const {
     SkScalar bottom = fBounds.fBottom;
@@ -1542,6 +1915,58 @@ bool SkOpSegment::calcLoopSpanCount(const SkOpSpan& thisSpan, int* smallCounts)
     return true;
 }
 
+double SkOpSegment::calcMissingTEnd(const SkOpSegment* ref, double loEnd, double min, double max,
+        double hiEnd, const SkOpSegment* other, int thisStart) {
+    if (max >= hiEnd) {
+        return -1;
+    }
+    int end = findOtherT(hiEnd, ref);
+    if (end < 0) {
+        return -1;
+    }
+    double tHi = span(end).fT;
+    double tLo, refLo;
+    if (thisStart >= 0) {
+        tLo = span(thisStart).fT;
+        refLo = min;
+    } else {
+        int start1 = findOtherT(loEnd, ref);
+        SkASSERT(start1 >= 0);
+        tLo = span(start1).fT;
+        refLo = loEnd;
+    }
+    double missingT = (max - refLo) / (hiEnd - refLo);
+    missingT = tLo + missingT * (tHi - tLo);
+    return missingT;
+}
+
+double SkOpSegment::calcMissingTStart(const SkOpSegment* ref, double loEnd, double min, double max,
+        double hiEnd, const SkOpSegment* other, int thisEnd) {
+    if (min <= loEnd) {
+        return -1;
+    }
+    int start = findOtherT(loEnd, ref);
+    if (start < 0) {
+        return -1;
+    }
+    double tLo = span(start).fT;
+    double tHi, refHi;
+    if (thisEnd >= 0) {
+        tHi = span(thisEnd).fT;
+        refHi = max;
+    } else {
+        int end1 = findOtherT(hiEnd, ref);
+        if (end1 < 0) {
+            return -1;
+        }
+        tHi = span(end1).fT;
+        refHi = hiEnd;
+    }
+    double missingT = (min - loEnd) / (refHi - loEnd);
+    missingT = tLo + missingT * (tHi - tLo);
+    return missingT;
+}
+
 // see if spans with two or more intersections have the same number on the other end
 void SkOpSegment::checkDuplicates() {
     debugValidate();
@@ -1561,6 +1986,9 @@ void SkOpSegment::checkDuplicates() {
         }
         do {
             const SkOpSpan* thisSpan = &fTs[index];
+            if (thisSpan->fNear) {
+                continue;
+            }
             SkOpSegment* other = thisSpan->fOther;
             int oIndex = thisSpan->fOtherIndex;
             int oStart = other->nextExactSpan(oIndex, -1) + 1;
@@ -1602,6 +2030,33 @@ void SkOpSegment::checkDuplicates() {
         if (missing.fSegment == missing.fOther) {
             continue;
         }
+#if 0  // FIXME: this eliminates spurious data from skpwww_argus_presse_fr_41 but breaks
+       // skpwww_fashionscandal_com_94 -- calcAngles complains, but I don't understand why
+        if (missing.fSegment->containsT(missing.fT, missing.fOther, missing.fOtherT)) {
+#if DEBUG_DUPLICATES
+            SkDebugf("skip 1 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fSegment->fID,
+                    missing.fT, missing.fOther->fID, missing.fOtherT);
+#endif
+            continue;
+        }
+        if (missing.fOther->containsT(missing.fOtherT, missing.fSegment, missing.fT)) {
+#if DEBUG_DUPLICATES
+            SkDebugf("skip 2 id=%d t=%1.9g other=%d otherT=%1.9g\n", missing.fOther->fID,
+                    missing.fOtherT, missing.fSegment->fID, missing.fT);
+#endif
+            continue;
+        }
+#endif
+        // skip if adding would insert point into an existing coincindent span
+        if (missing.fSegment->inCoincidentSpan(missing.fT, missingOther)
+                && missingOther->inCoincidentSpan(missing.fOtherT, this)) {
+            continue;
+        }
+        // skip if the created coincident spans are small
+        if (missing.fSegment->coincidentSmall(missing.fPt, missing.fT, missingOther)
+                && missingOther->coincidentSmall(missing.fPt, missing.fOtherT, missing.fSegment)) {
+            continue;
+        }
         const SkOpSpan* added = missing.fSegment->addTPair(missing.fT, missingOther,
                 missing.fOtherT, false, missing.fPt);
         if (added && added->fSmall) {
@@ -1777,8 +2232,10 @@ void SkOpSegment::checkMultiples() {
             continue;
         }
         // force the duplicates to agree on t and pt if not on the end
-        double thisT = fTs[index].fT;
-        const SkPoint& thisPt = fTs[index].fPt;
+        SkOpSpan& span = fTs[index];
+        double thisT = span.fT;
+        const SkPoint& thisPt = span.fPt;
+        span.fMultiple = true;
         bool aligned = false;
         while (++index < end) {
             aligned |= alignSpan(index, thisT, thisPt);
@@ -1786,6 +2243,7 @@ void SkOpSegment::checkMultiples() {
         if (aligned) {
             alignSpanState(start, end);
         }
+        fMultiples = true;
     }
     debugValidate();
 }
@@ -1844,9 +2302,10 @@ void SkOpSegment::checkSmall() {
         }
         const SkOpSpan& firstSpan = this->firstSpan(*thisSpan);
         const SkOpSpan& lastSpan = this->lastSpan(*thisSpan);
+        const SkOpSpan* nextSpan = &firstSpan + 1;
         ptrdiff_t smallCount = &lastSpan - &firstSpan + 1;
         SkASSERT(1 <= smallCount && smallCount < count());
-        if (smallCount <= 1) {
+        if (smallCount <= 1 && !nextSpan->fSmall) {
             SkASSERT(1 == smallCount);
             checkSmallCoincidence(firstSpan, NULL);
             continue;
@@ -1930,8 +2389,8 @@ nextSmallCheck:
             do {
                 ++nextSpan;
             } while (nextSpan->fSmall);
-            missing.fSegment->addTCoincident(missing.fPt, nextSpan->fPt, nextSpan->fT,
-                    missingOther);
+            SkAssertResult(missing.fSegment->addTCoincident(missing.fPt, nextSpan->fPt,
+                    nextSpan->fT, missingOther));
         } else if (otherSpan.fT > 0) {
             const SkOpSpan* priorSpan = &otherSpan;
             do {
@@ -2002,7 +2461,7 @@ void SkOpSegment::checkSmallCoincidence(const SkOpSpan& span,
     }
     SkASSERT(oSpan.fSmall);
     if (oStartIndex < oEndIndex) {
-        addTCoincident(span.fPt, next->fPt, next->fT, other);
+        SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, other));
     } else {
         addTCancel(span.fPt, next->fPt, other);
     }
@@ -2047,7 +2506,7 @@ void SkOpSegment::checkSmallCoincidence(const SkOpSpan& span,
                         oTest->fOtherT, tTest->fT);
 #endif
                 if (tTest->fT < oTest->fOtherT) {
-                    addTCoincident(span.fPt, next->fPt, next->fT, testOther);
+                    SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, testOther));
                 } else {
                     addTCancel(span.fPt, next->fPt, testOther);
                 }
@@ -2146,6 +2605,30 @@ void SkOpSegment::checkTiny() {
     }
 }
 
+bool SkOpSegment::coincidentSmall(const SkPoint& pt, double t, const SkOpSegment* other) const {
+    int count = this->count();
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = this->span(index);
+        if (span.fOther != other) {
+            continue;
+        }
+        if (span.fPt == pt) {
+            continue;
+        }
+        if (!AlmostEqualUlps(span.fPt, pt)) {
+            continue;
+        }
+        if (fVerb != SkPath::kCubic_Verb) {
+            return true;
+        }
+        double tInterval = t - span.fT;
+        double tMid = t - tInterval / 2;
+        SkDPoint midPt = dcubic_xy_at_t(fPts, tMid);
+        return midPt.approximatelyEqual(xyAtT(t));
+    }
+    return false;
+}
+
 bool SkOpSegment::findCoincidentMatch(const SkOpSpan* span, const SkOpSegment* other, int oStart,
         int oEnd, int step, SkPoint* startPt, SkPoint* endPt, double* endT) const {
     SkASSERT(span->fT == 0 || span->fT == 1);
@@ -2235,12 +2718,11 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
     SkASSERT(startIndex != endIndex);
     SkDEBUGCODE(const int count = fTs.count());
     SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
-    const int step = SkSign32(endIndex - startIndex);
-    const int end = nextExactSpan(startIndex, step);
-    SkASSERT(end >= 0);
-    SkOpSpan* endSpan = &fTs[end];
-    SkOpSegment* other;
-    if (isSimple(end)) {
+    int step = SkSign32(endIndex - startIndex);
+    *nextStart = startIndex;
+    SkOpSegment* other = isSimple(nextStart, &step);
+    if (other) 
+    {
     // mark the smaller of startIndex, endIndex done, and all adjacent
     // spans with the same T value (but not 'other' spans)
 #if DEBUG_WINDING
@@ -2251,8 +2733,6 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
             return NULL;
         }
         markDoneBinary(min);
-        other = endSpan->fOther;
-        *nextStart = endSpan->fOtherIndex;
         double startT = other->fTs[*nextStart].fT;
         *nextEnd = *nextStart;
         do {
@@ -2265,6 +2745,8 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
         }
         return other;
     }
+    const int end = nextExactSpan(startIndex, step);
+    SkASSERT(end >= 0);
     SkASSERT(startIndex - endIndex != 0);
     SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
     // more than one viable candidate -- measure angles to find best
@@ -2325,7 +2807,7 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
             continue;
         }
         if (!activeAngle) {
-            nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
+            (void) nextSegment->markAndChaseDoneBinary(nextAngle->start(), nextAngle->end());
         }
         SkOpSpan* last = nextAngle->lastMarked();
         if (last) {
@@ -2365,12 +2847,11 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next
     SkASSERT(startIndex != endIndex);
     SkDEBUGCODE(const int count = fTs.count());
     SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
-    const int step = SkSign32(endIndex - startIndex);
-    const int end = nextExactSpan(startIndex, step);
-    SkASSERT(end >= 0);
-    SkOpSpan* endSpan = &fTs[end];
-    SkOpSegment* other;
-    if (isSimple(end)) {
+    int step = SkSign32(endIndex - startIndex);
+    *nextStart = startIndex;
+    SkOpSegment* other = isSimple(nextStart, &step);
+    if (other) 
+    {
     // mark the smaller of startIndex, endIndex done, and all adjacent
     // spans with the same T value (but not 'other' spans)
 #if DEBUG_WINDING
@@ -2381,8 +2862,6 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next
             return NULL;
         }
         markDoneUnary(min);
-        other = endSpan->fOther;
-        *nextStart = endSpan->fOtherIndex;
         double startT = other->fTs[*nextStart].fT;
         *nextEnd = *nextStart;
         do {
@@ -2395,6 +2874,8 @@ SkOpSegment* SkOpSegment::findNextWinding(SkTDArray<SkOpSpan*>* chase, int* next
         }
         return other;
     }
+    const int end = nextExactSpan(startIndex, step);
+    SkASSERT(end >= 0);
     SkASSERT(startIndex - endIndex != 0);
     SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
     // more than one viable candidate -- measure angles to find best
@@ -2474,13 +2955,12 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort
     SkDEBUGCODE(int count = fTs.count());
     SkASSERT(startIndex < endIndex ? startIndex < count - 1 : startIndex > 0);
     int step = SkSign32(endIndex - startIndex);
-    int end = nextExactSpan(startIndex, step);
-    SkASSERT(end >= 0);
-    SkOpSpan* endSpan = &fTs[end];
-    SkOpSegment* other;
 // Detect cases where all the ends canceled out (e.g.,
-// there is no angle) and therefore there's only one valid connection
-    if (isSimple(end) || !spanToAngle(end, startIndex)) {
+// there is no angle) and therefore there's only one valid connection 
+    *nextStart = startIndex;
+    SkOpSegment* other = isSimple(nextStart, &step);
+    if (other)
+    {
 #if DEBUG_WINDING
         SkDebugf("%s simple\n", __FUNCTION__);
 #endif
@@ -2489,8 +2969,6 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort
             return NULL;
         }
         markDone(min, 1);
-        other = endSpan->fOther;
-        *nextStart = endSpan->fOtherIndex;
         double startT = other->fTs[*nextStart].fT;
         // FIXME: I don't know why the logic here is difference from the winding case
         SkDEBUGCODE(bool firstLoop = true;)
@@ -2517,6 +2995,8 @@ SkOpSegment* SkOpSegment::findNextXor(int* nextStart, int* nextEnd, bool* unsort
     SkASSERT(startIndex - endIndex != 0);
     SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
     // parallel block above with presorted version
+    int end = nextExactSpan(startIndex, step);
+    SkASSERT(end >= 0);
     SkOpAngle* angle = spanToAngle(end, startIndex);
     SkASSERT(angle);
 #if DEBUG_SORT
@@ -2562,24 +3042,12 @@ int SkOpSegment::findStartSpan(int startIndex) const {
     const SkOpSpan* span = &fTs[index];
     const SkPoint& firstPt = span->fPt;
     double firstT = span->fT;
+    const SkOpSpan* prior;
     do {
-        if (index > 0) {
-            if (span->fT != firstT) {
-                break;
-            }
-            if (!SkDPoint::ApproximatelyEqual(firstPt, fTs[index].fPt)) {
-                return -1;
-            }
-        }
-        ++index;
-        if (span->fTiny) {
-            if (!SkDPoint::ApproximatelyEqual(firstPt, fTs[index].fPt)) {
-                return -1;
-            }
-            firstT = fTs[index++].fT;
-        }
-        span = &fTs[index];
-    } while (true);
+        prior = span;
+        span = &fTs[++index];
+    } while (SkDPoint::ApproximatelyEqual(span->fPt, firstPt)
+            && (span->fT == firstT || prior->fTiny));
     return index;
 }
 
@@ -2595,8 +3063,26 @@ int SkOpSegment::findExactT(double t, const SkOpSegment* match) const {
     return -1;
 }
 
+int SkOpSegment::findOtherT(double t, const SkOpSegment* match) const {
+    int count = this->count();
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = fTs[index];
+        if (span.fOtherT == t && span.fOther == match) {
+            return index;
+        }
+    }
+    return -1;
+}
+
 int SkOpSegment::findT(double t, const SkPoint& pt, const SkOpSegment* match) const {
     int count = this->count();
+    // prefer exact matches over approximate matches
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = fTs[index];
+        if (span.fT == t && span.fOther == match) {
+            return index;
+        }
+    }
     for (int index = 0; index < count; ++index) {
         const SkOpSpan& span = fTs[index];
         if (approximately_equal_orderable(span.fT, t) && span.fOther == match) {
@@ -2647,10 +3133,11 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort
     SkASSERT(firstT - end != 0);
     SkOpAngle* markAngle = spanToAngle(firstT, end);
     if (!markAngle) {
-        markAngle = addSingletonAngles(firstT, step);
+        markAngle = addSingletonAngles(step);
     }
     markAngle->markStops();
-    const SkOpAngle* baseAngle = markAngle->findFirst();
+    const SkOpAngle* baseAngle = markAngle->next() == markAngle && !isVertical() ? markAngle
+            : markAngle->findFirst();
     if (!baseAngle) {
         return NULL;  // nothing to do
     }
@@ -2697,9 +3184,8 @@ SkOpSegment* SkOpSegment::findTop(int* tIndexPtr, int* endIndexPtr, bool* unsort
     if (leftSegment->verb() >= SkPath::kQuad_Verb) {
         const int tIndex = *tIndexPtr;
         const int endIndex = *endIndexPtr;
-        if (!leftSegment->clockwise(tIndex, endIndex)) {
-            bool swap = !leftSegment->monotonicInY(tIndex, endIndex)
-                    && !leftSegment->serpentine(tIndex, endIndex);
+        bool swap;
+        if (!leftSegment->clockwise(tIndex, endIndex, &swap)) {
     #if DEBUG_SWAP_TOP
             SkDebugf("%s swap=%d inflections=%d serpentine=%d controlledbyends=%d monotonic=%d\n",
                     __FUNCTION__,
@@ -2754,13 +3240,26 @@ void SkOpSegment::fixOtherTIndex() {
     }
 }
 
+bool SkOpSegment::inCoincidentSpan(double t, const SkOpSegment* other) const {
+    int foundEnds = 0;
+    int count = this->count();
+    for (int index = 0; index < count; ++index) {
+        const SkOpSpan& span = this->span(index);
+        if (span.fCoincident) {
+            foundEnds |= (span.fOther == other) << ((t > span.fT) + (t >= span.fT));
+        }
+    }
+    SkASSERT(foundEnds != 7);
+    return foundEnds == 0x3 || foundEnds == 0x5 || foundEnds == 0x6;  // two bits set
+}
+
 void SkOpSegment::init(const SkPoint pts[], SkPath::Verb verb, bool operand, bool evenOdd) {
     fDoneSpans = 0;
     fOperand = operand;
     fXor = evenOdd;
     fPts = pts;
     fVerb = verb;
-    fLoop = fSmall = fTiny = false;
+    fLoop = fMultiples = fSmall = fTiny = false;
 }
 
 void SkOpSegment::initWinding(int start, int end, SkOpAngle::IncludeType angleIncludeType) {
@@ -2793,16 +3292,13 @@ void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkSc
     SkASSERT(dx);
     int windVal = windValue(SkMin32(start, end));
 #if DEBUG_WINDING_AT_T
-    SkDebugf("%s oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, winding,
+    SkDebugf("%s id=%d oldWinding=%d hitDx=%c dx=%c windVal=%d", __FUNCTION__, debugID(), winding,
             hitDx ? hitDx > 0 ? '+' : '-' : '0', dx > 0 ? '+' : '-', windVal);
 #endif
     int sideWind = winding + (dx < 0 ? windVal : -windVal);
     if (abs(winding) < abs(sideWind)) {
         winding = sideWind;
     }
-#if DEBUG_WINDING_AT_T
-    SkDebugf(" winding=%d\n", winding);
-#endif
     SkDEBUGCODE(int oppLocal = oppSign(start, end));
     SkASSERT(hitOppDx || !oppWind || !oppLocal);
     int oppWindVal = oppValue(SkMin32(start, end));
@@ -2814,6 +3310,9 @@ void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkSc
             oppWind = oppSideWind;
         }
     }
+#if DEBUG_WINDING_AT_T
+    SkDebugf(" winding=%d oppWind=%d\n", winding, oppWind);
+#endif
     (void) markAndChaseWinding(start, end, winding, oppWind);
     // OPTIMIZATION: the reverse mark and chase could skip the first marking
     (void) markAndChaseWinding(end, start, winding, oppWind);
@@ -2824,12 +3323,12 @@ bool SkOpSegment::inLoop(const SkOpAngle* baseAngle, int spanCount, int* indexPt
         return false;
     }
     int index = *indexPtr;
-    int froIndex = fTs[index].fFromAngleIndex;
-    int toIndex = fTs[index].fToAngleIndex;
+    SkOpAngle* from = fTs[index].fFromAngle;
+    SkOpAngle* to = fTs[index].fToAngle;
     while (++index < spanCount) {
-        int nextFro = fTs[index].fFromAngleIndex;
-        int nextTo = fTs[index].fToAngleIndex;
-        if (froIndex != nextFro || toIndex != nextTo) {
+        SkOpAngle* nextFrom = fTs[index].fFromAngle;
+        SkOpAngle* nextTo = fTs[index].fToAngle;
+        if (from != nextFrom || to != nextTo) {
             break;
         }
     }
@@ -2850,26 +3349,9 @@ bool SkOpSegment::isMissing(double startT, const SkPoint& pt) const {
     return true;
 }
 
-bool SkOpSegment::isSimple(int end) const {
-#if 1
-    int count = fTs.count();
-    if (count == 2) {
-        return true;
-    }
-    double t = fTs[end].fT;
-    if (approximately_less_than_zero(t)) {
-        return !approximately_less_than_zero(fTs[1].fT);
-    }
-    if (approximately_greater_than_one(t)) {
-        return !approximately_greater_than_one(fTs[count - 2].fT);
-    }
-    return false;
-#else
-    // OPTIMIZE: code could be rejiggered to use this simpler test. To make this work, a little
-    // more logic is required to ignore the dangling canceled out spans
-    const SkOpSpan& span = fTs[end];
-    return span.fFromAngleIndex < 0 && span.fToAngleIndex < 0;
-#endif
+
+SkOpSegment* SkOpSegment::isSimple(int* end, int* step) {
+    return nextChase(end, step, NULL, NULL);
 }
 
 bool SkOpSegment::isTiny(const SkOpAngle* angle) const {
@@ -2912,7 +3394,7 @@ bool SkOpSegment::joinCoincidence(SkOpSegment* other, double otherT, const SkPoi
             if (cancel) {
                 match->addTCancel(startPt, endPt, other);
             } else {
-                match->addTCoincident(startPt, endPt, endT, other);
+                SkAssertResult(match->addTCoincident(startPt, endPt, endT, other));
             }
             return true;
         }
@@ -2928,11 +3410,12 @@ SkOpSpan* SkOpSegment::markAndChaseDoneBinary(int index, int endIndex) {
     int step = SkSign32(endIndex - index);
     int min = SkMin32(index, endIndex);
     markDoneBinary(min);
-    SkOpSpan* last;
+    SkOpSpan* last = NULL;
     SkOpSegment* other = this;
-    while ((other = other->nextChase(&index, step, &min, &last))) {
+    while ((other = other->nextChase(&index, &step, &min, &last))) {
         if (other->done()) {
-            return NULL;
+            SkASSERT(!last);
+            break;
         }
         other->markDoneBinary(min);
     }
@@ -2943,11 +3426,12 @@ SkOpSpan* SkOpSegment::markAndChaseDoneUnary(int index, int endIndex) {
     int step = SkSign32(endIndex - index);
     int min = SkMin32(index, endIndex);
     markDoneUnary(min);
-    SkOpSpan* last;
+    SkOpSpan* last = NULL;
     SkOpSegment* other = this;
-    while ((other = other->nextChase(&index, step, &min, &last))) {
+    while ((other = other->nextChase(&index, &step, &min, &last))) {
         if (other->done()) {
-            return NULL;
+            SkASSERT(!last);
+            break;
         }
         other->markDoneUnary(min);
     }
@@ -2960,12 +3444,13 @@ SkOpSpan* SkOpSegment::markAndChaseWinding(const SkOpAngle* angle, int winding)
     int step = SkSign32(endIndex - index);
     int min = SkMin32(index, endIndex);
     markWinding(min, winding);
-    SkOpSpan* last;
+    SkOpSpan* last = NULL;
     SkOpSegment* other = this;
-    while ((other = other->nextChase(&index, step, &min, &last))) {
+    while ((other = other->nextChase(&index, &step, &min, &last))) {
         if (other->fTs[min].fWindSum != SK_MinS32) {
-            SkASSERT(other->fTs[min].fWindSum == winding);
-            return NULL;
+//            SkASSERT(other->fTs[min].fWindSum == winding);
+            SkASSERT(!last);
+            break;
         }
         other->markWinding(min, winding);
     }
@@ -2976,12 +3461,13 @@ SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding)
     int min = SkMin32(index, endIndex);
     int step = SkSign32(endIndex - index);
     markWinding(min, winding);
-    SkOpSpan* last;
+    SkOpSpan* last = NULL;
     SkOpSegment* other = this;
-    while ((other = other->nextChase(&index, step, &min, &last))) {
+    while ((other = other->nextChase(&index, &step, &min, &last))) {
         if (other->fTs[min].fWindSum != SK_MinS32) {
             SkASSERT(other->fTs[min].fWindSum == winding || other->fTs[min].fLoop);
-            return NULL;
+            SkASSERT(!last);
+            break;
         }
         other->markWinding(min, winding);
     }
@@ -2992,14 +3478,32 @@ SkOpSpan* SkOpSegment::markAndChaseWinding(int index, int endIndex, int winding,
     int min = SkMin32(index, endIndex);
     int step = SkSign32(endIndex - index);
     markWinding(min, winding, oppWinding);
-    SkOpSpan* last;
+    SkOpSpan* last = NULL;
     SkOpSegment* other = this;
-    while ((other = other->nextChase(&index, step, &min, &last))) {
+    while ((other = other->nextChase(&index, &step, &min, &last))) {
         if (other->fTs[min].fWindSum != SK_MinS32) {
-            SkASSERT(other->fTs[min].fWindSum == winding || other->fTs[min].fLoop);
-            return NULL;
+#ifdef SK_DEBUG
+            if (!other->fTs[min].fLoop) {
+                if (fOperand == other->fOperand) {
+// FIXME: this is probably a bug -- rects4 asserts here
+//                    SkASSERT(other->fTs[min].fWindSum == winding);
+// FIXME: this is probably a bug -- rects3 asserts here
+//                    SkASSERT(other->fTs[min].fOppSum == oppWinding);
+                } else {
+                    SkASSERT(other->fTs[min].fWindSum == oppWinding);
+// FIXME: this is probably a bug -- skpwww_joomla_org_23 asserts here
+//                    SkASSERT(other->fTs[min].fOppSum == winding);
+                }
+            }
+            SkASSERT(!last);
+#endif
+            break;
+        }
+        if (fOperand == other->fOperand) {
+            other->markWinding(min, winding, oppWinding);
+        } else {
+            other->markWinding(min, oppWinding, winding);
         }
-        other->markWinding(min, winding, oppWinding);
     }
     return last;
 }
@@ -3163,13 +3667,45 @@ SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int windi
 }
 
 // from http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
-bool SkOpSegment::clockwise(int tStart, int tEnd) const {
+bool SkOpSegment::clockwise(int tStart, int tEnd, bool* swap) const {
     SkASSERT(fVerb != SkPath::kLine_Verb);
     SkPoint edge[4];
     subDivide(tStart, tEnd, edge);
     int points = SkPathOpsVerbToPoints(fVerb);
     double sum = (edge[0].fX - edge[points].fX) * (edge[0].fY + edge[points].fY);
+    bool sumSet = false;
     if (fVerb == SkPath::kCubic_Verb) {
+        SkDCubic cubic;
+        cubic.set(edge);
+        double inflectionTs[2];
+        int inflections = cubic.findInflections(inflectionTs);
+        // FIXME: this fixes cubicOp114 and breaks cubicOp58d
+        // the trouble is that cubics with inflections confuse whether the curve breaks towards
+        // or away, which in turn is used to determine if it is on the far right or left.
+        // Probably a totally different approach is in order. At one time I tried to project a
+        // horizontal ray to determine winding, but was confused by how to map the vertically
+        // oriented winding computation over. 
+        if (0 && inflections) {
+            double tLo = this->span(tStart).fT;
+            double tHi = this->span(tEnd).fT;
+            double tLoStart = tLo;
+            for (int index = 0; index < inflections; ++index) {
+                if (between(tLo, inflectionTs[index], tHi)) {
+                    tLo = inflectionTs[index];
+                }
+            }
+            if (tLo != tLoStart && tLo != tHi) {
+                SkDPoint sub[2];
+                sub[0] = cubic.ptAtT(tLo);
+                sub[1].set(edge[3]);
+                SkDPoint ctrl[2];
+                SkDCubic::SubDivide(fPts, sub[0], sub[1], tLo, tHi, ctrl);
+                edge[0] = sub[0].asSkPoint();
+                edge[1] = ctrl[0].asSkPoint();
+                edge[2] = ctrl[1].asSkPoint();
+                sum = (edge[0].fX - edge[3].fX) * (edge[0].fY + edge[3].fY);
+            }
+        }
         SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY);
         if (edge[1].fY < lesser && edge[2].fY < lesser) {
             SkDLine tangent1 = {{ {edge[0].fX, edge[0].fY}, {edge[1].fX, edge[1].fY} }};
@@ -3178,12 +3714,23 @@ bool SkOpSegment::clockwise(int tStart, int tEnd) const {
                 SkPoint topPt = cubic_top(fPts, fTs[tStart].fT, fTs[tEnd].fT);
                 sum += (topPt.fX - edge[0].fX) * (topPt.fY + edge[0].fY);
                 sum += (edge[3].fX - topPt.fX) * (edge[3].fY + topPt.fY);
-                return sum <= 0;
+                sumSet = true;
             }
         }
     }
-    for (int idx = 0; idx < points; ++idx){
-        sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
+    if (!sumSet) {
+        for (int idx = 0; idx < points; ++idx){
+            sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
+        }
+    }
+    if (fVerb == SkPath::kCubic_Verb) {
+        SkDCubic cubic;
+        cubic.set(edge);
+         *swap = sum > 0 && !cubic.monotonicInY() && !cubic.serpentine();
+    } else {
+        SkDQuad quad;
+        quad.set(edge);
+        *swap = sum > 0 && !quad.monotonicInY();
     }
     return sum <= 0;
 }
@@ -3316,15 +3863,6 @@ void SkOpSegment::matchWindingValue(int tIndex, double t, bool borrowWind) {
     }
 }
 
-// return span if when chasing, two or more radiating spans are not done
-// OPTIMIZATION: ? multiple spans is detected when there is only one valid
-// candidate and the remaining spans have windValue == 0 (canceled by
-// coincidence). The coincident edges could either be removed altogether,
-// or this code could be more complicated in detecting this case. Worth it?
-bool SkOpSegment::multipleSpans(int end) const {
-    return end > 0 && end < fTs.count() - 1;
-}
-
 bool SkOpSegment::nextCandidate(int* start, int* end) const {
     while (fTs[*end].fDone) {
         if (fTs[*end].fT == 1) {
@@ -3337,27 +3875,67 @@ bool SkOpSegment::nextCandidate(int* start, int* end) const {
     return true;
 }
 
-SkOpSegment* SkOpSegment::nextChase(int* index, const int step, int* min, SkOpSpan** last) {
-    int end = nextExactSpan(*index, step);
+static SkOpSegment* set_last(SkOpSpan** last, SkOpSpan* endSpan) {
+    if (last && !endSpan->fSmall) {
+        *last = endSpan;
+    }
+    return NULL;
+}
+
+SkOpSegment* SkOpSegment::nextChase(int* indexPtr, int* stepPtr, int* minPtr, SkOpSpan** last) {
+    int origIndex = *indexPtr;
+    int step = *stepPtr;
+    int end = nextExactSpan(origIndex, step);
     SkASSERT(end >= 0);
-    if (fTs[end].fSmall) {
-        *last = NULL;
-        return NULL;
+    SkOpSpan& endSpan = fTs[end];
+    SkOpAngle* angle = step > 0 ? endSpan.fFromAngle : endSpan.fToAngle;
+    int foundIndex;
+    int otherEnd;
+    SkOpSegment* other;
+    if (angle == NULL) {
+        if (endSpan.fT != 0 && endSpan.fT != 1) {
+            return NULL;
+        }
+        other = endSpan.fOther;
+        foundIndex = endSpan.fOtherIndex;
+        otherEnd = other->nextExactSpan(foundIndex, step);
+    } else {
+        int loopCount = angle->loopCount();
+        if (loopCount > 2) {
+            return set_last(last, &endSpan);
+        }
+        const SkOpAngle* next = angle->next();
+        if (angle->sign() != next->sign()) {
+#if DEBUG_WINDING
+            SkDebugf("%s mismatched signs\n", __FUNCTION__);
+#endif
+        //    return set_last(last, &endSpan);
+        }
+        other = next->segment();
+        foundIndex = end = next->start();
+        otherEnd = next->end();
     }
-    if (multipleSpans(end)) {
-        *last = &fTs[end];
-        return NULL;
+    int foundStep = foundIndex < otherEnd ? 1 : -1;
+    if (*stepPtr != foundStep) {
+        return set_last(last, &endSpan);
     }
-    const SkOpSpan& endSpan = fTs[end];
-    SkOpSegment* other = endSpan.fOther;
-    *index = endSpan.fOtherIndex;
-    SkASSERT(*index >= 0);
-    int otherEnd = other->nextExactSpan(*index, step);
+    SkASSERT(*indexPtr >= 0);
     SkASSERT(otherEnd >= 0);
-    *min = SkMin32(*index, otherEnd);
-    if (other->fTs[*min].fSmall) {
-        *last = NULL;
-        return NULL;
+#if 1
+    int origMin = origIndex + (step < 0 ? step : 0);
+    const SkOpSpan& orig = this->span(origMin);
+#endif
+    int foundMin = SkMin32(foundIndex, otherEnd);
+#if 1
+    const SkOpSpan& found = other->span(foundMin);
+    if (found.fWindValue != orig.fWindValue || found.fOppValue != orig.fOppValue) {
+          return set_last(last, &endSpan);
+    }
+#endif
+    *indexPtr = foundIndex;
+    *stepPtr = foundStep;
+    if (minPtr) {
+        *minPtr = foundMin;
     }
     return other;
 }
@@ -3414,6 +3992,45 @@ int SkOpSegment::nextExactSpan(int from, int step) const {
     return -1;
 }
 
+void SkOpSegment::pinT(const SkPoint& pt, double* t) {
+    if (pt == fPts[0]) {
+        *t = 0;
+    }
+    int count = SkPathOpsVerbToPoints(fVerb);
+    if (pt == fPts[count]) {
+        *t = 1;
+    }
+}
+
+bool SkOpSegment::reversePoints(const SkPoint& p1, const SkPoint& p2) const {
+    SkASSERT(p1 != p2);
+    int spanCount = count();
+    int p1IndexMin = -1;
+    int p2IndexMax = spanCount;
+    for (int index = 0; index < spanCount; ++index) {
+        const SkOpSpan& span = fTs[index];
+        if (span.fPt == p1) {
+            if (p1IndexMin < 0) {
+                p1IndexMin = index;
+            }
+        } else if (span.fPt == p2) {
+            p2IndexMax = index;
+        }
+    }
+    return p1IndexMin > p2IndexMax;
+}
+
+void SkOpSegment::setCoincidentRange(const SkPoint& startPt, const SkPoint& endPt, 
+        SkOpSegment* other) {
+    int count = this->count();
+    for (int index = 0; index < count; ++index) {
+        SkOpSpan &span = fTs[index];
+        if ((startPt == span.fPt || endPt == span.fPt) && other == span.fOther) {
+            span.fCoincident = true;
+        }
+    }
+}
+
 void SkOpSegment::setUpWindings(int index, int endIndex, int* sumMiWinding, int* sumSuWinding,
         int* maxWinding, int* sumWinding, int* oppMaxWinding, int* oppSumWinding) {
     int deltaSum = spanSign(index, endIndex);
@@ -3452,15 +4069,15 @@ void SkOpSegment::sortAngles() {
     }
     int index = 0;
     do {
-        int fromIndex = fTs[index].fFromAngleIndex;
-        int toIndex = fTs[index].fToAngleIndex;
-        if (fromIndex < 0 && toIndex < 0) {
+        SkOpAngle* fromAngle = fTs[index].fFromAngle;
+        SkOpAngle* toAngle = fTs[index].fToAngle;
+        if (!fromAngle && !toAngle) {
             index += 1;
             continue;
         }
         SkOpAngle* baseAngle = NULL;
-        if (fromIndex >= 0) {
-            baseAngle = &this->angle(fromIndex);
+        if (fromAngle) {
+            baseAngle = fromAngle;
             if (inLoop(baseAngle, spanCount, &index)) {
                 continue;
             }
@@ -3468,8 +4085,7 @@ void SkOpSegment::sortAngles() {
 #if DEBUG_ANGLE
         bool wroteAfterHeader = false;
 #endif
-        if (toIndex >= 0) {
-            SkOpAngle* toAngle = &this->angle(toIndex);
+        if (toAngle) {
             if (!baseAngle) {
                 baseAngle = toAngle;
                 if (inLoop(baseAngle, spanCount, &index)) {
@@ -3486,14 +4102,14 @@ void SkOpSegment::sortAngles() {
                 baseAngle->insert(toAngle);
             }
         }
-        int nextFrom, nextTo;
+        SkOpAngle* nextFrom, * nextTo;
         int firstIndex = index;
         do {
             SkOpSpan& span = fTs[index];
             SkOpSegment* other = span.fOther;
             SkOpSpan& oSpan = other->fTs[span.fOtherIndex];
-            int otherAngleIndex = oSpan.fFromAngleIndex;
-            if (otherAngleIndex >= 0) {
+            SkOpAngle* oAngle = oSpan.fFromAngle;
+            if (oAngle) {
 #if DEBUG_ANGLE
                 if (!wroteAfterHeader) {
                     SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
@@ -3501,13 +4117,12 @@ void SkOpSegment::sortAngles() {
                     wroteAfterHeader = true;
                 }
 #endif
-                SkOpAngle* oAngle = &other->angle(otherAngleIndex);
                 if (!oAngle->loopContains(*baseAngle)) {
                     baseAngle->insert(oAngle);
                 }
             }
-            otherAngleIndex = oSpan.fToAngleIndex;
-            if (otherAngleIndex >= 0) {
+            oAngle = oSpan.fToAngle;
+            if (oAngle) {
 #if DEBUG_ANGLE
                 if (!wroteAfterHeader) {
                     SkDebugf("%s [%d] tStart=%1.9g [%d]\n", __FUNCTION__, debugID(), fTs[index].fT,
@@ -3515,7 +4130,6 @@ void SkOpSegment::sortAngles() {
                     wroteAfterHeader = true;
                 }
 #endif
-                SkOpAngle* oAngle = &other->angle(otherAngleIndex);
                 if (!oAngle->loopContains(*baseAngle)) {
                     baseAngle->insert(oAngle);
                 }
@@ -3523,20 +4137,20 @@ void SkOpSegment::sortAngles() {
             if (++index == spanCount) {
                 break;
             }
-            nextFrom = fTs[index].fFromAngleIndex;
-            nextTo = fTs[index].fToAngleIndex;
-        } while (fromIndex == nextFrom && toIndex == nextTo);
+            nextFrom = fTs[index].fFromAngle;
+            nextTo = fTs[index].fToAngle;
+        } while (fromAngle == nextFrom && toAngle == nextTo);
         if (baseAngle && baseAngle->loopCount() == 1) {
             index = firstIndex;
             do {
                 SkOpSpan& span = fTs[index];
-                span.fFromAngleIndex = span.fToAngleIndex = -1;
+                span.fFromAngle = span.fToAngle = NULL;
                 if (++index == spanCount) {
                     break;
                 }
-                nextFrom = fTs[index].fFromAngleIndex;
-                nextTo = fTs[index].fToAngleIndex;
-            } while (fromIndex == nextFrom && toIndex == nextTo);
+                nextFrom = fTs[index].fFromAngle;
+                nextTo = fTs[index].fToAngle;
+            } while (fromAngle == nextFrom && toAngle == nextTo);
             baseAngle = NULL;
         }
 #if DEBUG_SORT
@@ -3749,7 +4363,8 @@ int SkOpSegment::windingAtT(double tHit, int tIndex, bool crossOpp, SkScalar* dx
     SkASSERT(winding != SK_MinS32);
     int windVal = crossOpp ? oppValue(tIndex) : windValue(tIndex);
 #if DEBUG_WINDING_AT_T
-    SkDebugf("%s oldWinding=%d windValue=%d", __FUNCTION__, winding, windVal);
+    SkDebugf("%s id=%d opp=%d tHit=%1.9g t=%1.9g oldWinding=%d windValue=%d", __FUNCTION__,
+            debugID(), crossOpp, tHit, t(tIndex), winding, windVal);
 #endif
     // see if a + change in T results in a +/- change in X (compute x'(T))
     *dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;