work in progress

git-svn-id: http://skia.googlecode.com/svn/trunk@3276 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/experimental/Intersection/EdgeWalker.cpp b/experimental/Intersection/EdgeWalker.cpp
index 5178bb2..fc53f63 100644
--- a/experimental/Intersection/EdgeWalker.cpp
+++ b/experimental/Intersection/EdgeWalker.cpp
@@ -14,9 +14,9 @@
 #include "SkTDArray.h"
 #include "TSearch.h"
 
-static bool fShowDebugf = true; // FIXME: remove once debugging is complete
-
-const int kOpenerVerbBitShift = 3; // leaves 3 bits for SkPath::Verb
+static bool gShowDebugf = true; // FIXME: remove once debugging is complete
+static bool gShowPath = false;
+static bool gDebugLessThan = false;
 
 static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
         double aRange[2], double bRange[2]) {
@@ -30,6 +30,13 @@
     return horizontalIntersect(aLine, y, aRange);
 }
 
+static SkScalar LineXAtT(const SkPoint a[2], double t) {
+    _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
+    double x;
+    xy_at_t(aLine, t, x, *(double*) 0);
+    return SkDoubleToScalar(x);
+}
+
 static SkScalar LineYAtT(const SkPoint a[2], double t) {
     _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
     double y;
@@ -119,17 +126,15 @@
 
 struct OutEdge {
     bool operator<(const OutEdge& rh) const {
-        const SkPoint& first = fPts.begin()[0];
-        const SkPoint& rhFirst = rh.fPts.begin()[0];
+        const SkPoint& first = fPts[0];
+        const SkPoint& rhFirst = rh.fPts[0];
         return first.fY == rhFirst.fY
                 ? first.fX < rhFirst.fX
                 : first.fY < rhFirst.fY;
     }
     
-    SkTDArray<SkPoint> fPts;
-    // contains the SkPath verb, plus 1<<kOpenerVerbBitShift if edge opens span
-    SkTDArray<uint8_t> fVerbs; // FIXME: not read from everywhere
-    bool fOpener;
+    SkPoint fPts[4];
+    uint8_t fVerb; // FIXME: not read from everywhere
 };
 
 class OutEdgeBuilder {
@@ -138,34 +143,11 @@
         : fFill(fill) {
         }
 
-    void addLine(const SkPoint line[2], bool opener) {
-        size_t count = fEdges.count();
-        for (size_t index = 0; index < count; ++index) {
-            OutEdge& edge = fEdges[index];
-            if (opener != edge.fOpener) {
-                continue;
-            }
-            SkTDArray<SkPoint>& pts = edge.fPts;
-            SkPoint& last = pts.top();
-            if (last == line[0]) {
-                SkTDArray<uint8_t>& verbs = edge.fVerbs;
-                uint8_t lastVerb = verbs.top();
-                if (lastVerb == SkPath::kLine_Verb
-                        && extendLine(&last - 1, line[1])) {
-                    last = line[1];
-                } else {
-                    *pts.append() = line[1];
-                    *verbs.append() = SkPath::kLine_Verb;
-                }
-                return;
-            }
-        }
+    void addLine(const SkPoint line[2]) {
         OutEdge& newEdge = fEdges.push_back();
-        *newEdge.fPts.append() = line[0];
-        *newEdge.fVerbs.append() = SkPath::kMove_Verb;
-        *newEdge.fPts.append() = line[1];
-        *newEdge.fVerbs.append() = SkPath::kLine_Verb;
-        newEdge.fOpener = opener;
+        newEdge.fPts[0] = line[0];
+        newEdge.fPts[1] = line[1];
+        newEdge.fVerb = SkPath::kLine_Verb;
     }
 
     void assemble(SkPath& simple) {
@@ -182,46 +164,70 @@
             if (listIndex >= listCount) {
                 break;
             }
-            SkPoint firstPt;
+            SkPoint firstPt, lastLine[2];
             bool doMove = true;
+            bool closed = false;
             int edgeIndex;
             do {
-                SkTDArray<SkPoint>& ptArray = fEdges[listIndex].fPts;
-                SkASSERT(ptArray.count() > 0);
-                SkPoint* pts, * end;
+                SkPoint* ptArray = fEdges[listIndex].fPts;
+                uint8_t verb = fEdges[listIndex].fVerb;
+                SkPoint* start, * end;
                 if (advance < 0) {
-                    pts = ptArray.end() - 1;
-                    end = ptArray.begin();
+                    start = &ptArray[verb];
+                    end = &ptArray[0];
                 } else {
-                    pts = ptArray.begin();
-                    end = ptArray.end() - 1;
+                    start = &ptArray[0];
+                    end = &ptArray[verb];
                 }
-                if (doMove) {
-                    firstPt = pts[0];
-                    simple.moveTo(pts[0].fX, pts[0].fY);
-                    if (fShowDebugf) {
-                        SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__, pts[0].fX, pts[0].fY);
-                    }
-                    doMove = false;
-                } else {
-                    simple.lineTo(pts[0].fX, pts[0].fY);
-                    if (fShowDebugf) {
-                        SkDebugf("%s 1 lineTo (%g,%g)\n", __FUNCTION__, pts[0].fX, pts[0].fY);
-                    }
-                    if (firstPt == pts[0]) {
-                        simple.close();
-                        if (fShowDebugf) {
-                            SkDebugf("%s close\n", __FUNCTION__);
+                switch (verb) {
+                    case SkPath::kLine_Verb:
+                        bool gap;
+                        if (doMove) {
+                            firstPt = *start;
+                            simple.moveTo(start->fX, start->fY);
+                            if (gShowDebugf) {
+                                SkDebugf("%s moveTo (%g,%g)\n", __FUNCTION__,
+                                        start->fX, start->fY);
+                            }
+                            lastLine[0] = *start;
+                            lastLine[1] = *end;
+                            doMove = false;
+                            closed = false;
+                            break;
+                        }
+                        gap = lastLine[1] != *start;
+                        if (gap) {
+                            SkASSERT(fFill && lastLine[1].fY == start->fY);
+                            simple.lineTo(lastLine[1].fX, lastLine[1].fY);
+                            if (gShowDebugf) {
+                                SkDebugf("%s lineTo x (%g,%g)\n", __FUNCTION__,
+                                        lastLine[1].fX, lastLine[1].fY);
+                            }
+                        }
+                        if (gap || !extendLine(lastLine, *end)) {
+                            SkASSERT(lastLine[1] == *start ||
+                                    (fFill && lastLine[1].fY == start->fY));
+                            simple.lineTo(start->fX, start->fY);
+                            if (gShowDebugf) {
+                                SkDebugf("%s lineTo (%g,%g)\n", __FUNCTION__,
+                                        start->fX, start->fY);
+                            }
+                            lastLine[0] = *start;
+                        }
+                        lastLine[1] = *end;
+                        if (firstPt == *end) {
+                            simple.lineTo(end->fX, end->fY);
+                            simple.close();
+                            if (gShowDebugf) {
+                                SkDebugf("%s close 1 (%g, %g)\n", __FUNCTION__,
+                                        end->fX, end->fY);
+                            }
+                            closed = true;
                         }
                         break;
-                    }
-                }
-                while (pts != end) {
-                    pts += advance;
-                    simple.lineTo(pts->fX, pts->fY);
-                    if (fShowDebugf) {
-                        SkDebugf("%s 2 lineTo (%g,%g)\n", __FUNCTION__, pts[0].fX, pts[0].fY);
-                    }
+                    default:
+                        // FIXME: add other curve types
+                        ;
                 }
                 if (advance < 0) {
                     edgeIndex = fTops[listIndex];
@@ -230,6 +236,15 @@
                     edgeIndex = fBottoms[listIndex];
                     fBottoms[listIndex] = 0;
                 }
+                if (!edgeIndex) {
+                    simple.lineTo(firstPt.fX, firstPt.fY);
+                    simple.close();
+                    if (gShowDebugf) {
+                        SkDebugf("%s close 2 (%g,%g)\n", __FUNCTION__,
+                            firstPt.fX, firstPt.fY);
+                    }
+                    break;
+                }
                 listIndex = abs(edgeIndex) - 1;
                 if (edgeIndex < 0) {
                     fTops[listIndex] = 0;
@@ -240,21 +255,63 @@
                 if (advance > 0 ^ edgeIndex < 0) {
                     advance = -advance;
                 }
-            } while (edgeIndex);
+            } while (edgeIndex && !closed);
         } while (true);
     }
     
-    static bool lessThan(SkTArray<OutEdge>& edges, const int* onePtr,
-            const int* twoPtr) {
-        int one = *onePtr;
-        const OutEdge& oneEdge = edges[(one < 0 ? -one : one) - 1];
-        const SkPoint* onePt = one < 0 ? oneEdge.fPts.begin()
-                : oneEdge.fPts.end() - 1;
-        int two = *twoPtr;
-        const OutEdge& twoEdge = edges[(two < 0 ? -two : two) - 1];
-        const SkPoint* twoPt = two < 0 ? twoEdge.fPts.begin()
-                : twoEdge.fPts.end() - 1;
-        return onePt->fY == twoPt->fY ? onePt->fX < twoPt->fX : onePt->fY < twoPt->fY;
+    // sort points by y, then x
+    // if x/y is identical, sort bottoms before tops
+    // if identical and both tops/bottoms, sort by angle
+    static bool lessThan(SkTArray<OutEdge>& edges, const int one,
+            const int two) {
+        const OutEdge& oneEdge = edges[abs(one) - 1];
+        int oneIndex = one < 0 ? 0 : oneEdge.fVerb;
+        const SkPoint& startPt1 = oneEdge.fPts[oneIndex];
+        const OutEdge& twoEdge = edges[abs(two) - 1];
+        int twoIndex = two < 0 ? 0 : twoEdge.fVerb;
+        const SkPoint& startPt2 = twoEdge.fPts[twoIndex];
+        if (startPt1.fY != startPt2.fY) {
+            if (gDebugLessThan) {
+                SkDebugf("%s %d<%d (%g,%g) %s startPt1.fY < startPt2.fY\n", __FUNCTION__,
+                        one, two, startPt1.fY, startPt2.fY,
+                        startPt1.fY < startPt2.fY ? "true" : "false");
+            }
+            return startPt1.fY < startPt2.fY;
+        }
+        if (startPt1.fX != startPt2.fX) {
+            if (gDebugLessThan) {
+                SkDebugf("%s %d<%d (%g,%g) %s startPt1.fX < startPt2.fX\n", __FUNCTION__,
+                        one, two, startPt1.fX, startPt2.fX,
+                        startPt1.fX < startPt2.fX ? "true" : "false");
+            }
+            return startPt1.fX < startPt2.fX;
+        }
+        const SkPoint& endPt1 = oneEdge.fPts[oneIndex ^ oneEdge.fVerb];
+        const SkPoint& endPt2 = twoEdge.fPts[twoIndex ^ twoEdge.fVerb];
+        SkScalar dy1 = startPt1.fY - endPt1.fY;
+        SkScalar dy2 = startPt2.fY - endPt2.fY;
+        SkScalar dy1y2 = dy1 * dy2;
+        if (dy1y2 < 0) { // different signs
+            if (gDebugLessThan) {
+                SkDebugf("%s %d<%d %s dy1 > 0\n", __FUNCTION__, one, two,
+                        dy1 > 0 ? "true" : "false");
+            }
+            return dy1 > 0; // one < two if one goes up and two goes down
+        }
+        if (dy1y2 == 0) {
+            if (gDebugLessThan) {
+                SkDebugf("%s %d<%d %s endPt1.fX < endPt2.fX\n", __FUNCTION__,
+                        one, two, endPt1.fX < endPt2.fX ? "true" : "false");
+            }
+            return endPt1.fX < endPt2.fX;
+        } 
+        SkScalar dx1y2 = (startPt1.fX - endPt1.fX) * dy2;
+        SkScalar dx2y1 = (startPt2.fX - endPt2.fX) * dy1;
+        if (gDebugLessThan) {
+            SkDebugf("%s %d<%d %s dy2 < 0 ^ dx1y2 < dx2y1\n", __FUNCTION__,
+                    one, two, dy2 < 0 ^ dx1y2 < dx2y1 ? "true" : "false");
+        }
+        return dy2 > 0 ^ dx1y2 < dx2y1;
     }
 
     // Sort the indices of paired points and then create more indices so
@@ -265,17 +322,19 @@
         if (!count) {
             return;
         }
-        SkASSERT(!fFill || (count & 1) == 0);
+        SkASSERT(!fFill || count > 1);
         fTops.setCount(count);
         sk_bzero(fTops.begin(), sizeof(fTops[0]) * count);
         fBottoms.setCount(count);
         sk_bzero(fBottoms.begin(), sizeof(fBottoms[0]) * count);
         SkTDArray<int> order;
         for (index = 1; index <= count; ++index) {
-            *order.append() = index;
             *order.append() = -index;
         }
-        QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), count * 2, lessThan);
+        for (index = 1; index <= count; ++index) {
+            *order.append() = index;
+        }
+        QSort<SkTArray<OutEdge>, int>(fEdges, order.begin(), order.end() - 1, lessThan);
         int* lastPtr = order.end() - 1;
         int* leftPtr = order.begin();
         while (leftPtr < lastPtr) {
@@ -286,13 +345,18 @@
             int rightIndex = *rightPtr;
             int rightOutIndex = abs(rightIndex) - 1;
             const OutEdge& right = fEdges[rightOutIndex];
-        // OPTIMIZATION: if fFill is true, we don't need leftMatch, rightMatch
-            SkPoint& leftMatch = left.fPts[leftIndex < 0 ? 0
-                    : left.fPts.count() - 1];
-            SkPoint& rightMatch = right.fPts[rightIndex < 0 ? 0
-                    : right.fPts.count() - 1];
-            SkASSERT(!fFill || leftMatch.fY == rightMatch.fY);
-            if (fFill || leftMatch == rightMatch) {
+            bool pairUp = fFill;
+            if (!pairUp) {
+                const SkPoint& leftMatch =
+                        left.fPts[leftIndex < 0 ? 0 : left.fVerb];
+                const SkPoint& rightMatch =
+                        right.fPts[rightIndex < 0 ? 0 : right.fVerb];
+                pairUp = leftMatch == rightMatch;
+            } else {
+                SkASSERT(left.fPts[leftIndex < 0 ? 0 : left.fVerb].fY
+                        == right.fPts[rightIndex < 0 ? 0 : right.fVerb].fY);
+            }
+            if (pairUp) {
                 if (leftIndex < 0) {
                     fTops[leftOutIndex] = rightIndex;
                 } else {
@@ -492,18 +556,36 @@
     while ((fVerb = iter.next(fPts)) != SkPath::kDone_Verb) {
         switch (fVerb) {
             case SkPath::kMove_Verb:
+                if (gShowPath) {
+                    SkDebugf("path.moveTo(%g, %g);\n", fPts[0].fX, fPts[0].fY);
+                }
                 startEdge();
                 continue;
             case SkPath::kLine_Verb:
+                if (gShowPath) {
+                    SkDebugf("path.lineTo(%g, %g);\n", fPts[1].fX, fPts[1].fY);
+                }
                 winding = direction(2);
                 break;
             case SkPath::kQuad_Verb:
+                if (gShowPath) {
+                    SkDebugf("path.quadTo(%g, %g, %g, %g);\n",
+                        fPts[1].fX, fPts[1].fY, fPts[2].fX, fPts[2].fY);
+                }
                 winding = direction(3);
                 break;
             case SkPath::kCubic_Verb:
+                if (gShowPath) {
+                    SkDebugf("path.cubicTo(%g, %g, %g, %g);\n",
+                        fPts[1].fX, fPts[1].fY, fPts[2].fX, fPts[2].fY,
+                        fPts[3].fX, fPts[3].fY);
+                }
                 winding = direction(4);
                 break;
             case SkPath::kClose_Verb:
+                if (gShowPath) {
+                    SkDebugf("path.close();\n");
+                }
                 SkASSERT(fCurrentEdge);
                 complete();
                 continue;
@@ -521,8 +603,9 @@
             // FIXME: if prior verb or this verb is a horizontal line, reverse
             // it instead of starting a new edge
             SkASSERT(fCurrentEdge);
-            fCurrentEdge->complete(fWinding);
-            startEdge();
+            if (complete()) {
+                startEdge();
+            }
         }
         fWinding = winding;
         addEdge();
@@ -532,6 +615,9 @@
             fEdges.pop_back();
         }
     }
+    if (gShowPath) {
+        SkDebugf("\n");
+    }
 }
 
 private:
@@ -558,7 +644,7 @@
         fVerb = edge->fVerbs.begin();
     }
 
-    bool next() {
+    bool advance() {
         SkASSERT(fVerb < fEdge->fVerbs.end());
         fPts += *fVerb++;
         return fVerb != fEdge->fVerbs.end();
@@ -586,16 +672,38 @@
 // ActiveEdge* may be faster to insert and search
 struct ActiveEdge {
     bool operator<(const ActiveEdge& rh) const {
-        return fX < rh.fX;
+        return fXAbove != rh.fXAbove ? fXAbove < rh.fXAbove
+                : fXBelow < rh.fXBelow;
     }
 
     void calcLeft() {
-        fX = fWorkEdge.fPts[fWorkEdge.verb()].fX;
+        // OPTIMIZE: put a kDone_Verb at the end of the verb list?
+        if (fDone)
+            return; // nothing to do; use last
+        switch (fWorkEdge.verb()) {
+            case SkPath::kLine_Verb: {
+                // OPTIMIZATION: if fXAbove, fXBelow have already been computed
+                //  for the fTIndex, don't do it again
+                // For identical x, this lets us know which edge is first.
+                // If both edges have T values < 1, check x at next T (fXBelow).
+                int add = (fTIndex <= fTs->count()) - 1;
+                double tAbove = t(fTIndex + add);
+                fXAbove = LineXAtT(fWorkEdge.fPts, tAbove);
+                double tBelow = t(fTIndex - ~add);
+                fXBelow = LineXAtT(fWorkEdge.fPts, tBelow);
+                break;
+            }
+            default:
+                // FIXME: add support for all curve types
+                SkASSERT(0);
+        }
     }
 
     void init(const InEdge* edge) {
         fWorkEdge.init(edge);
         initT();
+        fDone = false;
+        fYBottom = SK_ScalarMin;
     }
     
     void initT() {
@@ -608,19 +716,48 @@
   //  but templated arrays don't allow returning a pointer to the end() element
         fTIndex = 0;
     }
+    
+    bool isCoincidentWith(const ActiveEdge* edge) const {
+        if (fXAbove != edge->fXAbove || fXBelow != edge->fXBelow) {
+            return false;
+        }
+        switch (fWorkEdge.verb()) {
+            case SkPath::kLine_Verb: {
+                return (fWorkEdge.fPts[0].fX - fWorkEdge.fPts[1].fX) *
+                        (edge->fWorkEdge.fPts[0].fY - edge->fWorkEdge.fPts[1].fY) ==
+                        (fWorkEdge.fPts[0].fY - fWorkEdge.fPts[1].fY) *
+                        (edge->fWorkEdge.fPts[0].fX - edge->fWorkEdge.fPts[1].fX);
+            }
+            default:
+                // FIXME: add support for all curve types
+                SkASSERT(0);
+        }
+        return false;
+    }
 
-    bool nextT() {
+    bool advanceT() {
         SkASSERT(fTIndex <= fTs->count());
-        return ++fTIndex == fTs->count() + 1;
+        return ++fTIndex <= fTs->count();
     }
     
-    bool next() {
-        bool result = fWorkEdge.next();
+    bool advance() {
+    // FIXME: flip sense of next
+        bool result = fWorkEdge.advance();
+        fDone = !result;
         initT();
         return result;
     }
+    
+    bool done(SkScalar y) {
+        return fDone || fYBottom > y;
+    }
+    
+    double nextT() {
+        SkASSERT(fTIndex <= fTs->count());
+        return t(fTIndex + 1);
+    }
 
-    double t() {
+    double t() const {
         if (fTIndex == 0) {
             return 0;
         }
@@ -630,11 +767,24 @@
         return (*fTs)[fTIndex - 1];
     }
 
+    double t(int tIndex) const {
+        if (tIndex == 0) {
+            return 0;
+        }
+        if (tIndex > fTs->count()) {
+            return 1;
+        }
+        return (*fTs)[tIndex - 1];
+    }
+
     WorkEdge fWorkEdge;
     const SkTDArray<double>* fTs;
-    SkScalar fX;
+    SkScalar fXAbove;
+    SkScalar fXBelow;
+    SkScalar fYBottom;
     int fTIndex;
     bool fSkip;
+    bool fDone;
 };
 
 static void addToActive(SkTDArray<ActiveEdge>& activeEdges, const InEdge* edge) {
@@ -657,7 +807,6 @@
             WorkEdge wt;
             wt.init(test);
             do {
-                // FIXME: add all curve types
                 // OPTIMIZATION: if bottom intersection does not change
                 // the winding on either side of the split, don't intersect
                 if (wt.verb() == SkPath::kLine_Verb) {
@@ -667,25 +816,33 @@
                         test->fContainsIntercepts |= test->add(wtTs, pts,
                                 wt.verbIndex());
                     }
+                } else {
+                    // FIXME: add all curve types
+                    SkASSERT(0);
                 }
-            } while (wt.next());
+            } while (wt.advance());
         }
         test = *++testPtr;
     }
 }
 
 static void addIntersectingTs(InEdge** currentPtr, InEdge** lastPtr) {
-    InEdge** testPtr = currentPtr;
-    InEdge* test = *testPtr;
-    while (testPtr != lastPtr - 1) {
-        InEdge* next = *++testPtr;
-        if (!test->cached(next)
-                && Bounds::Intersects(test->fBounds, next->fBounds)) {
+    InEdge** testPtr = currentPtr - 1;
+    while (++testPtr != lastPtr - 1) {
+        InEdge* test = *testPtr;
+        InEdge** nextPtr = testPtr;
+        do {
+            InEdge* next = *++nextPtr;
+            if (test->cached(next)) {
+                continue;
+            }
+            if (!Bounds::Intersects(test->fBounds, next->fBounds)) {
+                continue;
+            }
             WorkEdge wt, wn;
             wt.init(test);
             wn.init(next);
             do {
-                // FIXME: add all combinations of curve types
                 if (wt.verb() == SkPath::kLine_Verb
                         && wn.verb() == SkPath::kLine_Verb) {
                     double wtTs[2], wnTs[2];
@@ -696,10 +853,12 @@
                         next->fContainsIntercepts |= next->add(wnTs, pts,
                                 wn.verbIndex());
                     }
+                } else {
+                    // FIXME: add all combinations of curve types
+                    SkASSERT(0);
                 }
-            } while (wt.bottom() <= wn.bottom() ? wt.next() : wn.next());
-        }
-        test = next;
+            } while (wt.bottom() <= wn.bottom() ? wt.advance() : wn.advance());
+        } while (nextPtr != lastPtr);
     }
 }
 
@@ -739,7 +898,6 @@
         WorkEdge wt;
         wt.init(test);
         do {
-            // FIXME: add all curve types
             const Intercepts& intercepts = test->fIntercepts[wt.verbIndex()];
             const SkTDArray<double>& fTs = intercepts.fTs;
             size_t count = fTs.count();
@@ -749,9 +907,12 @@
                     if (yIntercept > y && bottom > yIntercept) {
                         bottom = yIntercept;
                     }
+                } else {
+                    // FIXME: add all curve types
+                    SkASSERT(0);
                 }
             }
-        } while (wt.next());
+        } while (wt.advance());
     }
 }
 
@@ -842,7 +1003,7 @@
     for (index = 1; index < edgeCount; ++index) {
         winding += activePtr->fWorkEdge.winding();
         ActiveEdge* nextPtr = edgeList[index];
-        if (activePtr->fX == nextPtr->fX) {
+        if (activePtr->isCoincidentWith(nextPtr)) {
             if (!firstCoincident) {
                 firstCoincident = activePtr;
             }
@@ -871,7 +1032,7 @@
     int winding = 0;
     ActiveEdge** activeHandle = edgeList.begin() - 1;
     ActiveEdge** lastActive = edgeList.end();
-    if (fShowDebugf) {
+    if (gShowDebugf) {
         SkDebugf("%s y=%g bottom=%g\n", __FUNCTION__, y, bottom);
     }
     while (++activeHandle != lastActive) {
@@ -879,23 +1040,27 @@
         const WorkEdge& wt = activePtr->fWorkEdge;
         int lastWinding = winding;
         winding += wt.winding();
+        if (activePtr->done(y)) {
+            continue;
+        }
         int opener = (lastWinding & windingMask) == 0;
         bool closer = (winding & windingMask) == 0;
         SkASSERT(!opener | !closer);
         bool inWinding = opener | closer;
-        opener <<= kOpenerVerbBitShift;
+        const SkPoint* clipped;
+        uint8_t verb = wt.verb();
+        bool moreToDo, aboveBottom;
         do {
             double currentT = activePtr->t();
             SkASSERT(currentT < 1);
             const SkPoint* points = wt.fPts;
-            bool last;
+            double nextT;
             do {
-                last = activePtr->nextT();
-                double nextT = activePtr->t();
-                // FIXME: add all combinations of curve types
-                if (wt.verb() == SkPath::kLine_Verb) {
+                nextT = activePtr->nextT();
+                if (verb == SkPath::kLine_Verb) {
                     SkPoint clippedPts[2];
-                    const SkPoint* clipped;
+                    // FIXME: obtuse: want efficient way to say 
+                    // !currentT && currentT != 1 || !nextT && nextT != 1
                     if (currentT * nextT != 0 || currentT + nextT != 1) {
                         // OPTIMIZATION: if !inWinding, we only need 
                         // clipped[1].fY
@@ -905,25 +1070,23 @@
                         clipped = points;
                     }
                     if (inWinding && !activePtr->fSkip) {
-                        if (fShowDebugf) {
+                        if (gShowDebugf) {
                             SkDebugf("%s line %g,%g %g,%g\n", __FUNCTION__,
                                     clipped[0].fX, clipped[0].fY,
                                     clipped[1].fX, clipped[1].fY);
                         }
-                        outBuilder.addLine(clipped, opener);
+                        outBuilder.addLine(clipped);
                     }
-                    if (clipped[1].fY >= bottom) {
-                        if (last) {
-                            activePtr->next();
-                        }
-                        goto nextEdge;
-                    }
+                } else {
+                    // FIXME: add all curve types
+                    SkASSERT(0);
                 }
                 currentT = nextT;
-            } while (!last);
-        } while (activePtr->next());
-nextEdge:
-        ;
+                moreToDo = activePtr->advanceT();
+                activePtr->fYBottom = clipped[verb].fY;
+                aboveBottom = activePtr->fYBottom < bottom;
+            } while (moreToDo & aboveBottom);
+        } while ((moreToDo || activePtr->advance()) & aboveBottom);
     }
 }
 
@@ -933,9 +1096,10 @@
     simple.reset();
     simple.setFillType(SkPath::kEvenOdd_FillType);
     // turn path into list of edges increasing in y
-    // if an edge is a quad or a cubic with a y extrema, note it, but leave it unbroken
-    // once we have a list, sort it, then walk the list (walk edges twice that have y extrema's on top)
-    //  and detect crossings -- look for raw bounds that cross over, then tight bounds that cross
+    // if an edge is a quad or a cubic with a y extrema, note it, but leave it
+    // unbroken. Once we have a list, sort it, then walk the list (walk edges
+    // twice that have y extrema's on top)  and detect crossings -- look for raw
+    // bounds that cross over, then tight bounds that cross
     SkTArray<InEdge> edges;
     InEdgeBuilder builder(path, asFill, edges);
     SkTDArray<InEdge*> edgeList;
@@ -965,276 +1129,3 @@
     outBuilder.bridge();
     outBuilder.assemble(simple);
 }
-
-static void testSimplifyCoincidentVertical() {
-    SkPath path, out;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(10, 10, 30, 30);
-    path.addRect(10, 30, 30, 40);
-    simplify(path, true, out);
-    SkRect rect;
-    if (!out.isRect(&rect)) {
-        SkDebugf("%s expected rect\n", __FUNCTION__);
-    }
-    if (rect != SkRect::MakeLTRB(10, 10, 30, 40)) {
-        SkDebugf("%s expected union\n", __FUNCTION__);
-    }
-}
-
-static void testSimplifyCoincidentHorizontal() {
-    SkPath path, out;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(10, 10, 30, 30);
-    path.addRect(30, 10, 40, 30);
-    simplify(path, true, out);
-    SkRect rect;
-    if (!out.isRect(&rect)) {
-        SkDebugf("%s expected rect\n", __FUNCTION__);
-    }
-    if (rect != SkRect::MakeLTRB(10, 10, 40, 30)) {
-        SkDebugf("%s expected union\n", __FUNCTION__);
-    }
-}
-
-static void testSimplifyMulti() {
-    SkPath path, out;
-    path.setFillType(SkPath::kWinding_FillType);
-    path.addRect(10, 10, 30, 30);
-    path.addRect(20, 20, 40, 40);
-    simplify(path, true, out);
-    SkPath expected;
-    expected.setFillType(SkPath::kEvenOdd_FillType);
-    expected.moveTo(10,10); // two cutout corners
-    expected.lineTo(10,30);
-    expected.lineTo(20,30);
-    expected.lineTo(20,40);
-    expected.lineTo(40,40);
-    expected.lineTo(40,20);
-    expected.lineTo(30,20);
-    expected.lineTo(30,10);
-    expected.lineTo(10,10);
-    expected.close();
-    if (out != expected) {
-        SkDebugf("%s expected equal\n", __FUNCTION__);
-    }
-    
-    path = out;
-    path.addRect(30, 10, 40, 20);
-    path.addRect(10, 30, 20, 40);
-    simplify(path, true, out);
-    SkRect rect;
-    if (!out.isRect(&rect)) {
-        SkDebugf("%s expected rect\n", __FUNCTION__);
-    }
-    if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
-        SkDebugf("%s expected union\n", __FUNCTION__);
-    }
-    
-    path = out;
-    path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
-    simplify(path, true, out);
-    if (!out.isEmpty()) {
-        SkDebugf("%s expected empty\n", __FUNCTION__);
-    }
-}
-
-static void testSimplifyAddL() {
-    SkPath path, out;
-    path.moveTo(10,10); // 'L' shape
-    path.lineTo(10,40);
-    path.lineTo(40,40);
-    path.lineTo(40,20);
-    path.lineTo(30,20);
-    path.lineTo(30,10);
-    path.lineTo(10,10);
-    path.close();
-    path.addRect(30, 10, 40, 20); // missing notch of 'L'
-    simplify(path, true, out);
-    SkRect rect;
-    if (!out.isRect(&rect)) {
-        SkDebugf("%s expected rect\n", __FUNCTION__);
-    }
-    if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
-        SkDebugf("%s expected union\n", __FUNCTION__);
-    }
-}
-
-static void testSimplifyCoincidentCCW() {
-    SkPath path, out;
-    path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
-    path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
-    simplify(path, true, out);
-    SkRect rect;
-    if (!out.isRect(&rect)) {
-        SkDebugf("%s expected rect\n", __FUNCTION__);
-    }
-    if (rect != SkRect::MakeLTRB(10, 10, 40, 40)) {
-        SkDebugf("%s expected union\n", __FUNCTION__);
-    }
-}
-
-static void testSimplifyCoincidentCW() {
-    SkPath path, out;
-    path.addRect(10, 10, 40, 40, SkPath::kCCW_Direction);
-    path.addRect(10, 10, 40, 40, SkPath::kCW_Direction);
-    simplify(path, true, out);
-    if (!out.isEmpty()) {
-        SkDebugf("%s expected empty\n", __FUNCTION__);
-    }
-}
-
-static void testSimplifyCorner() {
-    SkPath path, out;
-    path.addRect(10, 10, 20, 20, SkPath::kCCW_Direction);
-    path.addRect(20, 20, 40, 40, SkPath::kCW_Direction);
-    simplify(path, true, out);
-    SkTDArray<SkRect> boundsArray;
-    contourBounds(out, boundsArray);
-    if (boundsArray.count() != 2) {
-        SkDebugf("%s expected 2 contours\n", __FUNCTION__);
-        return;
-    }
-    SkRect one = SkRect::MakeLTRB(10, 10, 20, 20);
-    SkRect two = SkRect::MakeLTRB(20, 20, 40, 40);
-    if (boundsArray[0] != one && boundsArray[0] != two
-            || boundsArray[1] != one && boundsArray[1] != two) {
-        SkDebugf("%s expected match\n", __FUNCTION__);
-    }
-}
-
-// non-intersecting test points, two equal sized rectangles
-static void lookForFailingTests(const SkPoint* pts, size_t ptsSize, int width,
-        int height, const SkRect& center) {
-    size_t index = 0;
-    for ( ; index < ptsSize; ++index) {
-        SkPath path, out;
-        path.addRect(center);
-        SkRect test = SkRect::MakeXYWH(pts[index].fX,
-                pts[index].fY, width, height);
-        path.addRect(test);
-        simplify(path, true, out);
-        SkPath::Iter iter(out, false);
-        SkPoint pts[2];
-        SkRect bounds[2];
-        bounds[0].setEmpty();
-        bounds[1].setEmpty();
-        SkRect* boundsPtr = bounds;
-        int count = 0;
-        SkPath::Verb verb;
-        while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-            switch (verb) {
-                case SkPath::kMove_Verb:
-                    if (!boundsPtr->isEmpty()) {
-                        SkASSERT(boundsPtr == bounds);
-                        ++boundsPtr;
-                    }
-                    boundsPtr->set(pts[0].fX, pts[0].fY, pts[0].fX, pts[0].fY);
-                    count = 0;
-                    break;
-                case SkPath::kLine_Verb: 
-                    count = 1;
-                    break;
-                case SkPath::kClose_Verb:
-                    count = 0;
-                    break;
-                default:
-                    SkDEBUGFAIL("bad verb");
-                    return;
-            }
-            for (int i = 1; i <= count; ++i) {
-                boundsPtr->growToInclude(pts[i].fX, pts[i].fY);
-            }
-        }
-        SkASSERT(bounds[0] == center && bounds[1] == test
-            || bounds[1] == center && bounds[0] == test);
-    }
-}
-
-static void twoEqualRects() {
-    SkPoint pts[] = {
-        { 0,  0}, {10,  0}, {20,  0}, {30,  0}, {40,  0}, {50,  0}, {60,  0}, // above
-                  { 0, 10}, { 0, 20}, { 0, 30}, { 0, 40}, { 0, 50}, { 0, 60}, // left
-                  {10, 60}, {20, 60}, {30, 60}, {40, 60}, {50, 60},           // below
-                  {60, 10}, {60, 20}, {60, 30}, {60, 40}, {60, 50}, {60, 60}, // right
-    };
-    size_t ptsCount = sizeof(pts) / sizeof(pts[0]);
-    SkRect center = SkRect::MakeLTRB(30, 30, 50, 50);
-    lookForFailingTests(pts, ptsCount, 20, 20, center);
-}
-
-static void largerOuter() {
-    SkRect center = SkRect::MakeLTRB(50, 50, 70, 70);
-    const size_t count = 9;
-    SkPoint pts[count];
-    size_t index;
-    for (index = 0; index < count; ++index) { // above
-        pts[index].fX = index * 10;
-        pts[index].fY = 0;
-    }
-    lookForFailingTests(pts, count, 40, 20, center);
-    for (index = 0; index < count; ++index) { // left
-        pts[index].fX = 0;
-        pts[index].fY = index * 10;
-    }
-    lookForFailingTests(pts, count, 20, 40, center);
-    for (index = 0; index < count; ++index) { // below
-        pts[index].fX = index * 10;
-        pts[index].fY = 80;
-    }
-    lookForFailingTests(pts, count, 40, 20, center);
-    for (index = 0; index < count; ++index) { // right
-        pts[index].fX = 80;
-        pts[index].fY = index * 10;
-    }
-    lookForFailingTests(pts, count, 20, 40, center);
-}
-
-static void smallerOuter() {
-    SkPoint pts[] = {
-        { 0,  0}, {10,  0}, {20,  0}, {30,  0}, {40,  0}, {50,  0}, {60,  0}, // above
-                  { 0, 10}, { 0, 20}, { 0, 30}, { 0, 40}, { 0, 50}, { 0, 60}, // left
-                  {10, 60}, {20, 60}, {30, 60}, {40, 60}, {50, 60},           // below
-                  {60, 10}, {60, 20}, {60, 30}, {60, 40}, {60, 50}, {60, 60}, // right
-    };
-    size_t ptsCount = sizeof(pts) / sizeof(pts[0]);
-    SkRect center = SkRect::MakeLTRB(20, 20, 50, 50);
-    lookForFailingTests(pts, ptsCount, 10, 10, center);
-}
-
-void testSimplify();
-
-void (*simplifyTests[])() = {
-    testSimplifyCorner,
-    testSimplifyCoincidentCW,
-    testSimplifyCoincidentCCW,
-    testSimplifyCoincidentVertical, 
-    testSimplifyCoincidentHorizontal,
-    testSimplifyAddL,                
-    testSimplifyMulti,               
-};
-
-size_t simplifyTestsCount = sizeof(simplifyTests) / sizeof(simplifyTests[0]);
-
-static void (*firstTest)() = 0;
-static bool lookForFailing = false;
-
-void testSimplify() {
-/* look for failing test cases */
-    if (lookForFailing) {
-// rects do not touch
-        twoEqualRects();
-        largerOuter();
-        smallerOuter();
-    }
-    size_t index = 0;
-    if (firstTest) {
-        while (index < simplifyTestsCount && simplifyTests[index] != firstTest) {
-            ++index;
-        }
-    }
-    for ( ; index < simplifyTestsCount; ++index) {
-        (*simplifyTests[index])();
-    }
-}
-
-