update pathops core and tests

split out skpclip (the test of 1M pictures) into its own project

TBR=reed

Author: caryclark@google.com

Review URL: https://codereview.chromium.org/400033002
diff --git a/gyp/most.gyp b/gyp/most.gyp
index 4675add..6c791b7 100644
--- a/gyp/most.gyp
+++ b/gyp/most.gyp
@@ -21,6 +21,7 @@
         'tests.gyp:tests',
         'tools.gyp:tools',
         'pathops_unittest.gyp:*',
+        'pathops_skpclip.gyp:*',
 #       'pdfviewer.gyp:pdfviewer',
         'dm.gyp:dm',
       ],
diff --git a/gyp/pathops_skpclip.gyp b/gyp/pathops_skpclip.gyp
new file mode 100755
index 0000000..a1e51d6
--- /dev/null
+++ b/gyp/pathops_skpclip.gyp
@@ -0,0 +1,46 @@
+# GYP file to build pathops skp clip test.
+{
+  'includes': [
+    'apptype_console.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'pathops_skpclip',
+      'type': 'executable',
+      'include_dirs': [
+        '../src/core',
+        '../src/effects',
+        '../src/lazy',
+        '../src/pathops',
+        '../src/pipe/utils',
+        '../src/utils',
+      ],
+      'dependencies': [ 
+        'flags.gyp:flags',
+        'skia_lib.gyp:skia_lib',
+        'tools.gyp:crash_handler',
+        'tools.gyp:resources',
+      ],
+      'sources': [
+		'../tests/PathOpsDebug.cpp',
+        '../tests/PathOpsSkpClipTest.cpp',
+      ],
+      'conditions': [
+        [ 'skia_android_framework == 1', {
+          'libraries': [
+            '-lskia',
+          ],
+          'libraries!': [
+            '-lz',
+            '-llog',
+          ],
+        }],
+        [ 'skia_gpu == 1', {
+          'include_dirs': [
+            '../src/gpu',
+          ],
+        }],
+      ],
+    },
+  ],
+}
diff --git a/gyp/pathops_unittest.gyp b/gyp/pathops_unittest.gyp
index 190b4f3..b8f6835 100644
--- a/gyp/pathops_unittest.gyp
+++ b/gyp/pathops_unittest.gyp
@@ -16,7 +16,6 @@
         '../tests/PathOpsCubicLineIntersectionIdeas.cpp',
         '../tests/PathOpsDebug.cpp',
         '../tests/PathOpsOpLoopThreadedTest.cpp',
-        '../tests/PathOpsSkpClipTest.cpp',
         '../tests/skia_test.cpp',
       ],
       'conditions': [
diff --git a/src/pathops/SkDLineIntersection.cpp b/src/pathops/SkDLineIntersection.cpp
index 9ae0107..b209474 100644
--- a/src/pathops/SkDLineIntersection.cpp
+++ b/src/pathops/SkDLineIntersection.cpp
@@ -173,21 +173,24 @@
             nearCount += t >= 0;
         }
         if (nearCount > 0) {
-            for (int iA = 0; iA < 2; ++iA) {
-                if (!aNotB[iA]) {
-                    continue;
+            // Skip if each segment contributes to one end point.
+            if (nearCount != 2 || aNotB[0] == aNotB[1]) {
+                for (int iA = 0; iA < 2; ++iA) {
+                    if (!aNotB[iA]) {
+                        continue;
+                    }
+                    int nearer = aNearB[iA] > 0.5;
+                    if (!bNotA[nearer]) {
+                        continue;
+                    }
+                    SkASSERT(a[iA] != b[nearer]);
+                    SkASSERT(iA == (bNearA[nearer] > 0.5));
+                    fNearlySame[iA] = true;
+                    insertNear(iA, nearer, a[iA], b[nearer]);
+                    aNearB[iA] = -1;
+                    bNearA[nearer] = -1;
+                    nearCount -= 2;
                 }
-                int nearer = aNearB[iA] > 0.5;
-                if (!bNotA[nearer]) {
-                    continue;
-                }
-                SkASSERT(a[iA] != b[nearer]);
-                SkASSERT(iA == (bNearA[nearer] > 0.5));
-                fNearlySame[iA] = true;
-                insertNear(iA, nearer, a[iA], b[nearer]);
-                aNearB[iA] = -1;
-                bNearA[nearer] = -1;
-                nearCount -= 2;
             }
             if (nearCount > 0) {
                 for (int iA = 0; iA < 2; ++iA) {
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 4e8b5d2..f6d989b 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -405,12 +405,14 @@
 }
 
 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 startIndex = endIndex - 1;
     while (fTs[startIndex].fT == 1 || fTs[startIndex].fTiny) {
-        ++startIndex;
-        SkASSERT(startIndex < spanCount - 1);
-        ++endIndex;
+        --startIndex;
+        SkASSERT(startIndex > 0);
+        --endIndex;
     }
     SkOpAngle& angle = fAngles.push_back();
     angle.set(this, spanCount - 1, startIndex);
@@ -815,7 +817,11 @@
 // FIXME? assert that only one other span has a valid windValue or oppValue
 void SkOpSegment::addSimpleAngle(int index) {
     SkOpSpan* span = &fTs[index];
-    if (index == 0) {
+    int idx;
+    int start, end;
+    if (span->fT == 0) {
+        idx = 0;
+        span = &fTs[0];
         do {
             if (span->fToAngle) {
                 SkASSERT(span->fToAngle->loopCount() == 2);
@@ -823,13 +829,15 @@
                 span->fFromAngle = span->fToAngle->next();
                 return;
             }
-            span = &fTs[++index];
+            span = &fTs[++idx];
         } while (span->fT == 0);
-        SkASSERT(index == 1);
-        index = 0;
-        SkASSERT(!fTs[0].fTiny && fTs[1].fT > 0);
-        addStartSpan(1);
+        SkASSERT(!fTs[0].fTiny && fTs[idx].fT > 0);
+        addStartSpan(idx);
+        start = 0;
+        end = idx;
     } else {
+        idx = count() - 1;
+        span = &fTs[idx];
         do {
             if (span->fFromAngle) {
                 SkASSERT(span->fFromAngle->loopCount() == 2);
@@ -837,29 +845,48 @@
                 span->fToAngle = span->fFromAngle->next();
                 return;
             }
-            span = &fTs[--index];
+            span = &fTs[--idx];
         } while (span->fT == 1);
-        SkASSERT(index == count() - 2);
-        index = count() - 1;
-        SkASSERT(!fTs[index - 1].fTiny && fTs[index - 1].fT < 1);
-        addEndSpan(index);
+        SkASSERT(!fTs[idx].fTiny && fTs[idx].fT < 1);
+        addEndSpan(++idx);
+        start = idx;
+        end = count();
     }
-    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) {
+    if (span->fT == 0) {
         SkASSERT(span->fOtherIndex - 1 >= 0);
         SkASSERT(span->fOtherT == 1);
-        SkDEBUGCODE(SkOpSpan& oPrior = other->fTs[span->fOtherIndex - 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 = span->fToAngle;
-        oAngle = oSpan.fFromAngle;
+        oAngle = oSpan->fFromAngle;
     } else {
         SkASSERT(span->fOtherIndex + 1 < other->count());
         SkASSERT(span->fOtherT == 0);
-        SkASSERT(!oSpan.fTiny && (other->fTs[span->fOtherIndex + 1].fT > 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;
@@ -873,7 +900,7 @@
         } while (true);
         other->addStartSpan(oIndex);
         angle = span->fFromAngle;
-        oAngle = oSpan.fToAngle;
+        oAngle = oSpan->fToAngle;
     }
     angle->insert(oAngle);
 }
@@ -1348,7 +1375,10 @@
                     success = true;
                     break;
                 }
-                oPeek = &other->fTs[++oPeekIndex];
+                if (++oPeekIndex == oCount) {
+                    break;
+                }
+                oPeek = &other->fTs[oPeekIndex];
             } while (endPt == oPeek->fPt);
         }
         if (success) {
@@ -3402,7 +3432,7 @@
     SkOpSegment* other = this;
     while ((other = other->nextChase(&index, &step, &min, &last))) {
         if (other->fTs[min].fWindSum != SK_MinS32) {
-            SkASSERT(other->fTs[min].fWindSum == winding);
+//            SkASSERT(other->fTs[min].fWindSum == winding);
             SkASSERT(!last);
             break;
         }
diff --git a/src/pathops/SkPathOpsDebug.cpp b/src/pathops/SkPathOpsDebug.cpp
index 96029b3..7db93f5 100644
--- a/src/pathops/SkPathOpsDebug.cpp
+++ b/src/pathops/SkPathOpsDebug.cpp
@@ -108,6 +108,7 @@
     const SkOpAngle* next = this;
     do {
         next->dumpOne(true);
+        SkDebugf("\n");
         next = next->fNext;
     } while (next && next != first);
 }
diff --git a/src/pathops/SkPathOpsDebug.h b/src/pathops/SkPathOpsDebug.h
index 9dc562f..211c503 100644
--- a/src/pathops/SkPathOpsDebug.h
+++ b/src/pathops/SkPathOpsDebug.h
@@ -169,6 +169,7 @@
         SkPathOpsDebug::DeleteNameStr)))
     static void BumpTestName(char* );
 #endif
+    static void ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration);
     static void ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name);
     static void DumpCoincidence(const SkTArray<class SkOpContour, true>& contours);
     static void DumpCoincidence(const SkTArray<class SkOpContour* , true>& contours);
diff --git a/tests/PathOpsDebug.cpp b/tests/PathOpsDebug.cpp
index af60318..8ac38aa 100755
--- a/tests/PathOpsDebug.cpp
+++ b/tests/PathOpsDebug.cpp
@@ -1,6 +1,7 @@
 #include "SkOpContour.h"
 #include "SkIntersectionHelper.h"
 #include "SkOpSegment.h"
+#include "SkString.h"
 
 inline void DebugDumpDouble(double x) {
     if (x == floor(x)) {
@@ -18,6 +19,137 @@
     }
 }
 
+
+#if DEBUG_SHOW_TEST_NAME
+
+static void output_scalar(SkScalar num) {
+    if (num == (int) num) {
+        SkDebugf("%d", (int) num);
+    } else {
+        SkString str;
+        str.printf("%1.9g", num);
+        int width = (int) str.size();
+        const char* cStr = str.c_str();
+        while (cStr[width - 1] == '0') {
+            --width;
+        }
+        str.resize(width);
+        SkDebugf("%sf", str.c_str());
+    }
+}
+
+static void output_points(const SkPoint* pts, int count) {
+    for (int index = 0; index < count; ++index) {
+        output_scalar(pts[index].fX);
+        SkDebugf(", ");
+        output_scalar(pts[index].fY);
+        if (index + 1 < count) {
+            SkDebugf(", ");
+        }
+    }
+    SkDebugf(");\n");
+}
+
+static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
+    uint8_t verb;
+    SkPoint pts[4];
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                SkDebugf("    %s.moveTo(", pathName);
+                output_points(&pts[0], 1);
+                continue;
+            case SkPath::kLine_Verb:
+                SkDebugf("    %s.lineTo(", pathName);
+                output_points(&pts[1], 1);
+                break;
+            case SkPath::kQuad_Verb:
+                SkDebugf("    %s.quadTo(", pathName);
+                output_points(&pts[1], 2);
+                break;
+            case SkPath::kCubic_Verb:
+                SkDebugf("    %s.cubicTo(", pathName);
+                output_points(&pts[1], 3);
+                break;
+            case SkPath::kClose_Verb:
+                SkDebugf("    %s.close();\n", pathName);
+                break;
+            default:
+                SkDEBUGFAIL("bad verb");
+                return;
+        }
+    }
+}
+
+static const char* gFillTypeStr[] = {
+    "kWinding_FillType",
+    "kEvenOdd_FillType",
+    "kInverseWinding_FillType",
+    "kInverseEvenOdd_FillType"
+};
+
+void SkPathOpsDebug::ShowOnePath(const SkPath& path, const char* name, bool includeDeclaration) {
+    SkPath::RawIter iter(path);
+#define SUPPORT_RECT_CONTOUR_DETECTION 0
+#if SUPPORT_RECT_CONTOUR_DETECTION
+    int rectCount = path.isRectContours() ? path.rectContours(NULL, NULL) : 0;
+    if (rectCount > 0) {
+        SkTDArray<SkRect> rects;
+        SkTDArray<SkPath::Direction> directions;
+        rects.setCount(rectCount);
+        directions.setCount(rectCount);
+        path.rectContours(rects.begin(), directions.begin());
+        for (int contour = 0; contour < rectCount; ++contour) {
+            const SkRect& rect = rects[contour];
+            SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
+                    rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
+                    ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
+        }
+        return;
+    }
+#endif
+    SkPath::FillType fillType = path.getFillType();
+    SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
+    if (includeDeclaration) {
+        SkDebugf("    SkPath %s;\n", name);
+    }
+    SkDebugf("    %s.setFillType(SkPath::%s);\n", name, gFillTypeStr[fillType]);
+    iter.setPath(path);
+    showPathContours(iter, name);
+}
+
+static void show_function_header(const char* functionName) {
+    SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
+    if (strcmp("skphealth_com76", functionName) == 0) {
+        SkDebugf("found it\n");
+    }
+}
+
+static const char* gOpStrs[] = {
+    "kDifference_PathOp",
+    "kIntersect_PathOp",
+    "kUnion_PathOp",
+    "kXor_PathOp",
+    "kReverseDifference_PathOp",
+};
+
+static void show_op(SkPathOp op, const char* pathOne, const char* pathTwo) {
+    SkDebugf("    testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
+    SkDebugf("}\n");
+}
+
+SK_DECLARE_STATIC_MUTEX(gTestMutex);
+
+void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
+        const char* testName) {
+    SkAutoMutexAcquire ac(gTestMutex);
+    show_function_header(testName);
+    ShowOnePath(a, "path", true);
+    ShowOnePath(b, "pathB", true);
+    show_op(shapeOp, "path", "pathB");
+}
+#endif
+
 // if not defined by PathOpsDebug.cpp ...
 #if !defined SK_DEBUG && FORCE_RELEASE
 bool SkPathOpsDebug::ValidWind(int wind) {
diff --git a/tests/PathOpsExtendedTest.cpp b/tests/PathOpsExtendedTest.cpp
index fe3d24d..2d74126 100644
--- a/tests/PathOpsExtendedTest.cpp
+++ b/tests/PathOpsExtendedTest.cpp
@@ -49,102 +49,6 @@
 static bool gComparePathsAssert = true;
 static bool gPathStrAssert = true;
 
-static const char* gFillTypeStr[] = {
-    "kWinding_FillType",
-    "kEvenOdd_FillType",
-    "kInverseWinding_FillType",
-    "kInverseEvenOdd_FillType"
-};
-
-static void output_scalar(SkScalar num) {
-    if (num == (int) num) {
-        SkDebugf("%d", (int) num);
-    } else {
-        SkString str;
-        str.printf("%1.9g", num);
-        int width = (int) str.size();
-        const char* cStr = str.c_str();
-        while (cStr[width - 1] == '0') {
-            --width;
-        }
-        str.resize(width);
-        SkDebugf("%sf", str.c_str());
-    }
-}
-
-static void output_points(const SkPoint* pts, int count) {
-    for (int index = 0; index < count; ++index) {
-        output_scalar(pts[index].fX);
-        SkDebugf(", ");
-        output_scalar(pts[index].fY);
-        if (index + 1 < count) {
-            SkDebugf(", ");
-        }
-    }
-    SkDebugf(");\n");
-}
-
-static void showPathContours(SkPath::RawIter& iter, const char* pathName) {
-    uint8_t verb;
-    SkPoint pts[4];
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
-        switch (verb) {
-            case SkPath::kMove_Verb:
-                SkDebugf("    %s.moveTo(", pathName);
-                output_points(&pts[0], 1);
-                continue;
-            case SkPath::kLine_Verb:
-                SkDebugf("    %s.lineTo(", pathName);
-                output_points(&pts[1], 1);
-                break;
-            case SkPath::kQuad_Verb:
-                SkDebugf("    %s.quadTo(", pathName);
-                output_points(&pts[1], 2);
-                break;
-            case SkPath::kCubic_Verb:
-                SkDebugf("    %s.cubicTo(", pathName);
-                output_points(&pts[1], 3);
-                break;
-            case SkPath::kClose_Verb:
-                SkDebugf("    %s.close();\n", pathName);
-                break;
-            default:
-                SkDEBUGFAIL("bad verb");
-                return;
-        }
-    }
-}
-
-static void showPath(const SkPath& path, const char* pathName, bool includeDeclaration) {
-    SkPath::RawIter iter(path);
-#define SUPPORT_RECT_CONTOUR_DETECTION 0
-#if SUPPORT_RECT_CONTOUR_DETECTION
-    int rectCount = path.isRectContours() ? path.rectContours(NULL, NULL) : 0;
-    if (rectCount > 0) {
-        SkTDArray<SkRect> rects;
-        SkTDArray<SkPath::Direction> directions;
-        rects.setCount(rectCount);
-        directions.setCount(rectCount);
-        path.rectContours(rects.begin(), directions.begin());
-        for (int contour = 0; contour < rectCount; ++contour) {
-            const SkRect& rect = rects[contour];
-            SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
-                    rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
-                    ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
-        }
-        return;
-    }
-#endif
-    SkPath::FillType fillType = path.getFillType();
-    SkASSERT(fillType >= SkPath::kWinding_FillType && fillType <= SkPath::kInverseEvenOdd_FillType);
-    if (includeDeclaration) {
-        SkDebugf("    SkPath %s;\n", pathName);
-    }
-    SkDebugf("    %s.setFillType(SkPath::%s);\n", pathName, gFillTypeStr[fillType]);
-    iter.setPath(path);
-    showPathContours(iter, pathName);
-}
-
 #if DEBUG_SHOW_TEST_NAME
 static void showPathData(const SkPath& path) {
     SkPath::RawIter iter(path);
@@ -225,29 +129,6 @@
 }
 
 #if DEBUG_SHOW_TEST_NAME
-
-void ShowFunctionHeader(const char* functionName) {
-    SkDebugf("\nstatic void %s(skiatest::Reporter* reporter, const char* filename) {\n", functionName);
-    if (strcmp("skphealth_com76", functionName) == 0) {
-        SkDebugf("found it\n");
-    }
-}
-
-static const char* gOpStrs[] = {
-    "kDifference_PathOp",
-    "kIntersect_PathOp",
-    "kUnion_PathOp",
-    "kXor_PathOp",
-    "kReverseDifference_PathOp",
-};
-
-void ShowOp(SkPathOp op, const char* pathOne, const char* pathTwo) {
-    SkDebugf("    testPathOp(reporter, %s, %s, %s, filename);\n", pathOne, pathTwo, gOpStrs[op]);
-    SkDebugf("}\n");
-}
-#endif
-
-#if DEBUG_SHOW_TEST_NAME
 static char hexorator(int x) {
     if (x < 10) {
         return x + '0';
@@ -420,8 +301,10 @@
     *gTestOp.append() = shapeOp;
     ++gTestNo;
     SkDebugf("    SkPath path, pathB;\n");
-    showPath(a, "path", false);
-    showPath(b, "pathB", false);
+#if DEBUG_SHOW_TEST_NAME
+    SkPathOpsDebug::ShowOnePath(a, "path", false);
+    SkPathOpsDebug::ShowOnePath(b, "pathB", false);
+#endif
     SkDebugf("    testPathOp(reporter, path, pathB, %s, filename);\n", opStrs[shapeOp]);
     SkDebugf("}\n");
     drawAsciiPaths(scaledOne, scaledTwo, true);
@@ -523,9 +406,11 @@
                   const char* pathStr) {
     SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
     path.setFillType(fillType);
+#if DEBUG_SHOW_TEST_NAME
     if (gShowPath) {
-        showPath(path, "path", false);
+        SkPathOpsDebug::ShowOnePath(path, "path", false);
     }
+#endif
     if (!Simplify(path, &out)) {
         SkDebugf("%s did not expect failure\n", __FUNCTION__);
         REPORTER_ASSERT(state.fReporter, 0);
@@ -576,20 +461,6 @@
 }
 
 #if DEBUG_SHOW_TEST_NAME
-
-SK_DECLARE_STATIC_MUTEX(gTestMutex);
-
-void SkPathOpsDebug::ShowPath(const SkPath& a, const SkPath& b, SkPathOp shapeOp,
-        const char* testName) {
-    SkAutoMutexAcquire ac(gTestMutex);
-    ShowFunctionHeader(testName);
-    showPath(a, "path", true);
-    showPath(b, "pathB", true);
-    ShowOp(shapeOp, "path", "pathB");
-}
-#endif
-
-#if DEBUG_SHOW_TEST_NAME
 static void showName(const SkPath& a, const SkPath& b, const SkPathOp shapeOp) {
     SkDebugf("\n");
     showPathData(a);
diff --git a/tests/PathOpsLineIntersectionTest.cpp b/tests/PathOpsLineIntersectionTest.cpp
index 379c2f1..0c6aac6 100644
--- a/tests/PathOpsLineIntersectionTest.cpp
+++ b/tests/PathOpsLineIntersectionTest.cpp
@@ -11,6 +11,10 @@
 
 // FIXME: add tests for intersecting, non-intersecting, degenerate, coincident
 static const SkDLine tests[][2] = {
+#if 0
+    // these do intersect at a pair of points, but not close enough for check results liking
+    {{{{365.848175,5081.15186}, {368,5103}}}, {{{367.967712,5102.61084}, {368.278717,5105.71045}}}},
+#endif
     {{{{30,20}, {30,50}}}, {{{24,30}, {36,30}}}},
     {{{{323,193}, {-317,193}}}, {{{0,994}, {0,0}}}},
     {{{{90,230}, {160,60}}}, {{{60,120}, {260,120}}}},
diff --git a/tests/PathOpsOpTest.cpp b/tests/PathOpsOpTest.cpp
index 1d63bd7..b7babd3 100644
--- a/tests/PathOpsOpTest.cpp
+++ b/tests/PathOpsOpTest.cpp
@@ -3461,10 +3461,36 @@
     testPathOp(reporter, path, pathB, kDifference_PathOp, filename);
 }
 
+#define TEST_ISSUE_2753 0
+#if TEST_ISSUE_2753
+static void issue2753(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path1;
+    path1.moveTo(142.701f, 110.568f);
+    path1.lineTo(142.957f, 100);
+    path1.lineTo(153.835f, 100);
+    path1.lineTo(154.592f, 108.188f);
+    path1.cubicTo(154.592f, 108.188f, 153.173f, 108.483f, 152.83f, 109.412f);
+    path1.cubicTo(152.83f, 109.412f, 142.701f, 110.568f, 142.701f, 110.568f);
+    path1.close();
+
+    SkPath path2;
+    path2.moveTo(39, 124.001f);
+    path2.cubicTo(39, 124.001f, 50.6f, 117.001f, 50.6f, 117.001f);
+    path2.cubicTo(50.6f, 117.001f, 164.601f, 85.2f, 188.201f, 117.601f);
+    path2.cubicTo(188.201f, 117.601f, 174.801f, 93, 39, 124.001f);
+    path2.close();
+
+    testPathOp(reporter, path1, path2, kUnion_PathOp, filename);
+}
+#endif
+
 static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
 static void (*stopTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static struct TestDesc tests[] = {
+#if TEST_ISSUE_2753  // FIXME: pair of cubics miss intersection
+    TEST(issue2753),
+#endif
 #if CUBIC_OP_114  // FIXME: curve with inflection is ordered the wrong way
     TEST(cubicOp114),
 #endif
diff --git a/tests/PathOpsSimplifyTest.cpp b/tests/PathOpsSimplifyTest.cpp
index 4bfab14..88547a0 100644
--- a/tests/PathOpsSimplifyTest.cpp
+++ b/tests/PathOpsSimplifyTest.cpp
@@ -4669,9 +4669,20 @@
     testSimplify(reporter, path, filename);
 }
 
+static void testRect3(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.addRect(0, 0, 60, 60, SkPath::kCCW_Direction);
+    path.addRect(10, 30, 40, 30, SkPath::kCCW_Direction);
+    path.addRect(24, 6, 36, 36, SkPath::kCCW_Direction);
+    path.addRect(32, 6, 36, 41, SkPath::kCCW_Direction);
+    testSimplify(reporter, path, filename);
+}
+
 static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static TestDesc tests[] = {
+    TEST(testRect3),
     TEST(testQuadralateral10),
     TEST(testQuads61),
     TEST(testQuads60),
diff --git a/tests/PathOpsSkpClipTest.cpp b/tests/PathOpsSkpClipTest.cpp
index da50545..0e3cee9 100755
--- a/tests/PathOpsSkpClipTest.cpp
+++ b/tests/PathOpsSkpClipTest.cpp
@@ -1,9 +1,13 @@
-
+#include "CrashHandler.h"
+// #include "OverwriteLine.h"
+#include "Resources.h"
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkColor.h"
 #include "SkColorPriv.h"
+#include "SkCommandLineFlags.h"
 #include "SkDevice.h"
+#include "SkForceLinking.h"
 #include "SkGraphics.h"
 #include "SkImageDecoder.h"
 #include "SkImageEncoder.h"
@@ -16,209 +20,133 @@
 #include "SkString.h"
 #include "SkTArray.h"
 #include "SkTDArray.h"
+#include "SkTemplates.h"
 #include "SkThreadPool.h"
 #include "SkTime.h"
-#include "Test.h"
 
-#ifdef SK_BUILD_FOR_WIN
-    #define PATH_SLASH "\\"
-    #define IN_DIR "D:\\skp\\slave"
-    #define OUT_DIR "D:\\skpOut\\1\\"
-#else
-    #define PATH_SLASH "/"
-    #define IN_DIR "/skp/2311328-7fc2228/slave"
-    #define OUT_DIR "/skpOut/4/"
-#endif
+__SK_FORCE_IMAGE_DECODER_LINKING;
 
-const struct {
+/* add local exceptions here */
+/* TODO : add command flag interface */
+const struct SkipOverTest {
     int directory;
     const char* filename;
-} skipOverSept[] = {
-    { 3, "http___www_americascup_com_.skp"},  // !simple->closed()
-    {18, "http___www_argus_presse_fr_.skp"},  // can't find winding of remaining vertical edge
-    {31, "http___www_narayana_verlag_de_.skp"},  // !simple->closed()
-    {36, "http___www_educationalcraft_com_.skp"},  // cubic / cubic near end / assert in SkIntersections::insert
-    {44, "http___www_cooksnaps_com_.skp"},  // !simple->isClosed()
-    {48, "http___www_narayana_publishers_com_.skp"},  // !simple->isClosed()
-    {51, "http___www_freedominthe50states_org_.skp"},  // corrupt dash data
-    {52, "http___www_aceinfographics_com_.skp"},  // right angle winding assert
-    {53, "http___www_lojaanabotafogo_com_br_.skp"},  // rrect validate assert
-    {57, "http___www_vantageproduction_com_.skp"},  // !isClosed()
-    {64, "http___www_etiqadd_com_.skp"},  // !simple->closed()
-    {84, "http___www_swapspacesystems_com_.skp"},  // !simple->closed()
-    {90, "http___www_tcmevents_org_.skp"},  // !simple->closed()
-    {96, "http___www_paseoitaigara_com_br_.skp"},  // !simple->closed()
-    {98, "http___www_mortgagemarketguide_com_.skp"},  // !simple->closed()
-    {99, "http___www_kitcheninspirations_wordpress_com_.skp"},  // checkSmall / bumpSpan
+    bool blamePathOps;
+} skipOver[] = {
+    { 2, "http___www_groupon_sg_.skp", false},  // SkAAClip::Builder::addRun SkASSERT(fBounds.contains(x, y));
+    { 6, "http___www_googleventures_com_.skp", true},  // addTCoincident SkASSERT(test->fT < 1);
+    { 7, "http___www_foxsports_nl_.skp", true},  // (no repro on mac) addT SkASSERT(this != other || fVerb == SkPath::kCubic_Verb)
+    {13, "http___www_modernqigong_com_.skp", false},  // SkAAClip::Builder::addRun SkASSERT(fBounds.contains(x, y));
+    {14, "http___www_devbridge_com_.skp", true},  // checkSmallCoincidence SkASSERT(!next->fSmall || checkMultiple);
+    {16, "http___www_1023world_net_.skp", true},  // bitmap decode assert (corrupt skp?)
+    {19, "http___www_alamdi_com_.skp", true},  // cubic/quad intersection
+    {26, "http___www_liveencounters_net_.skp", true},  // (no repro on mac) checkSmall addT:549 (line, expects cubic)
+    {28, "http___www_encros_fr_.skp", false},  // SkAAClip::Builder::addRun SkASSERT(fBounds.contains(x, y));
+    {37, "http___www_familysurvivalprotocol_wordpress_com_.skp", true},  // bumpSpan SkASSERT(span->fOppValue >= 0);
+    {39, "http___sufeinet_com_.skp", false}, // bitmap decode assert (corrupt skp?)
+    {41, "http___www_rano360_com_.skp", true}, // checkSmallCoincidence SkASSERT(!next->fSmall || checkMultiple);
+    {44, "http___www_firstunitedbank_com_.skp", true},  // addTCancel SkASSERT(oIndex > 0);
+    {46, "http___www_shinydemos_com_.skp", true},  // addSimpleAngle SkASSERT(index == count() - 2);
+    {48, "http___www_familysurvivalprotocol_com_.skp", true},  // bumpSpan SkASSERT "span->fOppValue >= 0"
+    {57, "http___www_lptemp_com_.skp", true}, // addTCoincident oPeek = &other->fTs[++oPeekIndex];
+    {71, "http___www_1milyonkahraman_org_.skp", true},  // addTCoincident SkASSERT(test->fT < 1);
+    {88, "http___www_apuntesdelechuza_wordpress_com_.skp", true},  // bumpSpan SkASSERT "span->fOppValue >= 0"
+    {89, "http___www_mobilizedconsulting_com_.skp", true}, // addTCancel SkASSERT(oIndex > 0);
+    {93, "http___www_simple_living_in_suffolk_co_uk_.skp", true},  // bumpSpan SkASSERT "span->fOppValue >= 0"
 };
 
-/* stats
-97 http___www_brandyandvinca_com_.skp pixelError=3
-95 http___www_into_asia_com_.skp pixelError=12
-93 http___www_lunarplanner_com_.skp pixelError=14
-98 http___www_lovelyitalia_com_.skp pixelError=17
-90 http___www_inter_partner_blogspot_com_.skp pixelError=18
-99 http___www_maxarea_com_.skp pixelError=26
-98 http___www_maroonsnet_org_.skp pixelError=33
-92 http___www_belinaart_ru_.skp pixelError=50
-100 http___www_chroot_ro_.skp pixelError=62
-99 http___www_hsbrands_com_.skp pixelError=98
-95 http___www_tournamentindicator_com_.skp pixelError=122
-93 http___www_businesses_com_au_.skp pixelError=162
-90 http___www_regenesys_net_.skp pixelError=182
-88 http___www_1863544208148625103_c18eac63985503fa85b06358959c1ba27fc36f82_blogspot_com_.skp pixelError=186
-97 http___www_pregacoesevangelica_com_br_.skp pixelError=240
-77 http___www_zhenggang_org_.skp pixelError=284
-96 http___slidesharemailer_com_.skp pixelError=522
-94 http___www_gensteel_com_.skp pixelError=555
-68 http___www_jf_eti_br_.skp pixelError=610
-83 http___www_swishiat_com_.skp pixelError=706
-96 http___www_matusikmissive_com_au_.skp pixelError=2580
-95 http___www_momentumnation_com_.skp pixelError=3938
-92 http___www_rssowl_com_.skp pixelError=5113
-96 http___www_sexxygirl_tv_.skp pixelError=7605
-99 http___www_georgevalah_wordpress_com_.skp pixelError=8386
-78 http___www_furbo_org_.skp pixelError=8656
-78 http___www_djxhemary_wordpress_com_.skp pixelError=8976
-100 http___www_mindcontrolblackassassins_com_.skp pixelError=31950
-98 http___bababillgates_free_fr_.skp pixelError=40237
-98 http___hepatite_ro_.skp pixelError=44370
-86 http___www_somethingwagging_com_.skp pixelError=47794
-84 http___www_beverageuniverse_com_.skp pixelError=65450
-50 http___www_aveksa_com_.skp pixelError=68194
-10 http___www_publiker_pl_.skp pixelError=89997
-61 http___www_dominos_co_id_.skp pixelError=476868
-87 http___www_du_edu_om_.skp time=46
-87 http___www_bigload_de_.skp time=46
-100 http___www_home_forum_com_.skp time=48
-97 http___www_hotamateurchat_com_.skp time=48
-97 http___www_myrsky_com_cn_.skp time=48
-98 http___www_techiegeex_com_.skp time=49
-82 http___www_fashionoutletsofchicago_com_.skp time=50
-77 http___www_dynamischbureau_nl_.skp time=50
-82 http___www_mayihelpu_co_in_.skp time=50
-84 http___www_vbox7_com_user_history_viewers_.skp time=50
-85 http___www_ktokogda_com_.skp time=50
-85 http___www_propertyturkeysale_com_.skp time=50
-85 http___www_51play_com_.skp time=50
-86 http___www_bayalarm_com_.skp time=50
-87 http___www_eaglepictures_com_.skp time=50
-88 http___www_atlasakvaryum_com_.skp time=50
-91 http___www_pioneerchryslerjeep_com_.skp time=50
-94 http___www_thepulsemag_com_.skp time=50
-95 http___www_dcshoes_com_ph_.skp time=50
-96 http___www_montrealmassage_ca_.skp time=50
-96 http___www_jkshahclasses_com_.skp time=50
-96 http___www_webcamconsult_com_.skp time=51
-100 http___www_bsoscblog_com_.skp time=52
-95 http___www_flaktwoods_com_.skp time=53
-91 http___www_qivivo_com_.skp time=54
-90 http___www_unitender_com_.skp time=56
-97 http___www_casinogaming_com_.skp time=56
-97 http___www_rootdownload_com_.skp time=56
-94 http___www_aspa_ev_de_.skp time=57
-98 http___www_tenpieknyswiat_pl_.skp time=57
-93 http___www_transocean_de_.skp time=58
-94 http___www_vdo2_blogspot_com_.skp time=58
-94 http___www_asmaissexy_com_br_.skp time=58
-100 http___www_prefeiturasjm_com_br_.skp time=60
-100 http___www_eduinsuranceclick_blogspot_com_.skp time=60
-96 http___www_bobdunsire_com_.skp time=61
-96 http___www_omgkettlecorn_com_.skp time=61
-85 http___www_fbbsessions_com_.skp time=62
-86 http___www_hector_ru_.skp time=62
-87 http___www_wereldsupporter_nl_.skp time=62
-90 http___www_arello_com_.skp time=62
-93 http___www_bayerplastics_com_.skp time=62
-93 http___www_superandolamovida_com_ar_.skp time=62
-96 http___www_med_rbf_ru_.skp time=62
-81 http___www_carnegiescience_edu_.skp time=65
-87 http___www_asanewengland_com_.skp time=65
-92 http___www_turkce_karakter_appspot_com_.skp time=65
-94 http___www_k3a_org_.skp time=65
-96 http___www_powermaccenter_com_.skp time=65
-98 http___www_avto49_ru_.skp time=67
-100 http___www_hetoldeambaecht_nl_.skp time=68
-95 http___www_marine_ie_.skp time=69
-96 http___www_quebecvapeboutique_com_.skp time=69
-95 http___www_brays_ingles_com_.skp time=70
-100 http___www_lacondesa_com_.skp time=72
-95 http___www_timbarrathai_com_au_.skp time=76
-95 http___www_cuissedegrenouille_com_.skp time=76
-95 http___www_iwama51_ru_.skp time=76
-99 http___www_fotoantologia_it_.skp time=76
-92 http___www_indian_architects_com_.skp time=78
-92 http___www_totalwomanspa_com_.skp time=78
-100 http___www_fachverband_spielhallen_de_.skp time=83
-93 http___www_golshanemehr_ir_.skp time=84
-95 http___www_maryesses_com_.skp time=84
-99 http___www_ddcorp_ca_.skp time=89
-90 http___www_brontops_com_.skp time=89
-94 http___www_robgolding_com_.skp time=89
-91 http___www_tecban_com_br_.skp time=91
-98 http___www_costamesakarate_com_.skp time=100
-95 http___www_monsexyblog_com_.skp time=103
-97 http___www_stornowaygazette_co_uk_.skp time=103
-93 http___www_fitforaframe_com_.skp time=104
-98 http___www_intentionoftheday_com_.skp time=113
-100 http___www_tailgateclothing_com_.skp time=117
-95 http___www_senbros_com_.skp time=118
-93 http___www_lettoblog_com_.skp time=121
-94 http___www_maxineschallenge_com_au_.skp time=125
-95 http___www_savvycard_net_.skp time=127
-95 http___www_open_ac_mu_.skp time=129
-96 http___www_avgindia_in_.skp time=135
-97 http___www_stocktonseaview_com_.skp time=135
-96 http___www_distroller_com_.skp time=142
-94 http___www_travoggalop_dk_.skp time=144
-100 http___www_history_im_.skp time=144
-94 http___www_playradio_sk_.skp time=145
-92 http___www_linglongglass_com_.skp time=151
-97 http___www_bizzna_com_.skp time=151
-96 http___www_spiros_ws_.skp time=154
-91 http___www_rosen_meents_co_il_.skp time=156
-81 http___www_hoteldeluxeportland_com_.skp time=158
-92 http___www_freetennis_org_.skp time=161
-93 http___www_aircharternetwork_com_au_.skp time=161
-94 http___www_austinparks_org_.skp time=165
-89 http___www_bevvy_co_.skp time=168
-91 http___www_sosyalhile_net_.skp time=168
-98 http___www_minvih_gob_ve_.skp time=171
-89 http___www_streetfoodmtl_com_.skp time=172
-92 http___www_loveslatinas_tumblr_com_.skp time=178
-93 http___www_madbites_co_in_.skp time=180
-94 http___www_rocktarah_ir_.skp time=185
-97 http___www_penthouselife_com_.skp time=185
-96 http___www_appymonkey_com_.skp time=196
-92 http___www_pasargadhotels_com_.skp time=203
-99 http___www_marina_mil_pe_.skp time=203
-89 http___www_kays_co_uk_.skp time=205
-77 http___www_334588_com_.skp time=211
-83 http___www_trendbad24_de_.skp time=211
-81 http___www_cdnetworks_co_kr_.skp time=216
-94 http___www_schellgames_com_.skp time=223
-95 http___www_juliaweddingnews_cn_.skp time=230
-92 http___www_xcrafters_pl_.skp time=253
-93 http___www_pondoo_com_.skp time=253
-96 http___www_helsinkicapitalpartners_fi_.skp time=255
-88 http___www_nadtexican_com_.skp time=259
-85 http___www_canstockphoto_hu_.skp time=266
-78 http___www_ecovacs_com_cn_.skp time=271
-93 http___www_brookfieldplaceny_com_.skp time=334
-93 http___www_fmastrengthtraining_com_.skp time=337
-94 http___www_turtleonthebeach_com_.skp time=394
-90 http___www_temptationthemovie_com_.skp time=413
-95 http___www_patongsawaddi_com_.skp time=491
-91 http___www_online_radio_appspot_com_.skp time=511
-68 http___www_richardmiller_co_uk_.skp time=528
-63 http___www_eschrade_com_.skp time=543
-55 http___www_interaction_inf_br_.skp time=625
-38 http___www_huskyliners_com_.skp time=632
-86 http___granda_net_.skp time=1067
-24 http___www_cocacolafm_com_br_.skp time=1081
-*/
+size_t skipOverCount = sizeof(skipOver) / sizeof(skipOver[0]);
 
-size_t skipOverSeptCount = sizeof(skipOverSept) / sizeof(skipOverSept[0]);
+
+/* customize file in/out here */
+/* TODO : add command flag interface */
+#define CHROME_VERSION "1e5dfa4-4a995df"
+#define SUMMARY_RUN 1
+
+#ifdef SK_BUILD_FOR_WIN
+    #define DRIVE_SPEC "D:"
+    #define PATH_SLASH "\\"
+#else
+    #define DRIVE_SPEC ""
+    #define PATH_SLASH "/"
+#endif
+
+#define IN_DIR_PRE  DRIVE_SPEC PATH_SLASH "skps"   PATH_SLASH "slave"
+#define OUT_DIR_PRE DRIVE_SPEC PATH_SLASH "skpOut" PATH_SLASH "slave"
+#define OUT_DIR_SUM DRIVE_SPEC PATH_SLASH "skpOut" PATH_SLASH "summary"
+#define DIR_POST               PATH_SLASH "All"    PATH_SLASH CHROME_VERSION
+
+static const char outOpDir[]     = "opClip";
+static const char outOldDir[]    = "oldClip";
+static const char outStatusDir[] = "statusTest";
+
+static SkString get_in_path(int dirNo, const char* filename) {
+    SkString path;
+    SkASSERT(dirNo);
+    path.appendf("%s%d%s", IN_DIR_PRE, dirNo, DIR_POST);
+    if (!sk_exists(path.c_str())) {
+        SkDebugf("could not read %s\n", path.c_str());
+        return SkString();
+    }
+    if (filename) {
+        path.appendf("%s%s", PATH_SLASH, filename);
+        if (!sk_exists(path.c_str())) {        
+            SkDebugf("could not read %s\n", path.c_str());
+            return SkString();
+        }
+    }
+    return path;
+}
+    
+static void make_recursive_dir(const SkString& path) {
+    if (sk_exists(path.c_str())) {
+        return;
+    }
+    const char* pathStr = path.c_str();
+    int last = (int) path.size();
+    do {
+        while (last > 0 && pathStr[--last] != PATH_SLASH[0])
+            ;
+        SkASSERT(last > 0);
+        SkString shorter(pathStr, last);
+        if (sk_mkdir(shorter.c_str())) {
+            break;
+        }
+    } while (true);
+    do {
+        while (last < (int) path.size() && pathStr[++last] != PATH_SLASH[0])
+            ;
+        SkString shorter(pathStr, last);
+        SkAssertResult(sk_mkdir(shorter.c_str()));
+    } while (last < (int) path.size());
+}
+
+static SkString get_out_path(int dirNo, const char* dirName) {
+    SkString path;
+    SkASSERT(dirNo);
+    SkASSERT(dirName);
+    path.appendf("%s%d%s%s%s", OUT_DIR_PRE, dirNo, DIR_POST, PATH_SLASH, dirName);
+    make_recursive_dir(path);
+    return path;
+}
+  
+static SkString get_sum_path(const char* dirName) {
+    SkString path;
+    SkASSERT(dirName);
+    path.appendf("%s%d%s%s", OUT_DIR_SUM, SUMMARY_RUN, PATH_SLASH, dirName);
+    SkDebugf("%s\n", path.c_str());
+    make_recursive_dir(path);
+    return path;
+}
+
+static SkString make_png_name(const char* filename) {
+    SkString pngName = SkString(filename);
+    pngName.remove(pngName.size() - 3, 3);
+    pngName.append("png");
+    return pngName;
+}
+
+////////////////////////////////////////////////////////
 
 enum TestStep {
     kCompareBits,
@@ -238,6 +166,13 @@
         fTestStep = kCompareBits;
         fScale = 1;
     }
+    
+    void init(int dirNo, const SkString& filename) {
+        fDirNo = dirNo;
+        strcpy(fFilename, filename.c_str());
+        fTestStep = kCompareBits;
+        fScale = 1;       
+    }
 
     SkString status() {
         SkString outStr;
@@ -262,14 +197,6 @@
 
     }
 
-    static void Test(int dirNo, const char* filename, TestStep testStep) {
-        TestResult test;
-        test.init(dirNo);
-        test.fTestStep = testStep;
-        strcpy(test.fFilename, filename);
-        test.testOne();
-    }
-
     void test(int dirNo, const SkString& filename) {
         init(dirNo);
         strcpy(fFilename, filename.c_str());
@@ -277,7 +204,7 @@
     }
 
     void testOne();
-
+    
     char fFilename[kMaxLength];
     TestStep fTestStep;
     int fDirNo;
@@ -308,28 +235,24 @@
 };
 
 struct TestState {
-    void init(int dirNo, skiatest::Reporter* reporter) {
-        fReporter = reporter;
+    void init(int dirNo) {
         fResult.init(dirNo);
     }
 
     SkTDArray<SortByPixel> fPixelWorst;
     SkTDArray<SortByTime> fSlowest;
-    skiatest::Reporter* fReporter;
     TestResult fResult;
 };
 
 struct TestRunner {
-    TestRunner(skiatest::Reporter* reporter, int threadCount)
-        : fNumThreads(threadCount)
-        , fReporter(reporter) {
+    TestRunner(int threadCount)
+        : fNumThreads(threadCount) {
     }
 
     ~TestRunner();
     void render();
     int fNumThreads;
     SkTDArray<class TestRunnable*> fRunnables;
-    skiatest::Reporter* fReporter;
 };
 
 class TestRunnable : public SkRunnable {
@@ -347,7 +270,7 @@
 class TestRunnableDir : public TestRunnable {
 public:
     TestRunnableDir(void (*testFun)(TestState*), int dirNo, TestRunner* runner) {
-        fState.init(dirNo, runner->fReporter);
+        fState.init(dirNo);
         fTestFun = testFun;
     }
 
@@ -356,7 +279,7 @@
 class TestRunnableFile : public TestRunnable {
 public:
     TestRunnableFile(void (*testFun)(TestState*), int dirNo, const char* name, TestRunner* runner) {
-        fState.init(dirNo, runner->fReporter);
+        fState.init(dirNo);
         strcpy(fState.fResult.fFilename, name);
         fTestFun = testFun;
     }
@@ -385,74 +308,6 @@
 
 ////////////////////////////////////////////////
 
-static const char outOpDir[] = OUT_DIR "opClip";
-static const char outOldDir[] = OUT_DIR "oldClip";
-static const char outSkpDir[] = OUT_DIR "skpTest";
-static const char outDiffDir[] = OUT_DIR "outTest";
-static const char outStatusDir[] = OUT_DIR "statusTest";
-
-static SkString make_filepath(int dirNo, const char* dir, const char* name) {
-    SkString path(dir);
-    if (dirNo) {
-        path.appendf("%d", dirNo);
-    }
-    path.append(PATH_SLASH);
-    path.append(name);
-    return path;
-}
-
-static SkString make_in_dir_name(int dirNo) {
-    SkString dirName(IN_DIR);
-    dirName.appendf("%d", dirNo);
-    if (!sk_exists(dirName.c_str())) {
-        SkDebugf("could not read dir %s\n", dirName.c_str());
-        return SkString();
-    }
-    return dirName;
-}
-
-static SkString make_stat_dir_name(int dirNo) {
-    SkString dirName(outStatusDir);
-    dirName.appendf("%d", dirNo);
-    if (!sk_exists(dirName.c_str())) {
-        SkDebugf("could not read dir %s\n", dirName.c_str());
-        return SkString();
-    }
-    return dirName;
-}
-
-static bool make_one_out_dir(const char* outDirStr) {
-    SkString outDir = make_filepath(0, outDirStr, "");
-    if (!sk_exists(outDir.c_str())) {
-        if (!sk_mkdir(outDir.c_str())) {
-            SkDebugf("could not create dir %s\n", outDir.c_str());
-            return false;
-        }
-    }
-    return true;
-}
-
-static bool make_out_dirs() {
-    SkString outDir = make_filepath(0, OUT_DIR, "");
-    if (!sk_exists(outDir.c_str())) {
-        if (!sk_mkdir(outDir.c_str())) {
-            SkDebugf("could not create dir %s\n", outDir.c_str());
-            return false;
-        }
-    }
-    return make_one_out_dir(outOldDir)
-            && make_one_out_dir(outOpDir)
-            && make_one_out_dir(outSkpDir)
-            && make_one_out_dir(outDiffDir)
-            && make_one_out_dir(outStatusDir);
-}
-
-static SkString make_png_name(const char* filename) {
-    SkString pngName = SkString(filename);
-    pngName.remove(pngName.size() - 3, 3);
-    pngName.append("png");
-    return pngName;
-}
 
 static int similarBits(const SkBitmap& gr, const SkBitmap& sk) {
     const int kRowCount = 3;
@@ -574,9 +429,9 @@
 }
 
 static void writePict(const SkBitmap& bitmap, const char* outDir, const char* pngName) {
-    SkString outFile = make_filepath(0, outDir, pngName);
-    if (!SkImageEncoder::EncodeFile(outFile.c_str(), bitmap,
-            SkImageEncoder::kPNG_Type, 100)) {
+    SkString outFile = get_sum_path(outDir);
+    outFile.appendf("%s%s", PATH_SLASH, pngName);
+    if (!SkImageEncoder::EncodeFile(outFile.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100)) {
         SkDebugf("unable to encode gr %s (width=%d height=%d)\n", pngName,
                     bitmap.width(), bitmap.height());
     }
@@ -607,7 +462,7 @@
             strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
         }
     #endif
-        SkString path = make_filepath(fDirNo, IN_DIR, fFilename);
+        SkString path = get_in_path(fDirNo, fFilename);
         SkFILEStream stream(path.c_str());
         if (!stream.isValid()) {
             SkDebugf("invalid stream %s\n", path.c_str());
@@ -665,189 +520,255 @@
     }
 }
 
-static SkString makeStatusString(int dirNo) {
-    SkString statName;
-    statName.printf("stats%d.txt", dirNo);
-    SkString statusFile = make_filepath(0, outStatusDir, statName.c_str());
-    return statusFile;
+DEFINE_string2(match, m, "PathOpsSkpClipThreaded",
+        "[~][^]substring[$] [...] of test name to run.\n"
+        "Multiple matches may be separated by spaces.\n"
+        "~ causes a matching test to always be skipped\n"
+        "^ requires the start of the test to match\n"
+        "$ requires the end of the test to match\n"
+        "^ and $ requires an exact match\n"
+        "If a test does not match any list entry,\n"
+        "it is skipped unless some list entry starts with ~");
+DEFINE_string2(dir, d, NULL, "range of directories (e.g., 1-100)");
+DEFINE_string2(skp, s, NULL, "skp to test");
+DEFINE_bool2(single, z, false, "run tests on a single thread internally.");
+DEFINE_int32(testIndex, 0, "override local test index (PathOpsSkpClipOneOff only).");
+DEFINE_int32(threads, SkThreadPool::kThreadPerCore,
+        "Run threadsafe tests on a threadpool with this many threads.");
+DEFINE_bool2(verbose, v, false, "enable verbose output.");
+
+static bool verbose() {
+    return FLAGS_verbose;
 }
 
-class PreParser {
+static int getThreadCount() {
+    return FLAGS_single ? 1 : FLAGS_threads;
+}
+
+class Dirs {
 public:
-    PreParser(int dirNo, bool threaded)
-        : fDirNo(dirNo)
-        , fIndex(0)
-        , fThreaded(threaded) {
-        SkString statusPath = makeStatusString(dirNo);
-        if (!sk_exists(statusPath.c_str())) {
-            return;
+    Dirs() {
+        reset();
+        sk_bzero(fRun, sizeof(fRun));
+        fSet = false;
+    }
+
+    int first() const {
+        int index = 0;
+        while (++index < kMaxDir) {
+            if (fRun[index]) {
+                return index;
+            }
         }
-        SkFILEStream reader;
-        reader.setPath(statusPath.c_str());
-        while (fetch(reader, &fResults.push_back()))
+        SkASSERT(0);
+        return -1;
+    }
+
+    int last() const {
+        int index = kMaxDir;
+        while (--index > 0 && !fRun[index])
             ;
-        fResults.pop_back();
+        return index;
     }
 
-    bool fetch(SkFILEStream& reader, TestResult* result) {
-        char c;
-        int i = 0;
-        result->init(fDirNo);
-        result->fPixelError = 0;
-        result->fTime = 0;
-        do {
-            bool readOne = reader.read(&c, 1) != 0;
-            if (!readOne) {
-//                SkASSERT(i == 0);   // the current text may be incomplete -- if so, ignore it
-                return false;
+    int next() {
+        while (++fIndex < kMaxDir) {
+            if (fRun[fIndex]) {
+                return fIndex;
             }
-            if (c == ' ') {
-                result->fFilename[i++] = '\0';
-                break;
-            }
-            result->fFilename[i++] = c;
-            SkASSERT(i < kMaxLength);
-        } while (true);
-        do {
-            if (!reader.read(&c, 1)) {
-                return false;
-            }
-            if (c == ' ') {
-                break;
-            }
-            SkASSERT(c >= '0' && c <= '9');
-            result->fPixelError = result->fPixelError * 10 + (c - '0');
-        } while (true);
-        bool minus = false;
-        do {
-            if (!reader.read(&c, 1)) {
-                return false;
-            }
-            if (c == '\n') {
-                break;
-            }
-            if (c == '-') {
-                minus = true;
-                continue;
-            }
-            SkASSERT(c >= '0' && c <= '9');
-            result->fTime = result->fTime * 10 + (c - '0');
-        } while (true);
-        if (minus) {
-            result->fTime = -result->fTime;
         }
-        return true;
+        return -1;
     }
 
-    bool match(const SkString& filename, SkFILEWStream* stream, TestResult* result) {
-        if (fThreaded) {
-            for (int index = 0; index < fResults.count(); ++index) {
-                const TestResult& test = fResults[index];
-                if (filename.equals(test.fFilename)) {
-                    *result = test;
-                    SkString outStr(result->status());
-                    stream->write(outStr.c_str(), outStr.size());
-                    return true;
-                }
-            }
-        } else if (fIndex < fResults.count()) {
-            *result = fResults[fIndex++];
-            SkASSERT(filename.equals(result->fFilename));
-            SkString outStr(result->status());
-            stream->write(outStr.c_str(), outStr.size());
-            return true;
+    void reset() {
+        fIndex = -1;
+    }
+
+    void set(int start, int end) {
+        while (start < end) {
+            fRun[start++] = 1;
         }
-        return false;
+        fSet = true;
+    }
+
+    void setDefault() {
+        if (!fSet) {
+            set(1, 100);
+        }
     }
 
 private:
-    int fDirNo;
+    enum {
+         kMaxDir = 101
+    };
+    char fRun[kMaxDir];
     int fIndex;
-    SkTArray<TestResult, true> fResults;
-    bool fThreaded;
-};
+    bool fSet;
+} gDirs;
 
-static bool doOneDir(TestState* state, bool threaded) {
-    int dirNo = state->fResult.fDirNo;
-    skiatest::Reporter* reporter = state->fReporter;
-    SkString dirName = make_in_dir_name(dirNo);
+class Filenames {
+public:
+    Filenames()
+        : fIndex(-1) {
+    }
+
+    const char* next() {
+        while (fNames && ++fIndex < fNames->count()) {
+            return (*fNames)[fIndex];
+        }
+        return NULL;
+    }
+    
+    void set(const SkCommandLineFlags::StringArray& names) {
+        fNames = &names;
+    }
+
+private:
+    int fIndex;
+    const SkCommandLineFlags::StringArray* fNames;
+} gNames;
+
+static bool buildTestDir(int dirNo, int firstDirNo, 
+        SkTDArray<TestResult>* tests, SkTDArray<SortByName*>* sorted) {
+    SkString dirName = get_out_path(dirNo, outStatusDir);
     if (!dirName.size()) {
         return false;
     }
     SkOSFile::Iter iter(dirName.c_str(), "skp");
     SkString filename;
-    int testCount = 0;
-    PreParser preParser(dirNo, threaded);
-    SkFILEWStream statusStream(makeStatusString(dirNo).c_str());
     while (iter.next(&filename)) {
-        for (size_t index = 0; index < skipOverSeptCount; ++index) {
-            if (skipOverSept[index].directory == dirNo
-                    && strcmp(filename.c_str(), skipOverSept[index].filename) == 0) {
-                goto checkEarlyExit;
+        TestResult test;
+        test.init(dirNo);
+        SkString spaceFile(filename);
+        char* spaces = spaceFile.writable_str();
+        int spaceSize = (int) spaceFile.size();
+        for (int index = 0; index < spaceSize; ++index) {
+            if (spaces[index] == '.') {
+                spaces[index] = ' ';
             }
         }
-        if (preParser.match(filename, &statusStream, &state->fResult)) {
-            (void) addError(state, state->fResult);
-            ++testCount;
-            goto checkEarlyExit;
+        int success = sscanf(spaces, "%s %d %d skp", test.fFilename,
+                &test.fPixelError, &test.fTime);
+        if (success < 3) {
+            SkDebugf("failed to scan %s matched=%d\n", filename.c_str(), success);
+            return false;
         }
-        {
-            TestResult& result = state->fResult;
-            result.test(dirNo, filename);
-            SkString outStr(result.status());
-            statusStream.write(outStr.c_str(), outStr.size());
-            statusStream.flush();
-            if (addError(state, result)) {
-                SkDebugf("%s", result.progress().c_str());
-            }
-        }
-        ++testCount;
-        if (reporter->verbose()) {
-            SkDebugf(".");
-            if (++testCount % 100 == 0) {
-                SkDebugf("%d\n", testCount);
-            }
-        }
-checkEarlyExit:
-        if (0 && testCount >= 1) {
-            return true;
+        *tests[dirNo - firstDirNo].append() = test;
+    }
+    if (!sorted) {
+        return true;
+    }
+    SkTDArray<TestResult>& testSet = tests[dirNo - firstDirNo];
+    int count = testSet.count();
+    for (int index = 0; index < count; ++index) {
+        *sorted[dirNo - firstDirNo].append() = (SortByName*) &testSet[index];
+    }
+    if (sorted[dirNo - firstDirNo].count()) {
+        SkTQSort<SortByName>(sorted[dirNo - firstDirNo].begin(),
+                sorted[dirNo - firstDirNo].end() - 1);
+        if (verbose()) {
+            SkDebugf("+");
         }
     }
     return true;
 }
 
-static bool initTest() {
+static void testSkpClip(TestState* data) {
+    data->fResult.testOne();
+    SkString statName(data->fResult.fFilename);
+    SkASSERT(statName.endsWith(".skp"));
+    statName.remove(statName.size() - 4, 4);
+    statName.appendf(".%d.%d.skp", data->fResult.fPixelError, data->fResult.fTime);
+    SkString statusFile = get_out_path(data->fResult.fDirNo, outStatusDir);
+    if (!statusFile.size()) {
+        SkDebugf("failed to create %s", statusFile.c_str());
+        return;
+    }
+    statusFile.appendf("%s%s", PATH_SLASH, statName.c_str());
+    SkFILE* file = sk_fopen(statusFile.c_str(), kWrite_SkFILE_Flag);
+    if (!file) {
+            SkDebugf("failed to create %s", statusFile.c_str());
+            return;
+    }
+    sk_fclose(file);
+    if (verbose()) {
+        if (data->fResult.fPixelError || data->fResult.fTime) {
+            SkDebugf("%s", data->fResult.progress().c_str());
+        } else {
+            SkDebugf(".");
+        }
+    }
+}
+
+bool Less(const SortByName& a, const SortByName& b);
+bool Less(const SortByName& a, const SortByName& b) {
+    return a < b;
+}
+
+static bool doOneDir(TestState* state, bool threaded) {
+    int dirNo = state->fResult.fDirNo;
+    SkString dirName = get_in_path(dirNo, NULL);
+    if (!dirName.size()) {
+        return false;
+    }
+    SkTDArray<TestResult> tests[1];
+    SkTDArray<SortByName*> sorted[1];
+    if (!buildTestDir(dirNo, dirNo, tests, sorted)) {
+        return false;
+    }
+    SkOSFile::Iter iter(dirName.c_str(), "skp");
+    SkString filename;
+    while (iter.next(&filename)) {
+        for (size_t index = 0; index < skipOverCount; ++index) {
+            if (skipOver[index].directory == dirNo
+                    && strcmp(filename.c_str(), skipOver[index].filename) == 0) {
+                goto checkEarlyExit;
+            }
+        }
+        {
+            SortByName name;
+            name.init(dirNo);
+            strncpy(name.fFilename, filename.c_str(), filename.size() - 4);  // drop .skp
+            int count = sorted[0].count();
+            int idx = SkTSearch<SortByName, Less>(sorted[0].begin(), count, &name, sizeof(&name));
+            if (idx >= 0) {
+                SortByName* found = sorted[0][idx];
+                (void) addError(state, *found);
+                continue;
+            }
+            TestResult test;
+            test.init(dirNo, filename);
+            state->fResult = test;
+            testSkpClip(state);
+#if 0 // artificially limit to a few while debugging code
+            static int debugLimit = 0;
+            if (++debugLimit == 5) {
+                return true;
+            }
+#endif
+        }
+checkEarlyExit:
+        ;
+    }
+    return true;
+}
+
+static void initTest() {
 #if !defined SK_BUILD_FOR_WIN && !defined SK_BUILD_FOR_MAC
     SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true);
     SK_CONF_SET("images.png.suppressDecoderWarnings", true);
 #endif
-    return make_out_dirs();
 }
 
-static bool initUberTest(int firstDirNo, int lastDirNo) {
-    if (!initTest()) {
-        return false;
-    }
-    for (int index = firstDirNo; index <= lastDirNo; ++index) {
-        SkString statusDir(outStatusDir);
-        statusDir.appendf("%d", index);
-        if (!make_one_out_dir(statusDir.c_str())) {
-            return false;
-        }
-    }
-    return true;
-}
-
-
 static void testSkpClipEncode(TestState* data) {
     data->fResult.testOne();
-    if (data->fReporter->verbose()) {
-       SkDebugf("+");
+    if (verbose()) {
+        SkDebugf("+");
     }
 }
 
-static void encodeFound(skiatest::Reporter* reporter, TestState& state) {
-    if (reporter->verbose()) {
+static void encodeFound(TestState& state) {
+    if (verbose()) {
         if (state.fPixelWorst.count()) {
             SkTDArray<SortByPixel*> worst;
             for (int index = 0; index < state.fPixelWorst.count(); ++index) {
@@ -873,9 +794,8 @@
             }
         }
     }
-
-    int threadCount = reporter->allowThreaded() ? SkThreadPool::kThreadPerCore : 1;
-    TestRunner testRunner(reporter, threadCount);
+    int threadCount = getThreadCount();
+    TestRunner testRunner(threadCount);
     for (int index = 0; index < state.fPixelWorst.count(); ++index) {
         const TestResult& result = state.fPixelWorst[index];
         SkString filename(result.fFilename);
@@ -886,28 +806,50 @@
                 (&testSkpClipEncode, result.fDirNo, filename.c_str(), &testRunner));
     }
     testRunner.render();
-#if 0
-    for (int index = 0; index < state.fPixelWorst.count(); ++index) {
-        const TestResult& result = state.fPixelWorst[index];
-        SkString filename(result.fFilename);
-        if (!filename.endsWith(".skp")) {
-            filename.append(".skp");
-        }
-        TestResult::Test(result.fDirNo, filename.c_str(), kEncodeFiles);
-        if (reporter->verbose()) SkDebugf("+");
-    }
-#endif
 }
 
-DEF_TEST(PathOpsSkpClip, reporter) {
-    if (!initTest()) {
-        return;
-    }
+class Test {
+public:
+    Test() {}
+    virtual ~Test() {}
+
+    const char* getName() { onGetName(&fName); return fName.c_str(); }
+    void run() { onRun(); }
+
+protected:
+    virtual void onGetName(SkString*) = 0;
+    virtual void onRun() = 0;
+
+private:
+    SkString    fName;
+};
+
+typedef SkTRegistry<Test*(*)(void*)> TestRegistry;
+
+#define DEF_TEST(name)                                        \
+    static void test_##name();                       \
+    class name##Class : public Test {                                   \
+    public:                                                             \
+        static Test* Factory(void*) { return SkNEW(name##Class); }      \
+    protected:                                                          \
+        virtual void onGetName(SkString* name) SK_OVERRIDE {            \
+            name->set(#name);                                           \
+        }                                                               \
+        virtual void onRun() SK_OVERRIDE { test_##name(); } \
+    };                                                                  \
+    static TestRegistry gReg_##name##Class(name##Class::Factory);       \
+    static void test_##name()
+
+DEF_TEST(PathOpsSkpClip) {
+    gDirs.setDefault();
+    initTest();
     SkTArray<TestResult, true> errors;
     TestState state;
-    state.init(0, reporter);
-    for (int dirNo = 1; dirNo <= 100; ++dirNo) {
-        if (reporter->verbose()) {
+    state.init(0);
+    int dirNo;
+    gDirs.reset();
+    while ((dirNo = gDirs.next()) > 0) {
+        if (verbose()) {
             SkDebugf("dirNo=%d\n", dirNo);
         }
         state.fResult.fDirNo = dirNo;
@@ -915,28 +857,29 @@
             break;
         }
     }
-    encodeFound(reporter, state);
+    encodeFound(state);
 }
 
 static void testSkpClipMain(TestState* data) {
         (void) doOneDir(data, true);
 }
 
-DEF_TEST(PathOpsSkpClipThreaded, reporter) {
-    if (!initTest()) {
-        return;
-    }
-    int threadCount = reporter->allowThreaded() ? SkThreadPool::kThreadPerCore : 1;
-    TestRunner testRunner(reporter, threadCount);
-    const int firstDirNo = 1;
-    for (int dirNo = firstDirNo; dirNo <= 100; ++dirNo) {
+DEF_TEST(PathOpsSkpClipThreaded) {
+    gDirs.setDefault();
+    initTest();
+    int threadCount = getThreadCount();
+    TestRunner testRunner(threadCount);
+    int dirNo;
+    gDirs.reset();
+    while ((dirNo = gDirs.next()) > 0) {
         *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnableDir,
                 (&testSkpClipMain, dirNo, &testRunner));
     }
     testRunner.render();
     TestState state;
-    state.init(0, reporter);
-    for (int dirNo = firstDirNo; dirNo <= 100; ++dirNo) {
+    state.init(0);
+    gDirs.reset();
+    while ((dirNo = gDirs.next()) > 0) {
         TestState& testState = testRunner.fRunnables[dirNo - 1]->fState;
         SkASSERT(testState.fResult.fDirNo == dirNo);
         for (int inner = 0; inner < testState.fPixelWorst.count(); ++inner) {
@@ -946,124 +889,59 @@
             addError(&state, testState.fSlowest[inner]);
         }
     }
-    encodeFound(reporter, state);
+    encodeFound(state);
 }
-
-static void testSkpClipUber(TestState* data) {
-    data->fResult.testOne();
-    SkString dirName = make_stat_dir_name(data->fResult.fDirNo);
-    if (!dirName.size()) {
-        return;
-    }
-    SkString statName(data->fResult.fFilename);
-    SkASSERT(statName.endsWith(".skp"));
-    statName.remove(statName.size() - 4, 4);
-    statName.appendf(".%d.%d.skp", data->fResult.fPixelError, data->fResult.fTime);
-    SkString statusFile = make_filepath(data->fResult.fDirNo, outStatusDir, statName.c_str());
-    SkFILE* file = sk_fopen(statusFile.c_str(), kWrite_SkFILE_Flag);
-    if (!file) {
-            SkDebugf("failed to create %s", statusFile.c_str());
-            return;
-    }
-    sk_fclose(file);
-    if (data->fReporter->verbose()) {
-        if (data->fResult.fPixelError || data->fResult.fTime) {
-            SkDebugf("%s", data->fResult.progress().c_str());
-        } else {
-            SkDebugf(".");
-        }
-    }
-}
-
-static bool buildTests(skiatest::Reporter* reporter, int firstDirNo, int lastDirNo, SkTDArray<TestResult>* tests,
-        SkTDArray<SortByName*>* sorted) {
-    for (int dirNo = firstDirNo; dirNo <= lastDirNo; ++dirNo) {
-        SkString dirName = make_stat_dir_name(dirNo);
-        if (!dirName.size()) {
+ 
+static bool buildTests(SkTDArray<TestResult>* tests, SkTDArray<SortByName*>* sorted) {
+    int firstDirNo = gDirs.first();
+    int dirNo;
+    while ((dirNo = gDirs.next()) > 0) {
+        if (!buildTestDir(dirNo, firstDirNo, tests, sorted)) {
             return false;
         }
-        SkOSFile::Iter iter(dirName.c_str(), "skp");
-        SkString filename;
-        while (iter.next(&filename)) {
-            TestResult test;
-            test.init(dirNo);
-            SkString spaceFile(filename);
-            char* spaces = spaceFile.writable_str();
-            int spaceSize = (int) spaceFile.size();
-            for (int index = 0; index < spaceSize; ++index) {
-                if (spaces[index] == '.') {
-                    spaces[index] = ' ';
-                }
-            }
-            int success = sscanf(spaces, "%s %d %d skp", test.fFilename,
-                    &test.fPixelError, &test.fTime);
-            if (success < 3) {
-                SkDebugf("failed to scan %s matched=%d\n", filename.c_str(), success);
-                return false;
-            }
-            *tests[dirNo - firstDirNo].append() = test;
-        }
-        if (!sorted) {
-            continue;
-        }
-        SkTDArray<TestResult>& testSet = tests[dirNo - firstDirNo];
-        int count = testSet.count();
-        for (int index = 0; index < count; ++index) {
-            *sorted[dirNo - firstDirNo].append() = (SortByName*) &testSet[index];
-        }
-        if (sorted[dirNo - firstDirNo].count()) {
-            SkTQSort<SortByName>(sorted[dirNo - firstDirNo].begin(),
-                    sorted[dirNo - firstDirNo].end() - 1);
-            if (reporter->verbose()) {
-                SkDebugf("+");
-            }
-       }
     }
     return true;
 }
 
-bool Less(const SortByName& a, const SortByName& b);
-bool Less(const SortByName& a, const SortByName& b) {
-    return a < b;
-}
-
-DEF_TEST(PathOpsSkpClipUberThreaded, reporter) {
-    const int firstDirNo = 1;
-    const int lastDirNo = 100;
-    if (!initUberTest(firstDirNo, lastDirNo)) {
+DEF_TEST(PathOpsSkpClipUberThreaded) {
+    gDirs.setDefault();
+    const int firstDirNo = gDirs.next();
+    const int lastDirNo = gDirs.last();
+    initTest();
+    int dirCount = lastDirNo - firstDirNo + 1;
+    SkAutoTDeleteArray<SkTDArray<TestResult> > tests(new SkTDArray<TestResult>[dirCount]);
+    SkAutoTDeleteArray<SkTDArray<SortByName*> > sorted(new SkTDArray<SortByName*>[dirCount]);
+    if (!buildTests(tests.get(), sorted.get())) {
         return;
     }
-    const int dirCount = lastDirNo - firstDirNo + 1;
-    SkTDArray<TestResult> tests[dirCount];
-    SkTDArray<SortByName*> sorted[dirCount];
-    if (!buildTests(reporter, firstDirNo, lastDirNo, tests, sorted)) {
-        return;
-    }
-    int threadCount = reporter->allowThreaded() ? SkThreadPool::kThreadPerCore : 1;
-    TestRunner testRunner(reporter, threadCount);
-    for (int dirNo = firstDirNo; dirNo <= lastDirNo; ++dirNo) {
-        SkString dirName = make_in_dir_name(dirNo);
+    int threadCount = getThreadCount();
+    TestRunner testRunner(threadCount);
+    int dirNo;
+    gDirs.reset();
+    while ((dirNo = gDirs.next()) > 0) {
+        SkString dirName = get_in_path(dirNo, NULL);
         if (!dirName.size()) {
             continue;
         }
         SkOSFile::Iter iter(dirName.c_str(), "skp");
         SkString filename;
         while (iter.next(&filename)) {
-            int count;
-            SortByName name;
-            for (size_t index = 0; index < skipOverSeptCount; ++index) {
-                if (skipOverSept[index].directory == dirNo
-                        && strcmp(filename.c_str(), skipOverSept[index].filename) == 0) {
+            for (size_t index = 0; index < skipOverCount; ++index) {
+                if (skipOver[index].directory == dirNo
+                        && strcmp(filename.c_str(), skipOver[index].filename) == 0) {
                     goto checkEarlyExit;
                 }
             }
-            name.init(dirNo);
-            strncpy(name.fFilename, filename.c_str(), filename.size() - 4);  // drop .skp
-            count = sorted[dirNo - firstDirNo].count();
-            if (SkTSearch<SortByName, Less>(sorted[dirNo - firstDirNo].begin(),
-                    count, &name, sizeof(&name)) < 0) {
-                *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnableFile,
-                        (&testSkpClipUber, dirNo, filename.c_str(), &testRunner));
+            {
+                SortByName name;
+                name.init(dirNo);
+                strncpy(name.fFilename, filename.c_str(), filename.size() - 4);  // drop .skp
+                int count = sorted.get()[dirNo - firstDirNo].count();
+                if (SkTSearch<SortByName, Less>(sorted.get()[dirNo - firstDirNo].begin(),
+                        count, &name, sizeof(&name)) < 0) {
+                    *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnableFile,
+                            (&testSkpClip, dirNo, filename.c_str(), &testRunner));
+                }
             }
     checkEarlyExit:
             ;
@@ -1071,13 +949,13 @@
 
     }
     testRunner.render();
-    SkTDArray<TestResult> results[dirCount];
-    if (!buildTests(reporter, firstDirNo, lastDirNo, results, NULL)) {
+    SkAutoTDeleteArray<SkTDArray<TestResult> > results(new SkTDArray<TestResult>[dirCount]);
+    if (!buildTests(results.get(), NULL)) {
         return;
     }
     SkTDArray<TestResult> allResults;
     for (int dirNo = firstDirNo; dirNo <= lastDirNo; ++dirNo) {
-        SkTDArray<TestResult>& array = results[dirNo - firstDirNo];
+        SkTDArray<TestResult>& array = results.get()[dirNo - firstDirNo];
         allResults.append(array.count(), array.begin());
     }
     int allCount = allResults.count();
@@ -1100,22 +978,144 @@
             *state.fSlowest.append() = *times[allCount - inner - 1];
         }
     }
-    encodeFound(reporter, state);
+    encodeFound(state);
 }
 
-DEF_TEST(PathOpsSkpClipOneOff, reporter) {
-    if (!initTest()) {
-        return;
+DEF_TEST(PathOpsSkpClipOneOff) {
+    const int testIndex = FLAGS_testIndex;
+    int dirNo = gDirs.next();
+    if (dirNo < 0) {
+        dirNo = skipOver[testIndex].directory;
     }
-    const int testIndex = 43 - 37;
-    int dirNo = skipOverSept[testIndex].directory;
-    SkAssertResult(make_in_dir_name(dirNo).size());
-    SkString filename(skipOverSept[testIndex].filename);
+    const char* skp = gNames.next();
+    if (!skp) {
+        skp = skipOver[testIndex].filename;
+    }
+    initTest();
+    SkAssertResult(get_in_path(dirNo, skp).size());
+    SkString filename(skp);
     TestResult state;
     state.test(dirNo, filename);
-    if (reporter->verbose()) {
+    if (verbose()) {
         SkDebugf("%s", state.status().c_str());
     }
     state.fTestStep = kEncodeFiles;
     state.testOne();
 }
+
+DEF_TEST(PathOpsTestSkipped) {
+    for (size_t index = 0; index < skipOverCount; ++index) {
+        const SkipOverTest& skip = skipOver[index];
+        if (!skip.blamePathOps) {
+            continue;
+        }
+        int dirNo = skip.directory;
+        const char* skp = skip.filename;
+        initTest();
+        SkAssertResult(get_in_path(dirNo, skp).size());
+        SkString filename(skp);
+        TestResult state;
+        state.test(dirNo, filename);
+        if (verbose()) {
+            SkDebugf("%s", state.status().c_str());
+        }
+        state.fTestStep = kEncodeFiles;
+        state.testOne();
+    }
+}
+
+DEF_TEST(PathOpsCopyFails) {
+    FLAGS_verbose = true;
+    for (size_t index = 0; index < skipOverCount; ++index) {
+        int dirNo = skipOver[index].directory;
+        SkDebugf("mkdir -p " IN_DIR_PRE "%d" DIR_POST "\n", dirNo);
+    }
+    for (size_t index = 0; index < skipOverCount; ++index) {
+        int dirNo = skipOver[index].directory;
+        const char* filename = skipOver[index].filename;
+        SkDebugf("rsync -av cary-linux.cnc:/tera" PATH_SLASH "skps" PATH_SLASH "slave"
+            "%d" DIR_POST "/%s " IN_DIR_PRE "%d" DIR_POST "\n", dirNo, filename, dirNo);
+    }
+}
+
+template TestRegistry* TestRegistry::gHead;
+
+class Iter {
+public:
+    Iter() { this->reset(); }
+    void reset() { fReg = TestRegistry::Head(); }
+
+    Test* next() {
+        if (fReg) {
+            TestRegistry::Factory fact = fReg->factory();
+            fReg = fReg->next();
+            Test* test = fact(NULL);
+            return test;
+        }
+        return NULL;
+    }
+
+private:
+    const TestRegistry* fReg;
+};
+
+int tool_main(int argc, char** argv);
+int tool_main(int argc, char** argv) {
+    SetupCrashHandler();
+    SkCommandLineFlags::SetUsage("");
+    SkCommandLineFlags::Parse(argc, argv);
+    SkGraphics::Init();
+    SkString header("PathOps SkpClip:");
+    if (!FLAGS_match.isEmpty()) {
+        header.appendf(" --match");
+        for (int index = 0; index < FLAGS_match.count(); ++index) {
+            header.appendf(" %s", FLAGS_match[index]);
+        }
+    }
+    if (!FLAGS_dir.isEmpty()) {
+        int count = FLAGS_dir.count();
+        for (int i = 0; i < count; ++i) {
+            const char* range = FLAGS_dir[i];
+            const char* dash = strchr(range, '-');
+            if (!dash) {
+                dash = strchr(range, ',');
+            }
+            int first = atoi(range);
+            int last = dash ? atoi(dash + 1) : first;
+            if (!first || !last) {
+                SkDebugf("couldn't parse --dir %s\n", range);
+                return 1;
+            }
+            gDirs.set(first, last);
+        }
+    }
+    if (!FLAGS_skp.isEmpty()) {
+        gNames.set(FLAGS_skp);
+    }
+#ifdef SK_DEBUG
+    header.append(" SK_DEBUG");
+#else
+    header.append(" SK_RELEASE");
+#endif
+    header.appendf(" skia_arch_width=%d", (int)sizeof(void*) * 8);
+    if (FLAGS_verbose) {
+        header.appendf("\n");
+    }
+    SkDebugf(header.c_str());
+    Iter iter;
+    Test* test;
+    while ((test = iter.next()) != NULL) {
+        SkAutoTDelete<Test> owned(test);
+        if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, test->getName())) {
+            test->run();
+        }
+    }
+    SkGraphics::Term();
+    return 0;
+}
+
+#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
+int main(int argc, char * const argv[]) {
+    return tool_main(argc, (char**) argv);
+}
+#endif
diff --git a/tests/PathOpsSkpTest.cpp b/tests/PathOpsSkpTest.cpp
index d62e326..2da38f4 100755
--- a/tests/PathOpsSkpTest.cpp
+++ b/tests/PathOpsSkpTest.cpp
@@ -8,6 +8,8 @@
 
 #define TEST(name) { name, #name }
 
+#define TEST_NEW_FAILURES 0
+
 static void skpcheeseandburger_com225(skiatest::Reporter* reporter, const char* filename) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -3547,9 +3549,233 @@
     testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
 }
 
+static void skpwww_9to5mac_com_64(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(365, 5101);
+    path.lineTo(365, 5082);
+    path.lineTo(366, 5083);
+    path.lineTo(367, 5092.96631f);
+    path.lineTo(367, 5100);
+    path.quadTo(367, 5101.50537f, 367.967712f, 5102.61084f);
+    path.lineTo(368.278717f, 5105.71045f);
+    path.quadTo(367.277618f, 5105.34863f, 366.464478f, 5104.53564f);
+    path.quadTo(365, 5103.07129f, 365, 5101);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(365, 5082);
+    pathB.lineTo(365.848175f, 5081.15186f);
+    pathB.lineTo(368, 5103);
+    pathB.lineTo(365, 5106);
+    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+}
+
+#if TEST_NEW_FAILURES
+// addTCoincident SkASSERT(test->fT < 1);
+static void skpwww_googleventures_com_32(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(725.911682f, 898.767456f);
+    path.lineTo(741.232544f, 885.911682f);
+    path.lineTo(754.088318f, 901.232544f);
+    path.lineTo(738.767456f, 914.088318f);
+    path.lineTo(725.911682f, 898.767456f);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(728.37677f, 870.59082f);
+    pathB.lineTo(754.088257f, 901.232605f);
+    pathB.lineTo(738.767395f, 914.088379f);
+    pathB.lineTo(713.055908f, 883.446594f);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+}
+
+// checkSmallCoincidence failed assertion "!next->fSmall || checkMultiple"
+static void skpwww_devbridge_com_22(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(4915, 1523);
+    path.quadTo(4887.24756f, 1523, 4867.62402f, 1542.6239f);
+    path.quadTo(4848, 1562.24768f, 4848, 1590);
+    path.quadTo(4848, 1617.75232f, 4867.62402f, 1637.3761f);
+    path.quadTo(4887.24756f, 1657, 4915, 1657);
+    path.quadTo(4942.75244f, 1657, 4962.37598f, 1637.3761f);
+    path.quadTo(4982, 1617.75232f, 4982, 1590);
+    path.quadTo(4982, 1562.24768f, 4962.37598f, 1542.6239f);
+    path.quadTo(4942.75244f, 1523, 4915, 1523);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(4981.99902f, 1590);
+    pathB.quadTo(4981.99902f, 1617.75232f, 4962.375f, 1637.3761f);
+    pathB.quadTo(4942.75146f, 1657, 4914.99902f, 1657);
+    pathB.quadTo(4887.24658f, 1657, 4867.62305f, 1637.3761f);
+    pathB.quadTo(4847.99902f, 1617.75232f, 4847.99902f, 1590);
+    pathB.quadTo(4847.99902f, 1562.24768f, 4867.62305f, 1542.6239f);
+    pathB.quadTo(4887.24658f, 1523, 4914.99902f, 1523);
+    pathB.quadTo(4942.75146f, 1523, 4962.375f, 1542.6239f);
+    pathB.quadTo(4981.99902f, 1562.24768f, 4981.99902f, 1590);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+}
+
+// cubic/quad intersection
+static void skpwww_alamdi_com_3(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(10210.8789f, 5315.87891f);
+    path.quadTo(10211.7578f, 5315, 10213, 5315);
+    path.lineTo(10230, 5315);
+    path.quadTo(10231.2422f, 5315, 10232.1211f, 5315.87891f);
+    path.quadTo(10233, 5316.75732f, 10233, 5318);
+    path.lineTo(10233, 5338);
+    path.quadTo(10233, 5339.24268f, 10232.1211f, 5340.12109f);
+    path.quadTo(10231.2422f, 5341, 10230, 5341);
+    path.lineTo(10213, 5341);
+    path.quadTo(10211.7578f, 5341, 10210.8789f, 5340.12109f);
+    path.quadTo(10210, 5339.24268f, 10210, 5338);
+    path.lineTo(10210, 5318);
+    path.quadTo(10210, 5316.75732f, 10210.8789f, 5315.87891f);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.moveTo(10213, 5315);
+    pathB.lineTo(10230, 5315);
+    pathB.cubicTo(10231.6572f, 5315, 10233, 5316.34326f, 10233, 5318);
+    pathB.lineTo(10233, 5338);
+    pathB.cubicTo(10233, 5339.10449f, 10231.6572f, 5340, 10230, 5340);
+    pathB.lineTo(10213, 5340);
+    pathB.cubicTo(10211.3428f, 5340, 10210, 5339.10449f, 10210, 5338);
+    pathB.lineTo(10210, 5318);
+    pathB.cubicTo(10210, 5316.34326f, 10211.3428f, 5315, 10213, 5315);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+}
+
+// bumpSpan failed assertion "span->fOppValue >= 0"
+static void skpwww_familysurvivalprotocol_wordpress_com_61(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(143, 14557);
+    path.lineTo(165, 14557);
+    path.lineTo(165, 14555.9902f);
+    path.lineTo(143, 14556);
+    path.lineTo(143, 14557);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(143, 14557);
+    pathB.lineTo(143, 14555.9902f);
+    pathB.lineTo(165, 14556);
+    pathB.lineTo(165, 14557);
+    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+}
+
+// addTCancel: failed assertion "oIndex > 0"
+static void skpwww_firstunitedbank_com_19(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(808.585815f, 11673.5859f);
+    path.quadTo(809.17157f, 11673, 810, 11673);
+    path.lineTo(1032, 11673);
+    path.quadTo(1038.21326f, 11673, 1042.60657f, 11677.3936f);
+    path.quadTo(1047, 11681.7871f, 1047, 11688);
+    path.quadTo(1047, 11682.2012f, 1042.60657f, 11678.1006f);
+    path.quadTo(1038.21326f, 11674, 1032, 11674);
+    path.lineTo(810, 11674);
+    path.quadTo(809.585815f, 11674, 809.292908f, 11674.293f);
+    path.quadTo(809, 11674.5859f, 809, 11675);
+    path.lineTo(809, 11701);
+    path.quadTo(809, 11701.4141f, 809.292908f, 11701.707f);
+    path.quadTo(809.585815f, 11702, 810, 11702);
+    path.lineTo(1032, 11702);
+    path.quadTo(1038.21326f, 11702, 1042.60657f, 11697.8994f);
+    path.quadTo(1047, 11693.7988f, 1047, 11688);
+    path.quadTo(1047, 11694.2129f, 1042.60657f, 11698.6064f);
+    path.quadTo(1038.21326f, 11703, 1032, 11703);
+    path.lineTo(810, 11703);
+    path.quadTo(809.17157f, 11703, 808.585815f, 11702.4141f);
+    path.quadTo(808, 11701.8281f, 808, 11701);
+    path.lineTo(808, 11675);
+    path.quadTo(808, 11674.1719f, 808.585815f, 11673.5859f);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(808, 11703);
+    pathB.lineTo(809.5f, 11701.5f);
+    pathB.lineTo(1062.91907f, 11687.0811f);
+    pathB.lineTo(1047, 11703);
+    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+}
+#endif
+
+// addSimpleAngle: failed assertion "index == count() - 2"
+static void skpwww_shinydemos_com_5(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(205.884888f, 648.203857f);
+    path.lineTo(771.570374f, 82.5183716f);
+    path.lineTo(1110.98169f, 421.929626f);
+    path.lineTo(545.296143f, 987.615112f);
+    path.lineTo(205.884888f, 648.203857f);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(771.570374f, 82.5183716f);
+    pathB.lineTo(1110.98169f, 421.929626f);
+    pathB.lineTo(545.296204f, 987.615051f);
+    pathB.lineTo(205.884949f, 648.203796f);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+}
+
+// addTCoincident oPeek = &other->fTs[++oPeekIndex];
+static void skpwww_lptemp_com_3(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(78.6429825f, 1394.30969f);
+    path.quadTo(79.6192932f, 1393.33337f, 81.0000076f, 1393.33337f);
+    path.lineTo(341, 1393.33337f);
+    path.quadTo(342.380707f, 1393.33337f, 343.357025f, 1394.30969f);
+    path.quadTo(344.333344f, 1395.28601f, 344.333344f, 1396.66675f);
+    path.lineTo(344.333344f, 1465.66663f);
+    path.quadTo(344.333344f, 1467.04736f, 343.357025f, 1468.02368f);
+    path.quadTo(342.380707f, 1469, 341, 1469);
+    path.lineTo(81.0000076f, 1469);
+    path.quadTo(79.6192932f, 1469, 78.6429825f, 1468.02368f);
+    path.quadTo(77.6666718f, 1467.04736f, 77.6666718f, 1465.66663f);
+    path.lineTo(77.6666718f, 1396.66675f);
+    path.quadTo(77.6666718f, 1395.28601f, 78.6429825f, 1394.30969f);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.moveTo(81, 1393.33337f);
+    pathB.lineTo(341, 1393.33337f);
+    pathB.cubicTo(342.840942f, 1393.33337f, 344.333344f, 1394.82568f, 344.333344f, 1396.66675f);
+    pathB.lineTo(344.333344f, 1465.66675f);
+    pathB.cubicTo(344.333344f, 1467.32361f, 342.840942f, 1468.66675f, 341, 1468.66675f);
+    pathB.lineTo(81, 1468.66675f);
+    pathB.cubicTo(79.15905f, 1468.66675f, 77.6666718f, 1467.32361f, 77.6666718f, 1465.66675f);
+    pathB.lineTo(77.6666718f, 1396.66675f);
+    pathB.cubicTo(77.6666718f, 1394.82568f, 79.15905f, 1393.33337f, 81, 1393.33337f);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp, filename);
+}
+
 static void (*firstTest)(skiatest::Reporter* , const char* filename) = 0;
 
 static struct TestDesc tests[] = {
+    TEST(skpwww_lptemp_com_3),
+    TEST(skpwww_shinydemos_com_5),
+#if TEST_NEW_FAILURES
+    TEST(skpwww_familysurvivalprotocol_wordpress_com_61),
+    TEST(skpwww_alamdi_com_3),
+    TEST(skpwww_devbridge_com_22),
+    TEST(skpwww_googleventures_com_32),
+#endif
+    TEST(skpwww_9to5mac_com_64),
     TEST(skpwww_wartepop_blogspot_com_br_6),
     TEST(skpwww_wartepop_blogspot_com_br_6a),
     TEST(skpwww_cooksnaps_com_32a),
diff --git a/tools/pathops_sorter.htm b/tools/pathops_sorter.htm
index aac151a..79a2c16 100644
--- a/tools/pathops_sorter.htm
+++ b/tools/pathops_sorter.htm
@@ -953,11 +953,17 @@
 {{{1020, 672}, {1020, 640.93395999999996}, {998.03301999999996, 618.96698000000004}}}
 </div>
 
+<div id="skpwww_9to5mac_com_64">
+{{{{365.848175,5081.15186}, {368,5103}}},
+{{{367.967712,5102.61084}, {368.278717,5105.71045}}}},
+</div>
+
 </div>
 
 <script type="text/javascript">
 
     var testDivs = [
+        skpwww_9to5mac_com_64,
         skpcarrot_is24x,
         skpwww_wartepop_blogspot_com_br_6,
         skpwww_wartepop_blogspot_com_br_6a,
diff --git a/tools/pathops_visualizer.htm b/tools/pathops_visualizer.htm
index 9872b38..7c109ee 100644
--- a/tools/pathops_visualizer.htm
+++ b/tools/pathops_visualizer.htm
@@ -2,85 +2,139 @@
 <head>
 <div height="0" hidden="true">
 
-<div id="skpwww_argus_presse_fr_41">
-  RunTestSet [skpwww_argus_presse_fr_41]
+<div id="issue2753">
+  RunTestSet [issue2753]
 
-{{1000,343}, {165,343}},
-{{165,343}, {165,364.869873}},
-{{165,364.869873}, {1000,364.869873}},
-{{1000,364.869873}, {1000,343}},
-op intersect
-{{165,343.000031}, {1000,343.000031}},
-{{1000,343.000031}, {1000,364.869904}},
-{{1000,364.869904}, {165,364.869904}},
-{{165,364.869904}, {165,343.000031}},
-debugShowLineIntersection wtTs[0]=0 {{165,343}, {165,364.869873}} {{165,343}} wnTs[0]=1 {{1000,343}, {165,343}}
-debugShowLineIntersection wtTs[0]=1 {{1000,364.869873}, {1000,343}} {{1000,343}} wnTs[0]=0 {{1000,343}, {165,343}}
-debugShowLineIntersection wtTs[0]=0 {{165,364.869873}, {1000,364.869873}} {{165,364.869873}} wnTs[0]=1 {{165,343}, {165,364.869873}}
-debugShowLineIntersection wtTs[0]=0 {{1000,364.869873}, {1000,343}} {{1000,364.869873}} wnTs[0]=1 {{165,364.869873}, {1000,364.869873}}
-debugShowLineIntersection wtTs[0]=0 {{165,343.000031}, {1000,343.000031}} {{165,343}} wtTs[1]=1 {{1000,343}} wnTs[0]=1 {{1000,343}, {165,343}} wnTs[1]=0
-debugShowLineIntersection wtTs[0]=0 {{1000,343.000031}, {1000,364.869904}} {{1000,343.000031}} wnTs[0]=0 {{1000,343}, {165,343}}
-debugShowLineIntersection wtTs[0]=1 {{165,364.869904}, {165,343.000031}} {{165,343.000031}} wnTs[0]=1 {{1000,343}, {165,343}}
-debugShowLineIntersection wtTs[0]=0 {{165,343.000031}, {1000,343.000031}} {{165,343}} wnTs[0]=0 {{165,343}, {165,364.869873}}
-debugShowLineIntersection wtTs[0]=1 {{1000,364.869904}, {165,364.869904}} {{165,364.869873}} wnTs[0]=1 {{165,343}, {165,364.869873}}
-debugShowLineIntersection wtTs[0]=0 {{165,364.869904}, {165,343.000031}} {{165,364.869904}} wtTs[1]=1 {{165,343.000031}} wnTs[0]=1 {{165,343}, {165,364.869873}} wnTs[1]=1.39541634e-006
-debugShowLineIntersection wtTs[0]=1 {{1000,343.000031}, {1000,364.869904}} {{1000,364.869904}} wnTs[0]=1 {{165,364.869873}, {1000,364.869873}}
-debugShowLineIntersection wtTs[0]=0 {{1000,364.869904}, {165,364.869904}} {{1000,364.869873}} wtTs[1]=1 {{165,364.869873}} wnTs[0]=1 {{165,364.869873}, {1000,364.869873}} wnTs[1]=0
-debugShowLineIntersection wtTs[0]=0 {{165,364.869904}, {165,343.000031}} {{165,364.869904}} wnTs[0]=0 {{165,364.869873}, {1000,364.869873}}
-debugShowLineIntersection wtTs[0]=1 {{165,343.000031}, {1000,343.000031}} {{1000,343}} wnTs[0]=1 {{1000,364.869873}, {1000,343}}
-debugShowLineIntersection wtTs[0]=0 {{1000,343.000031}, {1000,364.869904}} {{1000,343.000031}} wtTs[1]=1 {{1000,364.869904}} wnTs[0]=0.999999 {{1000,364.869873}, {1000,343}} wnTs[1]=0
-debugShowLineIntersection wtTs[0]=0 {{1000,364.869904}, {165,364.869904}} {{1000,364.869873}} wnTs[0]=0 {{1000,364.869873}, {1000,343}}
-debugShowLineIntersection wtTs[0]=0 {{1000,343.000031}, {1000,364.869904}} {{1000,343.000031}} wnTs[0]=1 {{165,343.000031}, {1000,343.000031}}
-debugShowLineIntersection wtTs[0]=1 {{165,364.869904}, {165,343.000031}} {{165,343.000031}} wnTs[0]=0 {{165,343.000031}, {1000,343.000031}}
-debugShowLineIntersection wtTs[0]=0 {{1000,364.869904}, {165,364.869904}} {{1000,364.869904}} wnTs[0]=1 {{1000,343.000031}, {1000,364.869904}}
-debugShowLineIntersection wtTs[0]=0 {{165,364.869904}, {165,343.000031}} {{165,364.869904}} wnTs[0]=1 {{1000,364.869904}, {165,364.869904}}
-SkOpSegment::debugShowTs - id=0 [o=3,5 t=0 1000,343.000031 w=1 o=0] [o=7,1 t=1 165,343 w=1 o=0]
-SkOpSegment::debugShowTs o id=4 [o=7,1 t=0 165,343 w=1 o=0] [o=3,5 t=1 1000,343.000031 w=1 o=0] operand
-SkOpSegment::debugShowTs + id=0 [o=3,5 t=0 1000,343.000031 w=1 o=0] [o=7,1 t=1 165,343 w=1 o=0]
-SkOpSegment::debugShowTs o id=4 [o=7,1 t=0 165,343 w=1 o=0] [o=3,5 t=1 1000,343.000031 w=1 o=0] operand
-SkOpSegment::debugShowTs - id=1 [o=4,0 t=0 165,343 w=1 o=0] [o=6,2 t=1 165,364.869873 w=1 o=0]
-SkOpSegment::debugShowTs o id=7 [o=6,2 t=0 165,364.869904 w=1 o=0] [o=4,0 t=1 165,343.000031 w=1 o=0] operand
-SkOpSegment::addTPair addTPair this=1 1.39541634e-006 other=7 1
-SkOpSegment::addTPair addTPair this=7 0 other=1 1
-SkOpSegment::debugShowTs + id=1 [o=4,0 t=0 165,343 w=1 o=0] [o=7 t=1.4e-006 165,343.000031 w=1 o=0] [o=7,6,2 t=1 165,364.869873 w=1 o=0]
-SkOpSegment::debugShowTs o id=7 [o=1,6,2 t=0 165,364.869904 w=1 o=0] [o=1,4,0 t=1 165,343.000031 w=1 o=0] operand
-SkOpSegment::debugShowTs - id=2 [o=1,7 t=0 165,364.869904 w=1 o=0] [o=5,3 t=1 1000,364.869873 w=1 o=0]
-SkOpSegment::debugShowTs o id=6 [o=5,3 t=0 1000,364.869873 w=1 o=0] [o=1,7 t=1 165,364.869904 w=1 o=0] operand
-SkOpSegment::debugShowTs + id=2 [o=1,7 t=0 165,364.869904 w=1 o=0] [o=5,3 t=1 1000,364.869873 w=1 o=0]
-SkOpSegment::debugShowTs o id=6 [o=5,3 t=0 1000,364.869873 w=1 o=0] [o=1,7 t=1 165,364.869904 w=1 o=0] operand
-SkOpSegment::debugShowTs - id=3 [o=6,2 t=0 1000,364.869873 w=1 o=0] [o=4,0 t=1 1000,343 w=1 o=0]
-SkOpSegment::debugShowTs o id=5 [o=4,0 t=0 1000,343.000031 w=1 o=0] [o=6,2 t=1 1000,364.869904 w=1 o=0] operand
-SkOpSegment::addTPair addTPair this=3 0 other=5 1
-SkOpSegment::addTPair addTPair this=5 0 other=3 0.999998605
-SkOpSegment::debugShowTs + id=3 [o=6,2,5 t=0 1000,364.869904 w=1 o=0] [o=5 t=1 1000,343.000031 w=1 o=0] [o=4,0 t=1 1000,343 w=1 o=0]
-SkOpSegment::debugShowTs o id=5 [o=3,4,0 t=0 1000,343.000031 w=1 o=0] [o=3,6,2 t=1 1000,364.869904 w=1 o=0] operand
-SkOpContour::calcCoincidentWinding count=4
-SkOpSegment::debugShowTs p id=0 [o=3,5 t=0 1000,343.000031 w=1 o=-1] [o=7,1 t=1 165,343 w=1 o=0]
-SkOpSegment::debugShowTs o id=4 [o=7,1 t=0 165,343 w=0 o=0] [o=3,5 t=1 1000,343.000031 w=1 o=0] operand done
-SkOpSegment::debugShowTs p id=1 [o=4,0 t=0 165,343 w=1 o=0] [o=7 t=1.4e-006 165,343.000031 w=1 o=-1] [o=7,6,2 t=1 165,364.869873 w=1 o=0]
-SkOpSegment::debugShowTs o id=7 [o=1,6,2 t=0 165,364.869904 w=0 o=0] [o=1,4,0 t=1 165,343.000031 w=1 o=0] operand done
-SkOpSegment::debugShowTs p id=2 [o=1,7 t=0 165,364.869904 w=1 o=-1] [o=5,3 t=1 1000,364.869873 w=1 o=0]
-SkOpSegment::debugShowTs o id=6 [o=5,3 t=0 1000,364.869873 w=0 o=0] [o=1,7 t=1 165,364.869904 w=1 o=0] operand done
-SkOpSegment::debugShowTs p id=3 [o=6,2,5 t=0 1000,364.869904 w=1 o=-1] [o=5 t=1 1000,343.000031 w=1 o=0] [o=4,0 t=1 1000,343 w=1 o=0]
-SkOpSegment::debugShowTs o id=5 [o=3,4,0 t=0 1000,343.000031 w=0 o=0] [o=3,6,2 t=1 1000,364.869904 w=1 o=0] operand done
-SkOpSegment::addTPair addTPair this=0 0 other=4 1
-SkOpSegment::addTPair addTPair this=0 1 other=4 0
-SkOpSegment::addTPair addTPair this=6 1 other=2 0
-SkOpSegment::addTPair addTPair duplicate this=2 0 other=6 1
-SkOpSegment::addTPair addTPair this=2 1 other=6 0
-SkOpContour::joinCoincidence count=4
-SkOpSegment::sortAngles [0] tStart=0 [0]
-SkOpSegment::sortAngles [0] tStart=1 [5]
-SkOpSegment::sortAngles [1] tStart=1.39541634e-006 [2]
-SkOpSegment::sortAngles [1] tStart=1 [5]
-SkOpSegment::sortAngles [2] tStart=1 [5]
-SkOpSegment::sortAngles [3] tStart=0.999998605 [3]
-SkOpSegment::debugShowActiveSpans id=0 (1000,343 165,343) t=0 (1000,343) tEnd=1 other=3 otherT=1 otherIndex=5 windSum=? windValue=1 oppValue=-1
-SkOpSegment::debugShowActiveSpans id=1 (165,343 165,364.869873) t=1.39541634e-006 (165,343.000031) tEnd=1 other=7 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=-1
-SkOpSegment::debugShowActiveSpans id=2 (165,364.869873 1000,364.869873) t=0 (165,364.869873) tEnd=1 other=6 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=-1
-SkOpSegment::debugShowActiveSpans id=3 (1000,364.869873 1000,343) t=0 (1000,364.869873) tEnd=0.999998605 other=6 otherT=0 otherIndex=2 windSum=? windValue=1 oppValue=-1
-Assemble
-
+{{142.701004,110.568001}, {142.957001,100}},
+{{142.957001,100}, {153.835007,100}},
+{{153.835007,100}, {154.591995,108.188004}},
+{{154.591995,108.188004}, {154.591995,108.188004}, {153.173004,108.483002}, {152.830002,109.412003}},
+{{152.830002,109.412003}, {152.830002,109.412003}, {142.701004,110.568001}, {142.701004,110.568001}},
+op union
+{{39,124.000999}, {39,124.000999}, {50.5999985,117.000999}, {50.5999985,117.000999}},
+{{50.5999985,117.000999}, {50.5999985,117.000999}, {164.600998,85.1999969}, {188.201004,117.600998}},
+{{188.201004,117.600998}, {188.201004,117.600998}, {174.800995,93}, {39,124.000999}},
+debugShowCubicIntersection no self intersect {{154.591995,108.188004}, {154.591995,108.188004}, {153.173004,108.483002}, {152.830002,109.412003}}
+debugShowLineIntersection wtTs[0]=1 {{142.701004,110.568001}, {142.957001,100}} {{142.957001,100}} wnTs[0]=0 {{142.957001,100}, {153.835007,100}}
+debugShowLineIntersection wtTs[0]=0 {{142.701004,110.568001}, {142.957001,100}} {{142.701004,110.568001}} wnTs[0]=1 {{152.830002,109.412003}, {142.701004,110.568001}}
+debugShowLineIntersection wtTs[0]=0 {{153.835007,100}, {154.591995,108.188004}} {{153.835007,100}} wnTs[0]=1 {{142.957001,100}, {153.835007,100}}
+debugShowCubicLineIntersection wtTs[0]=0 {{154.591995,108.188004}, {154.591995,108.188004}, {153.173004,108.483002}, {152.830002,109.412003}} {{154.591995,108.188004}} wnTs[0]=1 {{153.835007,100}, {154.591995,108.188004}}
+debugShowCubicLineIntersection wtTs[0]=1 {{154.591995,108.188004}, {154.591995,108.188004}, {153.173004,108.483002}, {152.830002,109.412003}} {{152.830002,109.412003}} wnTs[0]=0 {{152.830002,109.412003}, {142.701004,110.568001}}
+debugShowCubicLineIntersection wtTs[0]=0.671281996 {{50.5999985,117.000999}, {50.5999985,117.000999}, {164.600998,85.1999969}, {188.201004,117.600998}} {{142.883102,103.050758}} wnTs[0]=0.711321 {{142.701004,110.568001}, {142.957001,100}}
+debugShowCubicLineIntersection wtTs[0]=0.642192755 {{188.201004,117.600998}, {188.201004,117.600998}, {174.800995,93}, {39,124.000999}} {{142.753387,108.405373}} wnTs[0]=0.204639 {{142.701004,110.568001}, {142.957001,100}}
+debugShowCubicLineIntersection wtTs[0]=0.734814757 {{50.5999985,117.000999}, {50.5999985,117.000999}, {164.600998,85.1999969}, {188.201004,117.600998}} {{154.165848,103.578537}} wnTs[0]=0.437047 {{153.835007,100}, {154.591995,108.188004}}
+debugShowCubicIntersection no intersect {{154.591995,108.188004}, {154.591995,108.188004}, {153.173004,108.483002}, {152.830002,109.412003}} {{50.5999985,117.000999}, {50.5999985,117.000999}, {164.600998,85.1999969}, {188.201004,117.600998}}
+debugShowCubicIntersection no intersect {{154.591995,108.188004}, {154.591995,108.188004}, {153.173004,108.483002}, {152.830002,109.412003}} {{188.201004,117.600998}, {188.201004,117.600998}, {174.800995,93}, {39,124.000999}}
+debugShowCubicLineIntersection no intersect {{50.5999985,117.000999}, {50.5999985,117.000999}, {164.600998,85.1999969}, {188.201004,117.600998}} {{152.830002,109.412003}, {142.701004,110.568001}}
+debugShowCubicLineIntersection no intersect {{188.201004,117.600998}, {188.201004,117.600998}, {174.800995,93}, {39,124.000999}} {{152.830002,109.412003}, {142.701004,110.568001}}
+debugShowCubicIntersection no self intersect {{50.5999985,117.000999}, {50.5999985,117.000999}, {164.600998,85.1999969}, {188.201004,117.600998}}
+debugShowCubicIntersection no self intersect {{188.201004,117.600998}, {188.201004,117.600998}, {174.800995,93}, {39,124.000999}}
+debugShowCubicLineIntersection wtTs[0]=0 {{50.5999985,117.000999}, {50.5999985,117.000999}, {164.600998,85.1999969}, {188.201004,117.600998}} {{50.5999985,117.000999}} wnTs[0]=1 {{39,124.000999}, {50.5999985,117.000999}}
+debugShowCubicLineIntersection wtTs[0]=1 {{188.201004,117.600998}, {188.201004,117.600998}, {174.800995,93}, {39,124.000999}} {{39,124.000999}} wnTs[0]=0 {{39,124.000999}, {50.5999985,117.000999}}
+debugShowCubicIntersection wtTs[0]=1 {{50.5999985,117.000999}, {50.5999985,117.000999}, {164.600998,85.1999969}, {188.201004,117.600998}} {{188.201004,117.600998}} wnTs[0]=0 {{188.201004,117.600998}, {188.201004,117.600998}, {174.800995,93}, {39,124.000999}}
+SkOpSegment::sortAngles [0] tStart=0.204639461 [1]
+SkOpAngle::after [0/1] 21/21 tStart=0.204639461 tEnd=0 < [7/1] 1/29 tStart=0.642192755 tEnd=0 < [0/2] 5/5 tStart=0.204639461 tEnd=0.711321242  T 4
+SkOpAngle::after [0/1] 21/21 tStart=0.204639461 tEnd=0 < [7/2] 17/17 tStart=0.642192755 tEnd=1 < [7/1] 1/29 tStart=0.642192755 tEnd=0  F 4
+SkOpAngle::after [7/1] 1/29 tStart=0.642192755 tEnd=0 < [7/2] 17/17 tStart=0.642192755 tEnd=1 < [0/2] 5/5 tStart=0.204639461 tEnd=0.711321242  F 4
+SkOpAngle::after [0/2] 5/5 tStart=0.204639461 tEnd=0.711321242 < [7/2] 17/17 tStart=0.642192755 tEnd=1 < [0/1] 21/21 tStart=0.204639461 tEnd=0  T 4
+SkOpSegment::sortAngles [0] tStart=0.711321242 [2]
+SkOpAngle::after [0/3] 21/21 tStart=0.711321242 tEnd=0.204639461 < [6/1] 13/17 tStart=0.671281996 tEnd=0 < [0/4] 5/5 tStart=0.711321242 tEnd=1  F 4
+SkOpAngle::after [0/3] 21/21 tStart=0.711321242 tEnd=0.204639461 < [6/2] 29/29 tStart=0.671281996 tEnd=0.734814757 < [0/4] 5/5 tStart=0.711321242 tEnd=1  T 4
+SkOpSegment::sortAngles [2] tStart=0.437046747 [1]
+SkOpAngle::after [2/1] 9/9 tStart=0.437046747 tEnd=0 < [6/3] 13/13 tStart=0.734814757 tEnd=0.671281996 < [2/2] 25/25 tStart=0.437046747 tEnd=1  T 4
+SkOpAngle::after [2/1] 9/9 tStart=0.437046747 tEnd=0 < [6/4] 29/29 tStart=0.734814757 tEnd=1 < [6/3] 13/13 tStart=0.734814757 tEnd=0.671281996  F 4
+SkOpAngle::after [6/3] 13/13 tStart=0.734814757 tEnd=0.671281996 < [6/4] 29/29 tStart=0.734814757 tEnd=1 < [2/2] 25/25 tStart=0.437046747 tEnd=1  F 4
+SkOpAngle::after [2/2] 25/25 tStart=0.437046747 tEnd=1 < [6/4] 29/29 tStart=0.734814757 tEnd=1 < [2/1] 9/9 tStart=0.437046747 tEnd=0  T 4
+SkOpSegment::debugShowActiveSpans id=0 (142.701004,110.568001 142.957001,100) t=0 (142.701004,110.568001) tEnd=0.204639461 other=4 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=0 (142.701004,110.568001 142.957001,100) t=0.204639461 (142.753387,108.405373) tEnd=0.711321242 other=7 otherT=0.642192755 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=0 (142.701004,110.568001 142.957001,100) t=0.711321242 (142.883102,103.050758) tEnd=1 other=6 otherT=0.671281996 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=1 (142.957001,100 153.835007,100) t=0 (142.957001,100) tEnd=1 other=0 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=2 (153.835007,100 154.591995,108.188004) t=0 (153.835007,100) tEnd=0.437046747 other=1 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=2 (153.835007,100 154.591995,108.188004) t=0.437046747 (154.165848,103.578537) tEnd=1 other=6 otherT=0.734814757 otherIndex=2 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=3 (154.591995,108.188004 154.591995,108.188004 153.173004,108.483002 152.830002,109.412003) t=0 (154.591995,108.188004) tEnd=1 other=2 otherT=1 otherIndex=2 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=4 (152.830002,109.412003 142.701004,110.568001) t=0 (152.830002,109.412003) tEnd=1 other=3 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=5 (39,124.000999 50.5999985,117.000999) t=0 (39,124.000999) tEnd=1 other=7 otherT=1 otherIndex=2 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=6 (50.5999985,117.000999 50.5999985,117.000999 164.600998,85.1999969 188.201004,117.600998) t=0 (50.5999985,117.000999) tEnd=0.671281996 other=5 otherT=1 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=6 (50.5999985,117.000999 50.5999985,117.000999 164.600998,85.1999969 188.201004,117.600998) t=0.671281996 (142.883102,103.050758) tEnd=0.734814757 other=0 otherT=0.711321242 otherIndex=2 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=6 (50.5999985,117.000999 50.5999985,117.000999 164.600998,85.1999969 188.201004,117.600998) t=0.734814757 (154.165848,103.578537) tEnd=1 other=2 otherT=0.437046747 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=7 (188.201004,117.600998 188.201004,117.600998 174.800995,93 39,124.000999) t=0 (188.201004,117.600998) tEnd=0.642192755 other=6 otherT=1 otherIndex=3 windSum=? windValue=1 oppValue=0
+SkOpSegment::debugShowActiveSpans id=7 (188.201004,117.600998 188.201004,117.600998 174.800995,93 39,124.000999) t=0.642192755 (142.753387,108.405373) tEnd=1 other=0 otherT=0.204639461 otherIndex=1 windSum=? windValue=1 oppValue=0
+SkOpSegment::findTop
+SkOpAngle::dumpOne [0/5] next=1/1 sect=21/21  s=1 [3] e=0.711321242 [2] sgn=1 windVal=1 windSum=?
+SkOpAngle::dumpOne [1/1] next=0/5 sect=31/31  s=0 [0] e=1 [1] sgn=-1 windVal=1 windSum=? stop
+SkOpSegment::markWinding id=0 (142.701004,110.568001 142.957001,100) t=0.711321242 [2] (142.883102,103.050758) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::nextChase mismatched signs
+SkOpSegment::markWinding id=1 (142.957001,100 153.835007,100) t=0 [0] (142.957001,100) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=2 (153.835007,100 154.591995,108.188004) t=0 [0] (153.835007,100) tEnd=0.437046747 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=0 (142.701004,110.568001 142.957001,100) t=0.711321242 [2] (142.883102,103.050758) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::activeOp id=0 t=0.711321242 tEnd=1 op=union miFrom=1 miTo=0 suFrom=0 suTo=0 result=1
+SkOpSegment::nextChase mismatched signs
+SkOpSegment::findNextOp simple
+SkOpSegment::markDoneBinary id=0 (142.701004,110.568001 142.957001,100) t=0.711321242 [2] (142.883102,103.050758) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp current id=0 from=(142.883102,103.050758) to=(142.957001,100)
+SkOpSegment::findNextOp simple
+SkOpSegment::markDoneBinary id=1 (142.957001,100 153.835007,100) t=0 [0] (142.957001,100) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp current id=1 from=(142.957001,100) to=(153.835007,100)
+path.moveTo(142.883102,103.050758);
+path.lineTo(142.957001,100);
+SkOpSegment::markWinding id=6 (50.5999985,117.000999 50.5999985,117.000999 164.600998,85.1999969 188.201004,117.600998) t=0.671281996 [1] (142.883102,103.050758) tEnd=0.734814757 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last id=6 windSum=-1 small=0
+SkOpSegment::markWinding id=2 (153.835007,100 154.591995,108.188004) t=0.437046747 [1] (154.165848,103.578537) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=3 (154.591995,108.188004 154.591995,108.188004 153.173004,108.483002 152.830002,109.412003) t=0 [0] (154.591995,108.188004) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=4 (152.830002,109.412003 142.701004,110.568001) t=0 [0] (152.830002,109.412003) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=0 (142.701004,110.568001 142.957001,100) t=0 [0] (142.701004,110.568001) tEnd=0.204639461 newWindSum=-1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last id=0 windSum=? small=0
+SkOpSegment::markWinding id=6 (50.5999985,117.000999 50.5999985,117.000999 164.600998,85.1999969 188.201004,117.600998) t=0.734814757 [2] (154.165848,103.578537) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=7 (188.201004,117.600998 188.201004,117.600998 174.800995,93 39,124.000999) t=0 [0] (188.201004,117.600998) tEnd=0.642192755 newWindSum=-1 newOppSum=0 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last id=7 windSum=? small=0
+SkOpSegment::findNextOp
+SkOpAngle::dumpOne [2/1] next=6/3 sect=9/9  s=0.437046747 [1] e=0 [0] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0
+SkOpAngle::dumpOne [6/3] next=2/2 sect=13/13  s=0.734814757 [2] e=0.671281996 [1] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=-1 operand
+SkOpAngle::dumpOne [2/2] next=6/4 sect=25/25  s=0.437046747 [1] e=1 [2] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=-1
+SkOpAngle::dumpOne [6/4] next=2/1 sect=29/29  s=0.734814757 [2] e=1 [3] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=0 operand
+SkOpSegment::activeOp id=6 t=0.734814757 tEnd=0.671281996 op=union miFrom=1 miTo=1 suFrom=0 suTo=1 result=0
+SkOpSegment::markDoneBinary id=6 (50.5999985,117.000999 50.5999985,117.000999 164.600998,85.1999969 188.201004,117.600998) t=0.671281996 [1] (142.883102,103.050758) tEnd=0.734814757 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp chase.append id=6 windSum=-1 small=0
+SkOpSegment::activeOp id=2 t=0.437046747 tEnd=1 op=union miFrom=1 miTo=0 suFrom=1 suTo=1 result=0
+SkOpSegment::markDoneBinary id=2 (153.835007,100 154.591995,108.188004) t=0.437046747 [1] (154.165848,103.578537) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDoneBinary id=3 (154.591995,108.188004 154.591995,108.188004 153.173004,108.483002 152.830002,109.412003) t=0 [0] (154.591995,108.188004) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDoneBinary id=4 (152.830002,109.412003 142.701004,110.568001) t=0 [0] (152.830002,109.412003) tEnd=1 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDoneBinary id=0 (142.701004,110.568001 142.957001,100) t=0 [0] (142.701004,110.568001) tEnd=0.204639461 newWindSum=-1 newOppSum=-1 oppSum=-1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp chase.append id=0 windSum=-2147483647 small=0
+SkOpSegment::activeOp id=6 t=0.734814757 tEnd=1 op=union miFrom=0 miTo=0 suFrom=1 suTo=0 result=1
+SkOpSegment::findNextOp chase.append id=7 windSum=-2147483647 small=0
+SkOpSegment::markDoneBinary id=2 (153.835007,100 154.591995,108.188004) t=0 [0] (153.835007,100) tEnd=0.437046747 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp from:[2] to:[6] start=2 end=3
+bridgeOp current id=2 from=(153.835007,100) to=(154.165848,103.578537)
+path.lineTo(153.835007,100);
+SkOpSegment::findNextOp simple
+SkOpSegment::markDoneBinary id=6 (50.5999985,117.000999 50.5999985,117.000999 164.600998,85.1999969 188.201004,117.600998) t=0.734814757 [2] (154.165848,103.578537) tEnd=1 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+bridgeOp current id=6 from=(154.165848,103.578537) to=(188.201004,117.600998)
+path.lineTo(154.165848,103.578537);
+path.cubicTo(169.326965,104.931351, 181.942627,109.008728, 188.201004,117.600998);
+SkOpSegment::markWinding id=0 (142.701004,110.568001 142.957001,100) t=0.204639461 [1] (142.753387,108.405373) tEnd=0.711321242 newWindSum=1 newOppSum=-1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last id=0 windSum=-1 small=0
+SkOpSegment::markWinding id=7 (188.201004,117.600998 188.201004,117.600998 174.800995,93 39,124.000999) t=0.642192755 [1] (142.753387,108.405373) tEnd=1 newWindSum=-1 newOppSum=1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=5 (39,124.000999 50.5999985,117.000999) t=0 [0] (39,124.000999) tEnd=1 newWindSum=-1 newOppSum=1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markWinding id=6 (50.5999985,117.000999 50.5999985,117.000999 164.600998,85.1999969 188.201004,117.600998) t=0 [0] (50.5999985,117.000999) tEnd=0.671281996 newWindSum=-1 newOppSum=1 oppSum=? windSum=? windValue=1 oppValue=0
+SkOpSegment::markAngle last id=6 windSum=-1 small=0
+SkOpSegment::findNextOp
+SkOpAngle::dumpOne [7/1] next=0/2 sect=1/29  s=0.642192755 [1] e=0 [0] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=0 operand
+SkOpAngle::dumpOne [0/2] next=7/2 sect=5/5  s=0.204639461 [1] e=0.711321242 [2] sgn=-1 windVal=1 windSum=1 oppVal=0 oppSum=-1
+SkOpAngle::dumpOne [7/2] next=0/1 sect=17/17  s=0.642192755 [1] e=1 [2] sgn=-1 windVal=1 windSum=-1 oppVal=0 oppSum=1 operand
+SkOpAngle::dumpOne [0/1] next=7/1 sect=21/21  s=0.204639461 [1] e=0 [0] sgn=1 windVal=1 windSum=-1 oppVal=0 oppSum=-1 done
+SkOpSegment::activeOp id=0 t=0.204639461 tEnd=0.711321242 op=union miFrom=0 miTo=1 suFrom=1 suTo=1 result=0
+SkOpSegment::markDoneBinary id=0 (142.701004,110.568001 142.957001,100) t=0.204639461 [1] (142.753387,108.405373) tEnd=0.711321242 newWindSum=1 newOppSum=-1 oppSum=-1 windSum=1 windValue=1 oppValue=0
+SkOpSegment::findNextOp chase.append id=0 windSum=-1 small=0
+SkOpSegment::activeOp id=7 t=0.642192755 tEnd=1 op=union miFrom=1 miTo=1 suFrom=1 suTo=0 result=0
+SkOpSegment::markDoneBinary id=7 (188.201004,117.600998 188.201004,117.600998 174.800995,93 39,124.000999) t=0.642192755 [1] (142.753387,108.405373) tEnd=1 newWindSum=-1 newOppSum=1 oppSum=1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDoneBinary id=5 (39,124.000999 50.5999985,117.000999) t=0 [0] (39,124.000999) tEnd=1 newWindSum=-1 newOppSum=1 oppSum=1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::markDoneBinary id=6 (50.5999985,117.000999 50.5999985,117.000999 164.600998,85.1999969 188.201004,117.600998) t=0 [0] (50.5999985,117.000999) tEnd=0.671281996 newWindSum=-1 newOppSum=1 oppSum=1 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::activeOp id=0 t=0.204639461 tEnd=0 op=union miFrom=1 miTo=0 suFrom=0 suTo=0 result=1
+SkOpSegment::markDoneBinary id=7 (188.201004,117.600998 188.201004,117.600998 174.800995,93 39,124.000999) t=0 [0] (188.201004,117.600998) tEnd=0.642192755 newWindSum=-1 newOppSum=0 oppSum=0 windSum=-1 windValue=1 oppValue=0
+SkOpSegment::findNextOp from:[7] to:[0] start=1 end=0
+bridgeOp current id=7 from=(188.201004,117.600998) to=(142.753387,108.405373)
+path.cubicTo(188.201004,117.600998, 182.674683,107.455261, 142.753387,108.405373);
 </div>
 
 </div>
@@ -88,7 +142,7 @@
 <script type="text/javascript">
 
 var testDivs = [
-    skpwww_argus_presse_fr_41,
+    issue2753,
 ];
 
 var decimal_places = 3; // make this 3 to show more precision