| 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;
|
| };
|
|
|
|
|