path ops work in progress
authorcaryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 2 Oct 2013 14:49:34 +0000 (14:49 +0000)
committercaryclark@google.com <caryclark@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 2 Oct 2013 14:49:34 +0000 (14:49 +0000)
make more skps work

remove edit files

BUG=

Review URL: https://codereview.chromium.org/23542056

git-svn-id: http://skia.googlecode.com/svn/trunk@11570 2bbb7eff-a529-9590-31e7-b0007b416f81

44 files changed:
gyp/pathops_unittest.gyp
gyp/pathops_unittest.gypi
src/pathops/SkAddIntersections.cpp
src/pathops/SkDCubicIntersection.cpp
src/pathops/SkDCubicLineIntersection.cpp
src/pathops/SkDLineIntersection.cpp
src/pathops/SkDQuadImplicit.cpp
src/pathops/SkDQuadIntersection.cpp
src/pathops/SkDQuadLineIntersection.cpp
src/pathops/SkIntersectionHelper.h
src/pathops/SkIntersections.cpp
src/pathops/SkIntersections.h
src/pathops/SkOpAngle.cpp
src/pathops/SkOpContour.cpp
src/pathops/SkOpContour.h
src/pathops/SkOpSegment.cpp
src/pathops/SkOpSegment.h
src/pathops/SkPathOpsCommon.cpp
src/pathops/SkPathOpsCubic.cpp
src/pathops/SkPathOpsDebug.cpp
src/pathops/SkPathOpsDebug.h
src/pathops/SkPathOpsLine.cpp
src/pathops/SkPathOpsOp.cpp
src/pathops/SkPathOpsPoint.h
src/pathops/SkPathOpsQuad.cpp
src/pathops/SkPathOpsTypes.cpp
src/pathops/SkPathOpsTypes.h
src/pathops/SkPathWriter.cpp
src/pathops/SkQuarticRoot.cpp
tests/PathOpsCubicIntersectionTest.cpp
tests/PathOpsCubicIntersectionTestData.cpp
tests/PathOpsCubicLineIntersectionTest.cpp
tests/PathOpsCubicQuadIntersectionTest.cpp
tests/PathOpsCubicReduceOrderTest.cpp
tests/PathOpsExtendedTest.cpp
tests/PathOpsLineIntersectionTest.cpp
tests/PathOpsOpTest.cpp
tests/PathOpsQuadIntersectionTest.cpp
tests/PathOpsQuadIntersectionTestData.cpp
tests/PathOpsQuadLineIntersectionTest.cpp
tests/PathOpsSimplifyTest.cpp
tests/PathOpsSkpClipTest.cpp [changed mode: 0644->0755]
tests/PathOpsSkpTest.cpp [new file with mode: 0755]
tests/PathOpsThreadedCommon.h

index c7c32ef..ea4e5b8 100644 (file)
@@ -22,6 +22,7 @@
         'pathops_unittest.gypi',
       ],
       'sources': [
+        '../tests/PathOpsSkpClipTest.cpp',
         '../tests/Test.cpp',
         '../tests/skia_test.cpp',
         '../tests/Test.h',
index dfbc89c..e9f40d6 100644 (file)
@@ -35,6 +35,7 @@
     '../tests/PathOpsSimplifyRectThreadedTest.cpp',
     '../tests/PathOpsSimplifyTest.cpp',
     '../tests/PathOpsSimplifyTrianglesThreadedTest.cpp',
+    '../tests/PathOpsSkpTest.cpp',
     '../tests/PathOpsTestCommon.cpp',
     '../tests/PathOpsThreadedCommon.cpp',
     '../tests/PathOpsCubicIntersectionTestData.h',
index 0507984..5fa80ec 100644 (file)
@@ -363,15 +363,20 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
             if (pts == 2) {
                 if (wn.segmentType() <= SkIntersectionHelper::kLine_Segment
                         && wt.segmentType() <= SkIntersectionHelper::kLine_Segment) {
-                    wt.addCoincident(wn, ts, swap);
-                    continue;
-                }
-                if (wn.segmentType() >= SkIntersectionHelper::kQuad_Segment
+                    if (wt.addCoincident(wn, ts, swap)) {
+                        continue;
+                    }
+                    ts.cleanUpCoincidence();  // prefer (t == 0 or t == 1)
+                    pts = 1;
+                } else if (wn.segmentType() >= SkIntersectionHelper::kQuad_Segment
                         && wt.segmentType() >= SkIntersectionHelper::kQuad_Segment
                         && ts.isCoincident(0)) {
                     SkASSERT(ts.coincidentUsed() == 2);
-                    wt.addCoincident(wn, ts, swap);
-                    continue;
+                    if (wt.addCoincident(wn, ts, swap)) {
+                        continue;
+                    }
+                    ts.cleanUpCoincidence();  // prefer (t == 0 or t == 1)
+                    pts = 1;
                 }
             }
             if (pts >= 2) {
@@ -380,7 +385,11 @@ bool AddIntersectTs(SkOpContour* test, SkOpContour* next) {
                     const SkDPoint& next = ts.pt(pt + 1);
                     if (wt.isNear(ts[swap][pt], ts[swap][pt + 1], point, next)
                             && wn.isNear(ts[!swap][pt], ts[!swap][pt + 1], point, next)) {
-                        wt.addPartialCoincident(wn, ts, pt, swap);
+                        if (!wt.addPartialCoincident(wn, ts, pt, swap)) {
+                            // remove extra point if two map to same float values
+                            ts.cleanUpCoincidence();  // prefer (t == 0 or t == 1)
+                            pts = 1;
+                        }
                     }
                 }
             }
index 63d434f..ce23448 100644 (file)
@@ -15,8 +15,8 @@
 #include "SkTSort.h"
 
 #if ONE_OFF_DEBUG
-static const double tLimits1[2][2] = {{0.388600450, 0.388600452}, {0.245852802, 0.245852804}};
-static const double tLimits2[2][2] = {{-0.865211397, -0.865215212}, {-0.865207696, -0.865208078}};
+static const double tLimits1[2][2] = {{0.3, 0.4}, {0.8, 0.9}};
+static const double tLimits2[2][2] = {{-0.8, -0.9}, {-0.8, -0.9}};
 #endif
 
 #define DEBUG_QUAD_PART ONE_OFF_DEBUG && 1
@@ -124,7 +124,8 @@ static void intersect(const SkDCubic& cubic1, double t1s, double t1e, const SkDC
                 SkDPoint p1 = cubic1.ptAtT(to1);
                 SkDPoint p2 = cubic2.ptAtT(to2);
                 if (p1.approximatelyEqual(p2)) {
-                    SkASSERT(!locals.isCoincident(tIdx));
+    // FIXME: local edge may be coincident -- experiment with not propagating coincidence to caller
+//                    SkASSERT(!locals.isCoincident(tIdx));
                     if (&cubic1 != &cubic2 || !approximately_equal(to1, to2)) {
                         if (i.swapped()) {  //  FIXME: insert should respect swap
                             i.insert(to2, to1, p1);
@@ -249,39 +250,70 @@ static void intersect(const SkDCubic& cubic1, double t1s, double t1e, const SkDC
     i.downDepth();
 }
 
+    // if two ends intersect, check middle for coincidence
+bool SkIntersections::cubicCheckCoincidence(const SkDCubic& c1, const SkDCubic& c2) {
+    if (fUsed < 2) {
+        return false;
+    }
+    int last = fUsed - 1;
+    double tRange1 = fT[0][last] - fT[0][0];
+    double tRange2 = fT[1][last] - fT[1][0];
+    for (int index = 1; index < 5; ++index) {
+        double testT1 = fT[0][0] + tRange1 * index / 5;
+        double testT2 = fT[1][0] + tRange2 * index / 5;
+        SkDPoint testPt1 = c1.ptAtT(testT1);
+        SkDPoint testPt2 = c2.ptAtT(testT2);
+        if (!testPt1.approximatelyEqual(testPt2)) {
+            return false;
+        }
+    }
+    if (fUsed > 2) {
+        fPt[1] = fPt[last];
+        fT[0][1] = fT[0][last];
+        fT[1][1] = fT[1][last];
+        fUsed = 2;
+    }
+    fIsCoincident[0] = fIsCoincident[1] = 0x03;
+    return true;
+}
+
 #define LINE_FRACTION 0.1
 
 // intersect the end of the cubic with the other. Try lines from the end to control and opposite
 // end to determine range of t on opposite cubic.
-static void intersectEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2,
-                         const SkDRect& bounds2, bool selfIntersect, SkIntersections& i) {
-    SkDLine line;
+bool SkIntersections::cubicExactEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2) {
     int t1Index = start ? 0 : 3;
-    bool swap = i.swapped();
     double testT = (double) !start;
+    bool swap = swapped();
     // quad/quad at this point checks to see if exact matches have already been found
     // cubic/cubic can't reject so easily since cubics can intersect same point more than once
-    if (!selfIntersect) {
-        SkDLine tmpLine;
-        tmpLine[0] = tmpLine[1] = cubic2[t1Index];
-        tmpLine[1].fX += cubic2[2 - start].fY - cubic2[t1Index].fY;
-        tmpLine[1].fY -= cubic2[2 - start].fX - cubic2[t1Index].fX;
-        SkIntersections impTs;
-        impTs.intersectRay(cubic1, tmpLine);
-        for (int index = 0; index < impTs.used(); ++index) {
-            SkDPoint realPt = impTs.pt(index);
-            if (!tmpLine[0].approximatelyEqualHalf(realPt)) {
-                continue;
-            }
-            if (swap) {
-                i.insert(testT, impTs[0][index], tmpLine[0]);
-            } else {
-                i.insert(impTs[0][index], testT, tmpLine[0]);
-            }
-            return;
+    SkDLine tmpLine;
+    tmpLine[0] = tmpLine[1] = cubic2[t1Index];
+    tmpLine[1].fX += cubic2[2 - start].fY - cubic2[t1Index].fY;
+    tmpLine[1].fY -= cubic2[2 - start].fX - cubic2[t1Index].fX;
+    SkIntersections impTs;
+    impTs.intersectRay(cubic1, tmpLine);
+    for (int index = 0; index < impTs.used(); ++index) {
+        SkDPoint realPt = impTs.pt(index);
+        if (!tmpLine[0].approximatelyEqual(realPt)) {
+            continue;
+        }
+        if (swap) {
+            insert(testT, impTs[0][index], tmpLine[0]);
+        } else {
+            insert(impTs[0][index], testT, tmpLine[0]);
         }
+        return true;
     }
-    // don't bother if the two cubics are connnected
+    return false;
+}
+
+void SkIntersections::cubicNearEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2,
+                         const SkDRect& bounds2) {
+    SkDLine line;
+    int t1Index = start ? 0 : 3;
+    double testT = (double) !start;
+   // don't bother if the two cubics are connnected
     static const int kPointsInCubic = 4; // FIXME: move to DCubic, replace '4' with this
     static const int kMaxLineCubicIntersections = 3;
     SkSTArray<(kMaxLineCubicIntersections - 1) * kMaxLineCubicIntersections, double, true> tVals;
@@ -310,10 +342,10 @@ static void intersectEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cub
                 continue;
             }
             if (local.pt(idx2).approximatelyEqual(line[0])) {
-                if (i.swapped()) {  // FIXME: insert should respect swap
-                    i.insert(foundT, testT, line[0]);
+                if (swapped()) {  // FIXME: insert should respect swap
+                    insert(foundT, testT, line[0]);
                 } else {
-                    i.insert(testT, foundT, line[0]);
+                    insert(testT, foundT, line[0]);
                 }
             } else {
                 tVals.push_back(foundT);
@@ -334,12 +366,12 @@ static void intersectEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cub
         }
         double tMin2 = SkTMax(tVals[tIdx] - LINE_FRACTION, 0.0);
         double tMax2 = SkTMin(tVals[tLast] + LINE_FRACTION, 1.0);
-        int lastUsed = i.used();
-        intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i);
-        if (lastUsed == i.used()) {
+        int lastUsed = used();
+        ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
+        if (lastUsed == used()) {
             tMin2 = SkTMax(tVals[tIdx] - (1.0 / SkDCubic::gPrecisionUnit), 0.0);
             tMax2 = SkTMin(tVals[tLast] + (1.0 / SkDCubic::gPrecisionUnit), 1.0);
-            intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i);
+            ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
         }
         tIdx = tLast + 1;
     } while (tIdx < tVals.count());
@@ -404,15 +436,19 @@ tryNextHalfPlane:
 }
 
 int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) {
+    if (fMax == 0) {
+        fMax = 9;
+    }
     bool selfIntersect = &c1 == &c2;
     if (selfIntersect) {
-        if (c1[0].approximatelyEqualHalf(c1[3])) {
+        if (c1[0].approximatelyEqual(c1[3])) {
             insert(0, 1, c1[0]);
+            return fUsed;
         }
     } else {
         for (int i1 = 0; i1 < 4; i1 += 3) {
             for (int i2 = 0; i2 < 4; i2 += 3) {
-                if (c1[i1].approximatelyEqualHalf(c2[i2])) {
+                if (c1[i1].approximatelyEqual(c2[i2])) {
                     insert(i1 >> 1, i2 >> 1, c1[i1]);
                 }
             }
@@ -429,47 +465,47 @@ int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) {
     }
     // quad/quad does linear test here -- cubic does not
     // cubics which are really lines should have been detected in reduce step earlier
-    SkDRect c1Bounds, c2Bounds;
-    // FIXME: pass in cached bounds from caller
-    c1Bounds.setBounds(c1);  // OPTIMIZE use setRawBounds ?
-    c2Bounds.setBounds(c2);
-    intersectEnd(c1, false, c2, c2Bounds, selfIntersect, *this);
-    intersectEnd(c1, true, c2, c2Bounds, selfIntersect, *this);
+    int exactEndBits = 0;
     if (selfIntersect) {
         if (fUsed) {
             return fUsed;
         }
     } else {
+        exactEndBits |= cubicExactEnd(c1, false, c2) << 0;
+        exactEndBits |= cubicExactEnd(c1, true, c2) << 1;
         swap();
-        intersectEnd(c2, false, c1, c1Bounds, false, *this);
-        intersectEnd(c2, true, c1, c1Bounds, false, *this);
+        exactEndBits |= cubicExactEnd(c2, false, c1) << 2;
+        exactEndBits |= cubicExactEnd(c2, true, c1) << 3;
         swap();
     }
-    // if two ends intersect, check middle for coincidence
-    if (fUsed >= 2) {
+    if (cubicCheckCoincidence(c1, c2)) {
         SkASSERT(!selfIntersect);
-        int last = fUsed - 1;
-        double tRange1 = fT[0][last] - fT[0][0];
-        double tRange2 = fT[1][last] - fT[1][0];
-        for (int index = 1; index < 5; ++index) {
-            double testT1 = fT[0][0] + tRange1 * index / 5;
-            double testT2 = fT[1][0] + tRange2 * index / 5;
-            SkDPoint testPt1 = c1.ptAtT(testT1);
-            SkDPoint testPt2 = c2.ptAtT(testT2);
-            if (!testPt1.approximatelyEqual(testPt2)) {
-                goto skipCoincidence;
-            }
+        return fUsed;
+    }
+    // FIXME: pass in cached bounds from caller
+    SkDRect c1Bounds, c2Bounds;
+    c1Bounds.setBounds(c1);  // OPTIMIZE use setRawBounds ?
+    c2Bounds.setBounds(c2);
+    if (!(exactEndBits & 1)) {
+        cubicNearEnd(c1, false, c2, c2Bounds);
+    }
+    if (!(exactEndBits & 2)) {
+        cubicNearEnd(c1, true, c2, c2Bounds);
+    }
+    if (!selfIntersect) {
+        swap();
+        if (!(exactEndBits & 4)) {
+            cubicNearEnd(c2, false, c1, c1Bounds);
         }
-        if (fUsed > 2) {
-            fPt[1] = fPt[last];
-            fT[0][1] = fT[0][last];
-            fT[1][1] = fT[1][last];
-            fUsed = 2;
+        if (!(exactEndBits & 8)) {
+            cubicNearEnd(c2, true, c1, c1Bounds);
         }
-        fIsCoincident[0] = fIsCoincident[1] = 0x03;
+        swap();
+    }
+    if (cubicCheckCoincidence(c1, c2)) {
+        SkASSERT(!selfIntersect);
         return fUsed;
     }
-skipCoincidence:
     ::intersect(c1, 0, 1, c2, 0, 1, 1, *this);
     // If an end point and a second point very close to the end is returned, the second
     // point may have been detected because the approximate quads
@@ -501,9 +537,11 @@ skipCoincidence:
             }
         }
         if (match) {
+#if DEBUG_CONCIDENT
             if (((match + 1) & match) != 0) {
                 SkDebugf("%s coincident hole\n", __FUNCTION__);
             }
+#endif
             // for now, assume that everything from start to finish is coincident
             if (fUsed > 2) {
                   fPt[1] = fPt[last];
@@ -522,6 +560,7 @@ skipCoincidence:
 // OPTIMIZATION If this is a common use case, optimize by duplicating
 // the intersect 3 loop to avoid the promotion  / demotion code
 int SkIntersections::intersect(const SkDCubic& cubic, const SkDQuad& quad) {
+    fMax = 6;
     SkDCubic up = quad.toCubic();
     (void) intersect(cubic, up);
     return used();
@@ -535,6 +574,7 @@ describes a method to find the self intersection of a cubic by taking the gradie
 form dotted with the normal, and solving for the roots. My math foo is too poor to implement this.*/
 
 int SkIntersections::intersect(const SkDCubic& c) {
+    fMax = 1;
     // check to see if x or y end points are the extrema. Are other quick rejects possible?
     if (c.endsAreExtremaInXOrY()) {
         return false;
index 0abb75b..e9997e4 100644 (file)
@@ -86,6 +86,7 @@ public:
         , fLine(l)
         , fIntersections(i)
         , fAllowNear(true) {
+        i->setMax(3);
     }
 
     void allowNear(bool allow) {
@@ -122,7 +123,24 @@ public:
                 SkDebugf("%s pt=(%1.9g,%1.9g) cPt=(%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY,
                         cPt.fX, cPt.fY);
     #endif
+                for (int inner = 0; inner < fIntersections->used(); ++inner) {
+                    if (fIntersections->pt(inner) != pt) {
+                        continue;
+                    }
+                    double existingCubicT = (*fIntersections)[0][inner];
+                    if (cubicT == existingCubicT) {
+                        goto skipInsert;
+                    }
+                    // check if midway on cubic is also same point. If so, discard this
+                    double cubicMidT = (existingCubicT + cubicT) / 2;
+                    SkDPoint cubicMidPt = fCubic.ptAtT(cubicMidT);
+                    if (cubicMidPt.approximatelyEqual(pt)) {
+                        goto skipInsert;
+                    }
+                }
                 fIntersections->insert(cubicT, lineT, pt);
+        skipInsert:
+                ;
             }
         }
         return fIntersections->used();
index 4b818dc..fca0a04 100644 (file)
@@ -26,15 +26,44 @@ SkDPoint SkIntersections::Line(const SkDLine& a, const SkDLine& b) {
     return p;
 }
 
-int SkIntersections::computePoints(const SkDLine& line, int used) {
+void SkIntersections::cleanUpCoincidence() {
+    SkASSERT(fUsed == 2);
+    // both t values are good
+    bool startMatch = fT[0][0] == 0 && (fT[1][0] == 0 || fT[1][0] == 1);
+    bool endMatch = fT[0][1] == 1 && (fT[1][1] == 0 || fT[1][1] == 1);
+    if (startMatch || endMatch) {
+        removeOne(startMatch);
+        return;
+    }
+    // either t value is good
+    bool pStartMatch = fT[0][0] == 0 || fT[1][0] == 0 || fT[1][0] == 1;
+    bool pEndMatch = fT[0][1] == 1 || fT[1][1] == 0 || fT[1][1] == 1;
+    removeOne(pStartMatch || !pEndMatch);
+}
+
+void SkIntersections::cleanUpParallelLines(bool parallel) {
+    while (fUsed > 2) {
+        removeOne(1);
+    }
+    if (fUsed == 2 && !parallel) {
+        bool startMatch = fT[0][0] == 0 || fT[1][0] == 0 || fT[1][0] == 1;
+        bool endMatch = fT[0][1] == 1 || fT[1][1] == 0 || fT[1][1] == 1;
+        if ((!startMatch && !endMatch) || approximately_equal(fT[0][0], fT[0][1])) {
+            SkASSERT(startMatch || endMatch);
+            removeOne(endMatch);
+        }
+    }
+}
+
+void SkIntersections::computePoints(const SkDLine& line, int used) {
     fPt[0] = line.ptAtT(fT[0][0]);
     if ((fUsed = used) == 2) {
         fPt[1] = line.ptAtT(fT[0][1]);
     }
-    return fUsed;
 }
 
 int SkIntersections::intersectRay(const SkDLine& a, const SkDLine& b) {
+    fMax = 2;
     SkDVector aLen = a[1] - a[0];
     SkDVector bLen = b[1] - b[0];
     /* Slopes match when denom goes to zero:
@@ -69,11 +98,13 @@ int SkIntersections::intersectRay(const SkDLine& a, const SkDLine& b) {
         fT[1][0] = fT[1][1] = 1;
         used = 2;
     }
-    return computePoints(a, used);
+    computePoints(a, used);
+    return fUsed;
 }
 
 // note that this only works if both lines are neither horizontal nor vertical
 int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) {
+    fMax = 3;  // note that we clean up so that there is no more than two in the end
     // see if end points intersect the opposite line
     double t;
     for (int iA = 0; iA < 2; ++iA) {
@@ -103,8 +134,9 @@ int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) {
     double ayBxLen = ayLen * bxLen;
     // detect parallel lines the same way here and in SkOpAngle operator <
     // so that non-parallel means they are also sortable
-    bool unparallel = NotAlmostEqualUlps(axByLen, ayBxLen);
-    if (unparallel) {
+    bool unparallel = fAllowNear ? NotAlmostEqualUlps(axByLen, ayBxLen)
+            : NotAlmostDequalUlps(axByLen, ayBxLen);
+    if (unparallel && fUsed == 0) {
         double ab0y = a[0].fY - b[0].fY;
         double ab0x = a[0].fX - b[0].fX;
         double numerA = ab0y * bxLen - byLen * ab0x;
@@ -128,17 +160,8 @@ int SkIntersections::intersect(const SkDLine& a, const SkDLine& b) {
             }
         }
     }
-    while (fUsed > 2) {
-        removeOne(1);
-    }
-    if (fUsed == 2 && unparallel) {
-        bool startMatch = fT[0][0] == 0 || fT[1][0] == 0 || fT[1][0] == 1;
-        bool endMatch = fT[0][1] == 1 || fT[1][1] == 0 || fT[1][1] == 1;
-        if (!startMatch && !endMatch) {
-            SkASSERT(startMatch || endMatch);
-            removeOne(endMatch);
-        }
-    }
+    cleanUpParallelLines(!unparallel);
+    SkASSERT(fUsed <= 2);
     return fUsed;
 }
 
@@ -162,6 +185,7 @@ static double horizontal_intercept(const SkDLine& line, double y) {
 }
 
 int SkIntersections::horizontal(const SkDLine& line, double y) {
+    fMax = 2;
     int horizontalType = horizontal_coincident(line, y);
     if (horizontalType == 1) {
         fT[0][0] = horizontal_intercept(line, y);
@@ -174,6 +198,7 @@ int SkIntersections::horizontal(const SkDLine& line, double y) {
 
 int SkIntersections::horizontal(const SkDLine& line, double left, double right,
                                 double y, bool flipped) {
+    fMax = 2;
     // see if end points intersect the opposite line
     double t;
     const SkDPoint leftPt = { left, y };
@@ -203,26 +228,26 @@ int SkIntersections::horizontal(const SkDLine& line, double left, double right,
                     fT[1][index] = 1 - fT[1][index];
                 }
             }
-            return computePoints(line, result);
+            computePoints(line, result);
         }
     }
-    if (!fAllowNear && result != 2) {
-        return fUsed;
-    }
-    if ((t = line.nearPoint(leftPt)) >= 0) {
-        insert(t, (double) flipped, leftPt);
-    }
-    if (left != right) {
-        const SkDPoint rightPt = { right, y };
-        if ((t = line.nearPoint(rightPt)) >= 0) {
-            insert(t, (double) !flipped, rightPt);
+    if (fAllowNear || result == 2) {
+        if ((t = line.nearPoint(leftPt)) >= 0) {
+            insert(t, (double) flipped, leftPt);
         }
-        for (int index = 0; index < 2; ++index) {
-            if ((t = SkDLine::NearPointH(line[index], left, right, y)) >= 0) {
-                insert((double) index, flipped ? 1 - t : t, line[index]);
+        if (left != right) {
+            const SkDPoint rightPt = { right, y };
+            if ((t = line.nearPoint(rightPt)) >= 0) {
+                insert(t, (double) !flipped, rightPt);
+            }
+            for (int index = 0; index < 2; ++index) {
+                if ((t = SkDLine::NearPointH(line[index], left, right, y)) >= 0) {
+                    insert((double) index, flipped ? 1 - t : t, line[index]);
+                }
             }
         }
     }
+    cleanUpParallelLines(result == 2);
     return fUsed;
 }
 
@@ -246,6 +271,7 @@ static double vertical_intercept(const SkDLine& line, double x) {
 }
 
 int SkIntersections::vertical(const SkDLine& line, double x) {
+    fMax = 2;
     int verticalType = vertical_coincident(line, x);
     if (verticalType == 1) {
         fT[0][0] = vertical_intercept(line, x);
@@ -258,6 +284,7 @@ int SkIntersections::vertical(const SkDLine& line, double x) {
 
 int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
                               double x, bool flipped) {
+    fMax = 2;
     // see if end points intersect the opposite line
     double t;
     SkDPoint topPt = { x, top };
@@ -287,26 +314,26 @@ int SkIntersections::vertical(const SkDLine& line, double top, double bottom,
                     fT[1][index] = 1 - fT[1][index];
                 }
             }
-            return computePoints(line, result);
+            computePoints(line, result);
         }
     }
-    if (!fAllowNear && result != 2) {
-        return fUsed;
-    }
-    if ((t = line.nearPoint(topPt)) >= 0) {
-        insert(t, (double) flipped, topPt);
-    }
-    if (top != bottom) {
-        SkDPoint bottomPt = { x, bottom };
-        if ((t = line.nearPoint(bottomPt)) >= 0) {
-            insert(t, (double) !flipped, bottomPt);
+    if (fAllowNear || result == 2) {
+        if ((t = line.nearPoint(topPt)) >= 0) {
+            insert(t, (double) flipped, topPt);
         }
-        for (int index = 0; index < 2; ++index) {
-            if ((t = SkDLine::NearPointV(line[index], top, bottom, x)) >= 0) {
-                insert((double) index, flipped ? 1 - t : t, line[index]);
+        if (top != bottom) {
+            SkDPoint bottomPt = { x, bottom };
+            if ((t = line.nearPoint(bottomPt)) >= 0) {
+                insert(t, (double) !flipped, bottomPt);
+            }
+            for (int index = 0; index < 2; ++index) {
+                if ((t = SkDLine::NearPointV(line[index], top, bottom, x)) >= 0) {
+                    insert((double) index, flipped ? 1 - t : t, line[index]);
+                }
             }
         }
     }
+    cleanUpParallelLines(result == 2);
     return fUsed;
 }
 
index 84ad452..f0f66d1 100644 (file)
@@ -103,7 +103,7 @@ bool SkDQuadImplicit::match(const SkDQuadImplicit& p2) const {
         if (first == index) {
             continue;
         }
-        if (!AlmostEqualUlps(fP[index] * p2.fP[first], fP[first] * p2.fP[index])) {
+        if (!AlmostDequalUlps(fP[index] * p2.fP[first], fP[first] * p2.fP[index])) {
             return false;
         }
     }
index 5869d7d..71ebf9a 100644 (file)
@@ -139,7 +139,7 @@ static bool add_intercept(const SkDQuad& q1, const SkDQuad& q2, double tMin, dou
         return false;
     }
     SkDPoint pt2 = q1.ptAtT(rootTs[0][0]);
-    if (!pt2.approximatelyEqualHalf(mid)) {
+    if (!pt2.approximatelyEqual(mid)) {
         return false;
     }
     i->insertSwap(rootTs[0][0], tMid, pt2);
@@ -249,31 +249,36 @@ static bool is_linear(const SkDQuad& q1, const SkDQuad& q2, SkIntersections* i)
 }
 
 // FIXME: if flat measure is sufficiently large, then probably the quartic solution failed
-static void relaxed_is_linear(const SkDQuad& q1, const SkDQuad& q2, SkIntersections* i) {
-    double m1 = flat_measure(q1);
-    double m2 = flat_measure(q2);
-#if DEBUG_FLAT_QUADS
-    double min = SkTMin(m1, m2);
-    if (min > 5) {
-        SkDebugf("%s maybe not flat enough.. %1.9g\n", __FUNCTION__, min);
-    }
-#endif
+// avoid imprecision incurred with chopAt
+static void relaxed_is_linear(const SkDQuad* q1, double s1, double e1, const SkDQuad* q2, 
+        double s2, double e2, SkIntersections* i) {
+    double m1 = flat_measure(*q1);
+    double m2 = flat_measure(*q2);
     i->reset();
-    const SkDQuad& rounder = m2 < m1 ? q1 : q2;
-    const SkDQuad& flatter = m2 < m1 ? q2 : q1;
+    const SkDQuad* rounder, *flatter;
+    double sf, midf, ef, sr, er;
+    if (m2 < m1) {
+        rounder = q1;
+        sr = s1;
+        er = e1;
+        flatter = q2;
+        sf = s2;
+        midf = (s2 + e2) / 2;
+        ef = e2;
+    } else {
+        rounder = q2;
+        sr = s2;
+        er = e2;
+        flatter = q1;
+        sf = s1;
+        midf = (s1 + e1) / 2;
+        ef = e1;
+    }
     bool subDivide = false;
-    is_linear_inner(flatter, 0, 1, rounder, 0, 1, i, &subDivide);
+    is_linear_inner(*flatter, sf, ef, *rounder, sr, er, i, &subDivide);
     if (subDivide) {
-        SkDQuadPair pair = flatter.chopAt(0.5);
-        SkIntersections firstI, secondI;
-        relaxed_is_linear(pair.first(), rounder, &firstI);
-        for (int index = 0; index < firstI.used(); ++index) {
-            i->insert(firstI[0][index] * 0.5, firstI[1][index], firstI.pt(index));
-        }
-        relaxed_is_linear(pair.second(), rounder, &secondI);
-        for (int index = 0; index < secondI.used(); ++index) {
-            i->insert(0.5 + secondI[0][index] * 0.5, secondI[1][index], secondI.pt(index));
-        }
+        relaxed_is_linear(flatter, sf, midf, rounder, sr, er, i);
+        relaxed_is_linear(flatter, midf, ef, rounder, sr, er, i);
     }
     if (m2 < m1) {
         i->swapPts();
@@ -378,7 +383,7 @@ static void lookNearEnd(const SkDQuad& q1, const SkDQuad& q2, int testT,
     impTs.intersectRay(q1, tmpLine);
     for (int index = 0; index < impTs.used(); ++index) {
         SkDPoint realPt = impTs.pt(index);
-        if (!tmpLine[0].approximatelyEqualHalf(realPt)) {
+        if (!tmpLine[0].approximatelyEqual(realPt)) {
             continue;
         }
         if (swap) {
@@ -390,6 +395,7 @@ static void lookNearEnd(const SkDQuad& q1, const SkDQuad& q2, int testT,
 }
 
 int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
+    fMax = 4;
     // if the quads share an end point, check to see if they overlap
     for (int i1 = 0; i1 < 3; i1 += 2) {
         for (int i2 = 0; i2 < 3; i2 += 2) {
@@ -401,7 +407,7 @@ int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
     if (fAllowNear || true) {   // FIXME ? cubic/cubic intersection fails without (cubicOp67u)
         for (int i1 = 0; i1 < 3; i1 += 2) {
             for (int i2 = 0; i2 < 3; i2 += 2) {
-                if (q1[i1] != q2[i2] && q1[i1].approximatelyEqualHalf(q2[i2])) {
+                if (q1[i1] != q2[i2] && q1[i1].approximatelyEqual(q2[i2])) {
                     insertNear(i1 >> 1, i2 >> 1, q1[i1]);
                 }
             }
@@ -420,6 +426,7 @@ int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
         return fUsed;
     }
     SkIntersections swapped;
+    swapped.setMax(fMax);
     if (is_linear(q2, q1, &swapped)) {
         swapped.swapPts();
         set(swapped);
@@ -484,7 +491,7 @@ int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
     }
     if (r1Count == r2Count && r1Count <= 1) {
         if (r1Count == 1) {
-            if (pts1[0].approximatelyEqualHalf(pts2[0])) {
+            if (pts1[0].approximatelyEqual(pts2[0])) {
                 insert(roots1Copy[0], roots2Copy[0], pts1[0]);
             } else if (pts1[0].moreRoughlyEqual(pts2[0])) {
                 // experiment: try to find intersection by chasing t
@@ -506,7 +513,7 @@ int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
         dist[index] = DBL_MAX;
         closest[index] = -1;
         for (int ndex2 = 0; ndex2 < r2Count; ++ndex2) {
-            if (!pts2[ndex2].approximatelyEqualHalf(pts1[index])) {
+            if (!pts2[ndex2].approximatelyEqual(pts1[index])) {
                 continue;
             }
             double dx = pts2[ndex2].fX - pts1[index].fX;
@@ -532,7 +539,7 @@ int SkIntersections::intersect(const SkDQuad& q1, const SkDQuad& q2) {
         }
     }
     if (r1Count && r2Count && !foundSomething) {
-        relaxed_is_linear(q1, q2, this);
+        relaxed_is_linear(&q1, 0, 1, &q2, 0, 1, this);
         return fUsed;
     }
     int used = 0;
index b335c5a..14d7d9c 100644 (file)
@@ -99,6 +99,7 @@ public:
         , fLine(l)
         , fIntersections(i)
         , fAllowNear(true) {
+        i->setMax(2);
     }
 
     void allowNear(bool allow) {
index af246b7..1a4b1f0 100644 (file)
@@ -17,8 +17,8 @@ public:
         kCubic_Segment = SkPath::kCubic_Verb,
     };
 
-    void addCoincident(SkIntersectionHelper& other, const SkIntersections& ts, bool swap) {
-        fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
+    bool addCoincident(SkIntersectionHelper& other, const SkIntersections& ts, bool swap) {
+        return fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
     }
 
     // FIXME: does it make sense to write otherIndex now if we're going to
@@ -27,9 +27,10 @@ public:
         fContour->addOtherT(fIndex, index, otherT, otherIndex);
     }
 
-    void addPartialCoincident(SkIntersectionHelper& other, const SkIntersections& ts, int index,
+    bool addPartialCoincident(SkIntersectionHelper& other, const SkIntersections& ts, int index,
             bool swap) {
-        fContour->addPartialCoincident(fIndex, other.fContour, other.fIndex, ts, index, swap);
+        return fContour->addPartialCoincident(fIndex, other.fContour, other.fIndex, ts, index,
+                swap);
     }
 
     // Avoid collapsing t values that are close to the same since
@@ -77,7 +78,7 @@ public:
         double mid = (t1 + t2) / 2;
         SkDPoint midPtByT = segment.dPtAtT(mid);
         SkDPoint midPtByAvg = SkDPoint::Mid(pt1, pt2);
-        return midPtByT.approximatelyEqualHalf(midPtByAvg);
+        return midPtByT.approximatelyEqual(midPtByAvg);
     }
 
     SkScalar left() const {
index 3a5e24f..608ffe3 100644 (file)
@@ -45,6 +45,7 @@ int SkIntersections::coincidentUsed() const {
 int SkIntersections::cubicRay(const SkPoint pts[4], const SkDLine& line) {
     SkDCubic cubic;
     cubic.set(pts);
+    fMax = 3;
     return intersectRay(cubic, line);
 }
 
@@ -87,7 +88,12 @@ int SkIntersections::insert(double one, double two, const SkDPoint& pt) {
             break;
         }
     }
-    SkASSERT(fUsed < 9);
+    if (fUsed >= fMax) {
+        SkASSERT(0);  // FIXME : this error, if it is to be handled at runtime in release, must
+                      // be propagated all the way back down to the caller, and return failure.
+        fUsed = 0;
+        return 0;
+    }
     int remaining = fUsed - index;
     if (remaining > 0) {
         memmove(&fPt[index + 1], &fPt[index], sizeof(fPt[0]) * remaining);
@@ -132,6 +138,7 @@ void SkIntersections::offset(int base, double start, double end) {
 int SkIntersections::quadRay(const SkPoint pts[3], const SkDLine& line) {
     SkDQuad quad;
     quad.set(pts);
+    fMax = 2;
     return intersectRay(quad, line);
 }
 
index 389098d..f63a023 100644 (file)
@@ -25,6 +25,7 @@ public:
         sk_bzero(fIsCoincident, sizeof(fIsCoincident));
         sk_bzero(&fIsNear, sizeof(fIsNear));
         reset();
+        fMax = 0;  // require that the caller set the max
     }
 
     class TArray {
@@ -43,6 +44,7 @@ public:
         memcpy(fIsCoincident, i.fIsCoincident, sizeof(fIsCoincident));
         memcpy(&fIsNear, &i.fIsNear, sizeof(fIsNear));
         fUsed = i.fUsed;
+        fMax = i.fMax;
         fSwap = i.fSwap;
         SkDEBUGCODE(fDepth = i.fDepth);
     }
@@ -54,6 +56,7 @@ public:
     int cubic(const SkPoint a[4]) {
         SkDCubic cubic;
         cubic.set(a);
+        fMax = 1;  // self intersect
         return intersect(cubic);
     }
 
@@ -62,6 +65,7 @@ public:
         aCubic.set(a);
         SkDCubic bCubic;
         bCubic.set(b);
+        fMax = 9;
         return intersect(aCubic, bCubic);
     }
 
@@ -69,12 +73,14 @@ public:
                         bool flipped) {
         SkDCubic cubic;
         cubic.set(a);
+        fMax = 3;
         return horizontal(cubic, left, right, y, flipped);
     }
 
     int cubicVertical(const SkPoint a[4], SkScalar top, SkScalar bottom, SkScalar x, bool flipped) {
         SkDCubic cubic;
         cubic.set(a);
+        fMax = 3;
         return vertical(cubic, top, bottom, x, flipped);
     }
 
@@ -83,6 +89,7 @@ public:
         cubic.set(a);
         SkDLine line;
         line.set(b);
+        fMax = 3;
         return intersect(cubic, line);
     }
 
@@ -91,6 +98,7 @@ public:
         cubic.set(a);
         SkDQuad quad;
         quad.set(b);
+        fMax = 6;
         return intersect(cubic, quad);
     }
 
@@ -119,12 +127,14 @@ public:
                        bool flipped) {
         SkDLine line;
         line.set(a);
+        fMax = 2;
         return horizontal(line, left, right, y, flipped);
     }
 
     int lineVertical(const SkPoint a[2], SkScalar top, SkScalar bottom, SkScalar x, bool flipped) {
         SkDLine line;
         line.set(a);
+        fMax = 2;
         return vertical(line, top, bottom, x, flipped);
     }
 
@@ -132,6 +142,7 @@ public:
         SkDLine aLine, bLine;
         aLine.set(a);
         bLine.set(b);
+        fMax = 2;
         return intersect(aLine, bLine);
     }
 
@@ -143,12 +154,14 @@ public:
                        bool flipped) {
         SkDQuad quad;
         quad.set(a);
+        fMax = 2;
         return horizontal(quad, left, right, y, flipped);
     }
 
     int quadVertical(const SkPoint a[3], SkScalar top, SkScalar bottom, SkScalar x, bool flipped) {
         SkDQuad quad;
         quad.set(a);
+        fMax = 2;
         return vertical(quad, top, bottom, x, flipped);
     }
 
@@ -157,6 +170,7 @@ public:
         quad.set(a);
         SkDLine line;
         line.set(b);
+        fMax = 2;
         return intersect(quad, line);
     }
 
@@ -165,18 +179,23 @@ public:
         aQuad.set(a);
         SkDQuad bQuad;
         bQuad.set(b);
+        fMax = 4;
         return intersect(aQuad, bQuad);
     }
 
     int quadRay(const SkPoint pts[3], const SkDLine& line);
     void removeOne(int index);
 
-    // leaves flip, swap alone
+    // leaves flip, swap, max alone
     void reset() {
         fAllowNear = true;
         fUsed = 0;
     }
 
+    void setMax(int max) {
+        fMax = max;
+    }
+
     void swap() {
         fSwap ^= true;
     }
@@ -200,6 +219,7 @@ public:
     }
 
     static double Axial(const SkDQuad& , const SkDPoint& , bool vertical);
+    void cleanUpCoincidence();
     int coincidentUsed() const;
     int cubicRay(const SkPoint pts[4], const SkDLine& line);
     void flip();
@@ -246,7 +266,11 @@ public:
     }
 
 private:
-    int computePoints(const SkDLine& line, int used);
+    bool cubicCheckCoincidence(const SkDCubic& c1, const SkDCubic& c2);
+    bool cubicExactEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2);
+    void cubicNearEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2, const SkDRect& );
+    void cleanUpParallelLines(bool parallel);
+    void computePoints(const SkDLine& line, int used);
     // used by addCoincident to remove ordinary intersections in range
  //   void remove(double one, double two, const SkDPoint& startPt, const SkDPoint& endPt);
 
@@ -255,6 +279,7 @@ private:
     uint16_t fIsCoincident[2];  // bit set for each curve's coincident T
     uint16_t fIsNear;  // bit set for each T if 2nd curve's point is near but not equal to 1st
     unsigned char fUsed;
+    unsigned char fMax;
     bool fAllowNear;
     bool fSwap;
 #ifdef SK_DEBUG
index c1e2eae..5e1d9e7 100644 (file)
@@ -131,6 +131,9 @@ bool SkOpAngle::operator<(const SkOpAngle& rh) const {  // this/lh: left-hand; r
             if (!SkDLine::NearRay(x, y, rx, ry) && x_ry != rx_y) {
                 return COMPARE_RESULT("7 !fComputed && !rh.fComputed", x_ry < rx_y);
             }
+            if (fSide2 == 0 && rh.fSide2 == 0) {
+                return COMPARE_RESULT("7a !fComputed && !rh.fComputed", x_ry < rx_y);
+            }
         } else {
             // if the vector was a result of subdividing a curve, see if it is stable
             bool sloppy1 = x_ry < rx_y;
@@ -142,8 +145,12 @@ bool SkOpAngle::operator<(const SkOpAngle& rh) const {  // this/lh: left-hand; r
             }
         }
     }
-    if (fSide2 * rh.fSide2 == 0) {
-//        SkASSERT(fSide2 + rh.fSide2 != 0); // hitting this assert means coincidence was undetected
+    if (fSide2 * rh.fSide2 == 0) {  // one is zero
+#if DEBUG_ANGLE
+        if (fSide2 == rh.fSide2 && y_ry) {  // both is zero; coincidence was undetected
+            SkDebugf("%s coincidence!\n", __FUNCTION__);
+        }
+#endif
         return COMPARE_RESULT("9a fSide2 * rh.fSide2 == 0 ...", fSide2 < rh.fSide2);
     }
     // at this point, the initial tangent line is nearly coincident
@@ -409,8 +416,15 @@ void SkOpAngle::setSpans() {
 
 #ifdef SK_DEBUG
 void SkOpAngle::dump() const {
-    SkDebugf("id=%d (%1.9g,%1.9g) start=%d (%1.9g) end=%d (%1.9g)\n", fSegment->debugID(),
-            fSegment->xAtT(fStart), fSegment->yAtT(fStart), fStart, fSegment->span(fStart).fT,
-            fEnd, fSegment->span(fEnd).fT);
+    const SkOpSpan& spanStart = fSegment->span(fStart);
+    const SkOpSpan& spanEnd = fSegment->span(fEnd);
+    const SkOpSpan& spanMin = fStart < fEnd ? spanStart : spanEnd;
+    SkDebugf("id=%d (%1.9g,%1.9g) start=%d (%1.9g) end=%d (%1.9g) sumWind=",
+            fSegment->debugID(), fSegment->xAtT(fStart), fSegment->yAtT(fStart),
+            fStart, spanStart.fT, fEnd, spanEnd.fT);
+    SkPathOpsDebug::WindingPrintf(spanMin.fWindSum);
+    SkDebugf(" oppWind=");
+    SkPathOpsDebug::WindingPrintf(spanMin.fOppSum),
+    SkDebugf(" done=%d\n", spanMin.fDone);
 }
 #endif
index facba87..4aa12cd 100644 (file)
@@ -9,8 +9,17 @@
 #include "SkPathWriter.h"
 #include "SkTSort.h"
 
-void SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex,
+bool SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex,
         const SkIntersections& ts, bool swap) {
+    SkPoint pt0 = ts.pt(0).asSkPoint();
+    SkPoint pt1 = ts.pt(1).asSkPoint();
+    if (pt0 == pt1) {
+        // FIXME: one could imagine a case where it would be incorrect to ignore this
+        // suppose two self-intersecting cubics overlap to be coincident --
+        // this needs to check that by some measure the t values are far enough apart
+        // or needs to check to see if the self-intersection bit was set on the cubic segment
+        return false;
+    }
     SkCoincidence& coincidence = fCoincidences.push_back();
     coincidence.fOther = other;
     coincidence.fSegments[0] = index;
@@ -19,8 +28,9 @@ void SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex,
     coincidence.fTs[swap][1] = ts[0][1];
     coincidence.fTs[!swap][0] = ts[1][0];
     coincidence.fTs[!swap][1] = ts[1][1];
-    coincidence.fPts[0] = ts.pt(0).asSkPoint();
-    coincidence.fPts[1] = ts.pt(1).asSkPoint();
+    coincidence.fPts[0] = pt0;
+    coincidence.fPts[1] = pt1;
+    return true;
 }
 
 SkOpSegment* SkOpContour::nonVerticalSegment(int* start, int* end) {
@@ -57,8 +67,8 @@ void SkOpContour::addCoincidentPoints() {
             continue;
         }
     #if DEBUG_CONCIDENT
-        thisOne.debugShowTs();
-        other.debugShowTs();
+        thisOne.debugShowTs("-");
+        other.debugShowTs("o");
     #endif
         double startT = coincidence.fTs[0][0];
         double endT = coincidence.fTs[0][1];
@@ -66,6 +76,15 @@ void SkOpContour::addCoincidentPoints() {
         if ((cancelers = startSwapped = startT > endT)) {
             SkTSwap(startT, endT);
         }
+        if (startT == endT) { // if one is very large the smaller may have collapsed to nothing
+            if (endT <= 1 - FLT_EPSILON) {
+                endT += FLT_EPSILON;
+                SkASSERT(endT <= 1);
+            } else {
+                startT -= FLT_EPSILON;
+                SkASSERT(startT >= 0);
+            }
+        }
         SkASSERT(!approximately_negative(endT - startT));
         double oStartT = coincidence.fTs[1][0];
         double oEndT = coincidence.fTs[1][1];
@@ -76,43 +95,57 @@ void SkOpContour::addCoincidentPoints() {
         SkASSERT(!approximately_negative(oEndT - oStartT));
         if (cancelers) {
             // make sure startT and endT have t entries
+            const SkPoint& startPt = coincidence.fPts[startSwapped];
             if (startT > 0 || oEndT < 1
-                    || thisOne.isMissing(startT) || other.isMissing(oEndT)) {
-                thisOne.addTPair(startT, &other, oEndT, true, coincidence.fPts[startSwapped]);
+                    || thisOne.isMissing(startT, startPt) || other.isMissing(oEndT, startPt)) {
+                thisOne.addTPair(startT, &other, oEndT, true, startPt);
             }
+            const SkPoint& oStartPt = coincidence.fPts[oStartSwapped];
             if (oStartT > 0 || endT < 1
-                    || thisOne.isMissing(endT) || other.isMissing(oStartT)) {
-                other.addTPair(oStartT, &thisOne, endT, true, coincidence.fPts[oStartSwapped]);
+                    || thisOne.isMissing(endT, oStartPt) || other.isMissing(oStartT, oStartPt)) {
+                other.addTPair(oStartT, &thisOne, endT, true, oStartPt);
             }
         } else {
+            const SkPoint& startPt = coincidence.fPts[startSwapped];
             if (startT > 0 || oStartT > 0
-                    || thisOne.isMissing(startT) || other.isMissing(oStartT)) {
-                thisOne.addTPair(startT, &other, oStartT, true, coincidence.fPts[startSwapped]);
+                    || thisOne.isMissing(startT, startPt) || other.isMissing(oStartT, startPt)) {
+                thisOne.addTPair(startT, &other, oStartT, true, startPt);
             }
+            const SkPoint& oEndPt = coincidence.fPts[!oStartSwapped];
             if (endT < 1 || oEndT < 1
-                    || thisOne.isMissing(endT) || other.isMissing(oEndT)) {
-                other.addTPair(oEndT, &thisOne, endT, true, coincidence.fPts[!oStartSwapped]);
+                    || thisOne.isMissing(endT, oEndPt) || other.isMissing(oEndT, oEndPt)) {
+                other.addTPair(oEndT, &thisOne, endT, true, oEndPt);
             }
         }
     #if DEBUG_CONCIDENT
-        thisOne.debugShowTs();
-        other.debugShowTs();
+        thisOne.debugShowTs("+");
+        other.debugShowTs("o");
     #endif
     }
 }
 
-void SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex,
+bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex,
         const SkIntersections& ts, int ptIndex, bool swap) {
+    SkPoint pt0 = ts.pt(ptIndex).asSkPoint();
+    SkPoint pt1 = ts.pt(ptIndex + 1).asSkPoint();
+    if (SkDPoint::ApproximatelyEqual(pt0, pt1)) {
+        // FIXME: one could imagine a case where it would be incorrect to ignore this
+        // suppose two self-intersecting cubics overlap to form a partial coincidence --
+        // although it isn't clear why the regular coincidence could wouldn't pick this up
+        // this is exceptional enough to ignore for now
+        return false;
+    }
     SkCoincidence& coincidence = fPartialCoincidences.push_back();
     coincidence.fOther = other;
     coincidence.fSegments[0] = index;
     coincidence.fSegments[1] = otherIndex;
-    coincidence.fTs[swap][0] = ts[0][index];
-    coincidence.fTs[swap][1] = ts[0][index + 1];
-    coincidence.fTs[!swap][0] = ts[1][index];
-    coincidence.fTs[!swap][1] = ts[1][index + 1];
-    coincidence.fPts[0] = ts.pt(index).asSkPoint();
-    coincidence.fPts[1] = ts.pt(index + 1).asSkPoint();
+    coincidence.fTs[swap][0] = ts[0][ptIndex];
+    coincidence.fTs[swap][1] = ts[0][ptIndex + 1];
+    coincidence.fTs[!swap][0] = ts[1][ptIndex];
+    coincidence.fTs[!swap][1] = ts[1][ptIndex + 1];
+    coincidence.fPts[0] = pt0;
+    coincidence.fPts[1] = pt1;
+    return true;
 }
 
 void SkOpContour::calcCoincidentWinding() {
@@ -162,6 +195,15 @@ void SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence)
         SkTSwap<double>(startT, endT);
         SkTSwap<const SkPoint*>(startPt, endPt);
     }
+    if (startT == endT) { // if span is very large, the smaller may have collapsed to nothing
+        if (endT <= 1 - FLT_EPSILON) {
+            endT += FLT_EPSILON;
+            SkASSERT(endT <= 1);
+        } else {
+            startT -= FLT_EPSILON;
+            SkASSERT(startT >= 0);
+        }
+    }
     SkASSERT(!approximately_negative(endT - startT));
     double oStartT = coincidence.fTs[1][0];
     double oEndT = coincidence.fTs[1][1];
@@ -173,11 +215,11 @@ void SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence)
     if (cancelers) {
         thisOne.addTCancel(*startPt, *endPt, &other);
     } else {
-        thisOne.addTCoincident(*startPt, *endPt, &other);
+        thisOne.addTCoincident(*startPt, *endPt, endT, &other);
     }
 #if DEBUG_CONCIDENT
-    thisOne.debugShowTs();
-    other.debugShowTs();
+    thisOne.debugShowTs("p");
+    other.debugShowTs("o");
 #endif
 }
 
index a5635fe..a4ec6d3 100644 (file)
@@ -36,7 +36,7 @@ public:
                 : fBounds.fTop < rh.fBounds.fTop;
     }
 
-    void addCoincident(int index, SkOpContour* other, int otherIndex,
+    bool addCoincident(int index, SkOpContour* other, int otherIndex,
                        const SkIntersections& ts, bool swap);
     void addCoincidentPoints();
 
@@ -63,7 +63,7 @@ public:
         fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
     }
 
-    void addPartialCoincident(int index, SkOpContour* other, int otherIndex,
+    bool addPartialCoincident(int index, SkOpContour* other, int otherIndex,
                        const SkIntersections& ts, int ptIndex, bool swap);
 
     int addQuad(const SkPoint pts[3]) {
@@ -100,6 +100,9 @@ public:
             if (segment->verb() == SkPath::kLine_Verb) {
                 continue;
             }
+            if (segment->done()) {
+                continue;   // likely coincident, nothing to do
+            }
             segment->checkEnds();
         }
     }
index c0ef1f8..4d11eb3 100644 (file)
@@ -417,6 +417,8 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT, bool i
     //  binary search?
     int insertedAt = -1;
     size_t tCount = fTs.count();
+    const SkPoint& firstPt = fPts[0];
+    const SkPoint& lastPt = fPts[SkPathOpsVerbToPoints(fVerb)];
     for (size_t index = 0; index < tCount; ++index) {
         // OPTIMIZATION: if there are three or more identical Ts, then
         // the fourth and following could be further insertion-sorted so
@@ -424,10 +426,21 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT, bool i
         // This could later limit segment tests to the two adjacent
         // neighbors, although it doesn't help with determining which
         // circular direction to go in.
-        if (newT < fTs[index].fT) {
+        const SkOpSpan& span = fTs[index];
+        if (newT < span.fT) {
             insertedAt = index;
             break;
         }
+        if (newT == span.fT) {
+            if (pt == span.fPt) {
+                insertedAt = index;
+                break;
+            }
+            if ((pt == firstPt && newT == 0) || (span.fPt == lastPt && newT == 1)) {
+                insertedAt = index;
+                break;
+            }
+        }
     }
     SkOpSpan* span;
     if (insertedAt >= 0) {
@@ -502,6 +515,10 @@ int SkOpSegment::addT(SkOpSegment* other, const SkPoint& pt, double newT, bool i
         }
         double tEndInterval = span[more].fT - newT;
         if (precisely_negative(tEndInterval)) {
+            if ((span->fTiny = span[more].fTiny)) {
+                span->fDone = true;
+                ++fDoneSpans;
+            }
             break;
         }
         if (fVerb == SkPath::kCubic_Verb) {
@@ -556,11 +573,16 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
         SkASSERT(index < fTs.count());
         ++index;
     }
+    while (index > 0 && fTs[index].fT == fTs[index - 1].fT) {
+        --index;
+    }
     int oIndex = other->fTs.count();
     while (startPt != other->fTs[--oIndex].fPt) {  // look for startPt match
         SkASSERT(oIndex > 0);
     }
-    while (startPt == other->fTs[--oIndex].fPt) {  // look for first point beyond match
+    double oStartT = other->fTs[oIndex].fT;
+    // look for first point beyond match
+    while (startPt == other->fTs[--oIndex].fPt || oStartT == other->fTs[oIndex].fT) {
         SkASSERT(oIndex > 0);
     }
     SkOpSpan* test = &fTs[index];
@@ -574,7 +596,9 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
         bool track = test->fWindValue || oTest->fWindValue;
         bool bigger = test->fWindValue >= oTest->fWindValue;
         const SkPoint& testPt = test->fPt;
+        double testT = test->fT;
         const SkPoint& oTestPt = oTest->fPt;
+        double oTestT = oTest->fT;
         do {
             if (decrement) {
                 if (binary && bigger) {
@@ -587,7 +611,7 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
             }
             SkASSERT(index < fTs.count() - 1);
             test = &fTs[++index];
-        } while (testPt == test->fPt);
+        } while (testPt == test->fPt || testT == test->fT);
         SkDEBUGCODE(int originalWindValue = oTest->fWindValue);
         do {
             SkASSERT(oTest->fT < 1);
@@ -605,9 +629,8 @@ void SkOpSegment::addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpS
                 break;
             }
             oTest = &other->fTs[--oIndex];
-        } while (oTestPt == oTest->fPt);
-        SkASSERT(endPt != test->fPt || oTestPt == endPt);
-    } while (endPt != test->fPt);
+        } while (oTestPt == oTest->fPt || oTestT == oTest->fT);
+    } while (endPt != test->fPt && test->fT < 1);
     // FIXME: determine if canceled edges need outside ts added
     int outCount = outsidePts.count();
     if (!done() && outCount) {
@@ -645,7 +668,7 @@ void SkOpSegment::bumpCoincidentThis(const SkOpSpan& oTest, bool binary, int* in
             TrackOutside(outsideTs, oStartPt);
         }
         end = &fTs[++index];
-    } while (end->fPt == test->fPt);
+    } while ((end->fPt == test->fPt || end->fT == test->fT) && end->fT < 1);
     *indexPtr = index;
 }
 
@@ -685,10 +708,11 @@ void SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
     SkOpSpan* oEnd = oTest;
     const SkPoint& startPt = test.fPt;
     const SkPoint& oStartPt = oTest->fPt;
-    if (oStartPt == oEnd->fPt) {
+    double oStartT = oTest->fT;
+    if (oStartPt == oEnd->fPt || oStartT == oEnd->fT) {
         TrackOutside(oOutsidePts, startPt);
     }
-    while (oStartPt == oEnd->fPt) {
+    while (oStartPt == oEnd->fPt || oStartT == oEnd->fT) {
         zeroSpan(oEnd);
         oEnd = &fTs[++oIndex];
     }
@@ -704,7 +728,7 @@ void SkOpSegment::bumpCoincidentOther(const SkOpSpan& test, int* oIndexPtr,
 
 // set spans from start to end to increment the greater by one and decrement
 // the lesser
-void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt,
+void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
         SkOpSegment* other) {
     bool binary = fOperand != other->fOperand;
     int index = 0;
@@ -712,15 +736,24 @@ void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt,
         SkASSERT(index < fTs.count());
         ++index;
     }
+    double startT = fTs[index].fT;
+    while (index > 0 && fTs[index - 1].fT == startT) {
+        --index;
+    }
     int oIndex = 0;
     while (startPt != other->fTs[oIndex].fPt) {
         SkASSERT(oIndex < other->fTs.count());
         ++oIndex;
     }
+    double oStartT = other->fTs[oIndex].fT;
+    while (oIndex > 0 && other->fTs[oIndex - 1].fT == oStartT) {
+        --oIndex;
+    }
     SkSTArray<kOutsideTrackedTCount, SkPoint, true> outsidePts;
     SkSTArray<kOutsideTrackedTCount, SkPoint, true> oOutsidePts;
     SkOpSpan* test = &fTs[index];
     const SkPoint* testPt = &test->fPt;
+    double testT = test->fT;
     SkOpSpan* oTest = &other->fTs[oIndex];
     const SkPoint* oTestPt = &oTest->fPt;
     SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
@@ -760,13 +793,31 @@ void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt,
         }
         test = &fTs[index];
         testPt = &test->fPt;
-        if (endPt == *testPt) {
+        testT = test->fT;
+        if (endPt == *testPt || endT == testT) {
             break;
         }
         oTest = &other->fTs[oIndex];
         oTestPt = &oTest->fPt;
         SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
     } while (endPt != *oTestPt);
+    if (endPt != *testPt && endT != testT) {  // in rare cases, one may have ended before the other
+        int lastWind = test[-1].fWindValue;
+        int lastOpp = test[-1].fOppValue;
+        bool zero = lastWind == 0 && lastOpp == 0;
+        do {
+            if (test->fWindValue || test->fOppValue) {
+                test->fWindValue = lastWind;
+                test->fOppValue = lastOpp;
+                if (zero) {
+                    test->fDone = true;
+                    ++fDoneSpans;
+                }
+            }
+            test = &fTs[++index];
+            testPt = &test->fPt;
+        } while (endPt != *testPt);
+   }
     int outCount = outsidePts.count();
     if (!done() && outCount) {
         addCoinOutsides(outsidePts[0], endPt, other);
@@ -1325,7 +1376,7 @@ bool SkOpSegment::decrementSpan(SkOpSpan* span) {
 }
 
 bool SkOpSegment::bumpSpan(SkOpSpan* span, int windDelta, int oppDelta) {
-    SkASSERT(!span->fDone || span->fTiny);
+    SkASSERT(!span->fDone || span->fTiny || span->fSmall);
     span->fWindValue += windDelta;
     SkASSERT(span->fWindValue >= 0);
     span->fOppValue += oppDelta;
@@ -1384,17 +1435,20 @@ void SkOpSegment::checkEnds() {
             }
             const SkOpSpan& peekSpan = other->fTs[peekIndex];
             SkOpSegment* match = peekSpan.fOther;
+            if (match->done()) {
+                continue;  // if the edge has already been eaten (likely coincidence), ignore it
+            }
             const double matchT = peekSpan.fOtherT;
             // see if any of the spans match the other spans
             for (int tIndex = tStart + 1; tIndex < tLast; ++tIndex) {
                 const SkOpSpan& tSpan = fTs[tIndex];
                 if (tSpan.fOther == match) {
                     if (tSpan.fOtherT == matchT) {
-                        goto nextPeeker;
+                        goto nextPeekIndex;
                     }
                     double midT = (tSpan.fOtherT + matchT) / 2;
                     if (match->betweenPoints(midT, tSpan.fPt, peekSpan.fPt)) {
-                        goto nextPeeker;
+                        goto nextPeekIndex;
                     }
                 }
             }
@@ -1414,18 +1468,22 @@ void SkOpSegment::checkEnds() {
 #endif
             // this segment is missing a entry that the other contains
             // remember so we can add the missing one and recompute the indices
-            MissingSpan& missing = missingSpans.push_back();
-            SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
-            missing.fCommand = MissingSpan::kAddMissing;
-            missing.fT = t;
-            missing.fOther = match;
-            missing.fOtherT = matchT;
-            missing.fPt = peekSpan.fPt;
-        }
-nextPeeker:
-        ;
+            {
+                MissingSpan& missing = missingSpans.push_back();
+                SkDEBUGCODE(sk_bzero(&missing, sizeof(missing)));
+                missing.fCommand = MissingSpan::kAddMissing;
+                missing.fT = t;
+                missing.fOther = match;
+                missing.fOtherT = matchT;
+                missing.fPt = peekSpan.fPt;
+            }
+            break;
+nextPeekIndex:
+            ;
+        }
     }
     if (missingSpans.count() == 0) {
+        debugValidate();
         return;
     }
     // if one end is near the other point, look for a coincident span
@@ -1690,6 +1748,11 @@ SkOpSegment* SkOpSegment::findNextOp(SkTDArray<SkOpSpan*>* chase, int* nextStart
     SkSTArray<SkOpAngle::kStackBasedCount, SkOpAngle*, true> sorted;
     int calcWinding = computeSum(startIndex, end, SkOpAngle::kBinaryOpp, &angles, &sorted);
     bool sortable = calcWinding != SK_NaN32;
+    if (sortable && sorted.count() == 0) {
+        // if no edge has a computed winding sum, we can go no further
+        *unsortable = true;
+        return NULL;
+    }
     int angleCount = angles.count();
     int firstIndex = findStartingEdge(sorted, startIndex, end);
     SkASSERT(!sortable || firstIndex >= 0);
@@ -2204,14 +2267,17 @@ void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkSc
         }
     }
     (void) markAndChaseWinding(start, end, winding, oppWind);
+    // OPTIMIZATION: the reverse mark and chase could skip the first marking
+    (void) markAndChaseWinding(end, start, winding, oppWind);
 }
 
 // OPTIMIZE: successive calls could start were the last leaves off
 // or calls could specialize to walk forwards or backwards
-bool SkOpSegment::isMissing(double startT) const {
+bool SkOpSegment::isMissing(double startT, const SkPoint& pt) const {
     size_t tCount = fTs.count();
     for (size_t index = 0; index < tCount; ++index) {
-        if (approximately_zero(startT - fTs[index].fT)) {
+        const SkOpSpan& span = fTs[index];
+        if (approximately_zero(startT - span.fT) && pt == span.fPt) {
             return false;
         }
     }
@@ -2352,9 +2418,10 @@ SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, bool activeAngl
     }
 #if DEBUG_WINDING
     if (last) {
-        SkDebugf("%s last id=%d windSum=%d small=%d\n", __FUNCTION__,
-                last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
-                last->fSmall);
+        SkDebugf("%s last id=%d windSum=", __FUNCTION__,
+                last->fOther->fTs[last->fOtherIndex].fOther->debugID());
+        SkPathOpsDebug::WindingPrintf(last->fWindSum);
+        SkDebugf(" small=%d\n", last->fSmall);
     }
 #endif
     return last;
@@ -2377,9 +2444,10 @@ SkOpSpan* SkOpSegment::markAngle(int maxWinding, int sumWinding, int oppMaxWindi
     }
 #if DEBUG_WINDING
     if (last) {
-        SkDebugf("%s last id=%d windSum=%d small=%d\n", __FUNCTION__,
-                last->fOther->fTs[last->fOtherIndex].fOther->debugID(), last->fWindSum,
-                last->fSmall);
+        SkDebugf("%s last id=%d windSum=", __FUNCTION__,
+                last->fOther->fTs[last->fOtherIndex].fOther->debugID());
+        SkPathOpsDebug::WindingPrintf(last->fWindSum);
+        SkDebugf(" small=%d\n", last->fSmall);
     }
 #endif
     return last;
@@ -2491,7 +2559,7 @@ SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int windi
 SkOpSpan* SkOpSegment::markOneWinding(const char* funName, int tIndex, int winding,
                                       int oppWinding) {
     SkOpSpan& span = fTs[tIndex];
-    if (span.fDone) {
+    if (span.fDone && !span.fSmall) {
         return NULL;
     }
 #if DEBUG_MARK_DONE
@@ -3134,6 +3202,9 @@ void SkOpSegment::zeroSpan(SkOpSpan* span) {
     SkASSERT(span->fWindValue > 0 || span->fOppValue != 0);
     span->fWindValue = 0;
     span->fOppValue = 0;
+    if (span->fTiny || span->fSmall) {
+        return;
+    }
     SkASSERT(!span->fDone);
     span->fDone = true;
     ++fDoneSpans;
@@ -3162,8 +3233,8 @@ void SkOpSegment::debugAddTPair(double t, const SkOpSegment& other, double other
 #endif
 
 #if DEBUG_CONCIDENT
-void SkOpSegment::debugShowTs() const {
-    SkDebugf("%s id=%d", __FUNCTION__, fID);
+void SkOpSegment::debugShowTs(const char* prefix) const {
+    SkDebugf("%s %s id=%d", __FUNCTION__, prefix, fID);
     int lastWind = -1;
     int lastOpp = -1;
     double lastT = -1;
index acb114d..85531f5 100644 (file)
@@ -248,7 +248,8 @@ public:
     int addSelfT(SkOpSegment* other, const SkPoint& pt, double newT);
     int addT(SkOpSegment* other, const SkPoint& pt, double newT, bool isNear);
     void addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
-    void addTCoincident(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
+    void addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
+            SkOpSegment* other);
     void addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind, const SkPoint& pt);
     bool betweenTs(int lesser, double testT, int greater) const;
     void checkEnds();
@@ -269,7 +270,7 @@ public:
     void initWinding(int start, int end);
     void initWinding(int start, int end, double tHit, int winding, SkScalar hitDx, int oppWind,
                      SkScalar hitOppDx);
-    bool isMissing(double startT) const;
+    bool isMissing(double startT, const SkPoint& pt) const;
     bool isTiny(const SkOpAngle* angle) const;
     SkOpSpan* markAndChaseDoneBinary(int index, int endIndex);
     SkOpSpan* markAndChaseDoneUnary(int index, int endIndex);
@@ -316,7 +317,7 @@ public:
             bool sortable);
 #endif
 #if DEBUG_CONCIDENT
-    void debugShowTs() const;
+    void debugShowTs(const char* prefix) const;
 #endif
 #if DEBUG_SHOW_WINDING
     int debugShowWindingValues(int slotCount, int ofInterest) const;
index 7dd13a7..4db6079 100644 (file)
@@ -399,10 +399,6 @@ void MakeContourList(SkTArray<SkOpContour>& contours, SkTArray<SkOpContour*, tru
     SkTQSort<SkOpContour>(list.begin(), list.end() - 1);
 }
 
-static bool approximatelyEqual(const SkPoint& a, const SkPoint& b) {
-    return AlmostEqualUlps(a.fX, b.fX) && AlmostEqualUlps(a.fY, b.fY);
-}
-
 class DistanceLessThan {
 public:
     DistanceLessThan(double* distances) : fDistances(distances) { }
@@ -435,7 +431,7 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
         const SkPoint& eEnd = eContour.end();
 #if DEBUG_ASSEMBLE
         SkDebugf("%s contour", __FUNCTION__);
-        if (!approximatelyEqual(eStart, eEnd)) {
+        if (!SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
             SkDebugf("[%d]", runs.count());
         } else {
             SkDebugf("   ");
@@ -443,7 +439,7 @@ void Assemble(const SkPathWriter& path, SkPathWriter* simple) {
         SkDebugf(" start=(%1.9g,%1.9g) end=(%1.9g,%1.9g)\n",
                 eStart.fX, eStart.fY, eEnd.fX, eEnd.fY);
 #endif
-        if (approximatelyEqual(eStart, eEnd)) {
+        if (SkDPoint::ApproximatelyEqual(eStart, eEnd)) {
             eContour.toPath(simple);
             continue;
         }
index 662219a..8e8ec47 100644 (file)
@@ -155,7 +155,7 @@ int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) {
     if (approximately_zero(A + B + C + D)) {  // 1 is one root
         int num = SkDQuad::RootsReal(A, A + B, -D, s);
         for (int i = 0; i < num; ++i) {
-            if (AlmostEqualUlps(s[i], 1)) {
+            if (AlmostDequalUlps(s[i], 1)) {
                 return num;
             }
         }
@@ -186,11 +186,11 @@ int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) {
         *roots++ = r;
 
         r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
-        if (!AlmostEqualUlps(s[0], r)) {
+        if (!AlmostDequalUlps(s[0], r)) {
             *roots++ = r;
         }
         r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
-        if (!AlmostEqualUlps(s[0], r) && (roots - s == 1 || !AlmostEqualUlps(s[1], r))) {
+        if (!AlmostDequalUlps(s[0], r) && (roots - s == 1 || !AlmostDequalUlps(s[1], r))) {
             *roots++ = r;
         }
     } else {  // we have 1 real root
@@ -205,9 +205,9 @@ int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) {
         }
         r = A - adiv3;
         *roots++ = r;
-        if (AlmostEqualUlps(R2, Q3)) {
+        if (AlmostDequalUlps(R2, Q3)) {
             r = -A / 2 - adiv3;
-            if (!AlmostEqualUlps(s[0], r)) {
+            if (!AlmostDequalUlps(s[0], r)) {
                 *roots++ = r;
             }
         }
index 0505965..e52e47b 100644 (file)
@@ -89,6 +89,13 @@ void SkPathOpsDebug::DumpAngles(const SkTArray<SkOpAngle, true>& angles) {
         angles[index].dump();
     }
 }
+
+void SkPathOpsDebug::DumpAngles(const SkTArray<SkOpAngle* , true>& angles) {
+    int count = angles.count();
+    for (int index = 0; index < count; ++index) {
+        angles[index]->dump();
+    }
+}
 #endif  // SK_DEBUG || !FORCE_RELEASE
 
 #ifdef SK_DEBUG
@@ -132,4 +139,22 @@ void SkOpSpan::dump() const {
     }
     SkDebugf("\n");
 }
+
+void Dump(const SkTArray<class SkOpAngle, true>& angles) {
+    SkPathOpsDebug::DumpAngles(angles);
+}
+
+void Dump(const SkTArray<class SkOpAngle* , true>& angles) {
+    SkPathOpsDebug::DumpAngles(angles);
+}
+
+void Dump(const SkTArray<class SkOpAngle, true>* angles) {
+    SkPathOpsDebug::DumpAngles(*angles);
+}
+
+void Dump(const SkTArray<class SkOpAngle* , true>* angles) {
+    SkPathOpsDebug::DumpAngles(*angles);
+}
+
 #endif
+
index 912f2f5..4fabd4c 100644 (file)
@@ -160,8 +160,15 @@ public:
     static void ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name);
 #endif
     static void DumpAngles(const SkTArray<class SkOpAngle, true>& angles);
+    static void DumpAngles(const SkTArray<class SkOpAngle* , true>& angles);
 };
 
+// shorthand for calling from debugger
+void Dump(const SkTArray<class SkOpAngle, true>& angles);
+void Dump(const SkTArray<class SkOpAngle* , true>& angles);
+void Dump(const SkTArray<class SkOpAngle, true>* angles);
+void Dump(const SkTArray<class SkOpAngle* , true>* angles);
+
 #endif  // SK_DEBUG || !FORCE_RELEASE
 
 #endif
index 1b548fc..1e74123 100644 (file)
@@ -152,8 +152,6 @@ double SkDLine::NearPointH(const SkDPoint& xy, double left, double right, double
     if (!AlmostEqualUlps(largest, largest + dist)) { // is the dist within ULPS tolerance?
         return -1;
     }
-    t = SkPinT(t);
-    SkASSERT(between(0, t, 1));
     return t;
 }
 
@@ -189,8 +187,6 @@ double SkDLine::NearPointV(const SkDPoint& xy, double top, double bottom, double
     if (!AlmostEqualUlps(largest, largest + dist)) { // is the dist within ULPS tolerance?
         return -1;
     }
-    t = SkPinT(t);
-    SkASSERT(between(0, t, 1));
     return t;
 }
 
index e532fda..71ebef0 100644 (file)
@@ -165,8 +165,8 @@ static bool bridgeOp(SkTArray<SkOpContour*, true>& contourList, const SkPathOp o
             #endif
                         if (simple->isEmpty()) {
                             simple->init();
-                            break;
                         }
+                        break;
                     }
                     SkASSERT(unsortable || !current->done());
                     int nextStart = index;
index 51216b6..40688d8 100644 (file)
@@ -100,30 +100,40 @@ struct SkDPoint {
     // return approximately_equal(a.fY, fY) && approximately_equal(a.fX, fX);
     // because that will not take the magnitude of the values
     bool approximatelyEqual(const SkDPoint& a) const {
-        double denom = SkTMax(fabs(fX), SkTMax(fabs(fY),
-                SkTMax(fabs(a.fX), fabs(a.fY))));
-        if (precisely_zero(denom)) {
+        if (approximately_equal(fX, a.fX) && approximately_equal(fY, a.fY)) {
             return true;
         }
-        double inv = 1 / denom;
-        return approximately_equal(fX * inv, a.fX * inv)
-                && approximately_equal(fY * inv, a.fY * inv);
+        if (!RoughlyEqualUlps(fX, a.fX) || !RoughlyEqualUlps(fY, a.fY)) {
+            return false;
+        }
+        double dist = distance(a);  // OPTIMIZATION: can we compare against distSq instead ?
+        double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
+        double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
+        largest = SkTMax(largest, -tiniest);
+        return AlmostBequalUlps(largest, largest + dist); // is the dist within ULPS tolerance?
     }
 
     bool approximatelyEqual(const SkPoint& a) const {
-        return AlmostEqualUlps(SkDoubleToScalar(fX), a.fX)
-                && AlmostEqualUlps(SkDoubleToScalar(fY), a.fY);
+        SkDPoint dA;
+        dA.set(a);
+        return approximatelyEqual(dA);
     }
 
-    bool approximatelyEqualHalf(const SkDPoint& a) const {
-        double denom = SkTMax(fabs(fX), SkTMax(fabs(fY),
-                SkTMax(fabs(a.fX), fabs(a.fY))));
-        if (denom == 0) {
+    static bool ApproximatelyEqual(const SkPoint& a, const SkPoint& b) {
+        if (approximately_equal(a.fX, b.fX) && approximately_equal(a.fY, b.fY)) {
             return true;
         }
-        double inv = 1 / denom;
-        return approximately_equal_half(fX * inv, a.fX * inv)
-                && approximately_equal_half(fY * inv, a.fY * inv);
+        if (!RoughlyEqualUlps(a.fX, b.fX) || !RoughlyEqualUlps(a.fY, b.fY)) {
+            return false;
+        }
+        SkDPoint dA, dB;
+        dA.set(a);
+        dB.set(b);
+        double dist = dA.distance(dB);  // OPTIMIZATION: can we compare against distSq instead ?
+        float tiniest = SkTMin(SkTMin(SkTMin(a.fX, b.fX), a.fY), b.fY);
+        float largest = SkTMax(SkTMax(SkTMax(a.fX, b.fX), a.fY), b.fY);
+        largest = SkTMax(largest, -tiniest);
+        return AlmostBequalUlps((double) largest, largest + dist); // is dist within ULPS tolerance?
     }
 
     bool approximatelyZero() const {
@@ -152,11 +162,18 @@ struct SkDPoint {
         return result;
     }
 
-    double moreRoughlyEqual(const SkDPoint& a) const {
-        return more_roughly_equal(a.fY, fY) && more_roughly_equal(a.fX, fX);
+    bool moreRoughlyEqual(const SkDPoint& a) const {
+        if (roughly_equal(fX, a.fX) && roughly_equal(fY, a.fY)) {
+            return true;
+        }
+        double dist = distance(a);  // OPTIMIZATION: can we compare against distSq instead ?
+        double tiniest = SkTMin(SkTMin(SkTMin(fX, a.fX), fY), a.fY);
+        double largest = SkTMax(SkTMax(SkTMax(fX, a.fX), fY), a.fY);
+        largest = SkTMax(largest, -tiniest);
+        return RoughlyEqualUlps(largest, largest + dist); // is the dist within ULPS tolerance?
     }
 
-    double roughlyEqual(const SkDPoint& a) const {
+    bool roughlyEqual(const SkDPoint& a) const {
         return roughly_equal(a.fY, fY) && roughly_equal(a.fX, fX);
     }
 
index 1bd7796..63e2038 100644 (file)
@@ -122,7 +122,7 @@ int SkDQuad::RootsReal(const double A, const double B, const double C, double s[
     }
     /* normal form: x^2 + px + q = 0 */
     const double p2 = p * p;
-    if (!AlmostEqualUlps(p2, q) && p2 < q) {
+    if (!AlmostDequalUlps(p2, q) && p2 < q) {
         return 0;
     }
     double sqrt_D = 0;
@@ -131,7 +131,7 @@ int SkDQuad::RootsReal(const double A, const double B, const double C, double s[
     }
     s[0] = sqrt_D - p;
     s[1] = -sqrt_D - p;
-    return 1 + !AlmostEqualUlps(s[0], s[1]);
+    return 1 + !AlmostDequalUlps(s[0], s[1]);
 }
 
 bool SkDQuad::isLinear(int startIndex, int endIndex) const {
index 2d7388b..df73d11 100644 (file)
@@ -7,12 +7,30 @@
 #include "SkFloatBits.h"
 #include "SkPathOpsTypes.h"
 
+static bool arguments_denormalized(float a, float b, int epsilon) {
+    float denomalizedCheck = FLT_EPSILON * epsilon / 2;
+    return fabsf(a) <= denomalizedCheck && fabsf(b) <= denomalizedCheck;
+}
+
 // from http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
 // FIXME: move to SkFloatBits.h
 static bool equal_ulps(float a, float b, int epsilon) {
     if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) {
         return false;
     }
+    if (arguments_denormalized(a, b, epsilon)) {
+        return true;
+    }
+    int aBits = SkFloatAs2sCompliment(a);
+    int bBits = SkFloatAs2sCompliment(b);
+    // Find the difference in ULPs.
+    return aBits < bBits + epsilon && bBits < aBits + epsilon;
+}
+
+static bool d_equal_ulps(float a, float b, int epsilon) {
+    if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) {
+        return false;
+    }
     int aBits = SkFloatAs2sCompliment(a);
     int bBits = SkFloatAs2sCompliment(b);
     // Find the difference in ULPs.
@@ -23,6 +41,19 @@ static bool not_equal_ulps(float a, float b, int epsilon) {
     if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) {
         return false;
     }
+    if (arguments_denormalized(a, b, epsilon)) {
+        return false;
+    }
+    int aBits = SkFloatAs2sCompliment(a);
+    int bBits = SkFloatAs2sCompliment(b);
+    // Find the difference in ULPs.
+    return aBits >= bBits + epsilon || bBits >= aBits + epsilon;
+}
+
+static bool d_not_equal_ulps(float a, float b, int epsilon) {
+    if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) {
+        return false;
+    }
     int aBits = SkFloatAs2sCompliment(a);
     int bBits = SkFloatAs2sCompliment(b);
     // Find the difference in ULPs.
@@ -33,6 +64,9 @@ static bool less_ulps(float a, float b, int epsilon) {
     if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) {
         return false;
     }
+    if (arguments_denormalized(a, b, epsilon)) {
+        return a <= b - FLT_EPSILON * epsilon;
+    }
     int aBits = SkFloatAs2sCompliment(a);
     int bBits = SkFloatAs2sCompliment(b);
     // Find the difference in ULPs.
@@ -43,6 +77,9 @@ static bool less_or_equal_ulps(float a, float b, int epsilon) {
     if (!SkScalarIsFinite(a) || !SkScalarIsFinite(b)) {
         return false;
     }
+    if (arguments_denormalized(a, b, epsilon)) {
+        return a < b + FLT_EPSILON * epsilon;
+    }
     int aBits = SkFloatAs2sCompliment(a);
     int bBits = SkFloatAs2sCompliment(b);
     // Find the difference in ULPs.
@@ -55,6 +92,11 @@ bool AlmostBequalUlps(float a, float b) {
     return equal_ulps(a, b, UlpsEpsilon);
 }
 
+bool AlmostDequalUlps(float a, float b) {
+    const int UlpsEpsilon = 16;
+    return d_equal_ulps(a, b, UlpsEpsilon);
+}
+
 bool AlmostEqualUlps(float a, float b) {
     const int UlpsEpsilon = 16;
     return equal_ulps(a, b, UlpsEpsilon);
@@ -65,6 +107,11 @@ bool NotAlmostEqualUlps(float a, float b) {
     return not_equal_ulps(a, b, UlpsEpsilon);
 }
 
+bool NotAlmostDequalUlps(float a, float b) {
+    const int UlpsEpsilon = 16;
+    return d_not_equal_ulps(a, b, UlpsEpsilon);
+}
+
 bool RoughlyEqualUlps(float a, float b) {
     const int UlpsEpsilon = 256;
     return equal_ulps(a, b, UlpsEpsilon);
index bc39675..0ad10c2 100644 (file)
@@ -28,11 +28,22 @@ inline bool AlmostEqualUlps(double a, double b) {
     return AlmostEqualUlps(SkDoubleToScalar(a), SkDoubleToScalar(b));
 }
 
+// Use Almost Dequal when comparing should not special case denormalized values.
+bool AlmostDequalUlps(float a, float b);
+inline bool AlmostDequalUlps(double a, double b) {
+    return AlmostDequalUlps(SkDoubleToScalar(a), SkDoubleToScalar(b));
+}
+
 bool NotAlmostEqualUlps(float a, float b);
 inline bool NotAlmostEqualUlps(double a, double b) {
     return NotAlmostEqualUlps(SkDoubleToScalar(a), SkDoubleToScalar(b));
 }
 
+bool NotAlmostDequalUlps(float a, float b);
+inline bool NotAlmostDequalUlps(double a, double b) {
+    return NotAlmostDequalUlps(SkDoubleToScalar(a), SkDoubleToScalar(b));
+}
+
 // Use Almost Bequal when comparing coordinates in conjunction with between.
 bool AlmostBequalUlps(float a, float b);
 inline bool AlmostBequalUlps(double a, double b) {
index 5559026..46ec7b9 100644 (file)
@@ -90,7 +90,7 @@ void SkPathWriter::init() {
 }
 
 bool SkPathWriter::isClosed() const {
-    return !fEmpty && fFirstPt == fDefer[1];
+    return !fEmpty && SkDPoint::ApproximatelyEqual(fFirstPt, fDefer[1]);
 }
 
 void SkPathWriter::lineTo() {
index d3a6a78..dca96de 100644 (file)
@@ -152,7 +152,7 @@ int SkQuarticRootsReal(int firstCubicRoot, const double A, const double B, const
     // eliminate duplicates
     for (int i = 0; i < num - 1; ++i) {
         for (int j = i + 1; j < num; ) {
-            if (AlmostEqualUlps(s[i], s[j])) {
+            if (AlmostDequalUlps(s[i], s[j])) {
                 if (j < --num) {
                     s[j] = s[num];
                 }
index d04f2db..109c42e 100644 (file)
@@ -61,6 +61,7 @@ static void standardTestCases(skiatest::Reporter* reporter) {
             }
             REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
         }
+        reporter->bumpTestCount();
     }
 }
 
@@ -163,7 +164,17 @@ static const SkDCubic testSet[] = {
 const size_t testSetCount = SK_ARRAY_COUNT(testSet);
 
 static const SkDCubic newTestSet[] = {
-{{{134, 11414}, {131.990234375, 11414}, {130.32666015625, 11415.482421875}, {130.04275512695312, 11417.4130859375}}},
+#if 0  // FIXME: asserts coincidence, not working yet\r
+{{{195, 785}, {124.30755615234375, 785}, {67, 841.85986328125}, {67, 912}}},\r
+{{{67, 913}, {67, 842.30755615234375}, {123.85984039306641, 785}, {194, 785}}},
+#endif
+
+{{{399,657}, {399,661.970581}, {403.029449,666}, {408,666}}},
+{{{406,666}, {402.686279,666}, {400,663.313721}, {400,660}}},
+
+{{{0,5}, {3,5}, {3,0}, {3,2}}},
+{{{0,3}, {2,3}, {5,0}, {5,3}}},
+
 {{{132, 11419}, {130.89543151855469, 11419}, {130, 11418.1044921875}, {130, 11417}}},
 
 {{{3, 4}, {1, 5}, {4, 3}, {6, 4}}},
@@ -283,7 +294,8 @@ static const SkDCubic newTestSet[] = {
 
 const size_t newTestSetCount = SK_ARRAY_COUNT(newTestSet);
 
-static void oneOff(skiatest::Reporter* reporter, const SkDCubic& cubic1, const SkDCubic& cubic2) {
+static void oneOff(skiatest::Reporter* reporter, const SkDCubic& cubic1, const SkDCubic& cubic2,
+        bool coin) {
     SkASSERT(ValidCubic(cubic1));
     SkASSERT(ValidCubic(cubic2));
 #if ONE_OFF_DEBUG
@@ -317,6 +329,7 @@ static void oneOff(skiatest::Reporter* reporter, const SkDCubic& cubic1, const S
 #endif
     SkIntersections intersections;
     intersections.intersect(cubic1, cubic2);
+    REPORTER_ASSERT(reporter, !coin || intersections.used() == 2);
     double tt1, tt2;
     SkDPoint xy1, xy2;
     for (int pt3 = 0; pt3 < intersections.used(); ++pt3) {
@@ -334,18 +347,19 @@ static void oneOff(skiatest::Reporter* reporter, const SkDCubic& cubic1, const S
        REPORTER_ASSERT(reporter, xy2.approximatelyEqual(iPt));
        REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
     }
+    reporter->bumpTestCount();
 }
 
 static void oneOff(skiatest::Reporter* reporter, int outer, int inner) {
     const SkDCubic& cubic1 = testSet[outer];
     const SkDCubic& cubic2 = testSet[inner];
-    oneOff(reporter, cubic1, cubic2);
+    oneOff(reporter, cubic1, cubic2, false);
 }
 
 static void newOneOff(skiatest::Reporter* reporter, int outer, int inner) {
     const SkDCubic& cubic1 = newTestSet[outer];
     const SkDCubic& cubic2 = newTestSet[inner];
-    oneOff(reporter, cubic1, cubic2);
+    oneOff(reporter, cubic1, cubic2, false);
 }
 
 static void oneOffTests(skiatest::Reporter* reporter) {
@@ -412,6 +426,7 @@ static void CubicIntersection_RandTest(skiatest::Reporter* reporter) {
             SkDPoint xy2 = cubic2.ptAtT(tt2);
             REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2));
         }
+        reporter->bumpTestCount();
     }
 }
 
@@ -559,6 +574,7 @@ static void selfOneOff(skiatest::Reporter* reporter, int index) {
     SkDPoint pt1 = cubic.ptAtT(i[0][0]);
     SkDPoint pt2 = cubic.ptAtT(i[1][0]);
     REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2));
+    reporter->bumpTestCount();
 }
 
 static void cubicIntersectionSelfTest(skiatest::Reporter* reporter) {
@@ -568,6 +584,34 @@ static void cubicIntersectionSelfTest(skiatest::Reporter* reporter) {
     }
 }
 
+static const SkDCubic coinSet[] = {
+    {{{317, 711}, {322.52285766601562, 711}, {327, 715.4771728515625}, {327, 721}}},\r
+    {{{324.07107543945312, 713.928955078125}, {324.4051513671875, 714.26300048828125},\r
+            {324.71566772460937, 714.62060546875}, {325, 714.9990234375}}},\r
+
+    {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
+    {{{2, 3}, {0, 4}, {3, 2}, {5, 3}}},
+};
+
+size_t coinSetCount = SK_ARRAY_COUNT(coinSet);
+
+static void coinOneOff(skiatest::Reporter* reporter, int index) {
+    const SkDCubic& cubic1 = coinSet[index];
+    const SkDCubic& cubic2 = coinSet[index + 1];
+    oneOff(reporter, cubic1, cubic2, true);
+}
+
+static void cubicIntersectionCoinTest(skiatest::Reporter* reporter) {
+    size_t firstFail = 0;
+    for (size_t index = firstFail; index < coinSetCount; index += 2) {
+        coinOneOff(reporter, index);
+    }
+}
+
+static void PathOpsCubicCoinOneOffTest(skiatest::Reporter* reporter) {
+    coinOneOff(reporter, 0);
+}
+
 static void PathOpsCubicIntersectionOneOffTest(skiatest::Reporter* reporter) {
     newOneOff(reporter, 0, 1);
 }
@@ -579,6 +623,7 @@ static void PathOpsCubicSelfOneOffTest(skiatest::Reporter* reporter) {
 static void PathOpsCubicIntersectionTest(skiatest::Reporter* reporter) {
     oneOffTests(reporter);
     cubicIntersectionSelfTest(reporter);
+    cubicIntersectionCoinTest(reporter);
     standardTestCases(reporter);
     if (false) CubicIntersection_IntersectionFinder();
     if (false) CubicIntersection_RandTest(reporter);
@@ -590,3 +635,5 @@ DEFINE_TESTCLASS_SHORT(PathOpsCubicIntersectionTest)
 DEFINE_TESTCLASS_SHORT(PathOpsCubicIntersectionOneOffTest)
 
 DEFINE_TESTCLASS_SHORT(PathOpsCubicSelfOneOffTest)
+
+DEFINE_TESTCLASS_SHORT(PathOpsCubicCoinOneOffTest)
index 1b1168b..aafc613 100644 (file)
@@ -211,7 +211,7 @@ const SkDCubic lessEpsilonLines[] = {
     {{{1, 1}, {0, 0}, {0, 0}, {D, 0}}},
     {{{0, D}, {0, 0}, {1, 1}, {2, 2}}},  // 2 coincident
     {{{0, 0}, {1, 1}, {0, D}, {2, 2}}},
-    {{{0, 0}, {1, 1}, {2, 2}, {0, D}}},
+    {{{0, 0}, {1, 1}, {2, 2}, {1, 1+D}}},
     {{{1, 1}, {0, D}, {0, 0}, {2, 2}}},
     {{{1, 1}, {0, D}, {2, 2}, {0, 0}}},
     {{{1, 1}, {2, 2}, {D, 0}, {0, 0}}},
@@ -247,7 +247,7 @@ const SkDCubic negEpsilonLines[] = {
     {{{1, 1}, {0, 0}, {0, 0}, {N, 0}}},
     {{{0, N}, {0, 0}, {1, 1}, {2, 2}}},  // 2 coincident
     {{{0, 0}, {1, 1}, {0, N}, {2, 2}}},
-    {{{0, 0}, {1, 1}, {2, 2}, {0, N}}},
+    {{{0, 0}, {1, 1}, {2, 2}, {1, 1+N}}},
     {{{1, 1}, {0, N}, {0, 0}, {2, 2}}},
     {{{1, 1}, {0, N}, {2, 2}, {0, 0}}},
     {{{1, 1}, {2, 2}, {N, 0}, {0, 0}}},
index 95eb621..bfabfe8 100644 (file)
@@ -15,6 +15,12 @@ static struct lineCubic {
     SkDCubic cubic;
     SkDLine line;
 } lineCubicTests[] = {
+    {{{{0,1}, {1,6}, {4,1}, {4,3}}},\r
+            {{{6,1}, {1,4}}}},\r
+
+    {{{{0,1}, {2,6}, {4,1}, {5,4}}},
+            {{{6,2}, {1,4}}}},
+
     {{{{0,4}, {3,4}, {6,2}, {5,2}}},
             {{{4,3}, {2,6}}}},
 #if 0
index 76ecd01..09127e8 100644 (file)
@@ -17,12 +17,17 @@ static struct lineCubic {
     int answerCount;
     SkDPoint answers[2];
 } quadCubicTests[] = {
+    {{{{49, 47}, {49, 74.614250183105469}, {26.614250183105469, 97}, {-1, 97}}},\r
+     {{{-8.659739592076221e-015, 96.991401672363281}, {20.065492630004883, 96.645187377929688},
+       {34.355339050292969, 82.355339050292969}}}, 2,
+      {{34.355339050292969,82.355339050292969}, {34.306797674910243,82.403823585863449}}},
+
     {{{{10,234}, {10,229.58172607421875}, {13.581720352172852,226}, {18,226}}},
-        {{{18,226}, {14.686291694641113,226}, {12.342399597167969,228.3424072265625}}}, 1,
-        {{18,226}, {0,0}}},
+     {{{18,226}, {14.686291694641113,226}, {12.342399597167969,228.3424072265625}}}, 1,
+      {{18,226}, {0,0}}},
     {{{{10,234}, {10,229.58172607421875}, {13.581720352172852,226}, {18,226}}},
-        {{{12.342399597167969,228.3424072265625}, {10,230.68629455566406}, {10,234}}}, 1,
-        {{10,234}, {0,0}}},
+     {{{12.342399597167969,228.3424072265625}, {10,230.68629455566406}, {10,234}}}, 1,
+      {{10,234}, {0,0}}},
 };
 
 static const size_t quadCubicTests_count = SK_ARRAY_COUNT(quadCubicTests);
index 7b89bbe..e1520aa 100644 (file)
@@ -116,6 +116,8 @@ static void PathOpsReduceOrderCubicTest(skiatest::Reporter* reporter) {
         order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics, SkReduceOrder::kFill_Style);
         if (order == 1) {
             SkDebugf("[%d] notPointDegenerates order=%d\n", static_cast<int>(index), order);
+            order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics,
+                    SkReduceOrder::kFill_Style);
             REPORTER_ASSERT(reporter, 0);
         }
     }
@@ -152,6 +154,8 @@ static void PathOpsReduceOrderCubicTest(skiatest::Reporter* reporter) {
         order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics, SkReduceOrder::kFill_Style);
         if (order != 2) {
             SkDebugf("[%d] line less by epsilon/2 order=%d\n", static_cast<int>(index), order);
+            order = reducer.reduce(cubic, SkReduceOrder::kAllow_Quadratics,
+                    SkReduceOrder::kFill_Style);
             REPORTER_ASSERT(reporter, 0);
         }
     }
@@ -235,4 +239,5 @@ static void PathOpsReduceOrderCubicTest(skiatest::Reporter* reporter) {
 }
 
 #include "TestClassDef.h"
+
 DEFINE_TESTCLASS_SHORT(PathOpsReduceOrderCubicTest)
index c32bfa1..c3162ab 100644 (file)
@@ -9,6 +9,7 @@
 #include "PathOpsThreadedCommon.h"
 #include "SkBitmap.h"
 #include "SkCanvas.h"
+#include "SkForceLinking.h"
 #include "SkMatrix.h"
 #include "SkPaint.h"
 #include "SkStream.h"
@@ -18,6 +19,8 @@
 #include <sys/sysctl.h>
 #endif
 
+__SK_FORCE_IMAGE_DECODER_LINKING;
+
 static const char marker[] =
     "</div>\n"
     "\n"
@@ -627,29 +630,35 @@ bool testThreadedPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkP
     return innerPathOp(reporter, a, b, shapeOp, testName, true);
 }
 
+SK_DECLARE_STATIC_MUTEX(gMutex);
+
 int initializeTests(skiatest::Reporter* reporter, const char* test) {
 #ifdef SK_DEBUG
     SkPathOpsDebug::gMaxWindSum = 4;
     SkPathOpsDebug::gMaxWindValue = 4;
 #endif
-#if DEBUG_SHOW_TEST_NAME
-    testName = test;
-    size_t testNameSize = strlen(test);
-    SkFILEStream inFile("../../experimental/Intersection/op.htm");
-    if (inFile.isValid()) {
-        SkTDArray<char> inData;
-        inData.setCount(inFile.getLength());
-        size_t inLen = inData.count();
-        inFile.read(inData.begin(), inLen);
-        inFile.setPath(NULL);
-        char* insert = strstr(inData.begin(), marker);
-        if (insert) {
-            insert += sizeof(marker) - 1;
-            const char* numLoc = insert + 4 /* indent spaces */ + testNameSize - 1;
-            testNumber = atoi(numLoc) + 1;
+    if (reporter->verbose()) {
+        SkAutoMutexAcquire lock(gMutex);
+        testName = test;
+        size_t testNameSize = strlen(test);
+        SkFILEStream inFile("../../experimental/Intersection/op.htm");
+        if (inFile.isValid()) {
+            SkTDArray<char> inData;
+            inData.setCount(inFile.getLength());
+            size_t inLen = inData.count();
+            inFile.read(inData.begin(), inLen);
+            inFile.setPath(NULL);
+            char* insert = strstr(inData.begin(), marker);
+            if (insert) {
+                insert += sizeof(marker) - 1;
+                const char* numLoc = insert + 4 /* indent spaces */ + testNameSize - 1;
+                testNumber = atoi(numLoc) + 1;
+            }
         }
+    } else {
+        testName = "pathOpTest";
+        testNumber = 1;
     }
-#endif
     return reporter->allowThreaded() ? SkThreadPool::kThreadPerCore : 1;
 }
 
index ee15363..f37a509 100644 (file)
@@ -48,6 +48,12 @@ static const SkDLine noIntersect[][2] = {
 static const size_t noIntersect_count = SK_ARRAY_COUNT(noIntersect);
 
 static const SkDLine coincidentTests[][2] = {
+   {{{{0,482.5}, {-4.4408921e-016,682.5}}},
+    {{{0,683}, {0,482}}}},
+
+   {{{{1.77635684e-015,312}, {-1.24344979e-014,348}}},
+    {{{0,348}, {0,312}}}},
+
    {{{{979.304871, 561}, {1036.69507, 291}}},
     {{{985.681519, 531}, {982.159790, 547.568542}}}},
 
@@ -116,6 +122,7 @@ static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const Sk
         ts.vertical(line1, top, bottom, line2[0].fX, line2[0].fY != top);
         check_results(reporter, line1, line2, ts);
     }
+    reporter->bumpTestCount();
 }
 
 static void testOneCoincident(skiatest::Reporter* reporter, const SkDLine& line1,
@@ -127,6 +134,46 @@ static void testOneCoincident(skiatest::Reporter* reporter, const SkDLine& line1
     REPORTER_ASSERT(reporter, pts == 2);
     REPORTER_ASSERT(reporter, pts == ts.used());
     check_results(reporter, line1, line2, ts);
+    if (line1[0] == line1[1] || line2[0] == line2[1]) {
+        return;
+    }
+    if (line1[0].fY == line1[1].fY) {
+        double left = SkTMin(line1[0].fX, line1[1].fX);
+        double right = SkTMax(line1[0].fX, line1[1].fX);
+        SkIntersections ts;
+        ts.horizontal(line2, left, right, line1[0].fY, line1[0].fX != left);
+        REPORTER_ASSERT(reporter, pts == 2);
+        REPORTER_ASSERT(reporter, pts == ts.used());
+        check_results(reporter, line2, line1, ts);
+    }
+    if (line2[0].fY == line2[1].fY) {
+        double left = SkTMin(line2[0].fX, line2[1].fX);
+        double right = SkTMax(line2[0].fX, line2[1].fX);
+        SkIntersections ts;
+        ts.horizontal(line1, left, right, line2[0].fY, line2[0].fX != left);
+        REPORTER_ASSERT(reporter, pts == 2);
+        REPORTER_ASSERT(reporter, pts == ts.used());
+        check_results(reporter, line1, line2, ts);
+    }
+    if (line1[0].fX == line1[1].fX) {
+        double top = SkTMin(line1[0].fY, line1[1].fY);
+        double bottom = SkTMax(line1[0].fY, line1[1].fY);
+        SkIntersections ts;
+        ts.vertical(line2, top, bottom, line1[0].fX, line1[0].fY != top);
+        REPORTER_ASSERT(reporter, pts == 2);
+        REPORTER_ASSERT(reporter, pts == ts.used());
+        check_results(reporter, line2, line1, ts);
+    }
+    if (line2[0].fX == line2[1].fX) {
+        double top = SkTMin(line2[0].fY, line2[1].fY);
+        double bottom = SkTMax(line2[0].fY, line2[1].fY);
+        SkIntersections ts;
+        ts.vertical(line1, top, bottom, line2[0].fX, line2[0].fY != top);
+        REPORTER_ASSERT(reporter, pts == 2);
+        REPORTER_ASSERT(reporter, pts == ts.used());
+        check_results(reporter, line1, line2, ts);
+    }
+    reporter->bumpTestCount();
 }
 
 static void PathOpsLineIntersectionTest(skiatest::Reporter* reporter) {
@@ -135,13 +182,11 @@ static void PathOpsLineIntersectionTest(skiatest::Reporter* reporter) {
         const SkDLine& line1 = coincidentTests[index][0];
         const SkDLine& line2 = coincidentTests[index][1];
         testOneCoincident(reporter, line1, line2);
-        reporter->bumpTestCount();
     }
     for (index = 0; index < tests_count; ++index) {
         const SkDLine& line1 = tests[index][0];
         const SkDLine& line2 = tests[index][1];
         testOne(reporter, line1, line2);
-        reporter->bumpTestCount();
     }
     for (index = 0; index < noIntersect_count; ++index) {
         const SkDLine& line1 = noIntersect[index][0];
index dee99db..4d03577 100644 (file)
@@ -712,7 +712,6 @@ static void cubicOp37d(skiatest::Reporter* reporter) {
     testPathOp(reporter, path, pathB, kDifference_PathOp);
 }
 
-#if 1
 // this fails to detect a cubic/cubic intersection
 // the slight overlap is missed when the cubics are approximated by quadratics
 // and the subsequent line/cubic intersection also (correctly) misses the intersection
@@ -730,7 +729,6 @@ static void cubicOp38d(skiatest::Reporter* reporter) {
     pathB.close();
     testPathOp(reporter, path, pathB, kDifference_PathOp);
 }
-#endif
 
 static void cubicOp39d(skiatest::Reporter* reporter) {
     SkPath path, pathB;
@@ -1834,7 +1832,8 @@ static void skpkkiste_to98(skiatest::Reporter* reporter) {
     testPathOp(reporter, path, pathB, kIntersect_PathOp);
 }
 
-#if 01
+#define ISSUE_1417_WORKING_ON_LINUX_32 0
+#if ISSUE_1417_WORKING_ON_LINUX_32
 static void issue1417(skiatest::Reporter* reporter) {
     SkPath path1;
     path1.moveTo(122.58908843994140625f, 82.2836456298828125f);
@@ -2066,7 +2065,8 @@ static void rectOp3x(skiatest::Reporter* reporter) {
     testPathOp(reporter, path, pathB, kXOR_PathOp);
 }
 
-#if 0
+#define ISSUE_1435_WORKING 0
+#if ISSUE_1435_WORKING
 static void issue1435(skiatest::Reporter* reporter) {
     SkPath path1;
     path1.moveTo(160, 60);
@@ -2120,7 +2120,6 @@ static void issue1435(skiatest::Reporter* reporter) {
 }
 #endif
 
-#if 0
 static void bufferOverflow(skiatest::Reporter* reporter) {
     SkPath path;
     path.addRect(0,0, 300,170141183460469231731687303715884105728.);
@@ -2128,9 +2127,7 @@ static void bufferOverflow(skiatest::Reporter* reporter) {
     pathB.addRect(0,0, 300,16);
     testPathOp(reporter, path, pathB, kUnion_PathOp);
 }
-#endif
 
-#if 0
 static void skpkkiste_to716(skiatest::Reporter* reporter) {
     SkPath path;
     path.setFillType(SkPath::kEvenOdd_FillType);
@@ -2154,7 +2151,6 @@ static void skpkkiste_to716(skiatest::Reporter* reporter) {
     pathB.close();
     testPathOp(reporter, path, pathB, kIntersect_PathOp);
 }
-#endif
 
 static void loopEdge1(skiatest::Reporter* reporter) {
     SkPath path;
@@ -2277,12 +2273,789 @@ static void cubicOp91u(skiatest::Reporter* reporter) {
     pathB.close();
     testPathOp(reporter, path, pathB, kUnion_PathOp);
 }
+
+static void skpaaalgarve_org53(skiatest::Reporter* reporter) {  //  add t cancel\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+   path.moveTo(-1.24344979e-014f, 348);\r
+    path.lineTo(258, 348);\r
+    path.lineTo(258, 322);\r
+    path.quadTo(258, 317.857849f, 255.072006f, 314.928009f);\r
+    path.quadTo(252.142136f, 312, 248, 312);\r
+    path.lineTo(1.77635684e-015f, 312);\r
+    path.lineTo(-1.24344979e-014f, 348);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+   pathB.moveTo(0, 312);\r
+    pathB.lineTo(258, 312);\r
+    pathB.lineTo(258, 348);\r
+    pathB.lineTo(0, 348);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+
+static void skpabcspark_ca103(skiatest::Reporter* reporter) {  //  add t cancel\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(1.99840144e-015f, 494);\r
+    path.lineTo(97, 494);\r
+    path.quadTo(100.313705f, 494, 102.6576f, 491.657593f);\r
+    path.quadTo(105, 489.313721f, 105, 486);\r
+    path.lineTo(105, 425);\r
+    path.quadTo(105, 421.686279f, 102.6576f, 419.342407f);\r
+    path.quadTo(100.313705f, 417, 97, 417);\r
+    path.lineTo(2.22044605e-016f, 417);\r
+    path.lineTo(1.99840144e-015f, 494);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(0, 417);\r
+    pathB.lineTo(105, 417);\r
+    pathB.lineTo(105, 494);\r
+    pathB.lineTo(0, 494);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}
+
+static void skpacesoftech_com47(skiatest::Reporter* reporter) {  // partial coincidence\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(670.537415f, 285);\r
+    path.lineTo(670.387451f, 285);\r
+    path.lineTo(596.315186f, 314.850708f);\r
+    path.lineTo(626.19696f, 389);\r
+    path.lineTo(626.346863f, 389);\r
+    path.lineTo(700.419189f, 359.149261f);\r
+    path.lineTo(670.537415f, 285);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(663.318542f, 374.100616f);\r
+    pathB.quadTo(647.950989f, 380.293671f, 632.705322f, 373.806305f);\r
+    pathB.quadTo(617.459595f, 367.318909f, 611.266541f, 351.951355f);\r
+    pathB.quadTo(605.073486f, 336.58374f, 611.560913f, 321.338074f);\r
+    pathB.quadTo(618.048279f, 306.092407f, 633.415833f, 299.899353f);\r
+    pathB.quadTo(648.783447f, 293.706299f, 664.029114f, 300.193665f);\r
+    pathB.quadTo(679.27478f, 306.68103f, 685.467834f, 322.048645f);\r
+    pathB.quadTo(691.660889f, 337.416199f, 685.173523f, 352.661896f);\r
+    pathB.quadTo(678.686157f, 367.907562f, 663.318542f, 374.100616f);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+
+static void skpact_com43(skiatest::Reporter* reporter) {  // bridge op\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(1.45716772e-016f, 924.336121f);\r
+    path.lineTo(-1.11022302e-016f, 920);\r
+    path.lineTo(6, 920);\r
+    path.lineTo(6, 926);\r
+    path.lineTo(1.66389287f, 926);\r
+    path.quadTo(1.18842196f, 925.674561f, 0.756800175f, 925.243225f);\r
+    path.quadTo(0.325406998f, 924.811523f, 1.45716772e-016f, 924.336121f);\r
+    path.close();\r
+    path.moveTo(1, 921);\r
+    path.lineTo(5, 921);\r
+    path.lineTo(5, 925);\r
+    path.cubicTo(2.79086018f, 925, 1, 923.209167f, 1, 921);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(-1, 920);\r
+    pathB.lineTo(0, 920);\r
+    pathB.lineTo(3, 927);\r
+    pathB.lineTo(-1, 927);\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+
+static void skpadbox_lt8(skiatest::Reporter* reporter) {  // zero span\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(320.097229f, 628.573669f);\r
+    path.lineTo(610.227173f, 85.7786865f);\r
+    path.lineTo(946.652588f, 265.601807f);\r
+    path.lineTo(656.522644f, 808.39679f);\r
+    path.lineTo(320.097229f, 628.573669f);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kInverseWinding_FillType);\r
+    pathB.moveTo(333.866608f, 623.496155f);\r
+    pathB.lineTo(613.368042f, 100.585754f);\r
+    pathB.cubicTo(613.685303f, 99.9921265f, 614.423767f, 99.7681885f, 615.017395f, 100.085449f);\r
+    pathB.lineTo(932.633057f, 269.854553f);\r
+    pathB.cubicTo(933.226685f, 270.171875f, 933.450623f, 270.910278f, 933.133301f, 271.503906f);\r
+    pathB.lineTo(653.631897f, 794.414307f);\r
+    pathB.cubicTo(653.314636f, 795.007935f, 652.576172f, 795.231934f, 651.982544f, 794.914612f);\r
+    pathB.lineTo(334.366943f, 625.145508f);\r
+    pathB.cubicTo(333.773315f, 624.828247f, 333.549286f, 624.089783f, 333.866608f, 623.496155f);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}
+
+static void skpadindex_de4(skiatest::Reporter* reporter) {  // find chase op\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(0, 926);\r
+    path.lineTo(0, 0);\r
+    path.lineTo(1280, 0);\r
+    path.lineTo(1280, 926);\r
+    path.lineTo(0, 926);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(0, 312);\r
+    pathB.lineTo(8.20486257e-015f, 178);\r
+    pathB.lineTo(49, 178);\r
+    pathB.lineTo(49, 312);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}
+
+static void skpadithya_putr4_blogspot_com551(skiatest::Reporter* reporter) { // calc common\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(205.605804f, 142.334625f);\r
+    path.lineTo(254.665359f, 85.6058044f);\r
+    path.lineTo(311.394196f, 134.665359f);\r
+    path.lineTo(262.334625f, 191.39418f);\r
+    path.lineTo(205.605804f, 142.334625f);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(283.407959f, 110.462646f);\r
+    pathB.cubicTo(298.864319f, 123.829437f, 300.558258f, 147.195221f, 287.191467f, 162.651581f);\r
+    pathB.lineTo(286.537354f, 163.407959f);\r
+    pathB.cubicTo(273.170563f, 178.864334f, 249.804779f, 180.558258f, 234.348419f, 167.191467f);\r
+    pathB.lineTo(233.592026f, 166.537338f);\r
+    pathB.cubicTo(218.135666f, 153.170547f, 216.441727f, 129.804779f, 229.808517f, 114.348412f);\r
+    pathB.lineTo(230.462646f, 113.592026f);\r
+    pathB.cubicTo(243.829437f, 98.1356659f, 267.195221f, 96.4417267f, 282.651581f, 109.808517f);\r
+    pathB.lineTo(283.407959f, 110.462646f);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}
+
+static void skpadspert_de11(skiatest::Reporter* reporter) {  // mark and chase winding\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(-4.4408921e-016f, 682.5f);\r
+    path.lineTo(30.5f, 682.5f);\r
+    path.cubicTo(32.709137f, 682.5f, 34.5f, 680.709167f, 34.5f, 678.5f);\r
+    path.lineTo(34.5f, 486.5f);\r
+    path.cubicTo(34.5f, 484.290863f, 32.709137f, 482.5f, 30.5f, 482.5f);\r
+    path.lineTo(0, 482.5f);\r
+    path.lineTo(-4.4408921e-016f, 682.5f);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(0, 482);\r
+    pathB.lineTo(35, 482);\r
+    pathB.lineTo(35, 683);\r
+    pathB.lineTo(0, 683);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}
+
+static void skpaiaigames_com870(skiatest::Reporter* reporter) {  // cubic/cubic intersect\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(324.071075f, 845.071045f);\r
+    path.cubicTo(324.405151f, 844.737f, 324.715668f, 844.379395f, 325, 844.000977f);\r
+    path.lineTo(325, 842.127197f);\r
+    path.cubicTo(324.571411f, 842.956238f, 324.017761f, 843.710144f, 323.363953f, 844.363953f);\r
+    path.lineTo(324.071075f, 845.071045f);\r
+    path.close();\r
+    path.moveTo(323.363953f, 714.636047f);\r
+    path.lineTo(324.071075f, 713.928955f);\r
+    path.cubicTo(324.405151f, 714.263f, 324.715668f, 714.620605f, 325, 714.999023f);\r
+    path.lineTo(325, 716.872803f);\r
+    path.cubicTo(324.571411f, 716.043762f, 324.017761f, 715.289856f, 323.363953f, 714.636047f);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(317, 711);\r
+    pathB.cubicTo(322.522858f, 711, 327, 715.477173f, 327, 721);\r
+    pathB.lineTo(327, 838);\r
+    pathB.cubicTo(327, 843.522827f, 322.522858f, 848, 317, 848);\r
+    pathB.lineTo(155, 848);\r
+    pathB.cubicTo(149.477158f, 848, 145, 843.522827f, 145, 838);\r
+    pathB.lineTo(145, 721);\r
+    pathB.cubicTo(145, 715.477173f, 149.477158f, 711, 155, 711);\r
+    pathB.lineTo(317, 711);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+
+static void cubicOp92i(skiatest::Reporter* reporter) {\r
+    SkPath path, pathB;\r
+    path.setFillType(SkPath::kWinding_FillType);\r
+    path.moveTo(0, 1);\r
+    path.cubicTo(2, 6, 4, 1, 5, 4);\r
+    path.close();\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(1, 4);\r
+    pathB.cubicTo(4, 5, 1, 0, 6, 2);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}
+
+static void cubicOp93d(skiatest::Reporter* reporter) {\r
+    SkPath path, pathB;\r
+    path.setFillType(SkPath::kWinding_FillType);\r
+    path.moveTo(0, 1);\r
+    path.cubicTo(1, 6, 4, 1, 4, 3);\r
+    path.close();\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(1, 4);\r
+    pathB.cubicTo(3, 4, 1, 0, 6, 1);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kDifference_PathOp);\r
+}
+
+static void cubicOp94u(skiatest::Reporter* reporter) {\r
+    SkPath path, pathB;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(0, 3);\r
+    path.cubicTo(2, 3, 5, 0, 5, 3);\r
+    path.close();\r
+    pathB.setFillType(SkPath::kEvenOdd_FillType);\r
+    pathB.moveTo(0, 5);\r
+    pathB.cubicTo(3, 5, 3, 0, 3, 2);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kUnion_PathOp);\r
+}
+
+static void skpadbox_lt15(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(333.292084f, 624.570984f);\r
+    path.lineTo(614.229797f, 98.9735107f);\r
+    path.lineTo(933.457764f, 269.604431f);\r
+    path.lineTo(652.52002f, 795.201904f);\r
+    path.lineTo(333.292084f, 624.570984f);\r
+    path.close();\r
+    SkPath pathB;\r
+     pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(613.368042f, 100.585754f);\r
+    pathB.cubicTo(613.685303f, 99.9921265f, 614.423767f, 99.7681885f, 615.017395f, 100.085449f);\r
+    pathB.lineTo(932.633057f, 269.854553f);\r
+    pathB.cubicTo(933.226685f, 270.171875f, 933.450623f, 270.910278f, 933.133301f, 271.503906f);\r
+    pathB.lineTo(653.631897f, 794.414307f);\r
+    pathB.cubicTo(653.314636f, 795.007935f, 652.576172f, 795.231934f, 651.982544f, 794.914612f);\r
+    pathB.lineTo(334.366943f, 625.145508f);\r
+    pathB.cubicTo(333.773315f, 624.828247f, 333.549286f, 624.089783f, 333.866608f, 623.496155f);\r
+    pathB.lineTo(613.368042f, 100.585754f);\r
+     pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+
+static void skpadoption_org196(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(802, 367);\r
+    path.lineTo(802, 324);\r
+    path.lineTo(956, 324);\r
+    path.lineTo(956, 371);\r
+    path.quadTo(956, 373.071075f, 954.536011f, 374.536011f);\r
+    path.quadTo(953.071045f, 376, 951, 376);\r
+    path.lineTo(811, 376);\r
+    path.cubicTo(806.029419f, 376, 802, 371.970551f, 802, 367);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kInverseWinding_FillType);\r
+    pathB.moveTo(803, 326);\r
+    pathB.lineTo(955, 326);\r
+    pathB.lineTo(955, 370);\r
+    pathB.cubicTo(955, 372.761414f, 952.761414f, 375, 950, 375);\r
+    pathB.lineTo(808, 375);\r
+    pathB.cubicTo(805.238586f, 375, 803, 372.761414f, 803, 370);\r
+    pathB.lineTo(803, 326);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+
+static void skpadspert_net23(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(-2.220446e-018f, 483.5f);\r
+    path.lineTo(0, 482.5f);\r
+    path.lineTo(30.5f, 482.5f);\r
+    path.cubicTo(32.709137f, 482.5f, 34.5f, 484.290863f, 34.5f, 486.5f);\r
+    path.lineTo(34.5f, 678.5f);\r
+    path.cubicTo(34.5f, 680.709167f, 32.709137f, 682.5f, 30.5f, 682.5f);\r
+    path.lineTo(-4.4408921e-016f, 682.5f);\r
+    path.lineTo(-4.41868766e-016f, 681.5f);\r
+    path.lineTo(30.5f, 681.5f);\r
+    path.cubicTo(32.1568565f, 681.5f, 33.5f, 680.15686f, 33.5f, 678.5f);\r
+    path.lineTo(33.5f, 486.5f);\r
+    path.cubicTo(33.5f, 484.84314f, 32.1568565f, 483.5f, 30.5f, 483.5f);\r
+    path.lineTo(-2.220446e-018f, 483.5f);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(0, 482);\r
+    pathB.lineTo(35, 482);\r
+    pathB.lineTo(35, 683);\r
+    pathB.lineTo(0, 683);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+\r
+static void skpadventistmission_org572(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(1182.00037f, 926);\r
+    path.cubicTo(1181.08813f, 924.785583f, 1179.63586f, 924, 1178, 924);\r
+    path.lineTo(938, 924);\r
+    path.cubicTo(936.364197f, 924, 934.911865f, 924.785583f, 933.999634f, 926);\r
+    path.lineTo(1182.00037f, 926);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(934, 924);\r
+    pathB.lineTo(1182, 924);\r
+    pathB.lineTo(1182, 926);\r
+    pathB.lineTo(934, 926);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+
+static void skpagentxsites_com55(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(925, 27);\r
+    path.cubicTo(924.447693f, 27, 924, 27.4477158f, 924, 28);\r
+    path.lineTo(924, 55);\r
+    path.cubicTo(924, 55.5522842f, 924.447693f, 56, 925, 56);\r
+    path.lineTo(1103, 56);\r
+    path.cubicTo(1103.55225f, 56, 1104, 55.5522842f, 1104, 55);\r
+    path.lineTo(1104, 28);\r
+    path.cubicTo(1104, 27.4477158f, 1103.55225f, 27, 1103, 27);\r
+    path.lineTo(925, 27);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(1103, 27);\r
+    pathB.cubicTo(1104.10461f, 27, 1105, 27.8954315f, 1105, 29);\r
+    pathB.lineTo(1105, 54);\r
+    pathB.cubicTo(1105, 55.1045685f, 1104.10461f, 56, 1103, 56);\r
+    pathB.lineTo(926, 56);\r
+    pathB.cubicTo(924.895447f, 56, 924, 55.1045685f, 924, 54);\r
+    pathB.lineTo(924, 29);\r
+    pathB.cubicTo(924, 27.8954315f, 924.895447f, 27, 926, 27);\r
+    pathB.lineTo(1103, 27);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+
+static void skpbakosoft_com10(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(190, 170);\r
+    path.cubicTo(178.9543f, 170, 170, 178.9543f, 170, 190);\r
+    path.cubicTo(170, 201.0457f, 178.9543f, 210, 190, 210);\r
+    path.lineTo(370, 210);\r
+    path.cubicTo(381.045685f, 210, 390, 201.0457f, 390, 190);\r
+    path.cubicTo(390, 178.9543f, 381.045685f, 170, 370, 170);\r
+    path.lineTo(190, 170);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(210, 190);\r
+    pathB.quadTo(210, 198.284271f, 204.142136f, 204.142136f);\r
+    pathB.quadTo(198.284271f, 210, 190, 210);\r
+    pathB.quadTo(181.715729f, 210, 175.857864f, 204.142136f);\r
+    pathB.quadTo(170, 198.284271f, 170, 190);\r
+    pathB.quadTo(170, 181.715729f, 175.857864f, 175.857864f);\r
+    pathB.quadTo(181.715729f, 170, 190, 170);\r
+    pathB.quadTo(198.284271f, 170, 204.142136f, 175.857864f);\r
+    pathB.quadTo(210, 181.715729f, 210, 190);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+
+static void skpbambootheme_com12(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(47.8780937f, 58);\r
+    path.lineTo(0, 58);\r
+    path.lineTo(-8.65973959e-015f, 96.9914017f);\r
+    path.quadTo(20.0654926f, 96.6451874f, 34.3553391f, 82.3553391f);\r
+    path.quadTo(44.9466133f, 71.764061f, 47.8780937f, 58);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kEvenOdd_FillType);\r
+    pathB.moveTo(-1, -3);\r
+    pathB.lineTo(-1, -3);\r
+    pathB.cubicTo(26.6142502f, -3, 49, 19.3857498f, 49, 47);\r
+    pathB.lineTo(49, 47);\r
+    pathB.cubicTo(49, 74.6142502f, 26.6142502f, 97, -1, 97);\r
+    pathB.lineTo(-1, 97);\r
+    pathB.cubicTo(-28.6142502f, 97, -51, 74.6142502f, -51, 47);\r
+    pathB.lineTo(-51, 47);\r
+    pathB.cubicTo(-51, 19.3857498f, -28.6142502f, -3, -1, -3);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+
+static void skpakmmos_ru100(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(693.000488f, 926);\r
+    path.cubicTo(692.164734f, 925.37207f, 691.125793f, 925, 690, 925);\r
+    path.lineTo(578, 925);\r
+    path.cubicTo(576.874207f, 925, 575.835266f, 925.37207f, 574.999512f, 926);\r
+    path.lineTo(693.000488f, 926);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(575, 925);\r
+    pathB.lineTo(693, 925);\r
+    pathB.lineTo(693, 926);\r
+    pathB.lineTo(575, 926);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+
+#define SKPS_WORKING 0
+#if SKPS_WORKING
+static void skpcarpetplanet_ru22(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(195, 785);\r
+    path.cubicTo(124.307556f, 785, 67, 841.859863f, 67, 912);\r
+    path.lineTo(67, 913);\r
+    path.cubicTo(67, 917.388916f, 67.2243805f, 921.725769f, 67.662384f, 926);\r
+    path.lineTo(322, 926);\r
+    path.lineTo(322, 896.048035f);\r
+    path.cubicTo(314.09201f, 833.437622f, 260.247131f, 785, 195, 785);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(195, 785);\r
+    pathB.cubicTo(265.140167f, 785, 322, 842.307556f, 322, 913);\r
+    pathB.cubicTo(322, 983.692444f, 265.140167f, 1041, 195, 1041);\r
+    pathB.lineTo(194, 1041);\r
+    pathB.cubicTo(123.85984f, 1041, 67, 983.692444f, 67, 913);\r
+    pathB.cubicTo(67, 842.307556f, 123.85984f, 785, 194, 785);\r
+    pathB.lineTo(195, 785);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+\r
+static void skpcarrot_is24(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(945, 597);\r
+    path.quadTo(913.93396f, 597, 891.96698f, 618.96698f);\r
+    path.quadTo(870, 640.93396f, 870, 672);\r
+    path.quadTo(870, 703.06604f, 891.96698f, 725.03302f);\r
+    path.quadTo(913.93396f, 747, 945, 747);\r
+    path.quadTo(976.06604f, 747, 998.03302f, 725.03302f);\r
+    path.quadTo(1020, 703.06604f, 1020, 672);\r
+    path.quadTo(1020, 640.93396f, 998.03302f, 618.96698f);\r
+    path.quadTo(976.06604f, 597, 945, 597);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(945.080994f, 597.161987f);\r
+    pathB.cubicTo(903.659973f, 597.161987f, 870.080994f, 630.73999f, 870.080994f, 672.161987f);\r
+    pathB.cubicTo(870.080994f, 676.096008f, 870.387024f, 679.957031f, 870.971008f, 683.726013f);\r
+    pathB.cubicTo(876.53302f, 719.656006f, 907.593994f, 747.161987f, 945.080994f, 747.161987f);\r
+    pathB.cubicTo(982.567993f, 747.161987f, 1013.62903f, 719.656006f, 1019.19104f, 683.726013f);\r
+    pathB.cubicTo(1019.77502f, 679.955017f, 1020.08099f, 676.094971f, 1020.08099f, 672.161987f);\r
+    pathB.cubicTo(1020.08002f, 630.73999f, 986.502014f, 597.161987f, 945.080994f, 597.161987f);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}
+\r
+#endif\r
+\r
+static void skpbangalorenest_com4(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(0, 926);\r
+    path.lineTo(0, 0);\r
+    path.lineTo(1265, 0);\r
+    path.lineTo(1265, 926);\r
+    path.lineTo(0, 926);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(0, 290);\r
+    pathB.lineTo(-2.64514972e-014f, 146);\r
+    pathB.lineTo(30, 146);\r
+    pathB.lineTo(30, 290);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+
+static void skpbenzoteh_ru152(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(883, 23);\r
+    path.lineTo(883, 0);\r
+    path.lineTo(1122.5f, 0);\r
+    path.lineTo(1122.5f, 25.2136822f);\r
+    path.quadTo(1122.14441f, 25.9271851f, 1121.53601f, 26.5359993f);\r
+    path.quadTo(1120.07104f, 28, 1118, 28);\r
+    path.lineTo(888, 28);\r
+    path.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);\r
+    path.quadTo(883, 25.0710678f, 883, 23);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(883, 0);\r
+    pathB.lineTo(1123, 0);\r
+    pathB.lineTo(1123, 23);\r
+    pathB.quadTo(1123, 25.0710678f, 1121.53601f, 26.5359993f);\r
+    pathB.quadTo(1120.07104f, 28, 1118, 28);\r
+    pathB.lineTo(888, 28);\r
+    pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);\r
+    pathB.quadTo(883, 25.0710678f, 883, 23);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+
+static void skpbestred_ru37(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(883, 23);\r
+    path.lineTo(883, 0);\r
+    path.lineTo(1122.5f, 0);\r
+    path.lineTo(1122.5f, 25.2136822f);\r
+    path.quadTo(1122.14441f, 25.9271851f, 1121.53601f, 26.5359993f);\r
+    path.quadTo(1120.07104f, 28, 1118, 28);\r
+    path.lineTo(888, 28);\r
+    path.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);\r
+    path.quadTo(883, 25.0710678f, 883, 23);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(883, 0);\r
+    pathB.lineTo(1123, 0);\r
+    pathB.lineTo(1123, 23);\r
+    pathB.quadTo(1123, 25.0710678f, 1121.53601f, 26.5359993f);\r
+    pathB.quadTo(1120.07104f, 28, 1118, 28);\r
+    pathB.lineTo(888, 28);\r
+    pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);\r
+    pathB.quadTo(883, 25.0710678f, 883, 23);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+\r
+static void skpbingoentertainment_net189(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(896, 745.38678f);\r
+    path.lineTo(896, 873.38678f);\r
+    path.lineTo(922.567993f, 876.683716f);\r
+    path.lineTo(922.567993f, 748.683716f);\r
+    path.lineTo(896, 745.38678f);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(899.200928f, 745.783997f);\r
+    pathB.cubicTo(897.119385f, 745.525696f, 895.432007f, 752.031982f, 895.432007f, 760.316284f);\r
+    pathB.lineTo(895.432007f, 858.316284f);\r
+    pathB.cubicTo(895.432007f, 866.600586f, 897.119385f, 873.525696f, 899.200928f, 873.783997f);\r
+    pathB.lineTo(918.799133f, 876.216003f);\r
+    pathB.cubicTo(920.880615f, 876.474304f, 922.567993f, 869.968018f, 922.567993f, 861.683716f);\r
+    pathB.lineTo(922.567993f, 763.683716f);\r
+    pathB.cubicTo(922.567993f, 755.399414f, 920.880615f, 748.474304f, 918.799133f, 748.216003f);\r
+    pathB.lineTo(899.200928f, 745.783997f);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+
+static void skpcarrefour_ro62(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(1104, 453);\r
+    path.lineTo(399, 453);\r
+    path.lineTo(399, 657);\r
+    path.cubicTo(399, 661.970581f, 403.029449f, 666, 408, 666);\r
+    path.lineTo(1095, 666);\r
+    path.cubicTo(1099.97058f, 666, 1104, 661.970581f, 1104, 657);\r
+    path.lineTo(1104, 453);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kInverseWinding_FillType);\r
+    pathB.moveTo(400, 453);\r
+    pathB.lineTo(1103, 453);\r
+    pathB.lineTo(1103, 666);\r
+    pathB.lineTo(406, 666);\r
+    pathB.cubicTo(402.686279f, 666, 400, 663.313721f, 400, 660);\r
+    pathB.lineTo(400, 453);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+\r
+static void skpcaffelavazzait_com_ua21(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(883, 23);\r
+    path.lineTo(883, 0);\r
+    path.lineTo(1122.5f, 0);\r
+    path.lineTo(1122.5f, 25.2136822f);\r
+    path.quadTo(1122.14441f, 25.9271851f, 1121.53601f, 26.5359993f);\r
+    path.quadTo(1120.07104f, 28, 1118, 28);\r
+    path.lineTo(888, 28);\r
+    path.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);\r
+    path.quadTo(883, 25.0710678f, 883, 23);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(883, 0);\r
+    pathB.lineTo(1123, 0);\r
+    pathB.lineTo(1123, 23);\r
+    pathB.quadTo(1123, 25.0710678f, 1121.53601f, 26.5359993f);\r
+    pathB.quadTo(1120.07104f, 28, 1118, 28);\r
+    pathB.lineTo(888, 28);\r
+    pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);\r
+    pathB.quadTo(883, 25.0710678f, 883, 23);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+\r
+static void skpcamcorder_kz21(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(883, 23);\r
+    path.lineTo(883, 0);\r
+    path.lineTo(1122.5f, 0);\r
+    path.lineTo(1122.5f, 25.2136822f);\r
+    path.quadTo(1122.14441f, 25.9271851f, 1121.53601f, 26.5359993f);\r
+    path.quadTo(1120.07104f, 28, 1118, 28);\r
+    path.lineTo(888, 28);\r
+    path.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);\r
+    path.quadTo(883, 25.0710678f, 883, 23);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(883, 0);\r
+    pathB.lineTo(1123, 0);\r
+    pathB.lineTo(1123, 23);\r
+    pathB.quadTo(1123, 25.0710678f, 1121.53601f, 26.5359993f);\r
+    pathB.quadTo(1120.07104f, 28, 1118, 28);\r
+    pathB.lineTo(888, 28);\r
+    pathB.quadTo(885.928955f, 28, 884.463989f, 26.5359993f);\r
+    pathB.quadTo(883, 25.0710678f, 883, 23);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+\r
+static void skpcavablar_net563(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(160.000488f, 918);\r
+    path.cubicTo(159.164749f, 917.37207f, 158.125824f, 917, 157, 917);\r
+    path.lineTo(94, 917);\r
+    path.cubicTo(92.874176f, 917, 91.8352661f, 917.37207f, 90.9995193f, 918);\r
+    path.lineTo(160.000488f, 918);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(91, 917);\r
+    pathB.lineTo(160, 917);\r
+    pathB.lineTo(160, 918);\r
+    pathB.lineTo(91, 918);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+\r
+static void skpinsomnia_gr72(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(1138, 231);\r
+    path.lineTo(1137, 243.625748f);\r
+    path.lineTo(1137, 926);\r
+    path.lineTo(1139, 926);\r
+    path.lineTo(1139, 231);\r
+    path.lineTo(1138, 231);\r
+    path.close();\r
+    SkPath pathB;\r
+    pathB.setFillType(SkPath::kWinding_FillType);\r
+    pathB.moveTo(1139, 231);\r
+    pathB.lineTo(1138, 231);\r
+    pathB.lineTo(633, 6101);\r
+    pathB.lineTo(1139, 6607);\r
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);\r
+}\r
+\r
+static void cubicOp95u(skiatest::Reporter* reporter) {\r
+    SkPath path, pathB;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(0, 2);\r
+    path.cubicTo(2, 3, 5, 1, 3, 2);\r
+    path.close();\r
+    pathB.setFillType(SkPath::kEvenOdd_FillType);\r
+    pathB.moveTo(1, 5);\r
+    pathB.cubicTo(2, 3, 2, 0, 3, 2);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kUnion_PathOp);\r
+}
+
+static void cubicOp96d(skiatest::Reporter* reporter) {\r
+    SkPath path, pathB;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(1, 6);\r
+    path.cubicTo(0, 3, 6, 3, 5, 0);\r
+    path.close();\r
+    pathB.setFillType(SkPath::kEvenOdd_FillType);\r
+    pathB.moveTo(3, 6);\r
+    pathB.cubicTo(0, 5, 6, 1, 3, 0);\r
+    pathB.close();\r
+    testPathOp(reporter, path, pathB, kDifference_PathOp);\r
+}
+
 static void (*firstTest)(skiatest::Reporter* ) = 0;
 
 static struct TestDesc tests[] = {
- //   TEST(skpkkiste_to716),
- //   TEST(bufferOverflow),
- //   TEST(issue1435),
+#if ISSUE_1435_WORKING
+    TEST(issue1435),
+#endif
+#if SKPS_WORKING
+    TEST(skpcarrot_is24),
+    TEST(skpcarpetplanet_ru22),  // cubic/cubic intersect detects unwanted coincidence
+#endif
+#if ISSUE_1417_WORKING_ON_LINUX_32
+    TEST(issue1417),
+#endif
+    TEST(cubicOp96d),
+    TEST(cubicOp95u),
+    TEST(skpadbox_lt15),
+    TEST(skpagentxsites_com55),
+    TEST(skpadventistmission_org572),
+    TEST(skpadspert_net23),
+    TEST(skpadoption_org196),
+    TEST(skpbambootheme_com12),
+    TEST(skpbakosoft_com10),
+    TEST(skpakmmos_ru100),
+    TEST(skpbangalorenest_com4),
+    TEST(skpbingoentertainment_net189),
+    TEST(skpbestred_ru37),
+    TEST(skpbenzoteh_ru152),
+    TEST(skpcamcorder_kz21),
+    TEST(skpcaffelavazzait_com_ua21),
+    TEST(skpcarrefour_ro62),
+    TEST(skpcavablar_net563),
+    TEST(skpinsomnia_gr72),
+    TEST(skpadbox_lt8),
+    TEST(skpact_com43),
+    TEST(skpacesoftech_com47),
+    TEST(skpabcspark_ca103),
+    TEST(cubicOp94u),
+    TEST(cubicOp93d),
+    TEST(cubicOp92i),
+    TEST(skpadithya_putr4_blogspot_com551),
+    TEST(skpadindex_de4),
+    TEST(skpadspert_de11),
+    TEST(skpaiaigames_com870),
+    TEST(skpaaalgarve_org53),
+    TEST(skpkkiste_to716),
+    TEST(bufferOverflow),
     TEST(cubicOp91u),
     TEST(cubicOp90u),
     TEST(cubicOp89u),
@@ -2296,7 +3069,6 @@ static struct TestDesc tests[] = {
     TEST(rectOp1i),
     TEST(issue1418b),
     TEST(cubicOp85i),
-    TEST(issue1417),
     TEST(issue1418),
     TEST(skpkkiste_to98),
     TEST(skpahrefs_com29),
index 2d72b41..5d315b2 100644 (file)
@@ -53,6 +53,14 @@ static void standardTestCases(skiatest::Reporter* reporter) {
 }
 
 static const SkDQuad testSet[] = {
+{{{2.9999997378517067, 1.9737872594345709}, {2.9999997432230918, 1.9739647181863822}, {1.2414155459263587e-163, 5.2957833941332142e-315}}},\r
+{{{2.9999047485265304, 1.9739164225694723}, {3.0000947268526112, 1.9738379076623633}, {0.61149411077591886, 0.0028382324376270418}}},\r
+\r
+    {{{2.9999996843656502, 1.9721416019045801}, {2.9999997725237835, 1.9749798343422071},\r
+            {5.3039068214821359e-315, 8.9546185262775165e-307}}},\r
+    {{{2.9984791443874976, 1.974505741312242}, {2.9999992702127476, 1.9738772171479178},\r
+            {3.0015187977319759, 1.9732495027303418}}},\r
+
     {{{0.647069409,2.97691634}, {0.946860918,3.17625612}, {1.46875407,2.65105457}}},
     {{{0,1}, {0.723699095,2.82756208}, {1.08907197,2.97497449}}},
 
@@ -285,8 +293,10 @@ static void oneOffTests(skiatest::Reporter* reporter) {
 }
 
 static const SkDQuad coincidentTestSet[] = {
+#if 0
     {{{97.9337615966796875,100}, {88,112.94264984130859375}, {88,130}}},
     {{{88,130}, {88,124.80951690673828125}, {88.91983795166015625,120}}},
+#endif
     {{{369.850525, 145.675964}, {382.362915, 121.29287}, {406.211273, 121.29287}}},
     {{{369.850525, 145.675964}, {382.362915, 121.29287}, {406.211273, 121.29287}}},
     {{{8, 8}, {10, 10}, {8, -10}}},
index 0b08561..0706efc 100644 (file)
@@ -54,10 +54,10 @@ const SkDQuad quadraticModEpsilonLines[] = {
     {{{0, 0}, {1, 0}, {0, F}}},
     {{{1, 0}, {0, F}, {0, 0}}},
     {{{1, H}, {2, 0}, {3, 0}}},
-    {{{F, 0}, {0, 0}, {0, 1}}},
-    {{{0, 0}, {0, 1}, {F, 0}}},
-    {{{0, 1}, {F, 0}, {0, 0}}},
-    {{{H, 1}, {0, 2}, {0, 3}}},
+//  {{{F, 0}, {0, 0}, {0, 1}}},  // INVESTIGATE: even substituting K for F, quad is still linear.
+//  {{{0, 0}, {0, 1}, {F, 0}}},
+//  {{{0, 1}, {F, 0}, {0, 0}}},
+//  {{{H, 1}, {0, 2}, {0, 3}}},
     {{{0, F}, {0, 0}, {1, 1}}},
     {{{0, 0}, {1, 1}, {F, 0}}},
     {{{1, 1}, {F, 0}, {0, 0}}},
index 7ec8066..a871417 100644 (file)
@@ -85,6 +85,10 @@ static void testOneOffs(skiatest::Reporter* reporter) {
             SkDPoint quadXY = quad.ptAtT(quadT);
             double lineT = intersections[1][inner];
             SkDPoint lineXY = line.ptAtT(lineT);
+            if (!quadXY.approximatelyEqual(lineXY)) {
+                quadXY.approximatelyEqual(lineXY);
+                SkDebugf("");
+            }
             REPORTER_ASSERT(reporter, quadXY.approximatelyEqual(lineXY));
         }
     }
index 65b8d98..0198dec 100644 (file)
@@ -3905,9 +3905,24 @@ static void testQuad8(skiatest::Reporter* reporter) {
     testSimplify(reporter, path);
 }
 
-static void (*firstTest)(skiatest::Reporter* ) = testRect2;
+static void testTriangles4x(skiatest::Reporter* reporter) {\r
+    SkPath path;\r
+    path.setFillType(SkPath::kEvenOdd_FillType);\r
+    path.moveTo(0, 0);\r
+    path.quadTo(2, 0, 0, 3);\r
+    path.lineTo(2, 3);\r
+    path.close();\r
+    path.moveTo(0, 0);\r
+    path.lineTo(0, 1);\r
+    path.quadTo(3, 2, 2, 3);\r
+    path.close();\r
+    testSimplify(reporter, path);
+}
+
+static void (*firstTest)(skiatest::Reporter* ) = 0;
 
 static TestDesc tests[] = {
+    TEST(testTriangles4x),
     TEST(testQuad8),
     TEST(testTriangles3x),
     TEST(testRect2),
old mode 100644 (file)
new mode 100755 (executable)
index 146c42a..d2fa988
@@ -1,6 +1,7 @@
 #include "PathOpsExtendedTest.h"
 #include "PathOpsThreadedCommon.h"
 #include "SkBitmap.h"
+#include "SkColor.h"
 #include "SkDevice.h"
 #include "SkCanvas.h"
 #include "SkImageDecoder.h"
 #include "SkString.h"
 
 #ifdef SK_BUILD_FOR_WIN
-#define PATH_SLASH "\\"
-#define IN_DIR "D:" PATH_SLASH "skp"
-#define OUT_DIR "D:" PATH_SLASH
+    #define PATH_SLASH "\\"
+    #define IN_DIR "D:" PATH_SLASH "skp"
+    #define OUT_DIR "D:" PATH_SLASH
 #else
-#define PATH_SLASH "/"
-#define IN_DIR "/Volumes/Untitled" PATH_SLASH
-#define OUT_DIR PATH_SLASH
+    #define PATH_SLASH "/"
+    #if 1
+        #define IN_DIR "/usr/local/google/home/caryclark/new10k" PATH_SLASH
+        #define OUT_DIR "/usr/local/google/home/caryclark/out10k" PATH_SLASH
+    #else
+        #define IN_DIR "/usr/local/google/home/caryclark/6-18-13" PATH_SLASH
+        #define OUT_DIR "/usr/local/google/home/caryclark" PATH_SLASH
+    #endif
 #endif
 
 static const char pictDir[] = IN_DIR ;
 static const char outSkpClipDir[] = OUT_DIR "skpClip";
 static const char outOldClipDir[] = OUT_DIR "oldClip";
 
-static void make_filepath(SkString* path, const char* dir, const SkString& name) {
+static SkString make_filepath(const char* dir, const SkString& name) {
+    SkString path(dir);
     size_t len = strlen(dir);
-    path->set(dir);
     if (len > 0 && dir[len - 1] != PATH_SLASH[0]) {
-        path->append(PATH_SLASH);
+        path.append(PATH_SLASH);
     }
-    path->append(name);
+    path.append(name);
+    return path;
+}
+
+static SkString make_png_name(const SkString& filename) {
+    SkString pngName = SkString(filename);
+    pngName.remove(pngName.size() - 3, 3);
+    pngName.append("png");
+    return pngName;
 }
 
 static void testOne(const SkString& filename) {
+    if (filename == SkString("http___migracioncolombia_gov_co.skp")
+            || filename == SkString("http___miuki_info.skp")
+    ) {
+        return;
+    }
 #if DEBUG_SHOW_TEST_NAME
     SkString testName(filename);
     const char http[] = "http";
@@ -52,8 +71,7 @@ static void testOne(const SkString& filename) {
     testName.append("1");
     strncpy(DEBUG_FILENAME_STRING, testName.c_str(), DEBUG_FILENAME_STRING_LENGTH);
 #endif
-    SkString path;
-    make_filepath(&path, pictDir, filename);
+    SkString path = make_filepath(pictDir, filename);
     SkFILEStream stream(path.c_str());
     if (!stream.isValid()) {
         return;
@@ -65,60 +83,157 @@ static void testOne(const SkString& filename) {
     }
     int width = pic->width();
     int height = pic->height();
+
     SkBitmap bitmap;
-    bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
-    bool success = bitmap.allocPixels();
-    if (!success) {
-        SkDebugf("unable to allocate bitmap for %s\n", filename.c_str());
+    int scale = 1;
+    do {
+        bitmap.setConfig(SkBitmap::kARGB_8888_Config, (width + scale - 1) / scale,
+            (height + scale - 1) / scale);
+        bool success = bitmap.allocPixels();
+        bitmap.eraseColor(SK_ColorWHITE);
+        if (success) {
+            break;
+        }
+        SkDebugf("-%d-", scale);
+    } while ((scale *= 2) < 32);
+    if (scale >= 32) {
+        SkDebugf("unable to allocate bitmap for %s (w=%d h=%d)\n", filename.c_str(),
+                width, height);
         return;
     }
     SkCanvas canvas(bitmap);
-    SkString pngName(filename);
-    pngName.remove(pngName.size() - 3, 3);
-    pngName.append("png");
+    canvas.scale(1.0f / scale, 1.0f / scale);
+    SkString pngName = make_png_name(filename);
     for (int i = 0; i < 2; ++i) {
         bool useOp = i ? true : false;
         canvas.setAllowSimplifyClip(useOp);
         pic->draw(&canvas);
-        SkString outFile;
-        make_filepath(&outFile, useOp ? outSkpClipDir : outOldClipDir, pngName);
-        SkImageEncoder::EncodeFile(outFile.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
+        SkString outFile = make_filepath(useOp ? outSkpClipDir : outOldClipDir, pngName);
+        if (!SkImageEncoder::EncodeFile(outFile.c_str(), bitmap, SkImageEncoder::kPNG_Type,
+                100)) {
+            SkDebugf("unable to encode %s (width=%d height=%d)\n", pngName.c_str(),
+                     bitmap.width(), bitmap.height());
+        }
     }
     SkDELETE(pic);
 }
 
-const char skipBefore[] = "http___kkiste_to.skp";
+const char* tryFixed[] = {
+    0
+};
+
+size_t tryFixedCount = sizeof(tryFixed) / sizeof(tryFixed[0]);
+
+const char* skipOver[] = {
+    "http___carpetplanet_ru.skp",  // cubic/cubic intersect
+    "http___carrot_is.skp",  // bridgeOp()  SkASSERT(unsortable || !current->done());
+
+/*!*/"http___dotsrc_org.skp",  // asserts in png decode
+    "http___frauen_magazin_com.skp",  // bridgeOp()  SkASSERT(unsortable || !current->done());
+    "http___i_gino_com.skp",  // unexpected cubic/quad coincidence
+                            // {61, 857, 61, 789.06897, 116.068977, 734, 184, 734}
+                            // {184, 734, 133.051727, 734, 97.0258636, 770.025879}
+    "http___ilkoora_com.skp",  // assert wind sum != min32 from markDoneBinary / findNextOp #28k
+/*!*/"http___migracioncolombia_gov_co.skp",  // crashes on picture decode
+    "http___mm4everfriends_com.skp",  // bumpSpan/addTCoincident (from calcPartialCoincidentWinding)
+    "http___mtrk_uz.skp",  // checkEnds() assert #36.3k
+    "http___pchappy_com_au.skp",  // bridgeOp() assert unsortable || ! empty #37.2k
+    "http___sciality_com.skp",  // bridgeOp()  SkASSERT(unsortable || !current->done()); #32.4k
+/*!*/"http___sozialticker_com.skp",  // asserts in png decode
+    "http___sudoestenegocios_com.skp",  // assert fT < 1 in addTCoincident
+    "http___thesuburbanite_com.skp",  // bridgeOp()  SkASSERT(unsortable || !current->done());
+
+    "http___fluentin3months_com.skp", // calcCommonCoincidentWinding from calcPartialCoincidentWinding #38.3k
+    "http___teachersbadi_blogspot_in.skp",  // calcCommonCoincidentWinding from calcPartialCoincidentWinding #53.4k
+    "http___wsms_ru.skp",  // assert wind sum != min32 from markDoneBinary / findNextOp #49.5k
+    "http___voycer_de.skp",  // calcCommonCoincidentWinding from calcPartialCoincidentWinding #47k
+    "http___77hz_jp.skp",  // addTCancel from calcCoincidentWinding #47.1k
+
+    "http___hostloco_com.skp",  // t < 0  AddIntersectsT
+/*!*/"http___oggicronaca_it.skp",  // asserts in png decode
+    "http___sergeychunkevich_com.skp",  // t < 0  AddIntersectsT
+    "http___tracksflow_com.skp",  // assert otherEnd >= 0 from nextChase
+    "http___autobutler_dk.skp",  // t < 0  AddIntersectsT
+    "http___onlinecollege_org.skp",  // bridgeOp() assert unsortable || ! empty #100.1k
+    "http___national_com_au.skp",  // bridgeOp() assert unsortable || ! empty #110.2k
+/*!*/"http___anitadongre_com.skp",  // exceptionally large width and height
+    "http___rentacheat_com.skp",  // bridgeOp() assert unsortable || ! empty #110.8k
+/*!*/"http___gruesse_de.skp",  // asserts in png decode
+/*!*/"http___crn_in.png",  // width=1250047
+    "http___breakmystyle_com.skp",  // assert qPt == lPt in quad intersection
+    "http___naoxrane_ru.skp",  // assert t4+...t0 == 0 in quartic roots #128.3k
+    "http___tcmevents_org.skp",  // assert in addTCoincident (from calcPartialCoincidentWinding) #143.3k
+/*!*/"http___listbuildingcashsecrets_com.skp",  // asserts in png decode #152.7k
+/*!*/"http___skyscraperpage_com.skp",  // asserts in png decode #155.5k
+    "http___mlk_com.skp",  // bridgeOp() assert unsortable || ! empty #158.7k
+    "http___sd_graphic_net.skp",  // bridgeOp() assert unsortable || ! empty #163.3k
+    "http___kopepasah_com.skp",  // checkEnds() assert #188.2k
+/*!*/"http___darkreloaded_com.skp",  // asserts in png decode #188.4k
+    "http___redbullskatearcade_es.skp",  // bridgeOp() assert unsortable || ! empty #192.5k
+    "http___partainasdemo250_org.skp",  //  bridgeOp() assert unsortable || ! empty #200.2k
+
+// these failures are from the new 10k set
+    "http___www_freerepublic_com_.skp",  // assert in opangle <
+    "http___www_lavoixdunord_fr_.skp",  // bridgeOp() assert unsortable || ! empty
+    "http___www_booking_com_.skp",  // bridgeOp() assert unsortable || ! empty
+    "http___www_fj_p_com_.skp",  // markWinding assert from findChaseOp
+    "http___www_leadpages_net_.skp",  // assert in opangle <
+    "http___www_despegar_com_mx_.skp",  // bridgeOp() assert unsortable || ! empty
+};
+
+size_t skipOverCount = sizeof(skipOver) / sizeof(skipOver[0]);
 
 static void PathOpsSkpClipTest(skiatest::Reporter* reporter) {
     SkOSFile::Iter iter(pictDir, "skp");
     SkString filename;
     int testCount = 0;
     while (iter.next(&filename)) {
-        if (strcmp(filename.c_str(), skipBefore) < 0) {
+        SkString pngName = make_png_name(filename);
+        SkString oldPng = make_filepath(outOldClipDir, pngName);
+        SkString newPng = make_filepath(outSkpClipDir, pngName);
+        if (sk_exists(oldPng.c_str()) && sk_exists(newPng.c_str())) {
+            reporter->bumpTestCount();
             continue;
         }
+        for (size_t index = 0; index < skipOverCount; ++index) {
+            if (skipOver[index] && strcmp(filename.c_str(), skipOver[index]) == 0) {
+                reporter->bumpTestCount();
+                goto skipOver;
+            }
+        }
         testOne(filename);
         if (reporter->verbose()) {
             SkDebugf(".");
             if (++testCount % 100 == 0) {
-                SkDebugf("\n");
+                SkDebugf("%d\n", testCount);
             }
         }
+skipOver:
         reporter->bumpTestCount();
     }
 }
 
-static void testSkpClipMain(PathOpsThreadState* data) {
-        SkString str(data->fSerialNo);
-        testOne(str);
-        if (data->fReporter->verbose()) {
+static void bumpCount(skiatest::Reporter* reporter, bool skipping) {
+    if (reporter->verbose()) {
+        static int threadTestCount;
+        if (!skipping) {
             SkDebugf(".");
-            static int threadTestCount;
-            sk_atomic_inc(&threadTestCount);
-            if (threadTestCount % 100 == 0) {
-                SkDebugf("\n");
-            }
         }
+        sk_atomic_inc(&threadTestCount);
+        if (!skipping && threadTestCount % 100 == 0) {
+            SkDebugf("%d\n", threadTestCount);
+        }
+        if (skipping && threadTestCount % 10000 == 0) {
+            SkDebugf("%d\n", threadTestCount);
+        }
+    }
+}
+
+static void testSkpClipMain(PathOpsThreadState* data) {
+    SkString str(data->fSerialNo);
+    testOne(str);
+    bumpCount(data->fReporter, false);
+    data->fReporter->bumpTestCount();
 }
 
 static void PathOpsSkpClipThreadedTest(skiatest::Reporter* reporter) {
@@ -127,24 +242,52 @@ static void PathOpsSkpClipThreadedTest(skiatest::Reporter* reporter) {
     SkOSFile::Iter iter(pictDir, "skp");
     SkString filename;
     while (iter.next(&filename)) {
-        if (strcmp(filename.c_str(), skipBefore) < 0) {
+        SkString pngName = make_png_name(filename);
+        SkString oldPng = make_filepath(outOldClipDir, pngName);
+        SkString newPng = make_filepath(outSkpClipDir, pngName);
+        if (sk_exists(oldPng.c_str()) && sk_exists(newPng.c_str())) {
+            bumpCount(reporter, true);
             continue;
         }
+        for (size_t index = 0; index < skipOverCount; ++index) {
+            if (skipOver[index] && strcmp(filename.c_str(), skipOver[index]) == 0) {
+                bumpCount(reporter, true);
+                goto skipOver;
+            }
+        }
         *testRunner.fRunnables.append() = SkNEW_ARGS(PathOpsThreadedRunnable,
                 (&testSkpClipMain, filename.c_str(), &testRunner));
-        reporter->bumpTestCount();
+skipOver:
+        ;
     }
     testRunner.render();
 }
 
+static void PathOpsSkpClipFixedTest(skiatest::Reporter* reporter) {
+    for (size_t index = 0; index < tryFixedCount; ) {
+        SkString filename(tryFixed[index]);
+        testOne(filename);
+        ++index;
+        if (reporter->verbose()) {
+            SkDebugf(".");
+            if (index % 100 == 0) {
+                SkDebugf("\n");
+            }
+        }
+        reporter->bumpTestCount();
+    }
+}
+
 static void PathOpsSkpClipOneOffTest(skiatest::Reporter* reporter) {
-    SkString filename(skipBefore);
+    SkString filename("http___78_cn_.skp");
     testOne(filename);
 }
 
 #include "TestClassDef.h"
 DEFINE_TESTCLASS_SHORT(PathOpsSkpClipTest)
 
+DEFINE_TESTCLASS_SHORT(PathOpsSkpClipFixedTest)
+
 DEFINE_TESTCLASS_SHORT(PathOpsSkpClipOneOffTest)
 
 DEFINE_TESTCLASS_SHORT(PathOpsSkpClipThreadedTest)
diff --git a/tests/PathOpsSkpTest.cpp b/tests/PathOpsSkpTest.cpp
new file mode 100755 (executable)
index 0000000..75c2060
--- /dev/null
@@ -0,0 +1,703 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "PathOpsExtendedTest.h"
+
+#define TEST(name) { name, #name }
+
+static void skpcheeseandburger_com225(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(555, 468);
+    path.lineTo(555, 362);
+    path.lineTo(872, 362);
+    path.lineTo(872, 468);
+    path.lineTo(555, 468);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(859.11792f, 397.320343f);
+    pathB.cubicTo(855.523071f, 399.691284f, 853.721191f, 402.40863f, 853.721191f, 405.552216f);
+    pathB.cubicTo(853.721191f, 407.911163f, 854.727478f, 410.115387f, 857.043518f, 412.252716f);
+    pathB.cubicTo(859.920532f, 414.916138f, 862.704773f, 417.086426f, 864.679382f, 418.852386f);
+    pathB.cubicTo(866.382446f, 420.371765f, 867.19104f, 422.108795f, 867.19104f, 423.506378f);
+    pathB.cubicTo(867.19104f, 424.551605f, 866.741821f, 425.539886f, 865.935242f, 426.281616f);
+    pathB.cubicTo(865.250366f, 426.910553f, 864.662415f, 427.339813f, 864.139282f, 427.4646f);
+    pathB.cubicTo(863.536377f, 427.605347f, 862.259521f, 426.491272f, 860.366821f, 424.208191f);
+    pathB.cubicTo(858.345276f, 421.770355f, 857.317017f, 419.733856f, 857.317017f, 417.98587f);
+    pathB.cubicTo(857.317017f, 417.198212f, 857.942993f, 415.930389f, 857.942993f, 415.930389f);
+    pathB.cubicTo(857.942993f, 415.930389f, 852.106018f, 421.296173f, 852.279663f, 422.549042f);
+    pathB.cubicTo(852.462402f, 423.890747f, 853.669312f, 425.703613f, 855.876465f, 428.252258f);
+    pathB.cubicTo(858.038818f, 430.754944f, 859.4953f, 431.840088f, 860.190125f, 431.594513f);
+    pathB.cubicTo(862.571045f, 430.754944f, 865.48999f, 429.237549f, 868.44397f, 427.018372f);
+    pathB.cubicTo(870.505371f, 425.470032f, 871.582581f, 423.534332f, 871.582581f, 421.001678f);
+    pathB.cubicTo(871.582581f, 417.945923f, 870.056213f, 415.171692f, 867.015381f, 412.640045f);
+    pathB.cubicTo(863.683105f, 409.872803f, 861.445923f, 408.027954f, 860.551514f, 407.140503f);
+    pathB.cubicTo(858.660767f, 405.264709f, 857.765259f, 403.50174f, 857.765259f, 402.187988f);
+    pathB.cubicTo(857.765259f, 401.141785f, 858.339355f, 400.394073f, 859.476318f, 399.925873f);
+    pathB.cubicTo(860.004395f, 399.704254f, 861.270264f, 400.515869f, 863.156006f, 402.36969f);
+    pathB.cubicTo(865.094727f, 404.28241f, 866.203796f, 405.565186f, 866.383484f, 406.130219f);
+    pathB.cubicTo(868.250244f, 404.305359f, 869.179688f, 403.397919f, 871.046509f, 401.58902f);
+    pathB.cubicTo(868.26825f, 399.296967f, 864.431824f, 394.705841f, 863.156006f, 394.600037f);
+    pathB.cubicTo(863.145996f, 394.600037f, 863.136108f, 394.59903f, 863.126099f, 394.59903f);
+    pathB.cubicTo(862.352417f, 394.598022f, 859.909607f, 396.79425f, 859.11792f, 397.320343f);
+    pathB.moveTo(832.164246f, 394.307526f);
+    pathB.cubicTo(832.451721f, 394.425323f, 832.598511f, 394.486206f, 832.886963f, 394.605011f);
+    pathB.cubicTo(834.078979f, 395.474518f, 834.674927f, 395.90979f, 835.867859f, 396.781281f);
+    pathB.cubicTo(836.502808f, 397.325348f, 836.863159f, 398.000183f, 836.863159f, 398.964539f);
+    pathB.lineTo(836.863159f, 419.740845f);
+    pathB.cubicTo(836.863159f, 420.876923f, 836.319092f, 422.17868f, 835.055298f, 423.617188f);
+    pathB.cubicTo(836.39502f, 424.512665f, 837.063843f, 424.961884f, 838.39856f, 425.864349f);
+    pathB.cubicTo(839.477661f, 426.578125f, 841.37439f, 427.27594f, 842.275879f, 427.443634f);
+    pathB.cubicTo(842.999634f, 427.574402f, 843.82019f, 427.513519f, 844.354309f, 427.216034f);
+    pathB.cubicTo(846.956787f, 425.765503f, 848.689819f, 423.588257f, 848.58606f, 423.483429f);
+    pathB.cubicTo(848.58606f, 423.483429f, 846.877991f, 423.327698f, 845.971558f, 422.807587f);
+    pathB.cubicTo(845.253784f, 422.284485f, 844.892395f, 422.022949f, 844.171631f, 421.502838f);
+    pathB.cubicTo(843.361023f, 420.915833f, 842.907837f, 420.308899f, 842.907837f, 419.350525f);
+    pathB.lineTo(842.907837f, 399.445709f);
+    pathB.cubicTo(842.907837f, 398.053101f, 843.272217f, 397.417175f, 843.812256f, 397.518005f);
+    pathB.cubicTo(844.170654f, 397.583893f, 844.711731f, 398.122986f, 845.432495f, 398.782837f);
+    pathB.cubicTo(846.116333f, 399.402771f, 846.459717f, 399.709259f, 847.14856f, 400.3302f);
+    pathB.cubicTo(844.986206f, 402.099152f, 843.988892f, 403.926025f, 843.988892f, 405.932556f);
+    pathB.cubicTo(843.988892f, 410.209229f, 848.272583f, 410.951935f, 849.576355f, 408.394348f);
+    pathB.cubicTo(849.871826f, 407.816345f, 850.421875f, 406.214081f, 850.387939f, 406.196106f);
+    pathB.cubicTo(850.387939f, 406.196106f, 849.305786f, 406.771118f, 848.495239f, 406.615387f);
+    pathB.cubicTo(846.96582f, 406.316895f, 846.153198f, 405.46637f, 846.153198f, 403.89505f);
+    pathB.cubicTo(846.153198f, 401.796661f, 848.50116f, 399.09729f, 852.279663f, 396.270142f);
+    pathB.cubicTo(851.014893f, 395.315796f, 847.723511f, 391.546265f, 846.875f, 391.546265f);
+    pathB.cubicTo(846.330933f, 391.546265f, 843.988892f, 394.403351f, 843.273193f, 394.972382f);
+    pathB.cubicTo(840.889282f, 392.886963f, 839.700317f, 391.850739f, 837.312378f, 389.786285f);
+    pathB.cubicTo(835.257935f, 391.589203f, 834.225708f, 392.491638f, 832.164246f, 394.307526f);
+    pathB.moveTo(818.860107f, 392.707275f);
+    pathB.cubicTo(819.857361f, 393.382111f, 822.302124f, 395.764038f, 824.387573f, 397.051819f);
+    pathB.cubicTo(822.57666f, 398.249756f, 820.582092f, 399.687286f, 818.860107f, 400.827332f);
+    pathB.lineTo(818.860107f, 392.707275f);
+    pathB.close();
+    pathB.moveTo(810.69812f, 391.096039f);
+    pathB.cubicTo(810.69812f, 391.096039f, 812.786499f, 394.093903f, 812.786499f, 394.965393f);
+    pathB.lineTo(812.786499f, 415.743713f);
+    pathB.cubicTo(812.786499f, 417.753265f, 811.881042f, 418.497986f, 810.974609f, 419.769806f);
+    pathB.cubicTo(813.948486f, 421.160431f, 815.437988f, 421.864197f, 818.404846f, 423.283783f);
+    pathB.cubicTo(819.948181f, 423.95462f, 822.417969f, 424.592529f, 823.937317f, 423.782928f);
+    pathB.cubicTo(827.905518f, 421.663544f, 831.53125f, 417.600525f, 832.255005f, 415.191681f);
+    pathB.cubicTo(833.882263f, 409.877808f, 823.095825f, 411.495026f, 823.119751f, 411.518982f);
+    pathB.cubicTo(823.119751f, 411.518982f, 832.000488f, 411.874359f, 830.537964f, 416.29776f);
+    pathB.cubicTo(829.888123f, 418.253418f, 827.278564f, 420.292908f, 825.385864f, 419.55719f);
+    pathB.cubicTo(821.14209f, 417.915985f, 818.861023f, 417.414856f, 818.861023f, 414.970032f);
+    pathB.lineTo(818.861023f, 403.096436f);
+    pathB.cubicTo(822.126404f, 399.132233f, 831.289673f, 395.897797f, 831.356567f, 395.657227f);
+    pathB.cubicTo(831.356567f, 395.657227f, 823.022888f, 387.594055f, 821.763062f, 387.476257f);
+    pathB.cubicTo(821.755066f, 387.47525f, 821.746094f, 387.47525f, 821.737061f, 387.47525f);
+    pathB.cubicTo(820.793701f, 387.47525f, 810.72406f, 390.967255f, 810.69812f, 391.096039f);
+    pathB.moveTo(624.254211f, 390.498077f);
+    pathB.cubicTo(625.252502f, 390.893402f, 627.708252f, 392.592468f, 629.796692f, 393.307251f);
+    pathB.cubicTo(627.978821f, 395.006317f, 625.980225f, 397.000916f, 624.254211f, 398.618134f);
+    pathB.lineTo(624.254211f, 390.498077f);
+    pathB.close();
+    pathB.moveTo(627.160217f, 384.460449f);
+    pathB.cubicTo(626.286743f, 384.51535f, 616.076233f, 390.993225f, 616.086243f, 391.141968f);
+    pathB.cubicTo(616.086243f, 391.141968f, 618.173645f, 393.561798f, 618.173645f, 394.437317f);
+    pathB.lineTo(618.173645f, 415.216614f);
+    pathB.cubicTo(618.173645f, 417.222168f, 617.265198f, 418.219482f, 616.355774f, 419.742859f);
+    pathB.cubicTo(619.331665f, 420.307892f, 620.824097f, 420.599396f, 623.802979f, 421.198364f);
+    pathB.cubicTo(625.346313f, 421.437958f, 627.818115f, 421.39801f, 629.342468f, 420.166138f);
+    pathB.cubicTo(633.340576f, 416.939667f, 636.982361f, 411.871368f, 637.714111f, 409.263855f);
+    pathB.cubicTo(639.348267f, 403.500732f, 628.508911f, 408.111816f, 628.52887f, 408.126801f);
+    pathB.cubicTo(628.52887f, 408.126801f, 637.468506f, 405.998444f, 635.985046f, 410.844147f);
+    pathB.cubicTo(635.332153f, 412.984467f, 632.705688f, 415.748718f, 630.801941f, 415.541077f);
+    pathB.cubicTo(626.537292f, 415.072876f, 624.257202f, 415.202667f, 624.257202f, 412.755859f);
+    pathB.cubicTo(624.257202f, 408.007019f, 624.255188f, 405.636078f, 624.255188f, 400.884247f);
+    pathB.cubicTo(627.525574f, 396.016602f, 636.801636f, 390.283447f, 636.801636f, 389.97995f);
+    pathB.cubicTo(636.801636f, 389.97995f, 628.360168f, 384.458435f, 627.18219f, 384.458435f);
+    pathB.cubicTo(627.174194f, 384.460449f, 627.167236f, 384.460449f, 627.160217f, 384.460449f);
+    pathB.moveTo(796.530396f, 416.438538f);
+    pathB.cubicTo(795.892517f, 416.365662f, 794.527832f, 415.589996f, 792.348572f, 414.036652f);
+    pathB.lineTo(792.348572f, 391.425476f);
+    pathB.cubicTo(792.348572f, 390.465118f, 792.530273f, 390.047852f, 792.89563f, 390.088776f);
+    pathB.cubicTo(793.075317f, 390.109741f, 793.3479f, 390.317383f, 793.804077f, 390.629852f);
+    pathB.cubicTo(795.113831f, 391.585205f, 795.768738f, 392.059387f, 797.077515f, 393.018738f);
+    pathB.cubicTo(797.983948f, 393.648651f, 798.348267f, 394.219666f, 798.348267f, 394.742767f);
+    pathB.lineTo(798.348267f, 413.253998f);
+    pathB.cubicTo(798.348267f, 415.391327f, 797.783264f, 416.451508f, 796.728088f, 416.451508f);
+    pathB.cubicTo(796.664185f, 416.4505f, 796.598267f, 416.446533f, 796.530396f, 416.438538f);
+    pathB.moveTo(795.165771f, 383.714722f);
+    pathB.cubicTo(794.022705f, 383.851471f, 783.959961f, 388.652252f, 783.880127f, 388.873871f);
+    pathB.cubicTo(783.880127f, 388.873871f, 785.054077f, 389.871155f, 785.522339f, 390.606873f);
+    pathB.cubicTo(786.000488f, 391.361603f, 786.246094f, 391.9935f, 786.246094f, 392.427765f);
+    pathB.lineTo(786.246094f, 411.987183f);
+    pathB.cubicTo(786.246094f, 413.733185f, 784.160645f, 416.428558f, 784.246521f, 416.759979f);
+    pathB.cubicTo(784.258484f, 416.79892f, 785.432495f, 417.14032f, 785.793823f, 417.350952f);
+    pathB.cubicTo(786.739258f, 417.937958f, 787.213379f, 418.228455f, 788.161804f, 418.821442f);
+    pathB.cubicTo(789.342773f, 419.554199f, 790.619568f, 419.956482f, 791.892395f, 420.098236f);
+    pathB.cubicTo(794.533813f, 420.390747f, 796.717102f, 419.337555f, 798.349304f, 416.999573f);
+    pathB.lineTo(798.349304f, 425.212463f);
+    pathB.cubicTo(797.94696f, 425.47702f, 797.750305f, 425.609772f, 797.356018f, 425.874329f);
+    pathB.cubicTo(795.259583f, 423.619202f, 792.806824f, 422.286499f, 789.985657f, 421.984009f);
+    pathB.cubicTo(785.157959f, 421.463898f, 780.409119f, 428.344086f, 780.423096f, 428.346069f);
+    pathB.cubicTo(780.423096f, 428.346069f, 783.340088f, 424.960907f, 785.889709f, 425.218445f);
+    pathB.cubicTo(789.25592f, 425.565857f, 793.166199f, 430.745972f, 793.805115f, 430.790894f);
+    pathB.cubicTo(793.940857f, 430.798889f, 795.918457f, 429.091797f, 798.454102f, 427.383728f);
+    pathB.cubicTo(801.049683f, 425.635742f, 804.230225f, 423.886749f, 806.619141f, 423.980591f);
+    pathB.cubicTo(805.621826f, 423.586243f, 805.048828f, 423.074127f, 804.804199f, 422.609924f);
+    pathB.cubicTo(804.616577f, 422.25354f, 804.616577f, 421.539764f, 804.616577f, 420.31488f);
+    pathB.cubicTo(804.623535f, 411.732605f, 804.623535f, 403.147339f, 804.623535f, 394.562073f);
+    pathB.cubicTo(804.623535f, 392.464691f, 805.970215f, 391.000183f, 805.984192f, 390.896362f);
+    pathB.cubicTo(805.984192f, 390.896362f, 796.785034f, 383.7117f, 795.219666f, 383.7117f);
+    pathB.cubicTo(795.19873f, 383.712708f, 795.181763f, 383.712708f, 795.165771f, 383.714722f);
+    pathB.moveTo(648.092285f, 387.883545f);
+    pathB.cubicTo(649.095581f, 388.312805f, 651.55835f, 390.099762f, 653.655701f, 390.884399f);
+    pathB.cubicTo(651.831848f, 392.522583f, 649.82428f, 394.447296f, 648.092285f, 396.003601f);
+    pathB.lineTo(648.092285f, 387.883545f);
+    pathB.close();
+    pathB.moveTo(651.009277f, 381.943756f);
+    pathB.cubicTo(650.147766f, 381.983704f, 639.893372f, 388.105164f, 639.899353f, 388.254913f);
+    pathB.cubicTo(639.899353f, 388.254913f, 641.987793f, 390.744659f, 641.987793f, 391.617157f);
+    pathB.lineTo(641.987793f, 412.399475f);
+    pathB.cubicTo(641.987793f, 414.409027f, 641.082336f, 415.369354f, 640.169861f, 416.864807f);
+    pathB.cubicTo(643.155762f, 417.53064f, 644.650208f, 417.87207f, 647.638062f, 418.573853f);
+    pathB.cubicTo(649.188416f, 418.865356f, 651.666138f, 418.908295f, 653.19751f, 417.725311f);
+    pathB.cubicTo(657.204651f, 414.633636f, 660.859375f, 409.690125f, 661.590088f, 407.106567f);
+    pathB.cubicTo(663.231262f, 401.397339f, 652.356934f, 405.644073f, 652.375916f, 405.663025f);
+    pathB.cubicTo(652.375916f, 405.663025f, 661.338562f, 403.835175f, 659.857056f, 408.632935f);
+    pathB.cubicTo(659.199219f, 410.748291f, 656.568726f, 413.424713f, 654.656982f, 413.151184f);
+    pathB.cubicTo(650.381348f, 412.536224f, 648.092285f, 412.591125f, 648.092285f, 410.146332f);
+    pathB.lineTo(648.092285f, 398.270721f);
+    pathB.cubicTo(651.374634f, 393.5159f, 660.66571f, 388.09021f, 660.674683f, 387.791718f);
+    pathB.cubicTo(660.674683f, 387.791718f, 652.188232f, 381.941772f, 651.022278f, 381.942749f);
+    pathB.cubicTo(651.01825f, 381.942749f, 651.013245f, 381.942749f, 651.009277f, 381.943756f);
+    pathB.moveTo(761.636353f, 385.965851f);
+    pathB.cubicTo(761.927856f, 386.056702f, 762.071594f, 386.098633f, 762.363098f, 386.189453f);
+    pathB.cubicTo(763.570007f, 386.938171f, 764.175964f, 387.311554f, 765.376892f, 388.066254f);
+    pathB.cubicTo(766.019775f, 388.546417f, 766.384155f, 389.184326f, 766.384155f, 390.147675f);
+    pathB.lineTo(766.384155f, 410.924011f);
+    pathB.cubicTo(766.384155f, 412.057037f, 765.836121f, 413.410736f, 764.559326f, 414.979034f);
+    pathB.cubicTo(765.911987f, 415.738739f, 766.579834f, 416.12207f, 767.934509f, 416.887756f);
+    pathB.cubicTo(769.029602f, 417.495728f, 770.944336f, 418.000854f, 771.85675f, 418.075714f);
+    pathB.cubicTo(772.58551f, 418.134613f, 773.413086f, 417.987854f, 773.950195f, 417.638458f);
+    pathB.cubicTo(776.583618f, 415.917419f, 778.332642f, 413.564453f, 778.237793f, 413.473633f);
+    pathB.cubicTo(778.237793f, 413.473633f, 776.507812f, 413.497559f, 775.596313f, 413.066315f);
+    pathB.cubicTo(774.866577f, 412.61908f, 774.497253f, 412.39447f, 773.771484f, 411.951233f);
+    pathB.cubicTo(772.947876f, 411.444092f, 772.493652f, 410.877075f, 772.493652f, 409.919708f);
+    pathB.lineTo(772.493652f, 390.013885f);
+    pathB.cubicTo(772.493652f, 388.618286f, 772.860046f, 387.949432f, 773.407104f, 387.995361f);
+    pathB.cubicTo(773.771484f, 388.026306f, 774.318542f, 388.509491f, 775.049316f, 389.09848f);
+    pathB.cubicTo(775.742065f, 389.646515f, 776.088501f, 389.923065f, 776.77533f, 390.470123f);
+    pathB.cubicTo(774.590088f, 392.45871f, 773.589783f, 394.385376f, 773.589783f, 396.395935f);
+    pathB.cubicTo(773.589783f, 400.673584f, 777.907349f, 401.008026f, 779.237122f, 398.292694f);
+    pathB.cubicTo(779.539551f, 397.684723f, 780.089661f, 396.027557f, 780.058716f, 396.01358f);
+    pathB.cubicTo(780.058716f, 396.01358f, 778.970581f, 396.694427f, 778.149963f, 396.618561f);
+    pathB.cubicTo(776.598633f, 396.4758f, 775.775024f, 395.709106f, 775.775024f, 394.13681f);
+    pathB.cubicTo(775.775024f, 392.042419f, 778.149963f, 389.103455f, 781.973389f, 385.892975f);
+    pathB.cubicTo(780.697571f, 385.06839f, 777.326416f, 381.676208f, 776.506775f, 381.719147f);
+    pathB.cubicTo(775.908813f, 381.747101f, 773.588806f, 384.868744f, 772.860046f, 385.506622f);
+    pathB.cubicTo(770.451172f, 383.664795f, 769.248291f, 382.749359f, 766.843384f, 380.929504f);
+    pathB.cubicTo(764.758972f, 382.934052f, 763.716736f, 383.940338f, 761.636353f, 385.965851f);
+    pathB.moveTo(672.996521f, 379.821411f);
+    pathB.cubicTo(672.123047f, 379.891266f, 669.7052f, 382.898132f, 668.887573f, 383.64682f);
+    pathB.cubicTo(665.239868f, 386.999084f, 663.41095f, 390.213562f, 663.41095f, 393.356171f);
+    pathB.cubicTo(663.41095f, 395.715118f, 664.439209f, 397.642792f, 666.785156f, 399.150208f);
+    pathB.cubicTo(669.702148f, 401.02002f, 672.547302f, 402.439575f, 674.545837f, 403.655487f);
+    pathB.cubicTo(676.261902f, 404.697693f, 677.105469f, 406.231049f, 677.105469f, 407.625671f);
+    pathB.cubicTo(677.105469f, 408.671875f, 676.651245f, 409.777954f, 675.825684f, 410.7453f);
+    pathB.cubicTo(675.12384f, 411.569885f, 674.538879f, 412.145905f, 673.997803f, 412.417419f);
+    pathB.cubicTo(673.38385f, 412.724915f, 672.080078f, 411.958221f, 670.166382f, 410.198242f);
+    pathB.cubicTo(668.113892f, 408.319458f, 667.062683f, 406.55249f, 667.062683f, 404.808502f);
+    pathB.cubicTo(667.062683f, 404.020844f, 667.701599f, 402.580322f, 667.701599f, 402.580322f);
+    pathB.cubicTo(667.701599f, 402.580322f, 661.773804f, 409.542358f, 661.951477f, 410.7453f);
+    pathB.cubicTo(662.13916f, 412.037079f, 663.368042f, 413.524536f, 665.60321f, 415.469208f);
+    pathB.cubicTo(667.791443f, 417.368927f, 669.261963f, 418.074738f, 669.983704f, 417.630493f);
+    pathB.cubicTo(672.412537f, 416.138062f, 675.369446f, 413.822021f, 678.385254f, 410.790222f);
+    pathB.cubicTo(680.485657f, 408.677856f, 681.587769f, 406.446686f, 681.587769f, 403.917023f);
+    pathB.cubicTo(681.587769f, 400.859283f, 680.007446f, 398.490356f, 676.923767f, 396.806244f);
+    pathB.cubicTo(673.540588f, 394.957428f, 671.257507f, 393.71756f, 670.351074f, 393.075653f);
+    pathB.cubicTo(668.434326f, 391.71698f, 667.518921f, 390.193604f, 667.518921f, 388.88385f);
+    pathB.cubicTo(667.518921f, 387.837646f, 668.101929f, 386.934204f, 669.25592f, 386.156525f);
+    pathB.cubicTo(669.796997f, 385.788147f, 671.085815f, 386.257355f, 672.997498f, 387.592072f);
+    pathB.cubicTo(674.966125f, 388.968689f, 676.104187f, 389.951019f, 676.284851f, 390.465118f);
+    pathB.cubicTo(678.186584f, 388.130127f, 679.136963f, 386.966125f, 681.035706f, 384.646118f);
+    pathB.cubicTo(678.244507f, 383.133728f, 674.247375f, 379.819397f, 673.044434f, 379.819397f);
+    pathB.cubicTo(673.027466f, 379.819397f, 673.011475f, 379.820404f, 672.996521f, 379.821411f);
+    pathB.moveTo(732.95459f, 384.60318f);
+    pathB.cubicTo(733.246094f, 384.680054f, 733.391846f, 384.720001f, 733.689331f, 384.794861f);
+    pathB.cubicTo(735.072937f, 385.500641f, 735.769714f, 385.856049f, 737.162354f, 386.563812f);
+    pathB.cubicTo(737.891113f, 386.938171f, 738.164612f, 387.642975f, 738.164612f, 388.6073f);
+    pathB.lineTo(738.164612f, 408.510132f);
+    pathB.cubicTo(738.164612f, 410.257141f, 737.709412f, 411.893341f, 736.064209f, 413.416718f);
+    pathB.cubicTo(737.635498f, 414.235321f, 738.419189f, 414.651611f, 739.991455f, 415.475189f);
+    pathB.cubicTo(740.997742f, 416.034241f, 742.186707f, 416.344696f, 743.098145f, 416.379639f);
+    pathB.cubicTo(743.830872f, 416.410583f, 744.476807f, 416.175964f, 745.019836f, 415.851532f);
+    pathB.cubicTo(746.476318f, 414.977051f, 748.58075f, 413.571442f, 749.225647f, 413.079285f);
+    pathB.cubicTo(751.012573f, 414.253296f, 751.907043f, 414.845276f, 753.69696f, 416.028229f);
+    pathB.cubicTo(754.703247f, 416.610229f, 755.706543f, 416.84082f, 756.528076f, 416.892761f);
+    pathB.cubicTo(757.259827f, 416.93866f, 757.996582f, 416.807892f, 758.537659f, 416.494446f);
+    pathB.cubicTo(760.814758f, 415.174713f, 762.185425f, 413.509552f, 762.552734f, 412.830719f);
+    pathB.cubicTo(761.637329f, 412.681976f, 759.633789f, 411.58786f, 759.263428f, 411.387207f);
+    pathB.cubicTo(758.607544f, 410.994873f, 758.279114f, 410.803223f, 757.621216f, 410.413879f);
+    pathB.cubicTo(756.983276f, 410.020538f, 756.616943f, 409.301788f, 756.616943f, 408.343445f);
+    pathB.lineTo(756.616943f, 388.351746f);
+    pathB.cubicTo(756.616943f, 387.387421f, 757.164978f, 386.548859f, 758.627502f, 385.067383f);
+    pathB.cubicTo(755.523804f, 383.05484f, 753.97052f, 382.057556f, 750.862854f, 380.078949f);
+    pathB.cubicTo(749.001038f, 382.112457f, 748.069641f, 383.130707f, 746.207825f, 385.174194f);
+    pathB.cubicTo(746.501343f, 385.292999f, 746.647095f, 385.353912f, 746.939575f, 385.472687f);
+    pathB.cubicTo(747.996765f, 386.183472f, 748.525879f, 386.538879f, 749.587036f, 387.257629f);
+    pathB.cubicTo(750.224915f, 387.724823f, 750.498474f, 388.351746f, 750.498474f, 389.223267f);
+    pathB.lineTo(750.498474f, 407.822327f);
+    pathB.cubicTo(750.498474f, 408.694824f, 750.339722f, 409.955658f, 749.951416f, 410.847137f);
+    pathB.cubicTo(749.550049f, 411.761566f, 749.039978f, 411.585876f, 748.487915f, 411.560913f);
+    pathB.cubicTo(747.393799f, 411.503998f, 746.385498f, 410.53067f, 745.473083f, 410.022552f);
+    pathB.cubicTo(744.760254f, 409.627228f, 744.380981f, 409.013275f, 744.380981f, 407.965088f);
+    pathB.lineTo(744.380981f, 386.840363f);
+    pathB.cubicTo(744.380981f, 385.791138f, 744.833191f, 384.763916f, 745.657776f, 383.839508f);
+    pathB.cubicTo(742.656921f, 382.101501f, 741.161499f, 381.234985f, 738.162659f, 379.525909f);
+    pathB.cubicTo(736.083191f, 381.548431f, 735.039978f, 382.562683f, 732.95459f, 384.60318f);
+    pathB.moveTo(692.546936f, 385.171204f);
+    pathB.cubicTo(693.552246f, 385.667358f, 696.018005f, 387.607025f, 698.122375f, 388.521454f);
+    pathB.cubicTo(696.293518f, 390.043854f, 694.281982f, 391.844757f, 692.546936f, 393.294281f);
+    pathB.lineTo(692.546936f, 385.171204f);
+    pathB.close();
+    pathB.moveTo(695.4729f, 379.417084f);
+    pathB.cubicTo(694.635376f, 379.426086f, 684.32605f, 384.880707f, 684.322083f, 385.025452f);
+    pathB.cubicTo(684.322083f, 385.025452f, 686.422485f, 387.645966f, 686.422485f, 388.521454f);
+    pathB.lineTo(686.422485f, 409.300781f);
+    pathB.cubicTo(686.422485f, 411.312347f, 685.51001f, 412.21579f, 684.595581f, 413.65033f);
+    pathB.cubicTo(687.592468f, 414.504852f, 689.089905f, 414.945099f, 692.088745f, 415.833557f);
+    pathB.cubicTo(693.645081f, 416.221893f, 696.128784f, 416.420563f, 697.667114f, 415.334412f);
+    pathB.cubicTo(701.67926f, 412.494293f, 705.344971f, 407.783386f, 706.077698f, 405.240753f);
+    pathB.cubicTo(707.721924f, 399.638367f, 696.822632f, 403.198273f, 696.845581f, 403.216248f);
+    pathB.cubicTo(696.845581f, 403.216248f, 705.825134f, 401.960388f, 704.337708f, 406.658325f);
+    pathB.cubicTo(703.683838f, 408.733765f, 701.044373f, 411.241455f, 699.129639f, 410.847137f);
+    pathB.cubicTo(694.843018f, 409.968628f, 692.545959f, 409.876801f, 692.545959f, 407.432983f);
+    pathB.lineTo(692.545959f, 395.563354f);
+    pathB.cubicTo(695.838318f, 391.012177f, 705.134338f, 386.160522f, 705.162292f, 385.873993f);
+    pathB.cubicTo(705.162292f, 385.873993f, 696.635925f, 379.416107f, 695.473938f, 379.417084f);
+    pathB.cubicTo(695.474915f, 379.417084f, 695.473938f, 379.417084f, 695.4729f, 379.417084f);
+    pathB.moveTo(570.463562f, 420.81601f);
+    pathB.lineTo(570.463562f, 402.922729f);
+    pathB.cubicTo(571.039551f, 402.800934f, 571.327087f, 402.743042f, 571.901123f, 402.625244f);
+    pathB.lineTo(571.901123f, 423.142029f);
+    pathB.cubicTo(570.911804f, 422.823578f, 570.463562f, 422.123779f, 570.463562f, 420.81601f);
+    pathB.moveTo(570.463562f, 384.062134f);
+    pathB.cubicTo(571.039551f, 384.149963f, 571.327087f, 384.198883f, 571.901123f, 384.290741f);
+    pathB.lineTo(571.901123f, 401.580048f);
+    pathB.cubicTo(571.327087f, 401.695862f, 571.039551f, 401.756744f, 570.463562f, 401.874542f);
+    pathB.lineTo(570.463562f, 384.062134f);
+    pathB.close();
+    pathB.moveTo(573.880676f, 376.556f);
+    pathB.cubicTo(572.483093f, 376.996246f, 561.476013f, 385.624451f, 561.482971f, 385.70929f);
+    pathB.cubicTo(561.482971f, 385.70929f, 563.637268f, 388.554413f, 563.637268f, 389.688446f);
+    pathB.lineTo(563.637268f, 398.423462f);
+    pathB.cubicTo(556.411682f, 399.838043f, 555.429382f, 404.307373f, 555.418396f, 405.679993f);
+    pathB.lineTo(555.418396f, 405.724915f);
+    pathB.cubicTo(555.42041f, 405.94455f, 555.448364f, 406.073334f, 555.477295f, 406.083313f);
+    pathB.cubicTo(555.477295f, 406.083313f, 558.070862f, 404.250458f, 563.637268f, 403.222229f);
+    pathB.lineTo(563.637268f, 404.797516f);
+    pathB.cubicTo(556.993713f, 406.233063f, 555.191772f, 412.494293f, 555.569153f, 412.614105f);
+    pathB.cubicTo(555.569153f, 412.614105f, 561.572815f, 410.21521f, 563.637268f, 409.598267f);
+    pathB.lineTo(563.637268f, 424.00354f);
+    pathB.cubicTo(563.637268f, 426.357483f, 563.36676f, 427.901855f, 562.291565f, 429.70874f);
+    pathB.cubicTo(565.448181f, 430.067139f, 567.028442f, 430.256805f, 570.192017f, 430.653137f);
+    pathB.cubicTo(571.99292f, 430.893707f, 574.782166f, 430.669098f, 576.403381f, 429.136719f);
+    pathB.cubicTo(580.960571f, 424.828125f, 586.135681f, 419.346527f, 586.135681f, 416.115082f);
+    pathB.lineTo(586.135681f, 406.511566f);
+    pathB.cubicTo(586.135681f, 405.377533f, 586.047791f, 404.608856f, 586.678711f, 403.271149f);
+    pathB.cubicTo(584.151062f, 404.98819f, 582.888245f, 405.851715f, 580.362549f, 407.587738f);
+    pathB.cubicTo(579.281433f, 408.320465f, 579.192566f, 409.2948f, 579.192566f, 410.955933f);
+    pathB.lineTo(579.192566f, 421.869202f);
+    pathB.cubicTo(579.192566f, 423.180969f, 577.746033f, 423.273804f, 577.392639f, 423.266815f);
+    pathB.cubicTo(575.636658f, 423.228882f, 574.153259f, 423.295776f, 573.071106f, 423.077148f);
+    pathB.lineTo(573.071106f, 384.663086f);
+    pathB.cubicTo(575.230408f, 385.379852f, 576.309509f, 385.742249f, 578.473816f, 386.473999f);
+    pathB.cubicTo(579.373291f, 386.996094f, 579.553955f, 387.490234f, 579.553955f, 388.013336f);
+    pathB.cubicTo(581.861023f, 384.848785f, 583.015991f, 383.267487f, 585.325073f, 380.114899f);
+    pathB.cubicTo(581.680298f, 379.229431f, 575.865295f, 376.520081f, 574.157227f, 376.521057f);
+    pathB.cubicTo(574.047424f, 376.522064f, 573.955566f, 376.533051f, 573.880676f, 376.556f);
+    pathB.moveTo(593.447083f, 375.096527f);
+    pathB.cubicTo(592.363953f, 375.804291f, 591.821899f, 376.772644f, 591.821899f, 377.908691f);
+    pathB.lineTo(591.821899f, 419.46933f);
+    pathB.cubicTo(591.821899f, 420.517517f, 591.187012f, 422.018951f, 589.921143f, 423.991577f);
+    pathB.cubicTo(591.2948f, 424.412842f, 591.982605f, 424.622467f, 593.354248f, 425.050751f);
+    pathB.cubicTo(594.53125f, 425.462036f, 595.525513f, 425.555878f, 596.427979f, 425.404144f);
+    pathB.cubicTo(597.150757f, 425.279358f, 597.785645f, 424.914978f, 598.326721f, 424.475739f);
+    pathB.cubicTo(600.935242f, 422.385315f, 602.846985f, 419.809753f, 602.759094f, 419.749847f);
+    pathB.cubicTo(602.759094f, 419.749847f, 601.582153f, 419.935516f, 600.59082f, 419.831696f);
+    pathB.cubicTo(600.0448f, 419.74585f, 599.774231f, 419.700928f, 599.233154f, 419.615082f);
+    pathB.cubicTo(598.416565f, 419.484314f, 597.965332f, 418.860382f, 597.965332f, 417.988861f);
+    pathB.lineTo(597.965332f, 396.857147f);
+    pathB.cubicTo(597.965332f, 395.376678f, 598.326721f, 394.617004f, 598.867798f, 394.528137f);
+    pathB.cubicTo(599.232178f, 394.466248f, 599.773254f, 394.731812f, 600.59082f, 395.124115f);
+    pathB.cubicTo(601.601074f, 395.589325f, 602.111206f, 395.819946f, 603.123474f, 396.288116f);
+    pathB.cubicTo(603.93811f, 396.686432f, 603.93512f, 397.38324f, 603.93512f, 398.169891f);
+    pathB.cubicTo(603.93512f, 405.971497f, 603.93512f, 413.768127f, 603.93811f, 421.569702f);
+    pathB.cubicTo(603.93811f, 425.325256f, 601.109924f, 430.634155f, 601.133911f, 430.656128f);
+    pathB.cubicTo(601.133911f, 430.656128f, 605.184937f, 427.222015f, 607.017822f, 424.414825f);
+    pathB.cubicTo(609.118164f, 421.201355f, 610.280212f, 417.987854f, 610.280212f, 415.109802f);
+    pathB.lineTo(610.280212f, 394.593048f);
+    pathB.cubicTo(610.280212f, 393.890228f, 610.823242f, 393.112579f, 611.728699f, 392.020447f);
+    pathB.cubicTo(608.827698f, 390.960266f, 604.000977f, 387.703857f, 602.759094f, 387.967407f);
+    pathB.cubicTo(602.120239f, 388.104187f, 599.957947f, 391.29071f, 597.965332f, 393.27829f);
+    pathB.lineTo(597.965332f, 374.422668f);
+    pathB.cubicTo(597.965332f, 373.461334f, 598.326721f, 372.440063f, 598.867798f, 371.567566f);
+    pathB.cubicTo(596.701538f, 372.96817f, 595.616394f, 373.677948f, 593.447083f, 375.096527f);
+    pathB.moveTo(718.054138f, 409.318756f);
+    pathB.cubicTo(717.461182f, 408.789673f, 716.867188f, 408.178711f, 716.867188f, 407.218353f);
+    pathB.lineTo(716.867188f, 387.053986f);
+    pathB.cubicTo(716.867188f, 385.305969f, 717.323425f, 385.566528f, 718.328674f, 386.013763f);
+    pathB.cubicTo(719.645386f, 386.859314f, 720.307251f, 387.284576f, 721.622009f, 388.135132f);
+    pathB.cubicTo(722.266907f, 388.4935f, 722.903809f, 388.934753f, 722.903809f, 389.721405f);
+    pathB.lineTo(722.903809f, 407.794373f);
+    pathB.cubicTo(722.903809f, 408.66687f, 722.746094f, 410.490753f, 722.259888f, 410.758301f);
+    pathB.cubicTo(722.125122f, 410.83017f, 721.950439f, 410.862122f, 721.746826f, 410.862122f);
+    pathB.cubicTo(720.655701f, 410.864105f, 718.747925f, 409.936707f, 718.054138f, 409.318756f);
+    pathB.moveTo(711.928711f, 364.782227f);
+    pathB.cubicTo(711.195923f, 365.134613f, 710.648865f, 365.834412f, 710.648865f, 366.794769f);
+    pathB.lineTo(710.648865f, 407.392059f);
+    pathB.cubicTo(710.648865f, 409.397614f, 708.519531f, 411.37323f, 708.547485f, 411.684692f);
+    pathB.cubicTo(708.550476f, 411.745605f, 711.838867f, 413.067322f, 713.849365f, 414.368073f);
+    pathB.cubicTo(717.766663f, 416.906738f, 720.162537f, 415.845551f, 722.354797f, 414.073608f);
+    pathB.cubicTo(724.059875f, 412.69397f, 726.55957f, 410.981903f, 730.675537f, 410.124359f);
+    pathB.cubicTo(729.75708f, 409.143066f, 729.213013f, 407.993042f, 729.213013f, 406.683289f);
+    pathB.cubicTo(729.213013f, 399.630402f, 729.209045f, 396.103455f, 729.209045f, 389.047546f);
+    pathB.cubicTo(729.209045f, 387.648956f, 730.577698f, 385.292023f, 730.583679f, 385.149261f);
+    pathB.cubicTo(730.583679f, 385.149261f, 720.888306f, 378.762207f, 719.609497f, 378.947906f);
+    pathB.cubicTo(719.275085f, 378.996826f, 717.872498f, 381.118164f, 716.868225f, 381.896851f);
+    pathB.lineTo(716.868225f, 365.046783f);
+    pathB.cubicTo(716.868225f, 363.740021f, 716.960083f, 363.043213f, 717.597961f, 362);
+    pathB.cubicTo(715.331848f, 363.104095f, 714.19873f, 363.657166f, 711.928711f, 364.782227f);
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void skpeverytechpro_blogspot_com100(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(1074.29285f, 627.292786f);
+    path.quadTo(1074.58582f, 627, 1075, 627);
+    path.lineTo(1117, 627);
+    path.quadTo(1124.04163f, 627, 1129.02246f, 631.9776f);
+    path.quadTo(1134, 636.958374f, 1134, 644);
+    path.lineTo(1134, 645);
+    path.quadTo(1134, 652.041626f, 1129.02246f, 657.0224f);
+    path.quadTo(1124.04163f, 662, 1117, 662);
+    path.lineTo(1075, 662);
+    path.quadTo(1074.58582f, 662, 1074.29285f, 661.707214f);
+    path.quadTo(1074, 661.414185f, 1074, 661);
+    path.lineTo(1074, 628);
+    path.quadTo(1074, 627.585815f, 1074.29285f, 627.292786f);
+    path.close();
+    path.moveTo(1076, 629);
+    path.lineTo(1117, 629);
+    path.cubicTo(1125.2843f, 629, 1132, 635.715698f, 1132, 644);
+    path.lineTo(1132, 645);
+    path.cubicTo(1132, 653.284302f, 1125.2843f, 660, 1117, 660);
+    path.lineTo(1076, 660);
+    path.lineTo(1076, 629);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1074, 627);
+    pathB.lineTo(1075, 628);
+    pathB.lineTo(1116.5f, 644.5f);
+    pathB.lineTo(1134, 627);
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void skpflite_com41(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(301.464081f, 424);
+    path.lineTo(296, 433.46405f);
+    path.lineTo(296, 433.810822f);
+    path.lineTo(303.25589f, 438);
+    path.lineTo(304.729736f, 438);
+    path.lineTo(311, 427.139557f);
+    path.lineTo(311, 426.305237f);
+    path.lineTo(307.007202f, 424);
+    path.lineTo(301.464081f, 424);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(302.849854f, 421.599762f);
+    pathB.lineTo(311.510101f, 426.599762f);
+    pathB.lineTo(304.510101f, 438.724121f);
+    pathB.lineTo(295.849854f, 433.724121f);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void skpilkoora_com37(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(818, 157);
+    path.cubicTo(818, 148.715729f, 824.715698f, 142, 833, 142);
+    path.lineTo(909, 142);
+    path.lineTo(909, 143);
+    path.lineTo(833, 143);
+    path.cubicTo(825.268005f, 143, 819, 149.268005f, 819, 157);
+    path.lineTo(819, 926);
+    path.lineTo(818, 926);
+    path.lineTo(818, 157);
+    path.close();
+    path.moveTo(1184, 926);
+    path.lineTo(1185, 926);
+    path.lineTo(1185, 157);
+    path.cubicTo(1185, 148.715729f, 1178.2843f, 142, 1170, 142);
+    path.lineTo(1093, 142);
+    path.lineTo(1093, 143);
+    path.lineTo(1170, 143);
+    path.cubicTo(1177.73193f, 143, 1184, 149.268005f, 1184, 157);
+    path.lineTo(1184, 926);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1185, 142);
+    pathB.lineTo(1001.5f, 325.5f);
+    pathB.lineTo(1001.5f, 782.5f);
+    pathB.lineTo(1185, 966);
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void skpmm4everfriends_com43(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(540.74231f, 215.922546f);
+    path.cubicTo(540.893127f, 215.391159f, 541.443909f, 215.090134f, 541.972473f, 215.250168f);
+    path.lineTo(581.213318f, 227.131104f);
+    path.cubicTo(581.741882f, 227.291153f, 582.048157f, 227.851654f, 581.897339f, 228.383041f);
+    path.lineTo(576.708923f, 246.663925f);
+    path.cubicTo(576.558167f, 247.195297f, 576.007324f, 247.496338f, 575.47876f, 247.336288f);
+    path.lineTo(536.237915f, 235.455353f);
+    path.cubicTo(535.709351f, 235.295319f, 535.403137f, 234.734802f, 535.553894f, 234.20343f);
+    path.lineTo(540.74231f, 215.922546f);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(541.015381f, 214.960388f);
+    pathB.lineTo(582.17041f, 227.420883f);
+    pathB.lineTo(576.435852f, 247.626068f);
+    pathB.lineTo(535.280823f, 235.165573f);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void skpmtrk_uz27(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(33, 787);
+    path.lineTo(33, 412);
+    path.lineTo(1233, 412);
+    path.lineTo(1233, 787);
+    path.quadTo(1233, 793.213196f, 1228.60803f, 797.607971f);
+    path.quadTo(1224.21326f, 802, 1218, 802);
+    path.lineTo(48, 802);
+    path.quadTo(41.7867966f, 802, 37.3919983f, 797.607971f);
+    path.quadTo(33, 793.213196f, 33, 787);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(33, 412);
+    pathB.lineTo(1233, 412);
+    pathB.lineTo(1233, 787);
+    pathB.quadTo(1233, 793.213196f, 1228.60791f, 797.608032f);
+    pathB.quadTo(1224.21313f, 802, 1218, 802);
+    pathB.lineTo(48, 802);
+    pathB.quadTo(41.7867432f, 802, 37.3919678f, 797.608032f);
+    pathB.quadTo(33, 793.213196f, 33, 787);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+#define TRY_BROKEN_TESTS 0
+#if TRY_BROKEN_TESTS
+static void skpfrauen_magazin_com83(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(808, 886);
+    path.cubicTo(805.581055f, 886, 803.563293f, 887.717773f, 803.100037f, 890);
+    path.lineTo(1122.90002f, 890);
+    path.cubicTo(1122.43677f, 887.717773f, 1120.41895f, 886, 1118, 886);
+    path.lineTo(808, 886);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.moveTo(808, 886);
+    pathB.lineTo(1118, 886);
+    pathB.cubicTo(1120.76147f, 886, 1123, 888.238586f, 1123, 891);
+    pathB.lineTo(1123, 1521);
+    pathB.cubicTo(1123, 1523.20911f, 1120.76147f, 1525, 1118, 1525);
+    pathB.lineTo(808, 1525);
+    pathB.cubicTo(805.238586f, 1525, 803, 1523.20911f, 803, 1521);
+    pathB.lineTo(803, 891);
+    pathB.cubicTo(803, 888.238586f, 805.238586f, 886, 808, 886);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void skpi_gino_com16(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(184, 734);
+    path.quadTo(133.051727f, 734, 97.0258636f, 770.025879f);
+    path.quadTo(61, 806.051758f, 61, 857);
+    path.quadTo(61, 895.835083f, 81.9317017f, 926);
+    path.lineTo(286.068298f, 926);
+    path.quadTo(307, 895.835083f, 307, 857);
+    path.quadTo(307, 806.051758f, 270.974121f, 770.025879f);
+    path.quadTo(234.948273f, 734, 184, 734);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(185, 734);
+    pathB.cubicTo(252.93103f, 734, 308, 789.06897f, 308, 857);
+    pathB.cubicTo(308, 924.93103f, 252.93103f, 980, 185, 980);
+    pathB.lineTo(184, 980);
+    pathB.cubicTo(116.068977f, 980, 61, 924.93103f, 61, 857);
+    pathB.cubicTo(61, 789.06897f, 116.068977f, 734, 184, 734);
+    pathB.lineTo(185, 734);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void skppchappy_com_au102(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(363, 493);
+    path.cubicTo(360.790863f, 493, 359, 494.790863f, 359, 497);
+    path.lineTo(359, 656);
+    path.cubicTo(359, 658.209106f, 360.790863f, 660, 363, 660);
+    path.lineTo(623.001709f, 660);
+    path.cubicTo(624.657776f, 659.999023f, 626, 658.65625f, 626, 657);
+    path.lineTo(626, 496);
+    path.cubicTo(626, 494.343872f, 624.657959f, 493.00116f, 623.002075f, 493);
+    path.lineTo(363, 493);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.moveTo(362, 494);
+    pathB.lineTo(623, 494);
+    pathB.cubicTo(624.65686f, 494, 626, 494.895416f, 626, 496);
+    pathB.lineTo(626, 657);
+    pathB.cubicTo(626, 658.65686f, 624.65686f, 660, 623, 660);
+    pathB.lineTo(362, 660);
+    pathB.cubicTo(360.34314f, 660, 359, 658.65686f, 359, 657);
+    pathB.lineTo(359, 496);
+    pathB.cubicTo(359, 494.895416f, 360.34314f, 494, 362, 494);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void skpsciality_com161(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(656, 728);
+    path.cubicTo(653.790833f, 728, 652, 729.790833f, 652, 732);
+    path.lineTo(652, 789);
+    path.cubicTo(652, 791.209106f, 653.790833f, 793, 656, 793);
+    path.lineTo(769.001282f, 793);
+    path.cubicTo(770.657532f, 792.999268f, 772, 791.656433f, 772, 790);
+    path.lineTo(772, 731);
+    path.cubicTo(772, 729.34314f, 770.65686f, 728, 769, 728);
+    path.lineTo(656, 728);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kInverseWinding_FillType);
+    pathB.moveTo(655, 729);
+    pathB.lineTo(769, 729);
+    pathB.cubicTo(770.65686f, 729, 772, 729.895447f, 772, 731);
+    pathB.lineTo(772, 790);
+    pathB.cubicTo(772, 791.65686f, 770.65686f, 793, 769, 793);
+    pathB.lineTo(655, 793);
+    pathB.cubicTo(653.34314f, 793, 652, 791.65686f, 652, 790);
+    pathB.lineTo(652, 731);
+    pathB.cubicTo(652, 729.895447f, 653.34314f, 729, 655, 729);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void skpsudoestenegocios_com186(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0, 495);
+    path.lineTo(1.23685242e-14f, 293);
+    path.lineTo(44, 293);
+    path.quadTo(45.6568527f, 293, 46.8288002f, 294.171204f);
+    path.quadTo(48, 295.34314f, 48, 297);
+    path.lineTo(48, 491);
+    path.quadTo(48, 492.65686f, 46.8288002f, 493.828796f);
+    path.quadTo(45.6568527f, 495, 44, 495);
+    path.lineTo(0, 495);
+    path.close();
+    path.moveTo(1, 294);
+    path.lineTo(44, 294);
+    path.cubicTo(45.6568565f, 294, 47, 295.34314f, 47, 297);
+    path.lineTo(47, 491);
+    path.cubicTo(47, 492.65686f, 45.6568565f, 494, 44, 494);
+    path.lineTo(1, 494);
+    path.lineTo(1, 294);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(48, 495);
+    pathB.lineTo(24, 471);
+    pathB.lineTo(24, 317);
+    pathB.lineTo(48, 293);
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void skpthesuburbanite_com213(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(863.439026f, 692);
+    path.lineTo(863.283264f, 692);
+    path.lineTo(802, 708.420837f);
+    path.lineTo(802, 718.773621f);
+    path.lineTo(866, 701.624817f);
+    path.lineTo(866, 701.557922f);
+    path.lineTo(863.439026f, 692);
+    path.close();
+    SkPath pathB;
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(783.256775f, 713.443054f);
+    pathB.lineTo(863.428589f, 691.96106f);
+    pathB.lineTo(866.016724f, 701.620361f);
+    pathB.lineTo(785.84491f, 723.102356f);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+#endif
+
+static void (*firstTest)(skiatest::Reporter* ) = 0;
+
+static struct TestDesc tests[] = {
+#if TRY_BROKEN_TESTS
+    TEST(skppchappy_com_au102),
+    TEST(skpsciality_com161),
+    TEST(skpsudoestenegocios_com186),
+    TEST(skpfrauen_magazin_com83),
+    TEST(skpi_gino_com16),
+#endif
+    TEST(skpmtrk_uz27),
+    TEST(skpilkoora_com37),
+    TEST(skpmm4everfriends_com43),
+    TEST(skpflite_com41),
+    TEST(skpcheeseandburger_com225),
+    TEST(skpeverytechpro_blogspot_com100),
+};
+
+static const size_t testCount = SK_ARRAY_COUNT(tests);
+
+static bool runReverse = false;
+static void (*stopTest)(skiatest::Reporter* ) = 0;
+
+static void PathOpsSkpTest(skiatest::Reporter* reporter) {
+#if DEBUG_SHOW_TEST_NAME
+    strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
+#endif
+    RunTestSet(reporter, tests, testCount, firstTest, stopTest, runReverse);
+}
+
+#include "TestClassDef.h"
+
+DEFINE_TESTCLASS_SHORT(PathOpsSkpTest)
+
index e6d3bed..ee93390 100644 (file)
@@ -7,6 +7,7 @@
 #ifndef PathOpsThreadedCommon_DEFINED
 #define PathOpsThreadedCommon_DEFINED
 
+#include "SkGraphics.h"
 #include "SkRunnable.h"
 #include "SkTDArray.h"
 
@@ -25,7 +26,7 @@ struct PathOpsThreadState {
     unsigned char fD;
     char* fPathStr;
     const char* fKey;
-    char fSerialNo[64];
+    char fSerialNo[256];
     skiatest::Reporter* fReporter;
     SkBitmap* fBitmap;
 };
@@ -72,6 +73,7 @@ public:
         fState.fBitmap = &bitmap;
         char pathStr[PATH_STR_SIZE];
         fState.fPathStr = pathStr;
+        SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
         (*fTestFun)(&fState);
     }