Move segment mask from SkPath to SkPathRef

https://codereview.chromium.org/105083003/



git-svn-id: http://skia.googlecode.com/svn/trunk@12660 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 0b2ea61..1526461 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -83,7 +83,7 @@
      */
     void toggleInverseFillType() {
         fFillType ^= 2;
-     }
+    }
 
     enum Convexity {
         kUnknown_Convexity,
@@ -446,8 +446,8 @@
         @param dy3   The amount to add to the y-coordinate of the last point on
                      this contour, to specify the end point of a cubic curve
     */
-    void    rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
-                     SkScalar x3, SkScalar y3);
+    void rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+                  SkScalar x3, SkScalar y3);
 
     /** Append the specified arc to the path as a new contour. If the start of
         the path is different from the path's current last point, then an
@@ -461,8 +461,8 @@
                           treated mod 360.
         @param forceMoveTo If true, always begin a new contour with the arc
     */
-    void    arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
-                  bool forceMoveTo);
+    void arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
+               bool forceMoveTo);
 
     /** Append a line and arc to the current path. This is the same as the
         PostScript call "arct".
@@ -778,7 +778,7 @@
      *  set if the path contains 1 or more segments of that type.
      *  Returns 0 for an empty path (no segments).
      */
-    uint32_t getSegmentMasks() const { return fSegmentMask; }
+    uint32_t getSegmentMasks() const { return fPathRef->getSegmentMasks(); }
 
     enum Verb {
         kMove_Verb,     //!< iter.next returns 1 point
@@ -942,14 +942,15 @@
 #endif
         kConvexity_SerializationShift = 16, // requires 8 bits
         kFillType_SerializationShift = 8,   // requires 8 bits
-        kSegmentMask_SerializationShift = 0 // requires 4 bits
+#ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO
+        kOldSegmentMask_SerializationShift = 0 // requires 4 bits
+#endif
     };
 
     SkAutoTUnref<SkPathRef> fPathRef;
 
     int                 fLastMoveToIndex;
     uint8_t             fFillType;
-    uint8_t             fSegmentMask;
     mutable uint8_t     fConvexity;
     mutable uint8_t     fDirection;
 #ifdef SK_BUILD_FOR_ANDROID
diff --git a/include/core/SkPathRef.h b/include/core/SkPathRef.h
index 5d55c72..99fd90a 100644
--- a/include/core/SkPathRef.h
+++ b/include/core/SkPathRef.h
@@ -62,30 +62,24 @@
         /**
          * Adds the verb and allocates space for the number of points indicated by the verb. The
          * return value is a pointer to where the points for the verb should be written.
+         * 'weight' is only used if 'verb' is kConic_Verb
          */
-        SkPoint* growForVerb(int /*SkPath::Verb*/ verb) {
+        SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight = 0) {
             SkDEBUGCODE(fPathRef->validate();)
-            return fPathRef->growForVerb(verb);
+            return fPathRef->growForVerb(verb, weight);
         }
 
-        SkPoint* growForConic(SkScalar w);
-
         /**
-         * Allocates space for additional verbs and points and returns pointers to the new verbs and
-         * points. verbs will point one beyond the first new verb (index it using [~<i>]). pts points
-         * at the first new point (indexed normally [<i>]).
+         * Allocates space for multiple instances of a particular verb and the
+         * requisite points & weights.
+         * The return pointer points at the first new point (indexed normally [<i>]).
+         * If 'verb' is kConic_Verb, 'weights' will return a pointer to the
+         * space for the conic weights (indexed normally). 
          */
-        void grow(int newVerbs, int newPts, uint8_t** verbs, SkPoint** pts) {
-            SkASSERT(NULL != verbs);
-            SkASSERT(NULL != pts);
-            SkDEBUGCODE(fPathRef->validate();)
-            int oldVerbCnt = fPathRef->fVerbCnt;
-            int oldPointCnt = fPathRef->fPointCnt;
-            SkASSERT(verbs && pts);
-            fPathRef->grow(newVerbs, newPts);
-            *verbs = fPathRef->fVerbs - oldVerbCnt;
-            *pts = fPathRef->fPoints + oldPointCnt;
-            SkDEBUGCODE(fPathRef->validate();)
+        SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, 
+                                     int numVbs, 
+                                     SkScalar** weights = NULL) { 
+            return fPathRef->growForRepeatedVerb(verb, numVbs, weights); 
         }
 
         /**
@@ -123,6 +117,13 @@
         return SkToBool(fIsFinite);
     }
 
+    /**
+     *  Returns a mask, where each bit corresponding to a SegmentMask is
+     *  set if the path contains 1 or more segments of that type.
+     *  Returns 0 for an empty path (no segments).
+     */
+    uint32_t getSegmentMasks() const { return fSegmentMask; }
+
     /** Returns true if the path is an oval.
      *
      * @param rect      returns the bounding rect of this oval. It's a circle
@@ -199,6 +200,7 @@
 
     int countPoints() const { SkDEBUGCODE(this->validate();) return fPointCnt; }
     int countVerbs() const { SkDEBUGCODE(this->validate();) return fVerbCnt; }
+    int countWeights() const { SkDEBUGCODE(this->validate();) return fConicWeights.count(); }
 
     /**
      * Returns a pointer one beyond the first logical verb (last verb in memory order).
@@ -226,7 +228,7 @@
     /**
      * Convenience methods for getting to a verb or point by index.
      */
-    uint8_t atVerb(int index) {
+    uint8_t atVerb(int index) const {
         SkASSERT((unsigned) index < (unsigned) fVerbCnt);
         return this->verbs()[~index];
     }
@@ -240,12 +242,12 @@
     /**
      * Writes the path points and verbs to a buffer.
      */
-    void writeToBuffer(SkWBuffer* buffer);
+    void writeToBuffer(SkWBuffer* buffer) const;
 
     /**
      * Gets the number of bytes that would be written in writeBuffer()
      */
-    uint32_t writeSize();
+    uint32_t writeSize() const;
 
     /**
      * Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the
@@ -258,6 +260,7 @@
     enum SerializationOffsets {
         kIsFinite_SerializationShift = 25,  // requires 1 bit
         kIsOval_SerializationShift = 24,    // requires 1 bit
+        kSegmentMask_SerializationShift = 0 // requires 4 bits
     };
 
     SkPathRef() {
@@ -268,6 +271,7 @@
         fPoints = NULL;
         fFreeSpace = 0;
         fGenerationID = kEmptyGenID;
+        fSegmentMask = 0;
         fIsOval = false;
         SkDEBUGCODE(fEditorsAttached = 0;)
         SkDEBUGCODE(this->validate();)
@@ -311,6 +315,7 @@
         fBoundsIsDirty = true;      // this also invalidates fIsFinite
         fGenerationID = 0;
 
+        fSegmentMask = 0;
         fIsOval = false;
 
         size_t newSize = sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount;
@@ -340,26 +345,19 @@
     }
 
     /**
-     * Increases the verb count by newVerbs and the point count be newPoints. New verbs and points
-     * are uninitialized.
+     * Increases the verb count by numVbs and point count by the required amount. 
+     * The new points are uninitialized. All the new verbs are set to the specified 
+     * verb. If 'verb' is kConic_Verb, 'weights' will return a pointer to the
+     * uninitialized conic weights.
      */
-    void grow(int newVerbs, int newPoints) {
-        SkDEBUGCODE(this->validate();)
-        size_t space = newVerbs * sizeof(uint8_t) + newPoints * sizeof (SkPoint);
-        this->makeSpace(space);
-        fVerbCnt += newVerbs;
-        fPointCnt += newPoints;
-        fFreeSpace -= space;
-        fBoundsIsDirty = true;  // this also invalidates fIsFinite
-        SkDEBUGCODE(this->validate();)
-    }
+    SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, int numVbs, SkScalar** weights);
 
     /**
      * Increases the verb count 1, records the new verb, and creates room for the requisite number
      * of additional points. A pointer to the first point is returned. Any new points are
      * uninitialized.
      */
-    SkPoint* growForVerb(int /*SkPath::Verb*/ verb);
+    SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight);
 
     /**
      * Ensures that the free space available in the path ref is >= size. The verb and point counts
@@ -425,6 +423,7 @@
     };
 
     mutable SkRect      fBounds;
+    uint8_t             fSegmentMask;
     mutable uint8_t     fBoundsIsDirty;
     mutable SkBool8     fIsFinite;    // only meaningful if bounds are valid
     mutable SkBool8     fIsOval;
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index eaa6c93..af8b1aa 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -15,11 +15,6 @@
 #include "SkRRect.h"
 #include "SkThread.h"
 
-// This value is just made-up for now. When count is 4, calling memset was much
-// slower than just writing the loop. This seems odd, and hopefully in the
-// future this we appear to have been a fluke...
-#define MIN_COUNT_FOR_MEMSET_TO_BE_FAST 16
-
 ////////////////////////////////////////////////////////////////////////////
 
 /**
@@ -143,7 +138,6 @@
     //fPathRef is assumed to have been emptied by the caller.
     fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
     fFillType = kWinding_FillType;
-    fSegmentMask = 0;
     fConvexity = kUnknown_Convexity;
     fDirection = kUnknown_Direction;
 
@@ -182,7 +176,6 @@
     //fPathRef is assumed to have been set by the caller.
     fLastMoveToIndex = that.fLastMoveToIndex;
     fFillType        = that.fFillType;
-    fSegmentMask     = that.fSegmentMask;
     fConvexity       = that.fConvexity;
     fDirection       = that.fDirection;
 }
@@ -190,14 +183,8 @@
 bool operator==(const SkPath& a, const SkPath& b) {
     // note: don't need to look at isConvex or bounds, since just comparing the
     // raw data is sufficient.
-
-    // We explicitly check fSegmentMask as a quick-reject. We could skip it,
-    // since it is only a cache of info in the fVerbs, but its a fast way to
-    // notice a difference
-
     return &a == &b ||
-        (a.fFillType == b.fFillType && a.fSegmentMask == b.fSegmentMask &&
-         *a.fPathRef.get() == *b.fPathRef.get());
+        (a.fFillType == b.fFillType && *a.fPathRef.get() == *b.fPathRef.get());
 }
 
 void SkPath::swap(SkPath& that) {
@@ -207,7 +194,6 @@
         fPathRef.swap(&that.fPathRef);
         SkTSwap<int>(fLastMoveToIndex, that.fLastMoveToIndex);
         SkTSwap<uint8_t>(fFillType, that.fFillType);
-        SkTSwap<uint8_t>(fSegmentMask, that.fSegmentMask);
         SkTSwap<uint8_t>(fConvexity, that.fConvexity);
         SkTSwap<uint8_t>(fDirection, that.fDirection);
 #ifdef SK_BUILD_FOR_ANDROID
@@ -674,7 +660,6 @@
 
     SkPathRef::Editor ed(&fPathRef);
     ed.growForVerb(kLine_Verb)->set(x, y);
-    fSegmentMask |= kLine_SegmentMask;
 
     DIRTY_AFTER_EDIT;
 }
@@ -695,7 +680,6 @@
     SkPoint* pts = ed.growForVerb(kQuad_Verb);
     pts[0].set(x1, y1);
     pts[1].set(x2, y2);
-    fSegmentMask |= kQuad_SegmentMask;
 
     DIRTY_AFTER_EDIT;
 }
@@ -723,10 +707,9 @@
         this->injectMoveToIfNeeded();
 
         SkPathRef::Editor ed(&fPathRef);
-        SkPoint* pts = ed.growForConic(w);
+        SkPoint* pts = ed.growForVerb(kConic_Verb, w);
         pts[0].set(x1, y1);
         pts[1].set(x2, y2);
-        fSegmentMask |= kConic_SegmentMask;
 
         DIRTY_AFTER_EDIT;
     }
@@ -751,7 +734,6 @@
     pts[0].set(x1, y1);
     pts[1].set(x2, y2);
     pts[2].set(x3, y3);
-    fSegmentMask |= kCubic_SegmentMask;
 
     DIRTY_AFTER_EDIT;
 }
@@ -838,29 +820,19 @@
         return;
     }
 
-    SkPathRef::Editor ed(&fPathRef);
-    fLastMoveToIndex = ed.pathRef()->countPoints();
-    uint8_t* vb;
-    SkPoint* p;
-    // +close makes room for the extra kClose_Verb
-    ed.grow(count + close, count, &vb, &p);
+    fLastMoveToIndex = fPathRef->countPoints();
 
-    memcpy(p, pts, count * sizeof(SkPoint));
-    vb[~0] = kMove_Verb;
+    // +close makes room for the extra kClose_Verb
+    SkPathRef::Editor ed(&fPathRef, count+close, count);
+
+    ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY);
     if (count > 1) {
-        // cast to unsigned, so if MIN_COUNT_FOR_MEMSET_TO_BE_FAST is defined to
-        // be 0, the compiler will remove the test/branch entirely.
-        if ((unsigned)count >= MIN_COUNT_FOR_MEMSET_TO_BE_FAST) {
-            memset(vb - count, kLine_Verb, count - 1);
-        } else {
-            for (int i = 1; i < count; ++i) {
-                vb[~i] = kLine_Verb;
-            }
-        }
-        fSegmentMask |= kLine_SegmentMask;
+        SkPoint* p = ed.growForRepeatedVerb(kLine_Verb, count - 1);
+        memcpy(p, &pts[1], (count-1) * sizeof(SkPoint));
     }
+
     if (close) {
-        vb[~count] = kClose_Verb;
+        ed.growForVerb(kClose_Verb);
     }
 
     DIRTY_AFTER_EDIT;
@@ -1343,11 +1315,21 @@
     SkPoint pts[kSkBuildQuadArcStorage];
     int count = build_arc_points(oval, startAngle, sweepAngle, pts);
 
-    this->incReserve(count);
-    this->moveTo(pts[0]);
-    for (int i = 1; i < count; i += 2) {
-        this->quadTo(pts[i], pts[i+1]);
+    SkDEBUGCODE(this->validate();)
+    SkASSERT(count & 1);
+
+    fLastMoveToIndex = fPathRef->countPoints();
+
+    SkPathRef::Editor ed(&fPathRef, 1+(count-1)/2, count);
+
+    ed.growForVerb(kMove_Verb)->set(pts[0].fX, pts[0].fY);
+    if (count > 1) {
+        SkPoint* p = ed.growForRepeatedVerb(kQuad_Verb, (count-1)/2);
+        memcpy(p, &pts[1], (count-1) * sizeof(SkPoint));
     }
+
+    DIRTY_AFTER_EDIT;
+    SkDEBUGCODE(this->validate();)
 }
 
 /*
@@ -1671,7 +1653,6 @@
 
         if (this != dst) {
             dst->fFillType = fFillType;
-            dst->fSegmentMask = fSegmentMask;
             dst->fConvexity = fConvexity;
         }
 
@@ -2045,7 +2026,6 @@
 
     int32_t packed = (fConvexity << kConvexity_SerializationShift) |
                      (fFillType << kFillType_SerializationShift) |
-                     (fSegmentMask << kSegmentMask_SerializationShift) |
                      (fDirection << kDirection_SerializationShift)
 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO
                      | (0x1 << kNewFormat_SerializationShift)
@@ -2070,7 +2050,6 @@
 
     fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF;
     fFillType = (packed >> kFillType_SerializationShift) & 0xFF;
-    fSegmentMask = (packed >> kSegmentMask_SerializationShift) & 0xF;
     fDirection = (packed >> kDirection_SerializationShift) & 0x3;
 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO
     bool newFormat = (packed >> kNewFormat_SerializationShift) & 1;
@@ -2201,34 +2180,6 @@
             }
         }
     }
-
-    uint32_t mask = 0;
-    const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbs();
-    for (int i = 0; i < fPathRef->countVerbs(); i++) {
-        switch (verbs[~i]) {
-            case kLine_Verb:
-                mask |= kLine_SegmentMask;
-                break;
-            case kQuad_Verb:
-                mask |= kQuad_SegmentMask;
-                break;
-            case kConic_Verb:
-                mask |= kConic_SegmentMask;
-                break;
-            case kCubic_Verb:
-                mask |= kCubic_SegmentMask;
-            case kMove_Verb:  // these verbs aren't included in the segment mask.
-            case kClose_Verb:
-                break;
-            case kDone_Verb:
-                SkDEBUGFAIL("Done verb shouldn't be recorded.");
-                break;
-            default:
-                SkDEBUGFAIL("Unknown Verb");
-                break;
-        }
-    }
-    SkASSERT(mask == fSegmentMask);
 #endif // SK_DEBUG_PATH
 }
 #endif // SK_DEBUG
diff --git a/src/core/SkPathRef.cpp b/src/core/SkPathRef.cpp
index a02df30..a57e2f4 100644
--- a/src/core/SkPathRef.cpp
+++ b/src/core/SkPathRef.cpp
@@ -28,13 +28,6 @@
     SkDEBUGCODE(sk_atomic_inc(&fPathRef->fEditorsAttached);)
 }
 
-SkPoint* SkPathRef::Editor::growForConic(SkScalar w) {
-    SkDEBUGCODE(fPathRef->validate();)
-    SkPoint* pts = fPathRef->growForVerb(SkPath::kConic_Verb);
-    *fPathRef->fConicWeights.append() = w;
-    return pts;
-}
-
 //////////////////////////////////////////////////////////////////////////////
 void SkPathRef::CreateEmptyImpl(SkPathRef** empty) {
     *empty = SkNEW(SkPathRef);
@@ -105,6 +98,8 @@
         (*dst)->fBoundsIsDirty = true;
     }
 
+    (*dst)->fSegmentMask = src.fSegmentMask;
+
     // It's an oval only if it stays a rect.
     (*dst)->fIsOval = src.fIsOval && matrix.rectStaysRect();
 
@@ -118,6 +113,7 @@
     ) {
     SkPathRef* ref = SkNEW(SkPathRef);
     bool isOval;
+    uint8_t segmentMask;
 
     int32_t packed;
     if (!buffer->readS32(&packed)) {
@@ -130,9 +126,11 @@
 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO
     if (newFormat) {
 #endif
+        segmentMask = (packed >> kSegmentMask_SerializationShift) & 0xF;
         isOval  = (packed >> kIsOval_SerializationShift) & 1;
 #ifndef DELETE_THIS_CODE_WHEN_SKPS_ARE_REBUILT_AT_V16_AND_ALL_OTHER_INSTANCES_TOO
     } else {
+        segmentMask = (oldPacked >> SkPath::kOldSegmentMask_SerializationShift) & 0xF;
         isOval  = (oldPacked >> SkPath::kOldIsOval_SerializationShift) & 1;
     }
 #endif
@@ -159,6 +157,9 @@
         return NULL;
     }
     ref->fBoundsIsDirty = false;
+
+    // resetToSize clears fSegmentMask and fIsOval
+    ref->fSegmentMask = segmentMask;
     ref->fIsOval = isOval;
     return ref;
 }
@@ -172,6 +173,7 @@
         (*pathRef)->fFreeSpace = (*pathRef)->currSize();
         (*pathRef)->fGenerationID = 0;
         (*pathRef)->fConicWeights.rewind();
+        (*pathRef)->fSegmentMask = 0;
         (*pathRef)->fIsOval = false;
         SkDEBUGCODE((*pathRef)->validate();)
     } else {
@@ -185,6 +187,14 @@
 bool SkPathRef::operator== (const SkPathRef& ref) const {
     SkDEBUGCODE(this->validate();)
     SkDEBUGCODE(ref.validate();)
+
+    // We explicitly check fSegmentMask as a quick-reject. We could skip it,
+    // since it is only a cache of info in the fVerbs, but its a fast way to
+    // notice a difference
+    if (fSegmentMask != ref.fSegmentMask) {
+        return false;
+    }
+
     bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID;
 #ifdef SK_RELEASE
     if (genIDMatch) {
@@ -222,7 +232,7 @@
     return true;
 }
 
-void SkPathRef::writeToBuffer(SkWBuffer* buffer) {
+void SkPathRef::writeToBuffer(SkWBuffer* buffer) const {
     SkDEBUGCODE(this->validate();)
     SkDEBUGCODE(size_t beforePos = buffer->pos();)
 
@@ -231,7 +241,8 @@
     const SkRect& bounds = this->getBounds();
 
     int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift) |
-                     ((fIsOval & 1) << kIsOval_SerializationShift);
+                     ((fIsOval & 1) << kIsOval_SerializationShift) |
+                     (fSegmentMask << kSegmentMask_SerializationShift);
     buffer->write32(packed);
 
     // TODO: write gen ID here. Problem: We don't know if we're cross process or not from
@@ -248,7 +259,7 @@
     SkASSERT(buffer->pos() - beforePos == (size_t) this->writeSize());
 }
 
-uint32_t SkPathRef::writeSize() {
+uint32_t SkPathRef::writeSize() const {
     return uint32_t(5 * sizeof(uint32_t) +
                     fVerbCnt * sizeof(uint8_t) +
                     fPointCnt * sizeof(SkPoint) +
@@ -273,11 +284,91 @@
         fBounds = ref.fBounds;
         fIsFinite = ref.fIsFinite;
     }
+    fSegmentMask = ref.fSegmentMask;
     fIsOval = ref.fIsOval;
     SkDEBUGCODE(this->validate();)
 }
 
-SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb) {
+SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb, 
+                                        int numVbs, 
+                                        SkScalar** weights) {
+    // This value is just made-up for now. When count is 4, calling memset was much
+    // slower than just writing the loop. This seems odd, and hopefully in the
+    // future this will appear to have been a fluke...
+    static const unsigned int kMIN_COUNT_FOR_MEMSET_TO_BE_FAST = 16;
+
+    SkDEBUGCODE(this->validate();)
+    int pCnt;
+    bool dirtyAfterEdit = true;
+    switch (verb) {
+        case SkPath::kMove_Verb:
+            pCnt = numVbs;
+            dirtyAfterEdit = false;
+            break;
+        case SkPath::kLine_Verb:
+            fSegmentMask |= SkPath::kLine_SegmentMask;
+            pCnt = numVbs;
+            break;
+        case SkPath::kQuad_Verb:
+            fSegmentMask |= SkPath::kQuad_SegmentMask;
+            pCnt = 2 * numVbs;
+            break;
+        case SkPath::kConic_Verb:
+            fSegmentMask |= SkPath::kConic_SegmentMask;
+            pCnt = 2 * numVbs;
+            break;
+        case SkPath::kCubic_Verb:
+            fSegmentMask |= SkPath::kCubic_SegmentMask;
+            pCnt = 3 * numVbs;
+            break;
+        case SkPath::kClose_Verb:
+            SkDEBUGFAIL("growForRepeatedVerb called for kClose_Verb");
+            pCnt = 0;
+            dirtyAfterEdit = false;
+            break;
+        case SkPath::kDone_Verb:
+            SkDEBUGFAIL("growForRepeatedVerb called for kDone");
+            // fall through
+        default:
+            SkDEBUGFAIL("default should not be reached");
+            pCnt = 0;
+            dirtyAfterEdit = false;
+    }
+
+    size_t space = numVbs * sizeof(uint8_t) + pCnt * sizeof (SkPoint);
+    this->makeSpace(space);
+
+    SkPoint* ret = fPoints + fPointCnt;
+    uint8_t* vb = fVerbs - fVerbCnt;
+
+    // cast to unsigned, so if kMIN_COUNT_FOR_MEMSET_TO_BE_FAST is defined to
+    // be 0, the compiler will remove the test/branch entirely.
+    if ((unsigned)numVbs >= kMIN_COUNT_FOR_MEMSET_TO_BE_FAST) {
+        memset(vb - numVbs, verb, numVbs);
+    } else {
+        for (int i = 0; i < numVbs; ++i) {
+            vb[~i] = verb;
+        }
+    }
+
+    fVerbCnt += numVbs;
+    fPointCnt += pCnt;
+    fFreeSpace -= space;
+    fBoundsIsDirty = true;  // this also invalidates fIsFinite
+    if (dirtyAfterEdit) {
+        fIsOval = false;
+    }
+
+    if (SkPath::kConic_Verb == verb) {
+        SkASSERT(NULL != weights);
+        *weights = fConicWeights.append(numVbs);
+    }
+
+    SkDEBUGCODE(this->validate();)
+    return ret;
+}
+
+SkPoint* SkPathRef::growForVerb(int /* SkPath::Verb*/ verb, SkScalar weight) {
     SkDEBUGCODE(this->validate();)
     int pCnt;
     bool dirtyAfterEdit = true;
@@ -287,14 +378,19 @@
             dirtyAfterEdit = false;
             break;
         case SkPath::kLine_Verb:
+            fSegmentMask |= SkPath::kLine_SegmentMask;
             pCnt = 1;
             break;
         case SkPath::kQuad_Verb:
-            // fall through
+            fSegmentMask |= SkPath::kQuad_SegmentMask;
+            pCnt = 2;
+            break;
         case SkPath::kConic_Verb:
+            fSegmentMask |= SkPath::kConic_SegmentMask;
             pCnt = 2;
             break;
         case SkPath::kCubic_Verb:
+            fSegmentMask |= SkPath::kCubic_SegmentMask;
             pCnt = 3;
             break;
         case SkPath::kClose_Verb:
@@ -320,6 +416,11 @@
     if (dirtyAfterEdit) {
         fIsOval = false;
     }
+
+    if (SkPath::kConic_Verb == verb) {
+        *fConicWeights.append() = weight;
+    }
+
     SkDEBUGCODE(this->validate();)
     return ret;
 }
@@ -369,5 +470,36 @@
         }
         SkASSERT(SkToBool(fIsFinite) == isFinite);
     }
+
+#ifdef SK_DEBUG_PATH
+    uint32_t mask = 0;
+    for (int i = 0; i < fVerbCnt; ++i) {
+        switch (fVerbs[~i]) {
+            case SkPath::kMove_Verb:
+                break;
+            case SkPath::kLine_Verb:
+                mask |= SkPath::kLine_SegmentMask;
+                break;
+            case SkPath::kQuad_Verb:
+                mask |= SkPath::kQuad_SegmentMask;
+                break;
+            case SkPath::kConic_Verb:
+                mask |= SkPath::kConic_SegmentMask;
+                break;
+            case SkPath::kCubic_Verb:
+                mask |= SkPath::kCubic_SegmentMask;
+                break;
+            case SkPath::kClose_Verb:
+                break;
+            case SkPath::kDone_Verb:
+                SkDEBUGFAIL("Done verb shouldn't be recorded.");
+                break;
+            default:
+                SkDEBUGFAIL("Unknown Verb");
+                break;
+        }
+    }
+    SkASSERT(mask == fSegmentMask);
+#endif // SK_DEBUG_PATH
 }
 #endif
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index bea3ea5..991b4fd 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -3138,6 +3138,78 @@
     }
 }
 
+static void test_pathref(skiatest::Reporter* reporter) {
+    static const int kRepeatCnt = 10;
+
+    SkPathRef* pathRef = SkPathRef::CreateEmpty();
+    SkAutoTUnref<SkPathRef> pathRef2(SkPathRef::CreateEmpty());
+    SkMatrix mat;
+
+    mat.setTranslate(10, 10);
+
+    SkPathRef::CreateTransformedCopy(&pathRef2, *pathRef, mat);
+
+    SkPathRef::Editor ed(&pathRef2);
+
+    {
+        ed.growForRepeatedVerb(SkPath::kMove_Verb, kRepeatCnt);
+        REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countVerbs());
+        REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countPoints());
+        REPORTER_ASSERT(reporter, 0 == pathRef2->getSegmentMasks());
+        for (int i = 0; i < kRepeatCnt; ++i) {
+            REPORTER_ASSERT(reporter, SkPath::kMove_Verb == pathRef2->atVerb(i));
+        }
+        ed.resetToSize(0, 0, 0);
+    }
+
+    {
+        ed.growForRepeatedVerb(SkPath::kLine_Verb, kRepeatCnt);
+        REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countVerbs());
+        REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countPoints());
+        REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == pathRef2->getSegmentMasks());
+        for (int i = 0; i < kRepeatCnt; ++i) {
+            REPORTER_ASSERT(reporter, SkPath::kLine_Verb == pathRef2->atVerb(i));
+        }
+        ed.resetToSize(0, 0, 0);
+    }
+
+    {
+        ed.growForRepeatedVerb(SkPath::kQuad_Verb, kRepeatCnt);
+        REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countVerbs());
+        REPORTER_ASSERT(reporter, 2*kRepeatCnt == pathRef2->countPoints());
+        REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == pathRef2->getSegmentMasks());
+        for (int i = 0; i < kRepeatCnt; ++i) {
+            REPORTER_ASSERT(reporter, SkPath::kQuad_Verb == pathRef2->atVerb(i));
+        }
+        ed.resetToSize(0, 0, 0);
+    }
+
+    {
+        SkScalar* weights = NULL;
+        ed.growForRepeatedVerb(SkPath::kConic_Verb, kRepeatCnt, &weights);
+        REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countVerbs());
+        REPORTER_ASSERT(reporter, 2*kRepeatCnt == pathRef2->countPoints());
+        REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countWeights());
+        REPORTER_ASSERT(reporter, SkPath::kConic_SegmentMask == pathRef2->getSegmentMasks());
+        REPORTER_ASSERT(reporter, NULL != weights);
+        for (int i = 0; i < kRepeatCnt; ++i) {
+            REPORTER_ASSERT(reporter, SkPath::kConic_Verb == pathRef2->atVerb(i));
+        }
+        ed.resetToSize(0, 0, 0);
+    }
+
+    {
+        ed.growForRepeatedVerb(SkPath::kCubic_Verb, kRepeatCnt);
+        REPORTER_ASSERT(reporter, kRepeatCnt == pathRef2->countVerbs());
+        REPORTER_ASSERT(reporter, 3*kRepeatCnt == pathRef2->countPoints());
+        REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == pathRef2->getSegmentMasks());
+        for (int i = 0; i < kRepeatCnt; ++i) {
+            REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == pathRef2->atVerb(i));
+        }
+        ed.resetToSize(0, 0, 0);
+    }
+}
+
 static void test_operatorEqual(skiatest::Reporter* reporter) {
     SkPath a;
     SkPath b;
@@ -3297,5 +3369,6 @@
     test_conicTo_special_case(reporter);
     test_get_point(reporter);
     test_contains(reporter);
+    test_pathref(reporter);
     PathTest_Private::TestPathTo(reporter);
 }