Index: include/core/SkPathRef.h |
=================================================================== |
--- include/core/SkPathRef.h (revision 11578) |
+++ include/core/SkPathRef.h (working copy) |
@@ -23,7 +23,7 @@ |
* 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 |
+ * copy-on-write if the SkPathRef is shared by multiple 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. |
* |
@@ -38,6 +38,16 @@ |
public: |
SK_DECLARE_INST_COUNT(SkPathRef); |
+ enum Verb { |
+ kMove_Verb, //!< iter.next returns 1 point |
+ kLine_Verb, //!< iter.next returns 2 points |
+ kQuad_Verb, //!< iter.next returns 3 points |
+ kConic_Verb, //!< iter.next returns 3 points + iter.conicWeight() |
+ kCubic_Verb, //!< iter.next returns 4 points |
+ kClose_Verb, //!< iter.next returns 1 point (contour's moveTo pt) |
+ kDone_Verb, //!< iter.next returns 0 points |
+ }; |
+ |
class Editor { |
public: |
Editor(SkAutoTUnref<SkPathRef>* pathRef, |
@@ -63,29 +73,24 @@ |
* Adds the verb and allocates space for the number of points indicated by the verb. The |
* return value is a pointer to where the points for the verb should be written. |
*/ |
- SkPoint* growForVerb(int /*SkPath::Verb*/ verb) { |
+ SkPoint* growForVerb(Verb verb) { |
SkDEBUGCODE(fPathRef->validate();) |
return fPathRef->growForVerb(verb); |
} |
- SkPoint* growForConic(SkScalar w); |
- |
+ SkPoint* growForConic(SkScalar w) { |
+ SkDEBUGCODE(fPathRef->validate();) |
+ SkPoint* pts = fPathRef->growForVerb(kConic_Verb); |
+ *fPathRef->fConicWeights.append() = w; |
+ return pts; |
+ } |
/** |
- * 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 additional lines and returns a pointer to the new |
+ * points. The return pointer 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); |
+ SkPoint* growForLines(int numLines) { |
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();) |
+ return fPathRef->growForLines(numLines); |
} |
/** |
@@ -95,6 +100,7 @@ |
void resetToSize(int newVerbCnt, int newPointCnt, int newConicCount) { |
fPathRef->resetToSize(newVerbCnt, newPointCnt, newConicCount); |
} |
+ |
/** |
* Gets the path ref that is wrapped in the Editor. |
*/ |
@@ -117,7 +123,89 @@ |
return SkRef(gEmptyPathRef); |
} |
+ enum Direction { |
+ /** Direction either has not been or could not be computed */ |
+ kUnknown_Direction, |
+ /** clockwise direction for adding closed contours */ |
+ kCW_Direction, |
+ /** counter-clockwise direction for adding closed contours */ |
+ kCCW_Direction, |
+ }; |
+ |
/** |
+ * Return the opposite of the specified direction. kUnknown is its own |
+ * opposite. |
+ */ |
+ static Direction OppositeDirection(Direction dir) { |
+ static const Direction gOppositeDir[] = { |
+ kUnknown_Direction, |
+ kCCW_Direction, |
+ kCW_Direction |
+ }; |
+ return gOppositeDir[dir]; |
+ } |
+ |
+ Direction getDirection() const { return (Direction) fDirection; } |
+ |
+ enum Convexity { |
+ kUnknown_Convexity, |
+ kConvex_Convexity, |
+ kConcave_Convexity |
+ }; |
+ |
+ /** |
+ * Return the path's convexity, as stored in the path. If it is currently unknown, |
+ * then this function will attempt to compute the convexity (and cache the result). |
+ */ |
+ Convexity getConvexity() const { |
+ if (kUnknown_Convexity != fConvexity) { |
+ return static_cast<Convexity>(fConvexity); |
+ } else { |
+ return this->internalGetConvexity(); |
+ } |
+ } |
+ |
+ /** |
+ * Return the currently cached value for convexity, even if that is set to |
+ * kUnknown_Convexity. Note: getConvexity() will automatically call |
+ * ComputeConvexity and cache its return value if the current setting is |
+ * kUnknown. |
+ */ |
+ Convexity getConvexityOrUnknown() const { |
+ return (Convexity) fConvexity; |
+ } |
+ |
+ /** |
+ * Store a convexity setting in the path. There is no automatic check to |
+ * see if this value actually agrees with the return value that would be |
+ * computed by getConvexity(). |
+ * |
+ * Note: even if this is set to a "known" value, if the path is later |
+ * changed (e.g. lineTo(), addRect(), etc.) then the cached value will be |
+ * reset to kUnknown_Convexity. |
+ */ |
+ void setConvexity(Convexity convexity) { |
+ fConvexity = convexity; |
+ } |
+ |
+ /** Returns true if the path is an oval. |
+ * |
+ * @param rect returns the bounding rect of this oval. It's a circle |
+ * if the height and width are the same. |
+ * |
+ * @return true if this path is an oval. |
+ * Tracking whether a path is an oval is considered an |
+ * optimization for performance and so some paths that are in |
+ * fact ovals can report false. |
+ */ |
+ bool isOval(SkRect* rect) const { |
+ if (fIsOval && NULL != rect) { |
+ *rect = this->getBounds(); |
+ } |
+ return SkToBool(fIsOval); |
+ } |
+ |
+ /** |
* Returns true if all of the points in this path are finite, meaning there |
* are no infinities and no NaNs. |
*/ |
@@ -128,6 +216,20 @@ |
return SkToBool(fIsFinite); |
} |
+ enum SegmentMask { |
+ kLine_SegmentMask = 1 << 0, |
+ kQuad_SegmentMask = 1 << 1, |
+ kConic_SegmentMask = 1 << 2, |
+ kCubic_SegmentMask = 1 << 3, |
+ }; |
+ |
+ /** |
+ * 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; } |
+ |
bool hasComputedBounds() const { |
return !fBoundsIsDirty; |
} |
@@ -236,9 +338,17 @@ |
private: |
enum SerializationOffsets { |
+ kDirection_SerializationShift = 26, // requires 2 bits |
kIsFinite_SerializationShift = 25, // requires 1 bit |
+ kIsOval_SerializationShift = 24, // requires 1 bit |
+ kConvexity_SerializationShift = 16, // requires 8 bits |
+ // FillType (in SkPath) takes up 8 |
+ kSegmentMask_SerializationShift = 0 // requires 4 bits |
}; |
+ // flag to require a moveTo if we begin with something else, like lineTo etc. |
+ static const int kINITIAL_LASTMOVETOINDEX_VALUE = ~0; |
+ |
SkPathRef() { |
fBoundsIsDirty = true; // this also invalidates fIsFinite |
fPointCnt = 0; |
@@ -248,19 +358,23 @@ |
fFreeSpace = 0; |
fGenerationID = kEmptyGenID; |
SkDEBUGCODE(fEditorsAttached = 0;) |
+ fLastMoveToIndex = kINITIAL_LASTMOVETOINDEX_VALUE; |
+ fSegmentMask = 0; |
+ fConvexity = kUnknown_Convexity; |
+ fDirection = kUnknown_Direction; |
+ fIsOval = false; |
SkDEBUGCODE(this->validate();) |
} |
void copy(const SkPathRef& ref, int additionalReserveVerbs, int additionalReservePoints); |
// Return true if the computed bounds are finite. |
- static bool ComputePtBounds(SkRect* bounds, const SkPathRef& ref) { |
- int count = ref.countPoints(); |
+ static bool ComputePtBounds(SkRect* bounds, const SkPoint* points, int count) { |
if (count <= 1) { // we ignore just 1 point (moveto) |
bounds->setEmpty(); |
- return count ? ref.points()->isFinite() : true; |
+ return count ? points->isFinite() : true; |
} else { |
- return bounds->setBoundsCheck(ref.points(), count); |
+ return bounds->setBoundsCheck(points, count); |
} |
} |
@@ -269,7 +383,7 @@ |
SkDEBUGCODE(this->validate();) |
SkASSERT(fBoundsIsDirty); |
- fIsFinite = ComputePtBounds(&fBounds, *this); |
+ fIsFinite = ComputePtBounds(&fBounds, this->points(), this->countPoints()); |
fBoundsIsDirty = false; |
} |
@@ -289,6 +403,12 @@ |
fBoundsIsDirty = true; // this also invalidates fIsFinite |
fGenerationID = 0; |
+ fLastMoveToIndex = kINITIAL_LASTMOVETOINDEX_VALUE; |
+ fSegmentMask = 0; |
+ fConvexity = kUnknown_Convexity; |
+ fDirection = kUnknown_Direction; |
+ fIsOval = false; |
+ |
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; |
@@ -315,19 +435,79 @@ |
SkDEBUGCODE(this->validate();) |
} |
+ // This method assumes space has already been allocated for the new |
+ // verb and point. |
+ void injectMove() { |
+ SkScalar x, y; |
+ if (this->countVerbs() == 0) { |
+ x = y = 0; |
+ } else { |
+ const SkPoint& pt = this->atPoint(~fLastMoveToIndex); |
+ x = pt.fX; |
+ y = pt.fY; |
+ } |
+ fPoints[fPointCnt].set(x, y); |
+ fLastMoveToIndex = fPointCnt; |
+ ++fPointCnt; |
+ fVerbs[~fVerbCnt] = kMove_Verb; |
+ ++fVerbCnt; |
+ } |
+ |
+ |
/** |
- * Increases the verb count by newVerbs and the point count be newPoints. New verbs and points |
- * are uninitialized. |
+ * Increases the verb and point count by numLines. The new points |
+ * are uninitialized. All the new verbs are set to kLine_SegmentMask. |
*/ |
- void grow(int newVerbs, int newPoints) { |
+ SkPoint* growForLines(int numLines) { |
+ // This value is just made-up for now. When numLines is 3, 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; |
+ |
+ // TODO: The following isn't hyper-optimized for the lines case since |
+ // it should be expanded to handle quads, conics and cubics |
SkDEBUGCODE(this->validate();) |
- size_t space = newVerbs * sizeof(uint8_t) + newPoints * sizeof (SkPoint); |
+ bool dirtyAfterEdit = true; |
+ bool moveInjectionNeeded = false; |
+ |
+ moveInjectionNeeded = fLastMoveToIndex < 0; |
+ |
+ size_t space = numLines * sizeof(uint8_t) + numLines * sizeof (SkPoint); |
+ if (moveInjectionNeeded) { |
+ space += sizeof(uint8_t) + sizeof(SkPoint); |
+ } |
this->makeSpace(space); |
- fVerbCnt += newVerbs; |
- fPointCnt += newPoints; |
+ if (moveInjectionNeeded) { |
+ SkASSERT(dirtyAfterEdit); |
+ this->injectMove(); |
+ } |
+ SkPoint* ret = fPoints + fPointCnt; |
+ uint8_t* vb = fVerbs - fVerbCnt; |
+ |
+ // 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)numLines >= kMIN_COUNT_FOR_MEMSET_TO_BE_FAST) { |
+ memset(vb - numLines, kLine_Verb, numLines); |
+ } else { |
+ for (int i = 0; i < numLines; ++i) { |
+ vb[~i] = kLine_Verb; |
+ } |
+ } |
+ fSegmentMask |= kLine_SegmentMask; |
+ |
+ fVerbCnt += numLines; |
+ fPointCnt += numLines; |
fFreeSpace -= space; |
fBoundsIsDirty = true; // this also invalidates fIsFinite |
+ |
+ if (dirtyAfterEdit) { |
+ fConvexity = kUnknown_Convexity; |
+ fDirection = kUnknown_Direction; |
+ fIsOval = false; |
+ } |
+ |
SkDEBUGCODE(this->validate();) |
+ return ret; |
} |
/** |
@@ -335,7 +515,7 @@ |
* of additional points. A pointer to the first point is returned. Any new points are |
* uninitialized. |
*/ |
- SkPoint* growForVerb(int /*SkPath::Verb*/ verb); |
+ SkPoint* growForVerb(Verb verb); |
/** |
* Ensures that the free space available in the path ref is >= size. The verb and point counts |
@@ -397,13 +577,33 @@ |
SkDEBUGCODE(void validate() const;) |
+ Convexity internalGetConvexity() const; |
+ |
+ void setIsOval(bool isOval) { fIsOval = isOval; } |
+ void setDirection(Direction direction) { fDirection = direction; } |
+ |
+ /** |
+ * Tries to quickly compute the direction of the first non-degenerate |
+ * contour. If it can be computed, return true and set dir to that |
+ * direction. If it cannot be (quickly) determined, return false and ignore |
+ * the dir parameter. If the direction was determined, it is cached to make |
+ * subsequent calls return quickly. |
+ */ |
+ bool cheapComputeDirection(Direction* dir) const; |
+ |
enum { |
kMinSize = 256, |
}; |
mutable SkRect fBounds; |
+ int fLastMoveToIndex; |
+ |
+ uint8_t fSegmentMask; |
mutable uint8_t fBoundsIsDirty; |
+ mutable uint8_t fConvexity; |
+ mutable uint8_t fDirection; |
mutable SkBool8 fIsFinite; // only meaningful if bounds are valid |
+ mutable SkBool8 fIsOval; |
SkPoint* fPoints; // points to begining of the allocation |
uint8_t* fVerbs; // points just past the end of the allocation (verbs grow backwards) |
@@ -418,6 +618,8 @@ |
mutable int32_t fGenerationID; |
SkDEBUGCODE(int32_t fEditorsAttached;) // assert that only one editor in use at any time. |
+ friend class SkPath; |
+ |
typedef SkRefCnt INHERITED; |
}; |