diff options
author | bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-09-07 15:35:06 +0000 |
---|---|---|
committer | bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2012-09-07 15:35:06 +0000 |
commit | 69aca79b5c1cb43f8c3b5c1bee2c0342c8ff6ed1 (patch) | |
tree | 883978f35d9f98ad0f8bccf928a4c9717b168b67 | |
parent | 9a4125283ad56cea3b986337cb669dde14bf0ed8 (diff) |
SkPathRef: one allocation for pts+verbs, path GenID, copy-on-write
Review URL: https://codereview.appspot.com/6488063/
git-svn-id: http://skia.googlecode.com/svn/trunk@5433 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | gyp/core.gypi | 1 | ||||
-rw-r--r-- | include/core/SkPath.h | 13 | ||||
-rw-r--r-- | include/core/SkRefCnt.h | 6 | ||||
-rw-r--r-- | src/core/SkPath.cpp | 367 | ||||
-rw-r--r-- | src/core/SkPathRef.h | 531 |
5 files changed, 749 insertions, 169 deletions
diff --git a/gyp/core.gypi b/gyp/core.gypi index 2c3f4d4428..075d91539f 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -109,6 +109,7 @@ '<(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', diff --git a/include/core/SkPath.h b/include/core/SkPath.h index cf9f19a93c..bf284f1b90 100644 --- a/include/core/SkPath.h +++ b/include/core/SkPath.h @@ -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; diff --git a/include/core/SkRefCnt.h b/include/core/SkRefCnt.h index f527ebe0af..3a0f8be383 100644 --- a/include/core/SkRefCnt.h +++ b/include/core/SkRefCnt.h @@ -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 diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp index c6e349bd95..00d3772b57 100644 --- a/src/core/SkPath.cpp +++ b/src/core/SkPath.cpp @@ -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 index 0000000000..103677558a --- /dev/null +++ b/src/core/SkPathRef.h @@ -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 |