path ops -- handle non-finite numbers

Op() and Simplify() do nothing if the input
is non-finite. Add code and tests.
Review URL: https://codereview.chromium.org/14407006

git-svn-id: http://skia.googlecode.com/svn/trunk@8882 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/pathops/SkOpEdgeBuilder.cpp b/src/pathops/SkOpEdgeBuilder.cpp
index d1d7af8..57e3b41 100644
--- a/src/pathops/SkOpEdgeBuilder.cpp
+++ b/src/pathops/SkOpEdgeBuilder.cpp
@@ -16,6 +16,7 @@
     gContourID = 0;
     gSegmentID = 0;
 #endif
+    fUnparseable = false;
     fSecondHalf = preFetch();
 }
 
@@ -28,8 +29,10 @@
     preFetch();
 }
 
-void SkOpEdgeBuilder::finish() {
-    walk();
+bool SkOpEdgeBuilder::finish() {
+    if (fUnparseable || !walk()) {
+        return false;
+    }
     complete();
     if (fCurrentContour && !fCurrentContour->segments().count()) {
         fContours.pop_back();
@@ -51,6 +54,7 @@
                 &fReducePts[rIndex]);
     }
     fExtra.reset();  // we're done with this
+    return true;
 }
 
 // Note that copying the points here avoids copying the resulting path later.
@@ -59,6 +63,10 @@
 // OPTIMIZATION: This copies both sets of input points every time. If the input data was read
 // directly, the output path would only need to be copied if it was also one of the input paths.
 int SkOpEdgeBuilder::preFetch() {
+    if (!fPath->isFinite()) {
+        fUnparseable = true;
+        return 0;
+    }
     SkPath::RawIter iter(*fPath);
     SkPoint pts[4];
     SkPath::Verb verb;
@@ -74,14 +82,25 @@
     return fPathVerbs.count() - 1;
 }
 
-void SkOpEdgeBuilder::walk() {
+bool SkOpEdgeBuilder::close() {
+    if (fFinalCurveStart && fFinalCurveEnd && *fFinalCurveStart != *fFinalCurveEnd) {
+        *fReducePts.append() = *fFinalCurveStart;
+        *fReducePts.append() = *fFinalCurveEnd;
+        const SkPoint* lineStart = fReducePts.end() - 2;
+        *fExtra.append() = fCurrentContour->addLine(lineStart);
+    }
+    complete();
+    return true;
+}
+
+bool SkOpEdgeBuilder::walk() {
     SkPath::Verb reducedVerb;
     uint8_t* verbPtr = fPathVerbs.begin();
     uint8_t* endOfFirstHalf = &verbPtr[fSecondHalf];
     const SkPoint* pointsPtr = fPathPts.begin();
-    const SkPoint* finalCurveStart = NULL;
-    const SkPoint* finalCurveEnd = NULL;
     SkPath::Verb verb;
+    fFinalCurveStart = NULL;
+    fFinalCurveEnd = NULL;
     while ((verb = (SkPath::Verb) *verbPtr) != SkPath::kDone_Verb) {
         if (verbPtr == endOfFirstHalf) {
             fOperand = true;
@@ -89,64 +108,76 @@
         verbPtr++;
         switch (verb) {
             case SkPath::kMove_Verb:
-                complete();
+                if (fCurrentContour) {
+                    if (fAllowOpenContours) {
+                        complete();
+                    } else if (!close()) {
+                        return false;
+                    }
+                }
                 if (!fCurrentContour) {
                     fCurrentContour = fContours.push_back_n(1);
                     fCurrentContour->setOperand(fOperand);
                     fCurrentContour->setXor(fXorMask[fOperand] == kEvenOdd_PathOpsMask);
                     *fExtra.append() = -1;  // start new contour
                 }
-                finalCurveEnd = pointsPtr++;
+                fFinalCurveEnd = pointsPtr++;
                 continue;
-            case SkPath::kLine_Verb:
+            case SkPath::kLine_Verb: {
+                const SkPoint& lineEnd = pointsPtr[0];
+                const SkPoint& lineStart = pointsPtr[-1];
                 // skip degenerate points
-                if (pointsPtr[-1].fX != pointsPtr[0].fX || pointsPtr[-1].fY != pointsPtr[0].fY) {
-                    fCurrentContour->addLine(&pointsPtr[-1]);
+                if (lineStart.fX != lineEnd.fX || lineStart.fY != lineEnd.fY) {
+                    fCurrentContour->addLine(&lineStart);
                 }
-                break;
-            case SkPath::kQuad_Verb:
-                reducedVerb = SkReduceOrder::Quad(&pointsPtr[-1], &fReducePts);
+                } break;
+            case SkPath::kQuad_Verb: {
+                const SkPoint* quadStart = &pointsPtr[-1];
+                reducedVerb = SkReduceOrder::Quad(quadStart, &fReducePts);
                 if (reducedVerb == 0) {
                     break;  // skip degenerate points
                 }
                 if (reducedVerb == 1) {
-                    *fExtra.append() =
-                            fCurrentContour->addLine(fReducePts.end() - 2);
+                    const SkPoint* lineStart = fReducePts.end() - 2;
+                   *fExtra.append() = fCurrentContour->addLine(lineStart);
                     break;
                 }
-                fCurrentContour->addQuad(&pointsPtr[-1]);
-                break;
-            case SkPath::kCubic_Verb:
-                reducedVerb = SkReduceOrder::Cubic(&pointsPtr[-1], &fReducePts);
+                fCurrentContour->addQuad(quadStart);
+                } break;
+            case SkPath::kCubic_Verb: {
+                const SkPoint* cubicStart = &pointsPtr[-1];
+                reducedVerb = SkReduceOrder::Cubic(cubicStart, &fReducePts);
                 if (reducedVerb == 0) {
                     break;  // skip degenerate points
                 }
                 if (reducedVerb == 1) {
-                    *fExtra.append() = fCurrentContour->addLine(fReducePts.end() - 2);
+                    const SkPoint* lineStart = fReducePts.end() - 2;
+                    *fExtra.append() = fCurrentContour->addLine(lineStart);
                     break;
                 }
                 if (reducedVerb == 2) {
-                    *fExtra.append() = fCurrentContour->addQuad(fReducePts.end() - 3);
+                    const SkPoint* quadStart = fReducePts.end() - 3;
+                    *fExtra.append() = fCurrentContour->addQuad(quadStart);
                     break;
                 }
-                fCurrentContour->addCubic(&pointsPtr[-1]);
-                break;
+                fCurrentContour->addCubic(cubicStart);
+                } break;
             case SkPath::kClose_Verb:
                 SkASSERT(fCurrentContour);
-                if (finalCurveStart && finalCurveEnd
-                        && *finalCurveStart != *finalCurveEnd) {
-                    *fReducePts.append() = *finalCurveStart;
-                    *fReducePts.append() = *finalCurveEnd;
-                    *fExtra.append() = fCurrentContour->addLine(fReducePts.end() - 2);
+                if (!close()) {
+                    return false;
                 }
-                complete();
                 continue;
             default:
                 SkDEBUGFAIL("bad verb");
-                return;
+                return false;
         }
-        finalCurveStart = &pointsPtr[verb - 1];
+        fFinalCurveStart = &pointsPtr[verb - 1];
         pointsPtr += verb;
         SkASSERT(fCurrentContour);
     }
+   if (fCurrentContour && !fAllowOpenContours && !close()) {
+       return false;
+   }
+   return true;
 }