cumulative pathops patch

Replace the implicit curve intersection with a geometric curve intersection. The implicit intersection proved mathematically unstable and took a long time to zero in on an answer.

Use pointers instead of indices to refer to parts of curves. Indices required awkward renumbering.

Unify t and point values so that small intervals can be eliminated in one pass.

Break cubics up front to eliminate loops and cusps.

Make the Simplify and Op code more regular and eliminate arbitrary differences.

Add a builder that takes an array of paths and operators.

Delete unused code.

BUG=skia:3588
R=reed@google.com

Review URL: https://codereview.chromium.org/1037573004
diff --git a/tests/PathOpsExtendedTest.cpp b/tests/PathOpsExtendedTest.cpp
index e0d30ba..5889ac4 100644
--- a/tests/PathOpsExtendedTest.cpp
+++ b/tests/PathOpsExtendedTest.cpp
@@ -24,6 +24,7 @@
 __SK_FORCE_IMAGE_DECODER_LINKING;
 
 DEFINE_bool2(runFail, f, false, "run tests known to fail.");
+DEFINE_bool2(runBinary, f, false, "run tests known to fail binary sect.");
 
 static const char marker[] =
     "</div>\n"
@@ -33,11 +34,11 @@
     "var testDivs = [\n";
 
 static const char* opStrs[] = {
-    "kDifference_PathOp",
-    "kIntersect_PathOp",
-    "kUnion_PathOp",
+    "kDifference_SkPathOp",
+    "kIntersect_SkPathOp",
+    "kUnion_SkPathOp",
     "kXor_PathOp",
-    "kReverseDifference_PathOp",
+    "kReverseDifference_SkPathOp",
 };
 
 static const char* opSuffixes[] = {
@@ -47,10 +48,6 @@
     "o",
 };
 
-static bool gShowPath = false;
-static bool gComparePathsAssert = true;
-static bool gPathStrAssert = true;
-
 #if DEBUG_SHOW_TEST_NAME
 static void showPathData(const SkPath& path) {
     SkPath::RawIter iter(path);
@@ -82,6 +79,13 @@
                 lastPt = pts[2];
                 lastPtSet = true;
                 break;
+            case SkPath::kConic_Verb:
+                SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},  //weight=%1.9g\n",
+                        pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
+                        iter.conicWeight());
+                lastPt = pts[2];
+                lastPtSet = true;
+                break;
             case SkPath::kCubic_Verb:
                 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
                         pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
@@ -110,19 +114,19 @@
 
 void showOp(const SkPathOp op) {
     switch (op) {
-        case kDifference_PathOp:
+        case kDifference_SkPathOp:
             SkDebugf("op difference\n");
             break;
-        case kIntersect_PathOp:
+        case kIntersect_SkPathOp:
             SkDebugf("op intersect\n");
             break;
-        case kUnion_PathOp:
+        case kUnion_SkPathOp:
             SkDebugf("op union\n");
             break;
-        case kXOR_PathOp:
+        case kXOR_SkPathOp:
             SkDebugf("op xor\n");
             break;
-        case kReverseDifference_PathOp:
+        case kReverseDifference_SkPathOp:
             SkDebugf("op reverse difference\n");
             break;
         default:
@@ -273,7 +277,7 @@
     return true;
 }
 
-static int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one,
+int comparePaths(skiatest::Reporter* reporter, const char* filename, const SkPath& one,
         const SkPath& two, SkBitmap& bitmap) {
     int errors2x2;
     SkPath scaledOne, scaledTwo;
@@ -282,7 +286,6 @@
         return 0;
     }
     const int MAX_ERRORS = 9;
-    REPORTER_ASSERT(reporter, errors2x2 <= MAX_ERRORS || !gComparePathsAssert);
     return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
 }
 
@@ -303,7 +306,7 @@
     *gTestOp.append() = shapeOp;
     ++gTestNo;
     SkDebugf("    SkPath path, pathB;\n");
-#if DEBUG_SHOW_TEST_NAME
+#if 0 && DEBUG_SHOW_TEST_NAME
     SkPathOpsDebug::ShowOnePath(a, "path", false);
     SkPathOpsDebug::ShowOnePath(b, "pathB", false);
 #endif
@@ -334,17 +337,14 @@
         return 0;
     }
     if (errors2x2 == 0) {
-        if (gShowPath) {
-            showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
-        }
         return 0;
     }
-    if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
+    if (errors2x2 > MAX_ERRORS) {
         SkAutoMutexAcquire autoM(compareDebugOut3);
         SkDebugf("\n*** this test fails ***\n");
         showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
         REPORTER_ASSERT(reporter, 0);
-    } else if (gShowPath || errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
+    } else if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
         SkAutoMutexAcquire autoM(compareDebugOut4);
         showPathOpPath(testName, one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
     }
@@ -367,7 +367,7 @@
 static void outputToStream(const char* pathStr, const char* pathPrefix, const char* nameSuffix,
         const char* testFunction, bool twoPaths, SkMemoryWStream& outFile) {
 #if 0
-    outFile.writeText("<div id=\"");
+    outFile.writeText("\n<div id=\"");
     writeTestName(nameSuffix, outFile);
     outFile.writeText("\">\n");
     if (pathPrefix) {
@@ -412,15 +412,12 @@
 }
 
 SK_DECLARE_STATIC_MUTEX(simplifyDebugOut);
+
 bool testSimplify(SkPath& path, bool useXor, SkPath& out, PathOpsThreadState& state,
                   const char* pathStr) {
     SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
     path.setFillType(fillType);
-#if DEBUG_SHOW_TEST_NAME
-    if (gShowPath) {
-        SkPathOpsDebug::ShowOnePath(path, "path", false);
-    }
-#endif
+    state.fReporter->bumpTestCount();
     if (!Simplify(path, &out)) {
         SkDebugf("%s did not expect failure\n", __FUNCTION__);
         REPORTER_ASSERT(state.fReporter, 0);
@@ -430,7 +427,7 @@
         return true;
     }
     int result = comparePaths(state.fReporter, NULL, path, out, *state.fBitmap);
-    if (result && gPathStrAssert) {
+    if (result) {
         SkAutoMutexAcquire autoM(simplifyDebugOut);
         char temp[8192];
         sk_bzero(temp, sizeof(temp));
@@ -450,23 +447,39 @@
     return result == 0;
 }
 
-bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
-#if DEBUG_SHOW_TEST_NAME
+static bool inner_simplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
+        bool checkFail) {
+#if 0 && DEBUG_SHOW_TEST_NAME
     showPathData(path);
 #endif
     SkPath out;
     if (!Simplify(path, &out)) {
-        SkDebugf("%s did not expect failure\n", __FUNCTION__);
+        SkDebugf("%s did not expect %s failure\n", __FUNCTION__, filename);
         REPORTER_ASSERT(reporter, 0);
         return false;
     }
     SkBitmap bitmap;
-    int result = comparePaths(reporter, filename, path, out, bitmap);
-    if (result && gPathStrAssert) {
+    int errors = comparePaths(reporter, filename, path, out, bitmap);
+    if (!checkFail) {
+        if (!errors) {
+            SkDebugf("%s failing test %s now succeeds\n", __FUNCTION__, filename);
+            REPORTER_ASSERT(reporter, 0);
+            return false;
+        }
+    } else if (errors) {
         REPORTER_ASSERT(reporter, 0);
     }
     reporter->bumpTestCount();
-    return result == 0;
+    return errors == 0;
+}
+
+bool testSimplify(skiatest::Reporter* reporter, const SkPath& path, const char* filename) {
+    return inner_simplify(reporter, path, filename, true);
+}
+
+bool testSimplifyCheck(skiatest::Reporter* reporter, const SkPath& path, const char* filename,
+        bool checkFail) {
+    return inner_simplify(reporter, path, filename, checkFail);
 }
 
 #if DEBUG_SHOW_TEST_NAME
@@ -480,7 +493,7 @@
 
 static bool innerPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
         const SkPathOp shapeOp, const char* testName, bool threaded, bool expectSuccess) {
-#if DEBUG_SHOW_TEST_NAME
+#if 0 && DEBUG_SHOW_TEST_NAME
     showName(a, b, shapeOp);
 #endif
     SkPath out;
@@ -489,7 +502,7 @@
         REPORTER_ASSERT(reporter, 0);
         return false;
     }
-    if (threaded && !reporter->verbose()) {
+    if (!reporter->verbose()) {
         return true;
     }
     SkPath pathOut, scaledPathOut;
@@ -518,7 +531,7 @@
     scaledOut.setFillType(out.getFillType());
     int result = comparePaths(reporter, testName, pathOut, scaledPathOut, out, scaledOut, bitmap,
             a, b, shapeOp, scale, expectSuccess);
-    if (result && gPathStrAssert) {
+    if (result) {
         REPORTER_ASSERT(reporter, 0);
     }
     reporter->bumpTestCount();
@@ -604,6 +617,7 @@
 
 void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
                 void (*firstTest)(skiatest::Reporter* , const char* filename),
+                void (*skipTest)(skiatest::Reporter* , const char* filename),
                 void (*stopTest)(skiatest::Reporter* , const char* filename), bool reverse) {
     size_t index;
     if (firstTest) {
@@ -612,8 +626,7 @@
             --index;
         }
 #if DEBUG_SHOW_TEST_NAME
-        SkDebugf("<div id=\"%s\">\n", tests[index].str);
-        SkDebugf("  %s [%s]\n", __FUNCTION__, tests[index].str);
+        SkDebugf("\n<div id=\"%s\">\n", tests[index].str);
 #endif
         (*tests[index].fun)(reporter, tests[index].str);
         if (tests[index].fun == stopTest) {
@@ -622,11 +635,14 @@
     }
     index = reverse ? count - 1 : 0;
     size_t last = reverse ? 0 : count - 1;
+    bool foundSkip = !skipTest;
     do {
-        if (tests[index].fun != firstTest) {
+        if (tests[index].fun == skipTest) {
+            foundSkip = true;
+        }
+        if (foundSkip && tests[index].fun != firstTest) {
     #if DEBUG_SHOW_TEST_NAME
-            SkDebugf("<div id=\"%s\">\n", tests[index].str);
-            SkDebugf("  %s [%s]\n", __FUNCTION__, tests[index].str);
+            SkDebugf("\n<div id=\"%s\">\n", tests[index].str);
     #endif
             (*tests[index].fun)(reporter, tests[index].str);
         }