fail early if coincidence can't be resolved

Bail out if a very large value causes coincidence resolution to
fail.

TBR=
BUG=415866

Author: caryclark@google.com

Review URL: https://codereview.chromium.org/585913002
diff --git a/src/pathops/SkAddIntersections.cpp b/src/pathops/SkAddIntersections.cpp
index 52e751b..27422ed 100644
--- a/src/pathops/SkAddIntersections.cpp
+++ b/src/pathops/SkAddIntersections.cpp
@@ -434,7 +434,7 @@
 
 // resolve any coincident pairs found while intersecting, and
 // see if coincidence is formed by clipping non-concident segments
-void CoincidenceCheck(SkTArray<SkOpContour*, true>* contourList, int total) {
+bool CoincidenceCheck(SkTArray<SkOpContour*, true>* contourList, int total) {
     int contourCount = (*contourList).count();
     for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
         SkOpContour* contour = (*contourList)[cIndex];
@@ -446,10 +446,13 @@
     }
     for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
         SkOpContour* contour = (*contourList)[cIndex];
-        contour->calcCoincidentWinding();
+        if (!contour->calcCoincidentWinding()) {
+            return false;
+        }
     }
     for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
         SkOpContour* contour = (*contourList)[cIndex];
         contour->calcPartialCoincidentWinding();
     }
+    return true;
 }
diff --git a/src/pathops/SkAddIntersections.h b/src/pathops/SkAddIntersections.h
index 94ea436..4c1947b 100644
--- a/src/pathops/SkAddIntersections.h
+++ b/src/pathops/SkAddIntersections.h
@@ -13,6 +13,6 @@
 
 bool AddIntersectTs(SkOpContour* test, SkOpContour* next);
 void AddSelfIntersectTs(SkOpContour* test);
-void CoincidenceCheck(SkTArray<SkOpContour*, true>* contourList, int total);
+bool CoincidenceCheck(SkTArray<SkOpContour*, true>* contourList, int total);
 
 #endif
diff --git a/src/pathops/SkOpContour.cpp b/src/pathops/SkOpContour.cpp
index 467fab3..6d6ad79 100644
--- a/src/pathops/SkOpContour.cpp
+++ b/src/pathops/SkOpContour.cpp
@@ -267,7 +267,7 @@
     return true;
 }
 
-void SkOpContour::calcCoincidentWinding() {
+bool SkOpContour::calcCoincidentWinding() {
     int count = fCoincidences.count();
 #if DEBUG_CONCIDENT
     if (count > 0) {
@@ -276,8 +276,11 @@
 #endif
     for (int index = 0; index < count; ++index) {
         SkCoincidence& coincidence = fCoincidences[index];
-         calcCommonCoincidentWinding(coincidence);
+        if (!calcCommonCoincidentWinding(coincidence)) {
+            return false;
+        }
     }
+    return true;
 }
 
 void SkOpContour::calcPartialCoincidentWinding() {
@@ -471,11 +474,11 @@
             addTo2->addTCancel(missingPt1, missingPt2, addOther2);
         }
     } else if (missingT1 >= 0) {
-        addTo1->addTCoincident(missingPt1, missingPt2, addTo1 == addTo2 ? missingT2 : otherT2,
-                addOther1);
+        SkAssertResult(addTo1->addTCoincident(missingPt1, missingPt2,
+                addTo1 == addTo2 ? missingT2 : otherT2, addOther1));
     } else {
-        addTo2->addTCoincident(missingPt2, missingPt1, addTo2 == addTo1 ? missingT1 : otherT1,
-                addOther2);
+        SkAssertResult(addTo2->addTCoincident(missingPt2, missingPt1,
+                addTo2 == addTo1 ? missingT1 : otherT1, addOther2));
     }
 }
 
@@ -543,20 +546,20 @@
     }
 }
 
-void SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) {
+bool SkOpContour::calcCommonCoincidentWinding(const SkCoincidence& coincidence) {
     if (coincidence.fNearly[0] && coincidence.fNearly[1]) {
-        return;
+        return true;
     }
     int thisIndex = coincidence.fSegments[0];
     SkOpSegment& thisOne = fSegments[thisIndex];
     if (thisOne.done()) {
-        return;
+        return true;
     }
     SkOpContour* otherContour = coincidence.fOther;
     int otherIndex = coincidence.fSegments[1];
     SkOpSegment& other = otherContour->fSegments[otherIndex];
     if (other.done()) {
-        return;
+        return true;
     }
     double startT = coincidence.fTs[0][0];
     double endT = coincidence.fTs[0][1];
@@ -577,15 +580,17 @@
     }
     bump_out_close_span(&oStartT, &oEndT);
     SkASSERT(!approximately_negative(oEndT - oStartT));
+    bool success = true;
     if (cancelers) {
         thisOne.addTCancel(*startPt, *endPt, &other);
     } else {
-        thisOne.addTCoincident(*startPt, *endPt, endT, &other);
+        success = thisOne.addTCoincident(*startPt, *endPt, endT, &other);
     }
 #if DEBUG_CONCIDENT
     thisOne.debugShowTs("p");
     other.debugShowTs("o");
 #endif
+    return success;
 }
 
 void SkOpContour::resolveNearCoincidence() {
diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h
index d1b3cd0..899367a 100644
--- a/src/pathops/SkOpContour.h
+++ b/src/pathops/SkOpContour.h
@@ -114,7 +114,7 @@
     }
 
     bool calcAngles();
-    void calcCoincidentWinding();
+    bool calcCoincidentWinding();
     void calcPartialCoincidentWinding();
 
     void checkDuplicates() {
@@ -325,7 +325,7 @@
 private:
     void alignPt(int index, SkPoint* point, int zeroPt) const;
     int alignT(bool swap, int tIndex, SkIntersections* ts) const;
-    void calcCommonCoincidentWinding(const SkCoincidence& );
+    bool calcCommonCoincidentWinding(const SkCoincidence& );
     void checkCoincidentPair(const SkCoincidence& oneCoin, int oneIdx,
                              const SkCoincidence& twoCoin, int twoIdx, bool partial);
     void joinCoincidence(const SkTArray<SkCoincidence, true>& , bool partial);
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index 336ac11..5304e4b 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -1265,7 +1265,7 @@
 
 // set spans from start to end to increment the greater by one and decrement
 // the lesser
-void SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
+bool SkOpSegment::addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
         SkOpSegment* other) {
     bool binary = fOperand != other->fOperand;
     int index = 0;
@@ -1297,7 +1297,10 @@
     // SkASSERT(AlmostEqualUlps(*testPt, *oTestPt));
     do {
         SkASSERT(test->fT < 1);
-        SkASSERT(oTest->fT < 1);
+        if (oTest->fT == 1) {
+            // paths with extreme data may be so mismatched that we fail here
+            return false;
+        }
 
         // consolidate the winding count even if done
         if ((test->fWindValue == 0 && test->fOppValue == 0)
@@ -1403,6 +1406,7 @@
     }
     setCoincidentRange(startPt, endPt, other);
     other->setCoincidentRange(startPt, endPt, this);
+    return true;
 }
 
 // FIXME: this doesn't prevent the same span from being added twice
@@ -2385,8 +2389,8 @@
             do {
                 ++nextSpan;
             } while (nextSpan->fSmall);
-            missing.fSegment->addTCoincident(missing.fPt, nextSpan->fPt, nextSpan->fT,
-                    missingOther);
+            SkAssertResult(missing.fSegment->addTCoincident(missing.fPt, nextSpan->fPt,
+                    nextSpan->fT, missingOther));
         } else if (otherSpan.fT > 0) {
             const SkOpSpan* priorSpan = &otherSpan;
             do {
@@ -2457,7 +2461,7 @@
     }
     SkASSERT(oSpan.fSmall);
     if (oStartIndex < oEndIndex) {
-        addTCoincident(span.fPt, next->fPt, next->fT, other);
+        SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, other));
     } else {
         addTCancel(span.fPt, next->fPt, other);
     }
@@ -2502,7 +2506,7 @@
                         oTest->fOtherT, tTest->fT);
 #endif
                 if (tTest->fT < oTest->fOtherT) {
-                    addTCoincident(span.fPt, next->fPt, next->fT, testOther);
+                    SkAssertResult(addTCoincident(span.fPt, next->fPt, next->fT, testOther));
                 } else {
                     addTCancel(span.fPt, next->fPt, testOther);
                 }
@@ -3390,7 +3394,7 @@
             if (cancel) {
                 match->addTCancel(startPt, endPt, other);
             } else {
-                match->addTCoincident(startPt, endPt, endT, other);
+                SkAssertResult(match->addTCoincident(startPt, endPt, endT, other));
             }
             return true;
         }
diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h
index df87d05..24d08bd 100644
--- a/src/pathops/SkOpSegment.h
+++ b/src/pathops/SkOpSegment.h
@@ -284,7 +284,7 @@
     void addStartSpan(int endIndex);
     int addT(SkOpSegment* other, const SkPoint& pt, double newT);
     void addTCancel(const SkPoint& startPt, const SkPoint& endPt, SkOpSegment* other);
-    void addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
+    bool addTCoincident(const SkPoint& startPt, const SkPoint& endPt, double endT,
                         SkOpSegment* other);
     const SkOpSpan* addTPair(double t, SkOpSegment* other, double otherT, bool borrowWind,
                              const SkPoint& pt);
diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp
index 9a8a2cf..f7b7273 100644
--- a/src/pathops/SkPathOpsCommon.cpp
+++ b/src/pathops/SkPathOpsCommon.cpp
@@ -699,7 +699,9 @@
 #if DEBUG_SHOW_WINDING
     SkOpContour::debugShowWindingValues(contourList);
 #endif
-    CoincidenceCheck(contourList, total);
+    if (!CoincidenceCheck(contourList, total)) {
+        return false;
+    }
 #if DEBUG_SHOW_WINDING
     SkOpContour::debugShowWindingValues(contourList);
 #endif
diff --git a/tests/PathOpsOpTest.cpp b/tests/PathOpsOpTest.cpp
index 866b5b2..84485c1 100644
--- a/tests/PathOpsOpTest.cpp
+++ b/tests/PathOpsOpTest.cpp
@@ -3814,7 +3814,101 @@
     testPathFailOp(reporter, path1, path2, kUnion_PathOp, filename);
 }
 
+static void fuzz487a(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
+path.lineTo(SkBits2Float(0x4309999a), SkBits2Float(0x42c00000));
+path.cubicTo(SkBits2Float(0x4309999a), SkBits2Float(0x429a6666), SkBits2Float(0x42f9999a), SkBits2Float(0x4275999a), SkBits2Float(0x42d70001), SkBits2Float(0x42633333));
+path.lineTo(SkBits2Float(0x42e90001), SkBits2Float(0x41b8cccc));
+path.cubicTo(SkBits2Float(0x42dc6667), SkBits2Float(0x41ab3332), SkBits2Float(0x42cf3334), SkBits2Float(0x41a3ffff), SkBits2Float(0x42c20001), SkBits2Float(0x41a3ffff));
+path.lineTo(SkBits2Float(0x42c20001), SkBits2Float(0x425d999a));
+path.lineTo(SkBits2Float(0x42c20001), SkBits2Float(0x425d999a));
+path.cubicTo(SkBits2Float(0x429c6668), SkBits2Float(0x425d999a), SkBits2Float(0x4279999c), SkBits2Float(0x42886667), SkBits2Float(0x42673335), SkBits2Float(0x42ab0000));
+path.lineTo(SkBits2Float(0x41c0ccd0), SkBits2Float(0x42990000));
+path.cubicTo(SkBits2Float(0x41b33336), SkBits2Float(0x42a5999a), SkBits2Float(0x41ac0003), SkBits2Float(0x42b2cccd), SkBits2Float(0x41ac0003), SkBits2Float(0x42c00000));
+path.lineTo(SkBits2Float(0x4261999c), SkBits2Float(0x42c00000));
+path.lineTo(SkBits2Float(0x4261999c), SkBits2Float(0x42c00000));
+path.cubicTo(SkBits2Float(0x4261999c), SkBits2Float(0x434d3333), SkBits2Float(0x4364e667), SkBits2Float(0x4346b333), SkBits2Float(0x4364e667), SkBits2Float(0x43400000));
+path.lineTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
+path.lineTo(SkBits2Float(0x4309999a), SkBits2Float(0x42c00000));
+path.cubicTo(SkBits2Float(0x4309999a), SkBits2Float(0x42a20000), SkBits2Float(0x43016667), SkBits2Float(0x4287cccd), SkBits2Float(0x42ea999a), SkBits2Float(0x4273999a));
+path.lineTo(SkBits2Float(0x4306cccd), SkBits2Float(0x41f5999a));
+path.cubicTo(SkBits2Float(0x42f76667), SkBits2Float(0x41c26667), SkBits2Float(0x42dd999a), SkBits2Float(0x41a4cccd), SkBits2Float(0x42c23334), SkBits2Float(0x41a4cccd));
+path.lineTo(SkBits2Float(0x42c23334), SkBits2Float(0x425e0000));
+path.cubicTo(SkBits2Float(0x42a43334), SkBits2Float(0x425e0000), SkBits2Float(0x428a0001), SkBits2Float(0x427ecccd), SkBits2Float(0x42780002), SkBits2Float(0x4297999a));
+path.lineTo(SkBits2Float(0x41fccccd), SkBits2Float(0x42693333));
+path.cubicTo(SkBits2Float(0x41c9999a), SkBits2Float(0x428acccd), SkBits2Float(0x41ac0000), SkBits2Float(0x42a4999a), SkBits2Float(0x41ac0000), SkBits2Float(0x42c00000));
+path.lineTo(SkBits2Float(0x4261999a), SkBits2Float(0x42c00000));
+path.cubicTo(SkBits2Float(0x4261999a), SkBits2Float(0x42de0000), SkBits2Float(0x42813333), SkBits2Float(0x42f83333), SkBits2Float(0x42996666), SkBits2Float(0x4303199a));
+path.cubicTo(SkBits2Float(0x4272cccc), SkBits2Float(0x4303199a), SkBits2Float(0x423d3332), SkBits2Float(0x430de667), SkBits2Float(0x422d9999), SkBits2Float(0x431cb334));
+path.lineTo(SkBits2Float(0x7086a1dc), SkBits2Float(0x42eecccd));
+path.lineTo(SkBits2Float(0x41eb3333), SkBits2Float(0xc12ccccd));
+path.lineTo(SkBits2Float(0x42053333), SkBits2Float(0xc1cccccd));
+path.lineTo(SkBits2Float(0x42780000), SkBits2Float(0xc18f3334));
+path.cubicTo(SkBits2Float(0x43206666), SkBits2Float(0x43134ccd), SkBits2Float(0x43213333), SkBits2Float(0x430db333), SkBits2Float(0x43213333), SkBits2Float(0x43080000));
+path.lineTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
+path.close();
+
+    SkPath path2(path);
+    testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
+static void fuzz487b(skiatest::Reporter* reporter, const char* filename) {
+    SkPath path;
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
+path.lineTo(SkBits2Float(0x4309999a), SkBits2Float(0x42c00000));
+path.cubicTo(SkBits2Float(0x4309999a), SkBits2Float(0x429a6666), SkBits2Float(0x42f9999a), SkBits2Float(0x4275999a), SkBits2Float(0x42d70001), SkBits2Float(0x42633333));
+path.lineTo(SkBits2Float(0x42e90001), SkBits2Float(0x41b8cccc));
+path.cubicTo(SkBits2Float(0x42dc6667), SkBits2Float(0x41ab3332), SkBits2Float(0x42cf3334), SkBits2Float(0x41a3ffff), SkBits2Float(0x42c20001), SkBits2Float(0x41a3ffff));
+path.lineTo(SkBits2Float(0x42c20001), SkBits2Float(0x425d999a));
+path.lineTo(SkBits2Float(0x42c20001), SkBits2Float(0x425d999a));
+path.cubicTo(SkBits2Float(0x429c6668), SkBits2Float(0x425d999a), SkBits2Float(0x4279999c), SkBits2Float(0x42886667), SkBits2Float(0x42673335), SkBits2Float(0x42ab0000));
+path.lineTo(SkBits2Float(0x41c0ccd0), SkBits2Float(0x42990000));
+path.cubicTo(SkBits2Float(0x41b33336), SkBits2Float(0x42a5999a), SkBits2Float(0x41ac0003), SkBits2Float(0x42b2cccd), SkBits2Float(0x41ac0003), SkBits2Float(0x42c00000));
+path.lineTo(SkBits2Float(0x4261999c), SkBits2Float(0x42c00000));
+path.lineTo(SkBits2Float(0x4261999c), SkBits2Float(0x42c00000));
+path.cubicTo(SkBits2Float(0x4261999c), SkBits2Float(0x434d3333), SkBits2Float(0x4364e667), SkBits2Float(0x4346b333), SkBits2Float(0x4364e667), SkBits2Float(0x43400000));
+path.lineTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
+path.close();
+
+    SkPath path1(path);
+    path.reset();
+    path.setFillType((SkPath::FillType) 0);
+path.moveTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
+path.lineTo(SkBits2Float(0x4309999a), SkBits2Float(0x42c00000));
+path.cubicTo(SkBits2Float(0x4309999a), SkBits2Float(0x42a20000), SkBits2Float(0x43016667), SkBits2Float(0x4287cccd), SkBits2Float(0x42ea999a), SkBits2Float(0x4273999a));
+path.lineTo(SkBits2Float(0x4306cccd), SkBits2Float(0x41f5999a));
+path.cubicTo(SkBits2Float(0x42f76667), SkBits2Float(0x41c26667), SkBits2Float(0x42dd999a), SkBits2Float(0x41a4cccd), SkBits2Float(0x42c23334), SkBits2Float(0x41a4cccd));
+path.lineTo(SkBits2Float(0x42c23334), SkBits2Float(0x425e0000));
+path.cubicTo(SkBits2Float(0x42a43334), SkBits2Float(0x425e0000), SkBits2Float(0x428a0001), SkBits2Float(0x427ecccd), SkBits2Float(0x42780002), SkBits2Float(0x4297999a));
+path.lineTo(SkBits2Float(0x41fccccd), SkBits2Float(0x42693333));
+path.cubicTo(SkBits2Float(0x41c9999a), SkBits2Float(0x428acccd), SkBits2Float(0x41ac0000), SkBits2Float(0x42a4999a), SkBits2Float(0x41ac0000), SkBits2Float(0x42c00000));
+path.lineTo(SkBits2Float(0x4261999a), SkBits2Float(0x42c00000));
+path.cubicTo(SkBits2Float(0x4261999a), SkBits2Float(0x42de0000), SkBits2Float(0x42813333), SkBits2Float(0x42f83333), SkBits2Float(0x42996666), SkBits2Float(0x4303199a));
+path.cubicTo(SkBits2Float(0x4272cccc), SkBits2Float(0x4303199a), SkBits2Float(0x423d3332), SkBits2Float(0x430de667), SkBits2Float(0x422d9999), SkBits2Float(0x431cb334));
+path.lineTo(SkBits2Float(0x7086a1dc), SkBits2Float(0x42eecccd));
+path.lineTo(SkBits2Float(0x41eb3333), SkBits2Float(0xc12ccccd));
+path.lineTo(SkBits2Float(0x42053333), SkBits2Float(0xc1cccccd));
+path.lineTo(SkBits2Float(0x42780000), SkBits2Float(0xc18f3334));
+path.cubicTo(SkBits2Float(0x43206666), SkBits2Float(0x43134ccd), SkBits2Float(0x43213333), SkBits2Float(0x430db333), SkBits2Float(0x43213333), SkBits2Float(0x43080000));
+path.lineTo(SkBits2Float(0x432c8000), SkBits2Float(0x42c00000));
+path.close();
+
+    SkPath path2(path);
+    testPathFailOp(reporter, path1, path2, (SkPathOp) 2, filename);
+}
+
 static struct TestDesc failTests[] = {
+    TEST(fuzz487a),
+    TEST(fuzz487b),
     TEST(fuzz433b),
     TEST(fuzz433),
     TEST(bufferOverflow),