Revert r5557 (which itself was a revert of r5433). Relands SkPathRef. Will follow...
authorbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 3 Oct 2012 13:46:20 +0000 (13:46 +0000)
committerbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 3 Oct 2012 13:46:20 +0000 (13:46 +0000)
git-svn-id: http://skia.googlecode.com/svn/trunk@5783 2bbb7eff-a529-9590-31e7-b0007b416f81

gyp/core.gypi
include/core/SkPath.h
include/core/SkRefCnt.h
src/core/SkPath.cpp
src/core/SkPathRef.h [new file with mode: 0644]

index 7a6a7dd35c9b1b56e6b74a779c787a15b16ccbec..d8f1715d93dec92ecdf10bcc6e4e68747ef01c6a 100644 (file)
         '<(skia_src_path)/core/SkPathHeap.cpp',
         '<(skia_src_path)/core/SkPathHeap.h',
         '<(skia_src_path)/core/SkPathMeasure.cpp',
+        '<(skia_src_path)/core/SkPathRef.h',
         '<(skia_src_path)/core/SkPicture.cpp',
         '<(skia_src_path)/core/SkPictureFlat.cpp',
         '<(skia_src_path)/core/SkPictureFlat.h',
index cf9f19a93c2f135c84b60d60cacf325fa87280b8..bf284f1b9052aad8f94e7830f7d621beaf3f0290 100644 (file)
@@ -13,6 +13,7 @@
 #include "SkInstCnt.h"
 #include "SkMatrix.h"
 #include "SkTDArray.h"
+#include "SkRefCnt.h"
 
 #ifdef SK_BUILD_FOR_ANDROID
 #define GEN_ID_INC              fGenerationID++
@@ -26,6 +27,7 @@ class SkReader32;
 class SkWriter32;
 class SkAutoPathBoundsUpdate;
 class SkString;
+class SkPathRef;
 
 /** \class SkPath
 
@@ -255,9 +257,7 @@ public:
 
     /** Return the number of points in the path
      */
-    int countPoints() const {
-        return this->getPoints(NULL, 0);
-    }
+    int countPoints() const;
 
     /** Return the point at the specified index. If the index is out of range
          (i.e. is not 0 <= index < countPoints()) then the returned coordinates
@@ -275,9 +275,7 @@ public:
 
     /** Return the number of verbs in the path
      */
-    int countVerbs() const {
-        return this->getVerbs(NULL, 0);
-    }
+    int countVerbs() const;
 
     /** Returns the number of verbs in the path. Up to max verbs are copied. The
         verbs are copied as one byte per verb.
@@ -832,8 +830,7 @@ private:
         kSegmentMask_SerializationShift = 0
     };
 
-    SkTDArray<SkPoint>  fPts;
-    SkTDArray<uint8_t>  fVerbs;
+    SkAutoTUnref<SkPathRef>   fPathRef;
     mutable SkRect      fBounds;
     int                 fLastMoveToIndex;
     uint8_t             fFillType;
index f527ebe0af5fca75b83970029c7a5c0abfa95f8e..3a0f8be3833364251dac5339826ecefe688a9fca 100644 (file)
@@ -159,6 +159,12 @@ public:
         fObj = obj;
     }
 
+    void swap(SkAutoTUnref* other) {
+        T* tmp = fObj;
+        fObj = other->fObj;
+        other->fObj = tmp;
+    }
+
     /**
      *  Return the hosted object (which may be null), transferring ownership.
      *  The reference count is not modified, and the internal ptr is set to NULL
index c6e349bd95100950a04878ef5569347d241be451..00d3772b57cdebe63227910c9b6183959b13b8a7 100644 (file)
@@ -10,6 +10,8 @@
 #include "SkPath.h"
 #include "SkBuffer.h"
 #include "SkMath.h"
+#include "SkPathRef.h"
+#include "SkThread.h"
 
 SK_DEFINE_INST_COUNT(SkPath);
 
@@ -109,13 +111,13 @@ private:
 };
 
 // Return true if the computed bounds are finite.
-static bool compute_pt_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
-    int count = pts.count();
+static bool compute_pt_bounds(SkRect* bounds, const SkPathRef& ref) {
+    int count = ref.countPoints();
     if (count <= 1) {  // we ignore just 1 point (moveto)
         bounds->setEmpty();
-        return count ? pts.begin()->isFinite() : true;
+        return count ? ref.points()->isFinite() : true;
     } else {
-        return bounds->setBoundsCheck(pts.begin(), pts.count());
+        return bounds->setBoundsCheck(ref.points(), count);
     }
 }
 
@@ -139,7 +141,8 @@ static bool compute_pt_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
 #define INITIAL_LASTMOVETOINDEX_VALUE   ~0
 
 SkPath::SkPath()
-    : fFillType(kWinding_FillType)
+    : fPathRef(SkPathRef::CreateEmpty())
+    , fFillType(kWinding_FillType)
     , fBoundsIsDirty(true) {
     fConvexity = kUnknown_Convexity;
     fSegmentMask = 0;
@@ -154,9 +157,17 @@ SkPath::SkPath()
 
 SkPath::SkPath(const SkPath& src) {
     SkDEBUGCODE(src.validate();)
-    *this = src;
+    src.fPathRef.get()->ref();
+    fPathRef.reset(src.fPathRef.get());
+    fBounds         = src.fBounds;
+    fFillType       = src.fFillType;
+    fBoundsIsDirty  = src.fBoundsIsDirty;
+    fConvexity      = src.fConvexity;
+    fIsFinite       = src.fIsFinite;
+    fSegmentMask    = src.fSegmentMask;
+    fLastMoveToIndex = src.fLastMoveToIndex;
+    fIsOval         = src.fIsOval;
 #ifdef SK_BUILD_FOR_ANDROID
-    // the assignment operator above increments the ID so correct for that here
     fGenerationID = src.fGenerationID;
     fSourcePath = NULL;
 #endif
@@ -170,9 +181,9 @@ SkPath& SkPath::operator=(const SkPath& src) {
     SkDEBUGCODE(src.validate();)
 
     if (this != &src) {
+        src.fPathRef.get()->ref();
+        fPathRef.reset(src.fPathRef.get());
         fBounds         = src.fBounds;
-        fPts            = src.fPts;
-        fVerbs          = src.fVerbs;
         fFillType       = src.fFillType;
         fBoundsIsDirty  = src.fBoundsIsDirty;
         fConvexity      = src.fConvexity;
@@ -196,7 +207,7 @@ SK_API bool operator==(const SkPath& a, const SkPath& b) {
 
     return &a == &b ||
         (a.fFillType == b.fFillType && a.fSegmentMask == b.fSegmentMask &&
-         a.fVerbs == b.fVerbs && a.fPts == b.fPts);
+         *a.fPathRef.get() == *b.fPathRef.get());
 }
 
 void SkPath::swap(SkPath& other) {
@@ -204,8 +215,7 @@ void SkPath::swap(SkPath& other) {
 
     if (this != &other) {
         SkTSwap<SkRect>(fBounds, other.fBounds);
-        fPts.swap(other.fPts);
-        fVerbs.swap(other.fVerbs);
+        fPathRef.swap(&other.fPathRef);
         SkTSwap<uint8_t>(fFillType, other.fFillType);
         SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty);
         SkTSwap<uint8_t>(fConvexity, other.fConvexity);
@@ -234,8 +244,7 @@ void SkPath::setSourcePath(const SkPath* path) {
 void SkPath::reset() {
     SkDEBUGCODE(this->validate();)
 
-    fPts.reset();
-    fVerbs.reset();
+    fPathRef.reset(SkPathRef::CreateEmpty());
     GEN_ID_INC;
     fBoundsIsDirty = true;
     fConvexity = kUnknown_Convexity;
@@ -247,8 +256,7 @@ void SkPath::reset() {
 void SkPath::rewind() {
     SkDEBUGCODE(this->validate();)
 
-    fPts.rewind();
-    fVerbs.rewind();
+    SkPathRef::Rewind(&fPathRef);
     GEN_ID_INC;
     fConvexity = kUnknown_Convexity;
     fBoundsIsDirty = true;
@@ -259,18 +267,18 @@ void SkPath::rewind() {
 
 bool SkPath::isEmpty() const {
     SkDEBUGCODE(this->validate();)
-    return 0 == fVerbs.count();
+    return 0 == fPathRef->countVerbs();
 }
 
 bool SkPath::isLine(SkPoint line[2]) const {
-    int verbCount = fVerbs.count();
-    int ptCount = fPts.count();
+    int verbCount = fPathRef->countVerbs();
+    int ptCount = fPathRef->countVerbs();
 
     if (2 == verbCount && 2 == ptCount) {
-        const uint8_t* verbs = fVerbs.begin();
-        if (kMove_Verb == verbs[0] && kLine_Verb == verbs[1]) {
+        if (kMove_Verb == fPathRef->atVerb(0) &&
+            kLine_Verb == fPathRef->atVerb(1)) {
             if (line) {
-                const SkPoint* pts = fPts.begin();
+                const SkPoint* pts = fPathRef->points();
                 line[0] = pts[0];
                 line[1] = pts[1];
             }
@@ -327,13 +335,13 @@ bool SkPath::isRect(SkRect* rect) const {
     int nextDirection = 0;
     bool closedOrMoved = false;
     bool autoClose = false;
-    const uint8_t* verbs = fVerbs.begin();
-    const uint8_t* verbStop = fVerbs.end();
-    const SkPoint* pts = fPts.begin();
-    while (verbs != verbStop) {
-        switch (*verbs++) {
+    const SkPoint* pts = fPathRef->points();
+    int verbCnt = fPathRef->countVerbs();
+    int currVerb = 0;
+    while (currVerb < verbCnt) {
+        switch (fPathRef->atVerb(currVerb++)) {
             case kClose_Verb:
-                pts = fPts.begin();
+                pts = fPathRef->points();
                 autoClose = true;
             case kLine_Verb: {
                 SkScalar left = last.fX;
@@ -398,39 +406,56 @@ bool SkPath::isRect(SkRect* rect) const {
     return result;
 }
 
+int SkPath::countPoints() const {
+    return fPathRef->countPoints();
+}
+
 int SkPath::getPoints(SkPoint dst[], int max) const {
     SkDEBUGCODE(this->validate();)
 
     SkASSERT(max >= 0);
     SkASSERT(!max || dst);
-    int count = fPts.count();
-    fPts.copyRange(dst, 0, max);
-    return count;
+    int count = SkMin32(max, fPathRef->countPoints());
+    memcpy(dst, fPathRef->points(), count * sizeof(SkPoint));
+    return fPathRef->countPoints();
 }
 
 SkPoint SkPath::getPoint(int index) const {
-    if ((unsigned)index < (unsigned)fPts.count()) {
-        return fPts[index];
+    if ((unsigned)index < (unsigned)fPathRef->countPoints()) {
+        return fPathRef->atPoint(index);
     }
     return SkPoint::Make(0, 0);
 }
 
+int SkPath::countVerbs() const {
+    return fPathRef->countVerbs();
+}
+
+static inline void copy_verbs_reverse(uint8_t* inorderDst,
+                                      const uint8_t* reversedSrc,
+                                      int count) {
+    for (int i = 0; i < count; ++i) {
+        inorderDst[i] = reversedSrc[~i];
+    }
+}
+
 int SkPath::getVerbs(uint8_t dst[], int max) const {
     SkDEBUGCODE(this->validate();)
 
     SkASSERT(max >= 0);
     SkASSERT(!max || dst);
-    fVerbs.copyRange(dst, 0, max);
-    return fVerbs.count();
+    int count = SkMin32(max, fPathRef->countVerbs());
+    copy_verbs_reverse(dst, fPathRef->verbs(), count);
+    return fPathRef->countVerbs();
 }
 
 bool SkPath::getLastPt(SkPoint* lastPt) const {
     SkDEBUGCODE(this->validate();)
 
-    int count = fPts.count();
+    int count = fPathRef->countPoints();
     if (count > 0) {
         if (lastPt) {
-            *lastPt = fPts[count - 1];
+            *lastPt = fPathRef->atPoint(count - 1);
         }
         return true;
     }
@@ -443,12 +468,13 @@ bool SkPath::getLastPt(SkPoint* lastPt) const {
 void SkPath::setLastPt(SkScalar x, SkScalar y) {
     SkDEBUGCODE(this->validate();)
 
-    int count = fPts.count();
+    int count = fPathRef->countPoints();
     if (count == 0) {
         this->moveTo(x, y);
     } else {
         fIsOval = false;
-        fPts[count - 1].set(x, y);
+        SkPathRef::Editor ed(&fPathRef);
+        ed.atPoint(count-1)->set(x, y);
         GEN_ID_INC;
     }
 }
@@ -457,7 +483,7 @@ void SkPath::computeBounds() const {
     SkDEBUGCODE(this->validate();)
     SkASSERT(fBoundsIsDirty);
 
-    fIsFinite = compute_pt_bounds(&fBounds, fPts);
+    fIsFinite = compute_pt_bounds(&fBounds, *fPathRef.get());
     fBoundsIsDirty = false;
 }
 
@@ -485,24 +511,19 @@ void SkPath::setConvexity(Convexity c) {
 
 void SkPath::incReserve(U16CPU inc) {
     SkDEBUGCODE(this->validate();)
-
-    fVerbs.setReserve(fVerbs.count() + inc);
-    fPts.setReserve(fPts.count() + inc);
-
+    SkPathRef::Editor(&fPathRef, inc, inc);
     SkDEBUGCODE(this->validate();)
 }
 
 void SkPath::moveTo(SkScalar x, SkScalar y) {
     SkDEBUGCODE(this->validate();)
 
-    SkPoint* pt;
+    SkPathRef::Editor ed(&fPathRef);
 
     // remember our index
-    fLastMoveToIndex = fPts.count();
+    fLastMoveToIndex = ed.pathRef()->countPoints();
 
-    pt = fPts.append();
-    *fVerbs.append() = kMove_Verb;
-    pt->set(x, y);
+    ed.growForVerb(kMove_Verb)->set(x, y);
 
     GEN_ID_INC;
     DIRTY_AFTER_EDIT_NO_CONVEXITY_CHANGE;
@@ -517,10 +538,10 @@ void SkPath::rMoveTo(SkScalar x, SkScalar y) {
 void SkPath::injectMoveToIfNeeded() {
     if (fLastMoveToIndex < 0) {
         SkScalar x, y;
-        if (fVerbs.count() == 0) {
+        if (fPathRef->countVerbs() == 0) {
             x = y = 0;
         } else {
-            const SkPoint& pt = fPts[~fLastMoveToIndex];
+            const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex);
             x = pt.fX;
             y = pt.fY;
         }
@@ -533,8 +554,8 @@ void SkPath::lineTo(SkScalar x, SkScalar y) {
 
     this->injectMoveToIfNeeded();
 
-    fPts.append()->set(x, y);
-    *fVerbs.append() = kLine_Verb;
+    SkPathRef::Editor ed(&fPathRef);
+    ed.growForVerb(kLine_Verb)->set(x, y);
     fSegmentMask |= kLine_SegmentMask;
 
     GEN_ID_INC;
@@ -552,10 +573,10 @@ void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
 
     this->injectMoveToIfNeeded();
 
-    SkPoint* pts = fPts.append(2);
+    SkPathRef::Editor ed(&fPathRef);
+    SkPoint* pts = ed.growForVerb(kQuad_Verb);
     pts[0].set(x1, y1);
     pts[1].set(x2, y2);
-    *fVerbs.append() = kQuad_Verb;
     fSegmentMask |= kQuad_SegmentMask;
 
     GEN_ID_INC;
@@ -574,11 +595,11 @@ void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
 
     this->injectMoveToIfNeeded();
 
-    SkPoint* pts = fPts.append(3);
+    SkPathRef::Editor ed(&fPathRef);
+    SkPoint* pts = ed.growForVerb(kCubic_Verb);
     pts[0].set(x1, y1);
     pts[1].set(x2, y2);
     pts[2].set(x3, y3);
-    *fVerbs.append() = kCubic_Verb;
     fSegmentMask |= kCubic_SegmentMask;
 
     GEN_ID_INC;
@@ -596,16 +617,18 @@ void SkPath::rCubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
 void SkPath::close() {
     SkDEBUGCODE(this->validate();)
 
-    int count = fVerbs.count();
+    int count = fPathRef->countVerbs();
     if (count > 0) {
-        switch (fVerbs[count - 1]) {
+        switch (fPathRef->atVerb(count - 1)) {
             case kLine_Verb:
             case kQuad_Verb:
             case kCubic_Verb:
-            case kMove_Verb:
-                *fVerbs.append() = kClose_Verb;
+            case kMove_Verb: {
+                SkPathRef::Editor ed(&fPathRef);
+                ed.growForVerb(kClose_Verb);
                 GEN_ID_INC;
                 break;
+            }
             default:
                 // don't add a close if it's the first verb or a repeat
                 break;
@@ -653,31 +676,34 @@ void SkPath::addPoly(const SkPoint pts[], int count, bool close) {
         return;
     }
 
-    fLastMoveToIndex = fPts.count();
-    fPts.append(count, pts);
-
+    SkPathRef::Editor ed(&fPathRef);
+    fLastMoveToIndex = ed.pathRef()->countPoints();
+    uint8_t* vb;
+    SkPoint* p;
     // +close makes room for the extra kClose_Verb
-    uint8_t* vb = fVerbs.append(count + close);
-    vb[0] = kMove_Verb;
+    ed.grow(count + close, count, &vb, &p);
 
+    memcpy(p, pts, count * sizeof(SkPoint));
+    vb[~0] = kMove_Verb;
     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[1], kLine_Verb, count - 1);
+            memset(vb - count, kLine_Verb, count - 1);
         } else {
             for (int i = 1; i < count; ++i) {
-                vb[i] = kLine_Verb;
+                vb[~i] = kLine_Verb;
             }
         }
         fSegmentMask |= kLine_SegmentMask;
     }
     if (close) {
-        vb[count] = kClose_Verb;
+        vb[~count] = kClose_Verb;
     }
 
     GEN_ID_INC;
     DIRTY_AFTER_EDIT;
+    SkDEBUGCODE(this->validate();)
 }
 
 #define CUBIC_ARC_FACTOR    ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3)
@@ -823,9 +849,9 @@ void SkPath::addRoundRect(const SkRect& rect, const SkScalar rad[],
 }
 
 bool SkPath::hasOnlyMoveTos() const {
-    const uint8_t* verbs = fVerbs.begin();
-    const uint8_t* verbStop = fVerbs.end();
-    while (verbs != verbStop) {
+    int count = fPathRef->countVerbs();
+    const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMemBegin();
+    for (int i = 0; i < count; ++i) {
         if (*verbs == kLine_Verb ||
             *verbs == kQuad_Verb ||
             *verbs == kCubic_Verb) {
@@ -991,7 +1017,7 @@ void SkPath::arcTo(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle,
     int count = build_arc_points(oval, startAngle, sweepAngle, pts);
     SkASSERT((count & 1) == 1);
 
-    if (fVerbs.count() == 0) {
+    if (fPathRef->countVerbs() == 0) {
         forceMoveTo = true;
     }
     this->incReserve(count);
@@ -1103,7 +1129,7 @@ void SkPath::addPath(const SkPath& path, SkScalar dx, SkScalar dy) {
 }
 
 void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) {
-    this->incReserve(path.fPts.count());
+    SkPathRef::Editor(&fPathRef, path.countVerbs(), path.countPoints());
 
     fIsOval = false;
 
@@ -1153,21 +1179,23 @@ static const uint8_t gPtsInVerb[] = {
 
 // ignore the initial moveto, and stop when the 1st contour ends
 void SkPath::pathTo(const SkPath& path) {
-    int i, vcount = path.fVerbs.count();
-    if (vcount == 0) {
+    int i, vcount = path.fPathRef->countVerbs();
+    // exit early if the path is empty, or just has a moveTo.
+    if (vcount < 2) {
         return;
     }
 
-    this->incReserve(vcount);
+    SkPathRef::Editor(&fPathRef, vcount, path.countPoints());
 
     fIsOval = false;
 
-    const uint8_t*  verbs = path.fVerbs.begin();
-    const SkPoint*  pts = path.fPts.begin() + 1;    // 1 for the initial moveTo
+    const uint8_t* verbs = path.fPathRef->verbs();
+    // skip the initial moveTo
+    const SkPoint*  pts = path.fPathRef->points() + 1;
 
-    SkASSERT(verbs[0] == kMove_Verb);
+    SkASSERT(verbs[~0] == kMove_Verb);
     for (i = 1; i < vcount; i++) {
-        switch (verbs[i]) {
+        switch (verbs[~i]) {
             case kLine_Verb:
                 this->lineTo(pts[0].fX, pts[0].fY);
                 break;
@@ -1175,33 +1203,33 @@ void SkPath::pathTo(const SkPath& path) {
                 this->quadTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
                 break;
             case kCubic_Verb:
-                this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY,
-                              pts[2].fX, pts[2].fY);
+                this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
                 break;
             case kClose_Verb:
                 return;
         }
-        pts += gPtsInVerb[verbs[i]];
+        pts += gPtsInVerb[verbs[~i]];
     }
 }
 
 // ignore the last point of the 1st contour
 void SkPath::reversePathTo(const SkPath& path) {
-    int i, vcount = path.fVerbs.count();
-    if (vcount == 0) {
+    int i, vcount = path.fPathRef->countVerbs();
+    // exit early if the path is empty, or just has a moveTo.
+    if (vcount < 2) {
         return;
     }
 
-    this->incReserve(vcount);
+    SkPathRef::Editor(&fPathRef, vcount, path.countPoints());
 
     fIsOval = false;
 
-    const uint8_t*  verbs = path.fVerbs.begin();
-    const SkPoint*  pts = path.fPts.begin();
+    const uint8_t*  verbs = path.fPathRef->verbs();
+    const SkPoint*  pts = path.fPathRef->points();
 
-    SkASSERT(verbs[0] == kMove_Verb);
-    for (i = 1; i < vcount; i++) {
-        int n = gPtsInVerb[verbs[i]];
+    SkASSERT(verbs[~0] == kMove_Verb);
+    for (i = 1; i < vcount; ++i) {
+        int n = gPtsInVerb[verbs[~i]];
         if (n == 0) {
             break;
         }
@@ -1209,7 +1237,7 @@ void SkPath::reversePathTo(const SkPath& path) {
     }
 
     while (--i > 0) {
-        switch (verbs[i]) {
+        switch (verbs[~i]) {
             case kLine_Verb:
                 this->lineTo(pts[-1].fX, pts[-1].fY);
                 break;
@@ -1224,23 +1252,24 @@ void SkPath::reversePathTo(const SkPath& path) {
                 SkDEBUGFAIL("bad verb");
                 break;
         }
-        pts -= gPtsInVerb[verbs[i]];
+        pts -= gPtsInVerb[verbs[~i]];
     }
 }
 
 void SkPath::reverseAddPath(const SkPath& src) {
-    this->incReserve(src.fPts.count());
+    SkPathRef::Editor ed(&fPathRef, src.fPathRef->countPoints(), src.fPathRef->countVerbs());
 
-    const SkPoint* pts = src.fPts.end();
-    const uint8_t* startVerbs = src.fVerbs.begin();
-    const uint8_t* verbs = src.fVerbs.end();
+    const SkPoint* pts = src.fPathRef->pointsEnd();
+    // we will iterator through src's verbs backwards
+    const uint8_t* verbs = src.fPathRef->verbsMemBegin(); // points at the last verb
+    const uint8_t* verbsEnd = src.fPathRef->verbs(); // points just past the first verb
 
     fIsOval = false;
 
     bool needMove = true;
     bool needClose = false;
-    while (verbs > startVerbs) {
-        uint8_t v = *--verbs;
+    while (verbs < verbsEnd) {
+        uint8_t v = *(verbs++);
         int n = gPtsInVerb[v];
 
         if (needMove) {
@@ -1351,7 +1380,8 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
         }
 
         dst->swap(tmp);
-        matrix.mapPoints(dst->fPts.begin(), dst->fPts.count());
+        SkPathRef::Editor ed(&dst->fPathRef);
+        matrix.mapPoints(ed.points(), ed.pathRef()->countPoints());
     } else {
         /*
          *  If we're not in perspective, we can transform all of the points at
@@ -1366,7 +1396,7 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
          *  if it is non-finite. In those cases bounds need to stay empty,
          *  regardless of the matrix.
          */
-        if (!fBoundsIsDirty && matrix.rectStaysRect() && fPts.count() > 1) {
+        if (!fBoundsIsDirty && matrix.rectStaysRect() && fPathRef->countPoints() > 1) {
             dst->fBoundsIsDirty = false;
             if (fIsFinite) {
                 matrix.mapRect(&dst->fBounds, fBounds);
@@ -1382,21 +1412,16 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const {
             dst->fBoundsIsDirty = true;
         }
 
+        SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix);
+
         if (this != dst) {
-            dst->fVerbs = fVerbs;
-            dst->fPts.setCount(fPts.count());
             dst->fFillType = fFillType;
             dst->fSegmentMask = fSegmentMask;
             dst->fConvexity = fConvexity;
-            dst->fIsOval = fIsOval;
         }
 
-        matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count());
-
-        if (fIsOval) {
-            // It's an oval only if it stays a rect.
-            dst->fIsOval = matrix.rectStaysRect();
-        }
+        // It's an oval only if it stays a rect.
+        dst->fIsOval = fIsOval && matrix.rectStaysRect();
 
         SkDEBUGCODE(dst->validate();)
     }
@@ -1432,9 +1457,9 @@ SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
 }
 
 void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
-    fPts = path.fPts.begin();
-    fVerbs = path.fVerbs.begin();
-    fVerbStop = path.fVerbs.end();
+    fPts = path.fPathRef->points();
+    fVerbs = path.fPathRef->verbs();
+    fVerbStop = path.fPathRef->verbsMemBegin();
     fLastPt.fX = fLastPt.fY = 0;
     fMoveTo.fX = fMoveTo.fY = 0;
     fForceClose = SkToU8(forceClose);
@@ -1453,12 +1478,13 @@ bool SkPath::Iter::isClosedContour() const {
     const uint8_t* verbs = fVerbs;
     const uint8_t* stop = fVerbStop;
 
-    if (kMove_Verb == *verbs) {
-        verbs += 1; // skip the initial moveto
+    if (kMove_Verb == *(verbs - 1)) {
+        verbs -= 1; // skip the initial moveto
     }
 
-    while (verbs < stop) {
-        unsigned v = *verbs++;
+    while (verbs > stop) {
+        // verbs points one beyond the current verb, decrement first.
+        unsigned v = *(--verbs);
         if (kMove_Verb == v) {
             break;
         }
@@ -1510,14 +1536,14 @@ void SkPath::Iter::consumeDegenerateSegments() {
     const SkPoint* lastMovePt = 0;
     SkPoint lastPt = fLastPt;
     while (fVerbs != fVerbStop) {
-        unsigned verb = *fVerbs;
+        unsigned verb = *(fVerbs - 1); // fVerbs is one beyond the current verb
         switch (verb) {
             case kMove_Verb:
                 // Keep a record of this most recent move
                 lastMoveVerb = fVerbs;
                 lastMovePt = fPts;
                 lastPt = fPts[0];
-                fVerbs++;
+                fVerbs--;
                 fPts++;
                 break;
 
@@ -1528,7 +1554,7 @@ void SkPath::Iter::consumeDegenerateSegments() {
                     return;
                 }
                 // A close at any other time must be ignored
-                fVerbs++;
+                fVerbs--;
                 break;
 
             case kLine_Verb:
@@ -1541,7 +1567,7 @@ void SkPath::Iter::consumeDegenerateSegments() {
                     return;
                 }
                 // Ignore this line and continue
-                fVerbs++;
+                fVerbs--;
                 fPts++;
                 break;
 
@@ -1555,7 +1581,7 @@ void SkPath::Iter::consumeDegenerateSegments() {
                     return;
                 }
                 // Ignore this line and continue
-                fVerbs++;
+                fVerbs--;
                 fPts += 2;
                 break;
 
@@ -1569,7 +1595,7 @@ void SkPath::Iter::consumeDegenerateSegments() {
                     return;
                 }
                 // Ignore this line and continue
-                fVerbs++;
+                fVerbs--;
                 fPts += 3;
                 break;
 
@@ -1594,14 +1620,15 @@ SkPath::Verb SkPath::Iter::doNext(SkPoint ptsParam[4]) {
         return kDone_Verb;
     }
 
-    unsigned        verb = *fVerbs++;
+    // fVerbs is one beyond the current verb, decrement first
+    unsigned verb = *(--fVerbs);
     const SkPoint* SK_RESTRICT srcPts = fPts;
     SkPoint* SK_RESTRICT       pts = ptsParam;
 
     switch (verb) {
         case kMove_Verb:
             if (fNeedClose) {
-                fVerbs -= 1;
+                fVerbs++; // move back one verb
                 verb = this->autoClose(pts);
                 if (verb == kClose_Verb) {
                     fNeedClose = false;
@@ -1640,7 +1667,7 @@ SkPath::Verb SkPath::Iter::doNext(SkPoint ptsParam[4]) {
         case kClose_Verb:
             verb = this->autoClose(pts);
             if (verb == kLine_Verb) {
-                fVerbs -= 1;
+                fVerbs++; // move back one verb
             } else {
                 fNeedClose = false;
                 fSegmentState = kEmptyContour_SegmentState;
@@ -1669,9 +1696,9 @@ SkPath::RawIter::RawIter(const SkPath& path) {
 }
 
 void SkPath::RawIter::setPath(const SkPath& path) {
-    fPts = path.fPts.begin();
-    fVerbs = path.fVerbs.begin();
-    fVerbStop = path.fVerbs.end();
+    fPts = path.fPathRef->points();
+    fVerbs = path.fPathRef->verbs();
+    fVerbStop = path.fPathRef->verbsMemBegin();
     fMoveTo.fX = fMoveTo.fY = 0;
     fLastPt.fX = fLastPt.fY = 0;
 }
@@ -1682,8 +1709,9 @@ SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
         return kDone_Verb;
     }
 
-    unsigned        verb = *fVerbs++;
-    const SkPoint*  srcPts = fPts;
+    // fVerbs points one beyond next verb so decrement first.
+    unsigned verb = *(--fVerbs);
+    const SkPoint* srcPts = fPts;
 
     switch (verb) {
         case kMove_Verb:
@@ -1729,16 +1757,23 @@ uint32_t SkPath::writeToMemory(void* storage) const {
     SkDEBUGCODE(this->validate();)
 
     if (NULL == storage) {
-        const int byteCount = 3 * sizeof(int32_t)
-                      + sizeof(SkPoint) * fPts.count()
-                      + sizeof(uint8_t) * fVerbs.count()
+        const int byteCount = sizeof(int32_t)
+#if NEW_PICTURE_FORMAT
+                      + fPathRef->writeSize()
+#else
+                      + 2 * sizeof(int32_t)
+                      + sizeof(SkPoint) * fPathRef->countPoints()
+                      + sizeof(uint8_t) * fPathRef->countVerbs()
+#endif
                       + sizeof(SkRect);
         return SkAlign4(byteCount);
     }
 
     SkWBuffer   buffer(storage);
-    buffer.write32(fPts.count());
-    buffer.write32(fVerbs.count());
+#if !NEW_PICTURE_FORMAT
+    buffer.write32(fPathRef->countPoints());
+    buffer.write32(fPathRef->countVerbs());
+#endif
 
     // Call getBounds() to ensure (as a side-effect) that fBounds
     // and fIsFinite are computed.
@@ -1753,8 +1788,7 @@ uint32_t SkPath::writeToMemory(void* storage) const {
 
     buffer.write32(packed);
 
-    buffer.write(fPts.begin(), sizeof(SkPoint) * fPts.count());
-    buffer.write(fVerbs.begin(), fVerbs.count());
+    fPathRef->writeToBuffer(&buffer);
 
     buffer.write(&bounds, sizeof(bounds));
 
@@ -1764,8 +1798,10 @@ uint32_t SkPath::writeToMemory(void* storage) const {
 
 uint32_t SkPath::readFromMemory(const void* storage) {
     SkRBuffer   buffer(storage);
-    fPts.setCount(buffer.readS32());
-    fVerbs.setCount(buffer.readS32());
+#if !NEW_PICTURE_FORMAT
+    int32_t pcount = buffer.readS32();
+    int32_t vcount = buffer.readS32();
+#endif
 
     uint32_t packed = buffer.readS32();
     fIsFinite = (packed >> kIsFinite_SerializationShift) & 1;
@@ -1774,8 +1810,11 @@ uint32_t SkPath::readFromMemory(const void* storage) {
     fFillType = (packed >> kFillType_SerializationShift) & 0xFF;
     fSegmentMask = (packed >> kSegmentMask_SerializationShift) & 0xFF;
 
-    buffer.read(fPts.begin(), sizeof(SkPoint) * fPts.count());
-    buffer.read(fVerbs.begin(), fVerbs.count());
+#if NEW_PICTURE_FORMAT
+    fPathRef.reset(SkPathRef::CreateFromBuffer(&buffer));
+#else
+    fPathRef.reset(SkPathRef::CreateFromBuffer(vcount, pcount, &buffer));
+#endif
 
     buffer.read(&fBounds, sizeof(fBounds));
     fBoundsIsDirty = false;
@@ -1839,16 +1878,14 @@ void SkPath::dump() const {
 void SkPath::validate() const {
     SkASSERT(this != NULL);
     SkASSERT((fFillType & ~3) == 0);
-    fPts.validate();
-    fVerbs.validate();
 
     if (!fBoundsIsDirty) {
         SkRect bounds;
 
-        bool isFinite = compute_pt_bounds(&bounds, fPts);
+        bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get());
         SkASSERT(SkToBool(fIsFinite) == isFinite);
 
-        if (fPts.count() <= 1) {
+        if (fPathRef->countPoints() <= 1) {
             // if we're empty, fBounds may be empty but translated, so we can't
             // necessarily compare to bounds directly
             // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will
@@ -1867,8 +1904,9 @@ void SkPath::validate() const {
     }
 
     uint32_t mask = 0;
-    for (int i = 0; i < fVerbs.count(); i++) {
-        switch (fVerbs[i]) {
+    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;
@@ -1877,6 +1915,15 @@ void SkPath::validate() const {
                 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);
@@ -2015,7 +2062,7 @@ SkPath::Convexity SkPath::ComputeConvexity(const SkPath& path) {
 
 class ContourIter {
 public:
-    ContourIter(const SkTDArray<uint8_t>& verbs, const SkTDArray<SkPoint>& pts);
+    ContourIter(const SkPathRef& pathRef);
 
     bool done() const { return fDone; }
     // if !done() then these may be called
@@ -2032,20 +2079,18 @@ private:
     SkDEBUGCODE(int fContourCounter;)
 };
 
-ContourIter::ContourIter(const SkTDArray<uint8_t>& verbs,
-                         const SkTDArray<SkPoint>& pts) {
-    fStopVerbs = verbs.begin() + verbs.count();
-
+ContourIter::ContourIter(const SkPathRef& pathRef) {
+    fStopVerbs = pathRef.verbsMemBegin();
     fDone = false;
-    fCurrPt = pts.begin();
-    fCurrVerb = verbs.begin();
+    fCurrPt = pathRef.points();
+    fCurrVerb = pathRef.verbs();
     fCurrPtCount = 0;
     SkDEBUGCODE(fContourCounter = 0;)
     this->next();
 }
 
 void ContourIter::next() {
-    if (fCurrVerb >= fStopVerbs) {
+    if (fCurrVerb <= fStopVerbs) {
         fDone = true;
     }
     if (fDone) {
@@ -2055,12 +2100,12 @@ void ContourIter::next() {
     // skip pts of prev contour
     fCurrPt += fCurrPtCount;
 
-    SkASSERT(SkPath::kMove_Verb == fCurrVerb[0]);
+    SkASSERT(SkPath::kMove_Verb == fCurrVerb[~0]);
     int ptCount = 1;    // moveTo
     const uint8_t* verbs = fCurrVerb;
 
-    for (++verbs; verbs < fStopVerbs; ++verbs) {
-        switch (*verbs) {
+    for (--verbs; verbs > fStopVerbs; --verbs) {
+        switch (verbs[~0]) {
             case SkPath::kMove_Verb:
                 goto CONTOUR_END;
             case SkPath::kLine_Verb:
@@ -2237,7 +2282,7 @@ bool SkPath::cheapComputeDirection(Direction* dir) const {
     // is unknown, so we don't call isConvex()
     const Convexity conv = this->getConvexityOrUnknown();
 
-    ContourIter iter(fVerbs, fPts);
+    ContourIter iter(*fPathRef.get());
 
     // initialize with our logical y-min
     SkScalar ymax = this->getBounds().fTop;
diff --git a/src/core/SkPathRef.h b/src/core/SkPathRef.h
new file mode 100644 (file)
index 0000000..3391663
--- /dev/null
@@ -0,0 +1,531 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPathRef_DEFINED
+#define SkPathRef_DEFINED
+
+#include "SkRefCnt.h"
+#include <stddef.h> // ptrdiff_t
+
+// When we're ready to break the picture format. Changes:
+// * Write genID.
+// * SkPathRef read/write counts (which will change the field order)
+// * SkPathRef reads/writes verbs backwards.
+#define NEW_PICTURE_FORMAT 0
+
+/**
+ * Holds the path verbs and points. It is versioned by a generation ID. None of its public methods
+ * modify the contents. To modify or append to the verbs/points wrap the SkPathRef in an
+ * SkPathRef::Editor object. Installing the editor resets the generation ID. It also performs
+ * copy-on-write if the SkPathRef is shared by multipls SkPaths. The caller passes the Editor's
+ * constructor a SkAutoTUnref, which may be updated to point to a new SkPathRef after the editor's
+ * constructor returns.
+ *
+ * The points and verbs are stored in a single allocation. The points are at the begining of the
+ * allocation while the verbs are stored at end of the allocation, in reverse order. Thus the points
+ * and verbs both grow into the middle of the allocation until the meet. To access verb i in the
+ * verb array use ref.verbs()[~i] (because verbs() returns a pointer just beyond the first
+ * logical verb or the last verb in memory).
+ */
+class SkPathRef : public ::SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkPathRef);
+
+    class Editor {
+    public:
+        Editor(SkAutoTUnref<SkPathRef>* pathRef,
+               int incReserveVerbs = 0,
+               int incReservePoints = 0) {
+            if (pathRef->get()->getRefCnt() > 1) {
+                SkPathRef* copy = SkNEW(SkPathRef);
+                copy->copy(*pathRef->get(), incReserveVerbs, incReservePoints);
+                pathRef->reset(copy);
+            } else {
+                (*pathRef)->incReserve(incReserveVerbs, incReservePoints);
+            }
+            fPathRef = pathRef->get();
+            fPathRef->fGenerationID = 0;
+            SkDEBUGCODE(sk_atomic_inc(&fPathRef->fEditorsAttached);)
+        }
+
+        ~Editor() { SkDEBUGCODE(sk_atomic_dec(&fPathRef->fEditorsAttached);) }
+
+        /**
+         * Returns the array of points.
+         */
+        SkPoint* points() { return fPathRef->fPoints; }
+
+        /**
+         * Gets the ith point. Shortcut for this->points() + i
+         */
+        SkPoint* atPoint(int i) {
+            SkASSERT((unsigned) i < (unsigned) fPathRef->fPointCnt);
+            return this->points() + i;
+        };
+
+        /**
+         * 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.
+         */
+        SkPoint* growForVerb(SkPath::Verb verb) {
+            fPathRef->validate();
+            return fPathRef->growForVerb(verb);
+        }
+
+        /**
+         * 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>]).
+         */
+        void grow(int newVerbs, int newPts, uint8_t** verbs, SkPoint** pts) {
+            SkASSERT(NULL != verbs);
+            SkASSERT(NULL != pts);
+            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;
+            fPathRef->validate();
+        }
+
+        /**
+         * Resets the path ref to a new verb and point count. The new verbs and points are
+         * uninitialized.
+         */
+        void resetToSize(int newVerbCnt, int newPointCnt) {
+            fPathRef->resetToSize(newVerbCnt, newPointCnt);
+        }
+        /**
+         * Gets the path ref that is wrapped in the Editor.
+         */
+        SkPathRef* pathRef() { return fPathRef; }
+
+    private:
+        SkPathRef* fPathRef;
+    };
+
+public:
+
+    /**
+     * Gets a path ref with no verbs or points.
+     */
+    static SkPathRef* CreateEmpty() {
+        static SkAutoTUnref<SkPathRef> gEmptyPathRef(SkNEW(SkPathRef));
+        gEmptyPathRef.get()->ref();
+        return gEmptyPathRef.get();
+    }
+
+    /**
+     * Transforms a path ref by a matrix, allocating a new one only if necessary.
+     */
+    static void CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst,
+                                      const SkPathRef& src,
+                                      const SkMatrix& matrix) {
+        src.validate();
+        if (matrix.isIdentity()) {
+            if (dst->get() != &src) {
+                dst->reset(const_cast<SkPathRef*>(&src));
+                (*dst)->validate();
+                src.ref();
+            }
+            return;
+        }
+        int32_t rcnt = dst->get()->getRefCnt();
+        if (&src == dst->get() && 1 == rcnt) {
+            matrix.mapPoints((*dst)->fPoints, (*dst)->fPointCnt);
+            return;
+        } else if (rcnt > 1) {
+            dst->reset(SkNEW(SkPathRef));
+        }
+        (*dst)->resetToSize(src.fVerbCnt, src.fPointCnt);
+        memcpy((*dst)->verbsMemWritable(), src.verbsMemBegin(), src.fVerbCnt * sizeof(uint8_t));
+        matrix.mapPoints((*dst)->fPoints, src.points(), src.fPointCnt);
+        (*dst)->validate();
+    }
+
+#if NEW_PICTURE_FORMAT
+    static SkPathRef* CreateFromBuffer(SkRBuffer* buffer) {
+        SkPathRef* ref = SkNEW(SkPathRef);
+        ref->fGenerationID = buffer->readU32();
+        int32_t verbCount = buffer->readS32();
+        int32_t pointCount = buffer->readS32();
+        ref->resetToSize(verbCount, pointCount);
+
+        SkASSERT(verbCount == ref->countVerbs());
+        SkASSERT(pointCount == ref->countPoints());
+        buffer->read(ref->verbsMemWritable(), verbCount * sizeof(uint8_t));
+        buffer->read(ref->fPoints, pointCount * sizeof(SkPoint));
+        return ref;
+    }
+#else
+    static SkPathRef* CreateFromBuffer(int verbCount, int pointCount, SkRBuffer* buffer) {
+        SkPathRef* ref = SkNEW(SkPathRef);
+
+        ref->resetToSize(verbCount, pointCount);
+        SkASSERT(verbCount == ref->countVerbs());
+        SkASSERT(pointCount == ref->countPoints());
+        buffer->read(ref->fPoints, pointCount * sizeof(SkPoint));
+        for (int i = 0; i < verbCount; ++i) {
+            ref->fVerbs[~i] = buffer->readU8();
+        }
+        return ref;
+    }
+#endif
+
+    /**
+     * Rollsback a path ref to zero verbs and points with the assumption that the path ref will be
+     * repopulated with approximately the same number of verbs and points. A new path ref is created
+     * only if necessary.
+     */
+    static void Rewind(SkAutoTUnref<SkPathRef>* pathRef) {
+        if (1 == (*pathRef)->getRefCnt()) {
+            (*pathRef)->validate();
+            (*pathRef)->fVerbCnt = 0;
+            (*pathRef)->fPointCnt = 0;
+            (*pathRef)->fFreeSpace = (*pathRef)->currSize();
+            (*pathRef)->fGenerationID = 0;
+            (*pathRef)->validate();
+        } else {
+            int oldVCnt = (*pathRef)->countVerbs();
+            int oldPCnt = (*pathRef)->countPoints();
+            pathRef->reset(SkNEW(SkPathRef));
+            (*pathRef)->resetToSize(0, 0, oldVCnt, oldPCnt);
+        }
+    }
+
+    virtual ~SkPathRef() {
+        this->validate();
+        sk_free(fPoints);
+    }
+
+    int countPoints() const { this->validate(); return fPointCnt; }
+    int countVerbs() const { this->validate(); return fVerbCnt; }
+
+    /**
+     * Returns a pointer one beyond the first logical verb (last verb in memory order).
+     */
+    const uint8_t* verbs() const { this->validate(); return fVerbs; }
+
+    /**
+     * Returns a const pointer to the first verb in memory (which is the last logical verb).
+     */
+    const uint8_t* verbsMemBegin() const { return this->verbs() - fVerbCnt; }
+
+    /**
+     * Returns a const pointer to the first point.
+     */
+    const SkPoint* points() const { this->validate(); return fPoints; }
+
+    /**
+     * Shortcut for this->points() + this->countPoints()
+     */
+    const SkPoint* pointsEnd() const { return this->points() + this->countPoints(); }
+
+    /**
+     * Convenience methods for getting to a verb or point by index.
+     */
+    uint8_t atVerb(int index) {
+        SkASSERT((unsigned) index < (unsigned) fVerbCnt);
+        return this->verbs()[~index];
+    }
+    const SkPoint& atPoint(int index) const {
+        SkASSERT((unsigned) index < (unsigned) fPointCnt);
+        return this->points()[index];
+    }
+
+    bool operator== (const SkPathRef& ref) const {
+        this->validate();
+        ref.validate();
+        bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID;
+#ifdef SK_RELEASE
+        if (genIDMatch) {
+            return true;
+        }
+#endif
+        if (fPointCnt != ref.fPointCnt ||
+            fVerbCnt != ref.fVerbCnt) {
+            SkASSERT(!genIDMatch);
+            return false;
+        }
+        if (0 != memcmp(this->verbsMemBegin(),
+                        ref.verbsMemBegin(),
+                        ref.fVerbCnt * sizeof(uint8_t))) {
+            SkASSERT(!genIDMatch);
+            return false;
+        }
+        if (0 != memcmp(this->points(),
+                        ref.points(),
+                        ref.fPointCnt * sizeof(SkPoint))) {
+            SkASSERT(!genIDMatch);
+            return false;
+        }
+        // We've done the work to determine that these are equal. If either has a zero genID, copy
+        // the other's. If both are 0 then genID() will compute the next ID.
+        if (0 == fGenerationID) {
+            fGenerationID = ref.genID();
+        } else if (0 == ref.fGenerationID) {
+            ref.fGenerationID = this->genID();
+        }
+        return true;
+    }
+
+    /**
+     * Writes the path points and verbs to a buffer.
+     */
+#if NEW_PICTURE_FORMAT
+    void writeToBuffer(SkWBuffer* buffer) {
+        this->validate();
+        SkDEBUGCODE(size_t beforePos = buffer->pos();)
+
+        // TODO: write gen ID here. Problem: We don't know if we're cross process or not from
+        // SkWBuffer. Until this is fixed we write 0.
+        buffer->write32(0);
+        buffer->write32(this->fVerbCnt);
+        buffer->write32(this->fPointCnt);
+        buffer->write(this->verbsMemBegin(), fVerbCnt * sizeof(uint8_t));
+        buffer->write(fPoints, fPointCnt * sizeof(SkPoint));
+
+        SkASSERT(buffer->pos() - beforePos == (size_t) this->writeSize());
+    }
+
+    /**
+     * Gets the number of bytes that would be written in writeBuffer()
+     */
+    uint32_t writeSize() {
+        return 3 * sizeof(uint32_t) + fVerbCnt * sizeof(uint8_t) + fPointCnt * sizeof(SkPoint);
+    }
+#else
+    void writeToBuffer(SkWBuffer* buffer) {
+        this->validate();
+        buffer->write(fPoints, fPointCnt * sizeof(SkPoint));
+        for (int i = 0; i < fVerbCnt; ++i) {
+            buffer->write8(fVerbs[~i]);
+        }
+    }
+#endif
+
+private:
+    SkPathRef() {
+        fPointCnt = 0;
+        fVerbCnt = 0;
+        fVerbs = NULL;
+        fPoints = NULL;
+        fFreeSpace = 0;
+        fGenerationID = kEmptyGenID;
+        SkDEBUGCODE(fEditorsAttached = 0;)
+        this->validate();
+    }
+
+    void copy(const SkPathRef& ref, int additionalReserveVerbs, int additionalReservePoints) {
+        this->validate();
+        this->resetToSize(ref.fVerbCnt, ref.fPointCnt,
+                          additionalReserveVerbs, additionalReservePoints);
+        memcpy(this->verbsMemWritable(), ref.verbsMemBegin(), ref.fVerbCnt * sizeof(uint8_t));
+        memcpy(this->fPoints, ref.fPoints, ref.fPointCnt * sizeof(SkPoint));
+        // We could call genID() here to force a real ID (instead of 0). However, if we're making
+        // a copy then presumably we intend to make a modification immediately afterwards.
+        fGenerationID = ref.fGenerationID;
+        this->validate();
+    }
+
+    /** Makes additional room but does not change the counts or change the genID */
+    void incReserve(int additionalVerbs, int additionalPoints) {
+        this->validate();
+        size_t space = additionalVerbs * sizeof(uint8_t) + additionalPoints * sizeof (SkPoint);
+        this->makeSpace(space);
+        this->validate();
+    }
+
+    /** Resets the path ref with verbCount verbs and pointCount points, all unitialized. Also
+     *  allocates space for reserveVerb additional verbs and reservePoints additional points.*/
+    void resetToSize(int verbCount, int pointCount, int reserveVerbs = 0, int reservePoints = 0) {
+        this->validate();
+        fGenerationID = 0;
+
+        size_t newSize = sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount;
+        size_t newReserve = sizeof(uint8_t) * reserveVerbs + sizeof(SkPoint) * reservePoints;
+        size_t minSize = newSize + newReserve;
+
+        ptrdiff_t sizeDelta = this->currSize() - minSize;
+
+        if (sizeDelta < 0 || static_cast<size_t>(sizeDelta) >= 3 * minSize) {
+            sk_free(fPoints);
+            fPoints = NULL;
+            fVerbs = NULL;
+            fFreeSpace = 0;
+            fVerbCnt = 0;
+            fPointCnt = 0;
+            this->makeSpace(minSize);
+            fVerbCnt = verbCount;
+            fPointCnt = pointCount;
+            fFreeSpace -= newSize;
+        } else {
+            fPointCnt = pointCount;
+            fVerbCnt = verbCount;
+            fFreeSpace = this->currSize() - minSize;
+        }
+        this->validate();
+    }
+
+    /**
+     * Increases the verb count by newVerbs and the point count be newPoints. New verbs and points
+     * are uninitialized.
+     */
+    void grow(int newVerbs, int newPoints) {
+        this->validate();
+        size_t space = newVerbs * sizeof(uint8_t) + newPoints * sizeof (SkPoint);
+        this->makeSpace(space);
+        fVerbCnt += newVerbs;
+        fPointCnt += newPoints;
+        fFreeSpace -= space;
+        this->validate();
+    }
+
+    /**
+     * 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(SkPath::Verb verb) {
+        this->validate();
+        int pCnt;
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                pCnt = 1;
+                break;
+            case SkPath::kLine_Verb:
+                pCnt = 1;
+                break;
+            case SkPath::kQuad_Verb:
+                pCnt = 2;
+                break;
+            case SkPath::kCubic_Verb:
+                pCnt = 3;
+                break;
+            default:
+                pCnt = 0;
+        }
+        size_t space = sizeof(uint8_t) + pCnt * sizeof (SkPoint);
+        this->makeSpace(space);
+        this->fVerbs[~fVerbCnt] = verb;
+        SkPoint* ret = fPoints + fPointCnt;
+        fVerbCnt += 1;
+        fPointCnt += pCnt;
+        fFreeSpace -= space;
+        this->validate();
+        return ret;
+    }
+
+    /**
+     * Ensures that the free space available in the path ref is >= size. The verb and point counts
+     * are not changed.
+     */
+    void makeSpace(size_t size) {
+        this->validate();
+        ptrdiff_t growSize = size - fFreeSpace;
+        if (growSize <= 0) {
+            return;
+        }
+        size_t oldSize = this->currSize();
+        // round to next multiple of 8 bytes
+        growSize = (growSize + 7) & ~static_cast<size_t>(7);
+        // we always at least double the allocation
+        if (static_cast<size_t>(growSize) < oldSize) {
+            growSize = oldSize;
+        }
+        if (growSize < kMinSize) {
+            growSize = kMinSize;
+        }
+        size_t newSize = oldSize + growSize;
+        // Note that realloc could memcpy more than we need. It seems to be a win anyway. TODO:
+        // encapsulate this.
+        fPoints = reinterpret_cast<SkPoint*>(sk_realloc_throw(fPoints, newSize));
+        size_t oldVerbSize = fVerbCnt * sizeof(uint8_t);
+        void* newVerbsDst = reinterpret_cast<void*>(
+                                reinterpret_cast<intptr_t>(fPoints) + newSize - oldVerbSize);
+        void* oldVerbsSrc = reinterpret_cast<void*>(
+                                reinterpret_cast<intptr_t>(fPoints) + oldSize - oldVerbSize);
+        memmove(newVerbsDst, oldVerbsSrc, oldVerbSize);
+        fVerbs = reinterpret_cast<uint8_t*>(reinterpret_cast<intptr_t>(fPoints) + newSize);
+        fFreeSpace += growSize;
+        this->validate();
+    }
+
+    /**
+     * Private, non-const-ptr version of the public function verbsMemBegin().
+     */
+    uint8_t* verbsMemWritable() {
+        this->validate();
+        return fVerbs - fVerbCnt;
+    }
+
+    /**
+     * Gets the total amount of space allocated for verbs, points, and reserve.
+     */
+    size_t currSize() const {
+        return reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints);
+    }
+
+    /**
+     * Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the
+     * same ID then they have the same verbs and points. However, two path refs may have the same
+     * contents but different genIDs. Zero is reserved and means an ID has not yet been determined
+     * for the path ref.
+     */
+    int32_t genID() const {
+        SkDEBUGCODE(SkASSERT(!fEditorsAttached));
+        if (!fGenerationID) {
+            if (0 == fPointCnt && 0 == fVerbCnt) {
+                fGenerationID = kEmptyGenID;
+            } else {
+                static int32_t  gPathRefGenerationID;
+                // do a loop in case our global wraps around, as we never want to return a 0 or the
+                // empty ID
+                do {
+                    fGenerationID = sk_atomic_inc(&gPathRefGenerationID) + 1;
+                } while (fGenerationID <= kEmptyGenID);
+            }
+        }
+        return fGenerationID;
+    }
+
+    void validate() const {
+        SkASSERT(static_cast<ptrdiff_t>(fFreeSpace) >= 0);
+        SkASSERT(reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints) >= 0);
+        SkASSERT((NULL == fPoints) == (NULL == fVerbs));
+        SkASSERT(!(NULL == fPoints && 0 != fFreeSpace));
+        SkASSERT(!(NULL == fPoints && 0 != fFreeSpace));
+        SkASSERT(!(NULL == fPoints && fPointCnt));
+        SkASSERT(!(NULL == fVerbs && fVerbCnt));
+        SkASSERT(this->currSize() ==
+                 fFreeSpace + sizeof(SkPoint) * fPointCnt + sizeof(uint8_t) * fVerbCnt);
+    }
+
+    enum {
+        kMinSize = 256,
+    };
+
+    SkPoint*            fPoints; // points to begining of the allocation
+    uint8_t*            fVerbs; // points just past the end of the allocation (verbs grow backwards)
+    int                 fVerbCnt;
+    int                 fPointCnt;
+    size_t              fFreeSpace; // redundant but saves computation
+    enum {
+        kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs.
+    };
+    mutable int32_t     fGenerationID;
+    SkDEBUGCODE(int32_t fEditorsAttached;) // assert that only one editor in use at any time.
+
+    typedef SkRefCnt INHERITED;
+};
+
+SK_DEFINE_INST_COUNT(SkPathRef);
+
+#endif