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
This commit is contained in:
parent
9a4125283a
commit
69aca79b5c
@ -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',
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
531
src/core/SkPathRef.h
Normal file
531
src/core/SkPathRef.h
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user