automatically inject a moveTo if we see a close followed by a line/quad/cubic



git-svn-id: http://skia.googlecode.com/svn/trunk@3027 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 859486c..ec79a49 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -740,6 +740,7 @@
     SkTDArray<SkPoint>  fPts;
     SkTDArray<uint8_t>  fVerbs;
     mutable SkRect      fBounds;
+    int                 fLastMoveToIndex;
     uint8_t             fFillType;
     uint8_t             fSegmentMask;
     mutable uint8_t     fBoundsIsDirty;
@@ -767,6 +768,14 @@
     */
     void reversePathTo(const SkPath&);
 
+    // called before we add points for lineTo, quadTo, cubicTo, checking to see
+    // if we need to inject a leading moveTo first
+    //
+    //  SkPath path; path.lineTo(...);   <--- need a leading moveTo(0, 0)
+    // SkPath path; ... path.close(); path.lineTo(...) <-- need a moveTo(previous moveTo)
+    //
+    inline void injectMoveToIfNeeded();
+
     friend const SkPoint* sk_get_path_points(const SkPath&, int index);
     friend class SkAutoPathBoundsUpdate;
 };
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index c99db4c..11e0079 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -101,11 +101,15 @@
 
 ////////////////////////////////////////////////////////////////////////////
 
+// flag to require a moveTo if we begin with something else, like lineTo etc.
+#define INITIAL_LASTMOVETOINDEX_VALUE   ~0
+
 SkPath::SkPath() 
     : fFillType(kWinding_FillType)
     , fBoundsIsDirty(true) {
     fConvexity = kUnknown_Convexity;
     fSegmentMask = 0;
+    fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
 #ifdef SK_BUILD_FOR_ANDROID
     fGenerationID = 0;
 #endif
@@ -135,6 +139,7 @@
         fBoundsIsDirty  = src.fBoundsIsDirty;
         fConvexity      = src.fConvexity;
         fSegmentMask    = src.fSegmentMask;
+        fLastMoveToIndex = src.fLastMoveToIndex;
         GEN_ID_INC;
     }
     SkDEBUGCODE(this->validate();)
@@ -165,6 +170,7 @@
         SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty);
         SkTSwap<uint8_t>(fConvexity, other.fConvexity);
         SkTSwap<uint8_t>(fSegmentMask, other.fSegmentMask);
+        SkTSwap<int>(fLastMoveToIndex, other.fLastMoveToIndex);
         GEN_ID_INC;
     }
 }
@@ -184,6 +190,7 @@
     fBoundsIsDirty = true;
     fConvexity = kUnknown_Convexity;
     fSegmentMask = 0;
+    fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
 }
 
 void SkPath::rewind() {
@@ -195,6 +202,7 @@
     fConvexity = kUnknown_Convexity;
     fBoundsIsDirty = true;
     fSegmentMask = 0;
+    fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
 }
 
 bool SkPath::isEmpty() const {
@@ -410,6 +418,9 @@
     int      vc = fVerbs.count();
     SkPoint* pt;
 
+    // remember our index
+    fLastMoveToIndex = fPts.count();
+
 #ifdef SK_OLD_EMPTY_PATH_BEHAVIOR
     if (vc > 0 && fVerbs[vc - 1] == kMove_Verb) {
         pt = &fPts[fPts.count() - 1];
@@ -433,13 +444,25 @@
     this->moveTo(pt.fX + x, pt.fY + y);
 }
 
+void SkPath::injectMoveToIfNeeded() {
+    if (fLastMoveToIndex < 0) {
+        SkScalar x, y;
+        if (fVerbs.count() == 0) {
+            x = y = 0;
+        } else {
+            const SkPoint& pt = fPts[~fLastMoveToIndex];
+            x = pt.fX;
+            y = pt.fY;
+        }
+        this->moveTo(x, y);
+    }
+}
+
 void SkPath::lineTo(SkScalar x, SkScalar y) {
     SkDEBUGCODE(this->validate();)
 
-    if (fVerbs.count() == 0) {
-        fPts.append()->set(0, 0);
-        *fVerbs.append() = kMove_Verb;
-    }
+    this->injectMoveToIfNeeded();
+
     fPts.append()->set(x, y);
     *fVerbs.append() = kLine_Verb;
     fSegmentMask |= kLine_SegmentMask;
@@ -457,10 +480,7 @@
 void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
     SkDEBUGCODE(this->validate();)
 
-    if (fVerbs.count() == 0) {
-        fPts.append()->set(0, 0);
-        *fVerbs.append() = kMove_Verb;
-    }
+    this->injectMoveToIfNeeded();
 
     SkPoint* pts = fPts.append(2);
     pts[0].set(x1, y1);
@@ -482,10 +502,8 @@
                      SkScalar x3, SkScalar y3) {
     SkDEBUGCODE(this->validate();)
 
-    if (fVerbs.count() == 0) {
-        fPts.append()->set(0, 0);
-        *fVerbs.append() = kMove_Verb;
-    }
+    this->injectMoveToIfNeeded();
+
     SkPoint* pts = fPts.append(3);
     pts[0].set(x1, y1);
     pts[1].set(x2, y2);
@@ -525,6 +543,15 @@
                 break;
         }
     }
+
+    // signal that we need a moveTo to follow us (unless we're done)
+#if 0
+    if (fLastMoveToIndex >= 0) {
+        fLastMoveToIndex = ~fLastMoveToIndex;
+    }
+#else
+    fLastMoveToIndex ^= ~fLastMoveToIndex >> (8 * sizeof(fLastMoveToIndex) - 1);
+#endif
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index fadb0d9..9298d53 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -889,12 +889,14 @@
     // 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     expectedVerbs[22]; // May have leading moveTo
     SkPath::Verb     nextVerb;
+
     for (int i = 0; i < 500; ++i) {
         p.reset();
         bool lastWasClose = true;
         bool haveMoveTo = false;
+        SkPoint lastMoveToPt = { 0, 0 };
         int numPoints = 0;
         int numVerbs = (rand.nextU() >> 16) % 10;
         int numIterVerbs = 0;
@@ -907,13 +909,14 @@
                 case SkPath::kMove_Verb:
                     expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
                     p.moveTo(expectedPts[numPoints]);
+                    lastMoveToPt = expectedPts[numPoints];
                     numPoints += 1;
                     lastWasClose = false;
                     haveMoveTo = true;
                     break;
                 case SkPath::kLine_Verb:
                     if (!haveMoveTo) {
-                        expectedPts[numPoints++].set(0, 0);
+                        expectedPts[numPoints++] = lastMoveToPt;
                         expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
                         haveMoveTo = true;
                     }
@@ -924,7 +927,7 @@
                     break;
                 case SkPath::kQuad_Verb:
                     if (!haveMoveTo) {
-                        expectedPts[numPoints++].set(0, 0);
+                        expectedPts[numPoints++] = lastMoveToPt;
                         expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
                         haveMoveTo = true;
                     }
@@ -936,7 +939,7 @@
                     break;
                 case SkPath::kCubic_Verb:
                     if (!haveMoveTo) {
-                        expectedPts[numPoints++].set(0, 0);
+                        expectedPts[numPoints++] = lastMoveToPt;
                         expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
                         haveMoveTo = true;
                     }
@@ -950,6 +953,7 @@
                     break;
                 case SkPath::kClose_Verb:
                     p.close();
+                    haveMoveTo = false;
                     lastWasClose = true;
                     break;
                 default:;