Add the ability to iterate through a path without modification. This change is
required by WebKit SVG in order to correctly draw markers and endcaps.

BUG=415
TEST=TestPath in the unit tests
Review URL: http://codereview.appspot.com/5505097

git-svn-id: http://skia.googlecode.com/svn/trunk@2962 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/degeneratesegments.cpp b/gm/degeneratesegments.cpp
index 99dd4ad..63d9dba 100644
--- a/gm/degeneratesegments.cpp
+++ b/gm/degeneratesegments.cpp
@@ -31,8 +31,8 @@
     typedef SkPoint (*AddSegmentFunc)(SkPath&, SkPoint&);
     
     // We need to use explicit commands here, instead of addPath, because we
-    // do not want the SkPath::Iter used in addPath to remove the degenerate
-    // segments before we send th epath off for drawing.
+    // do not want the moveTo that is added at the beginning of a path to
+    // appear in the appended path.
     static SkPoint AddMove(SkPath& path, SkPoint& startPt) {
         SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
         path.moveTo(moveToPt);
@@ -207,52 +207,52 @@
     }
     
     virtual void onDraw(SkCanvas* canvas) {
-	static const AddSegmentFunc gSegmentFunctions[] = {
-	    AddMove,
-	    AddMoveClose,
-	    AddDegenLine,
-	    AddMoveDegenLine,
-	    AddMoveDegenLineClose,
-	    AddDegenQuad,
-	    AddMoveDegenQuad,
-	    AddMoveDegenQuadClose,
-	    AddDegenCubic,
-	    AddMoveDegenCubic,
-	    AddMoveDegenCubicClose,
-	    AddClose,
-	    AddLine,
-	    AddMoveLine,
-	    AddMoveLineClose,
-	    AddQuad,
-	    AddMoveQuad,
-	    AddMoveQuadClose,
-	    AddCubic,
-	    AddMoveCubic,
-	    AddMoveCubicClose
-	};
-	static const char* gSegmentNames[] = {
-	    "Move",
-	    "MoveClose",
-	    "DegenLine",
-	    "MoveDegenLine",
-	    "MoveDegenLineClose",
-	    "DegenQuad",
-	    "MoveDegenQuad",
-	    "MoveDegenQuadClose",
-	    "DegenCubic",
-	    "MoveDegenCubic",
-	    "MoveDegenCubicClose",
-	    "Close",
-	    "Line",
-	    "MoveLine",
-	    "MoveLineClose",
-	    "Quad",
-	    "MoveQuad",
-	    "MoveQuadClose",
-	    "Cubic",
-	    "MoveCubic",
-	    "MoveCubicClose"
-	};
+    static const AddSegmentFunc gSegmentFunctions[] = {
+        AddMove,
+        AddMoveClose,
+        AddDegenLine,
+        AddMoveDegenLine,
+        AddMoveDegenLineClose,
+        AddDegenQuad,
+        AddMoveDegenQuad,
+        AddMoveDegenQuadClose,
+        AddDegenCubic,
+        AddMoveDegenCubic,
+        AddMoveDegenCubicClose,
+        AddClose,
+        AddLine,
+        AddMoveLine,
+        AddMoveLineClose,
+        AddQuad,
+        AddMoveQuad,
+        AddMoveQuadClose,
+        AddCubic,
+        AddMoveCubic,
+        AddMoveCubicClose
+    };
+    static const char* gSegmentNames[] = {
+        "Move",
+        "MoveClose",
+        "DegenLine",
+        "MoveDegenLine",
+        "MoveDegenLineClose",
+        "DegenQuad",
+        "MoveDegenQuad",
+        "MoveDegenQuadClose",
+        "DegenCubic",
+        "MoveDegenCubic",
+        "MoveDegenCubicClose",
+        "Close",
+        "Line",
+        "MoveLine",
+        "MoveLineClose",
+        "Quad",
+        "MoveQuad",
+        "MoveQuadClose",
+        "Cubic",
+        "MoveCubic",
+        "MoveCubicClose"
+    };
 
         struct FillAndName {
             SkPath::FillType fFill;
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 98c79a6..cbbccd5 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -676,6 +676,31 @@
         void consumeDegenerateSegments();
     };
 
+    /** Iterate through the verbs in the path, providing the associated points.
+    */
+    class SK_API RawIter {
+    public:
+        RawIter();
+        RawIter(const SkPath&);
+
+        void setPath(const SkPath&);
+
+        /** Return the next verb in this iteration of the path. When all
+            segments have been visited, return kDone_Verb.
+         
+            @param  pts The points representing the current verb and/or segment
+            @return The verb for the current segment
+        */
+        Verb next(SkPoint pts[4]);
+
+    private:
+        const SkPoint*  fPts;
+        const uint8_t*  fVerbs;
+        const uint8_t*  fVerbStop;
+        SkPoint         fMoveTo;
+        SkPoint         fLastPt;
+    };
+
     void dump(bool forceClose, const char title[] = NULL) const;
     void dump() const;
 
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 22860bf..75a1e40 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -935,7 +935,7 @@
 void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) {
     this->incReserve(path.fPts.count());
 
-    Iter    iter(path, false);
+    RawIter iter(path);
     SkPoint pts[4];
     Verb    verb;
 
@@ -1447,6 +1447,82 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+SkPath::RawIter::RawIter() {
+#ifdef SK_DEBUG
+    fPts = NULL;
+    fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
+#endif
+    // need to init enough to make next() harmlessly return kDone_Verb
+    fVerbs = NULL;
+    fVerbStop = NULL;
+}
+
+SkPath::RawIter::RawIter(const SkPath& path) {
+    this->setPath(path);
+}
+
+void SkPath::RawIter::setPath(const SkPath& path) {
+    fPts = path.fPts.begin();
+    fVerbs = path.fVerbs.begin();
+    fVerbStop = path.fVerbs.end();
+    fMoveTo.fX = fMoveTo.fY = 0;
+    fLastPt.fX = fLastPt.fY = 0;
+}
+
+SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
+    if (fVerbs == fVerbStop) {
+        return kDone_Verb;
+    }
+
+    unsigned        verb = *fVerbs++;
+    const SkPoint*  srcPts = fPts;
+
+    switch (verb) {
+        case kMove_Verb:
+            if (pts) {
+                pts[0] = *srcPts;
+            }
+            fMoveTo = srcPts[0];
+            fLastPt = fMoveTo;
+            srcPts += 1;
+            break;
+        case kLine_Verb:
+            if (pts) {
+                pts[0] = fLastPt;
+                pts[1] = srcPts[0];
+            }
+            fLastPt = srcPts[0];
+            srcPts += 1;
+            break;
+        case kQuad_Verb:
+            if (pts) {
+                pts[0] = fLastPt;
+                memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
+            }
+            fLastPt = srcPts[1];
+            srcPts += 2;
+            break;
+        case kCubic_Verb:
+            if (pts) {
+                pts[0] = fLastPt;
+                memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
+            }
+            fLastPt = srcPts[2];
+            srcPts += 3;
+            break;
+        case kClose_Verb:
+            fLastPt = fMoveTo;
+            if (pts) {
+                pts[0] = fMoveTo;
+            }
+            break;
+    }
+    fPts = srcPts;
+    return (Verb)verb;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 /*
     Format in flattened buffer: [ptCount, verbCount, pts[], verbs[]]
 */
@@ -1477,7 +1553,6 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
 
 void SkPath::dump(bool forceClose, const char title[]) const {
     Iter    iter(*this, forceClose);
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index 407fcf4..270fa68 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -9,6 +9,7 @@
 #include "SkPaint.h"
 #include "SkPath.h"
 #include "SkParse.h"
+#include "SkRandom.h"
 #include "SkReader32.h"
 #include "SkSize.h"
 #include "SkWriter32.h"
@@ -523,21 +524,28 @@
 }
 
 static void test_zero_length_paths(skiatest::Reporter* reporter) {
-    SkPath p;
-    SkRect bounds;
+    SkPath  p;
+    SkPoint pt;
+    SkRect  bounds;
 
     // Lone moveTo case
     p.moveTo(SK_Scalar1, SK_Scalar1);
-    bounds.set(0, 0, 0, 0);
     REPORTER_ASSERT(reporter, !p.isEmpty());
     REPORTER_ASSERT(reporter, 1 == p.countPoints());
+    p.getLastPt(&pt);
+    REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
+    REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
+    bounds.set(0, 0, 0, 0);
     REPORTER_ASSERT(reporter, bounds == p.getBounds());
 
     // MoveTo-MoveTo case
     p.moveTo(SK_Scalar1*2, SK_Scalar1);
-    bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
     REPORTER_ASSERT(reporter, !p.isEmpty());
     REPORTER_ASSERT(reporter, 2 == p.countPoints());
+    p.getLastPt(&pt);
+    REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1*2);
+    REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
+    bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
     REPORTER_ASSERT(reporter, bounds == p.getBounds()); 
 
     // moveTo-close case
@@ -659,6 +667,302 @@
 
 #define kCurveSegmentMask   (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
 
+static void test_segment_masks(skiatest::Reporter* reporter) {
+    SkPath p;
+    p.moveTo(0, 0);
+    p.quadTo(100, 100, 200, 200);
+    REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
+    REPORTER_ASSERT(reporter, !p.isEmpty());
+    p.cubicTo(100, 100, 200, 200, 300, 300);
+    REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
+    REPORTER_ASSERT(reporter, !p.isEmpty());
+    p.reset();
+    p.moveTo(0, 0);
+    p.cubicTo(100, 100, 200, 200, 300, 300);
+    REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
+    REPORTER_ASSERT(reporter, !p.isEmpty());
+}
+
+static void test_iter(skiatest::Reporter* reporter) {
+    SkPath p;
+    SkPoint pts[4];
+
+    // Test an iterator with no path
+    SkPath::Iter noPathIter;
+    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
+    // Test that setting an empty path works
+    noPathIter.setPath(p, false);
+    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
+    // Test that close path makes no difference for an empty path
+    noPathIter.setPath(p, true);
+    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
+    
+    // Test an iterator with an initial empty path
+    SkPath::Iter iter(p, false);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+    // Test that close path makes no difference
+    SkPath::Iter forceCloseIter(p, true);
+    REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
+
+    // Test that a move-only path produces nothing when iterated.
+    p.moveTo(SK_Scalar1, 0);
+    iter.setPath(p, false);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+    // No matter how many moves we add, we should still get nothing back.
+    p.moveTo(SK_Scalar1*2, 0);
+    p.moveTo(SK_Scalar1*3, 0);
+    p.moveTo(SK_Scalar1*4, 0);
+    p.moveTo(SK_Scalar1*5, 0);
+    iter.setPath(p, false);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+    // Nor should force closing
+    forceCloseIter.setPath(p, true);
+    REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
+
+    // Initial closes should be ignored
+    p.reset();
+    p.close();
+    iter.setPath(p, false);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+    // Even if force closed
+    forceCloseIter.setPath(p, true);
+    REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
+
+    // Move/close sequences should also be ignored
+    p.reset();
+    p.close();
+    p.moveTo(SK_Scalar1, 0);
+    p.close();
+    p.close();
+    p.moveTo(SK_Scalar1*2, 0);
+    p.close();
+    p.moveTo(SK_Scalar1*3, 0);
+    p.moveTo(SK_Scalar1*4, 0);
+    p.close();
+    iter.setPath(p, false);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+    // Even if force closed
+    forceCloseIter.setPath(p, true);
+    REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
+
+    // The GM degeneratesegments.cpp test is more extensive
+}
+
+static void test_raw_iter(skiatest::Reporter* reporter) {
+    SkPath p;
+    SkPoint pts[4];
+
+    // Test an iterator with no path
+    SkPath::RawIter noPathIter;
+    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
+    // Test that setting an empty path works
+    noPathIter.setPath(p);
+    REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
+    
+    // Test an iterator with an initial empty path
+    SkPath::RawIter iter(p);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+    // Test that a move-only path returns the move.
+    p.moveTo(SK_Scalar1, 0);
+    iter.setPath(p);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
+    REPORTER_ASSERT(reporter, pts[0].fY == 0);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+    // No matter how many moves we add, we should get them all back
+    p.moveTo(SK_Scalar1*2, SK_Scalar1);
+    p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
+    iter.setPath(p);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
+    REPORTER_ASSERT(reporter, pts[0].fY == 0);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
+    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
+    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+    // Initial close is never ever stored
+    p.reset();
+    p.close();
+    iter.setPath(p);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+    // Move/close sequences
+    p.reset();
+    p.close(); // Not stored, no purpose
+    p.moveTo(SK_Scalar1, 0);
+    p.close();
+    p.close(); // Not stored, no purpose
+    p.moveTo(SK_Scalar1*2, SK_Scalar1);
+    p.close();
+    p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
+    p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
+    p.close();
+    iter.setPath(p);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
+    REPORTER_ASSERT(reporter, pts[0].fY == 0);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
+    REPORTER_ASSERT(reporter, pts[0].fY == 0);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
+    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
+    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
+    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
+    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
+    REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
+    REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
+    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+    // Generate random paths and verify
+    SkPoint randomPts[25];
+    for (int i = 0; i < 5; ++i) {
+        for (int j = 0; j < 5; ++j) {
+            randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
+        }
+    }
+
+    // Max of 10 segments, max 3 points per segment
+    SkRandom rand(9876543);
+    SkPoint          expectedPts[31]; // May have leading moveTo
+    SkPath::Verb     expectedVerbs[11]; // May have leading moveTo
+    SkPath::Verb     nextVerb;
+    for (int i = 0; i < 500; ++i) {
+        p.reset();
+        bool lastWasClose = true;
+        bool haveMoveTo = false;
+        int numPoints = 0;
+        int numVerbs = (rand.nextU() >> 16) % 10;
+        int numIterVerbs = 0;
+        for (int j = 0; j < numVerbs; ++j) {
+            do {
+                nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
+            } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
+            int numRequiredPts;
+            switch (nextVerb) {
+                case SkPath::kMove_Verb:
+                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
+                    p.moveTo(expectedPts[numPoints]);
+                    numPoints += 1;
+                    lastWasClose = false;
+                    haveMoveTo = true;
+                    break;
+                case SkPath::kLine_Verb:
+                    if (!haveMoveTo) {
+                        expectedPts[numPoints++].set(0, 0);
+                        expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
+                        haveMoveTo = true;
+                    }
+                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
+                    p.lineTo(expectedPts[numPoints]);
+                    numPoints += 1;
+                    lastWasClose = false;
+                    break;
+                case SkPath::kQuad_Verb:
+                    if (!haveMoveTo) {
+                        expectedPts[numPoints++].set(0, 0);
+                        expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
+                        haveMoveTo = true;
+                    }
+                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
+                    expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
+                    p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
+                    numPoints += 2;
+                    lastWasClose = false;
+                    break;
+                case SkPath::kCubic_Verb:
+                    if (!haveMoveTo) {
+                        expectedPts[numPoints++].set(0, 0);
+                        expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
+                        haveMoveTo = true;
+                    }
+                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
+                    expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
+                    expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
+                    p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
+                              expectedPts[numPoints + 2]);
+                    numPoints += 3;
+                    lastWasClose = false;
+                    break;
+                case SkPath::kClose_Verb:
+                    p.close();
+                    lastWasClose = true;
+                    break;
+                default:;
+            }
+            expectedVerbs[numIterVerbs++] = nextVerb;
+        }
+        
+        iter.setPath(p);
+        numVerbs = numIterVerbs;
+        numIterVerbs = 0;
+        int numIterPts = 0;
+        SkPoint lastMoveTo;
+        SkPoint lastPt;
+        lastMoveTo.set(0, 0);
+        lastPt.set(0, 0);
+        while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
+            REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
+            numIterVerbs++;
+            switch (nextVerb) {
+                case SkPath::kMove_Verb:
+                    REPORTER_ASSERT(reporter, numIterPts < numPoints);
+                    REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
+                    lastPt = lastMoveTo = pts[0];
+                    numIterPts += 1;
+                    break;
+                case SkPath::kLine_Verb:
+                    REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
+                    REPORTER_ASSERT(reporter, pts[0] == lastPt);
+                    REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
+                    lastPt = pts[1];
+                    numIterPts += 1;
+                    break;
+                case SkPath::kQuad_Verb:
+                    REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
+                    REPORTER_ASSERT(reporter, pts[0] == lastPt);
+                    REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
+                    REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
+                    lastPt = pts[2];
+                    numIterPts += 2;
+                    break;
+                case SkPath::kCubic_Verb:
+                    REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
+                    REPORTER_ASSERT(reporter, pts[0] == lastPt);
+                    REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
+                    REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
+                    REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
+                    lastPt = pts[3];
+                    numIterPts += 3;
+                    break;
+                case SkPath::kClose_Verb:
+                    REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
+                    lastPt = lastMoveTo;
+                    break;
+                default:;
+            }
+        }
+        REPORTER_ASSERT(reporter, numIterPts == numPoints);
+        REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
+    }
+}
+
 void TestPath(skiatest::Reporter* reporter);
 void TestPath(skiatest::Reporter* reporter) {
     {
@@ -734,35 +1038,16 @@
     REPORTER_ASSERT(reporter, !p.isRect(NULL));
     test_isRect(reporter);
 
-    SkPoint pt;
-
-    p.moveTo(SK_Scalar1, 0);
-    p.getLastPt(&pt);
-    REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-
     test_zero_length_paths(reporter);
     test_convexity(reporter);
     test_convexity2(reporter);
     test_close(reporter);
-
-    p.reset();
-    p.moveTo(0, 0);
-    p.quadTo(100, 100, 200, 200);
-    REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    p.cubicTo(100, 100, 200, 200, 300, 300);
-    REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    p.reset();
-    p.moveTo(0, 0);
-    p.cubicTo(100, 100, 200, 200, 300, 300);
-    REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-
+    test_segment_masks(reporter);
     test_flattening(reporter);
     test_transform(reporter);
     test_bounds(reporter);
+    test_iter(reporter);
+    test_raw_iter(reporter);
 }
 
 #include "TestClassDef.h"