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