Home | History | Annotate | Download | only in core
      1 
      2 /*
      3  * Copyright 2012 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 #ifndef SkPathRef_DEFINED
     10 #define SkPathRef_DEFINED
     11 
     12 #include "../private/SkTDArray.h"
     13 #include "SkMatrix.h"
     14 #include "SkPoint.h"
     15 #include "SkRRect.h"
     16 #include "SkRect.h"
     17 #include "SkRefCnt.h"
     18 #include <stddef.h> // ptrdiff_t
     19 
     20 class SkRBuffer;
     21 class SkWBuffer;
     22 
     23 /**
     24  * Holds the path verbs and points. It is versioned by a generation ID. None of its public methods
     25  * modify the contents. To modify or append to the verbs/points wrap the SkPathRef in an
     26  * SkPathRef::Editor object. Installing the editor resets the generation ID. It also performs
     27  * copy-on-write if the SkPathRef is shared by multiple SkPaths. The caller passes the Editor's
     28  * constructor a SkAutoTUnref, which may be updated to point to a new SkPathRef after the editor's
     29  * constructor returns.
     30  *
     31  * The points and verbs are stored in a single allocation. The points are at the begining of the
     32  * allocation while the verbs are stored at end of the allocation, in reverse order. Thus the points
     33  * and verbs both grow into the middle of the allocation until the meet. To access verb i in the
     34  * verb array use ref.verbs()[~i] (because verbs() returns a pointer just beyond the first
     35  * logical verb or the last verb in memory).
     36  */
     37 
     38 class SK_API SkPathRef : public ::SkRefCnt {
     39 public:
     40     class Editor {
     41     public:
     42         Editor(SkAutoTUnref<SkPathRef>* pathRef,
     43                int incReserveVerbs = 0,
     44                int incReservePoints = 0);
     45 
     46         ~Editor() { SkDEBUGCODE(sk_atomic_dec(&fPathRef->fEditorsAttached);) }
     47 
     48         /**
     49          * Returns the array of points.
     50          */
     51         SkPoint* points() { return fPathRef->getPoints(); }
     52         const SkPoint* points() const { return fPathRef->points(); }
     53 
     54         /**
     55          * Gets the ith point. Shortcut for this->points() + i
     56          */
     57         SkPoint* atPoint(int i) {
     58             SkASSERT((unsigned) i < (unsigned) fPathRef->fPointCnt);
     59             return this->points() + i;
     60         };
     61         const SkPoint* atPoint(int i) const {
     62             SkASSERT((unsigned) i < (unsigned) fPathRef->fPointCnt);
     63             return this->points() + i;
     64         };
     65 
     66         /**
     67          * Adds the verb and allocates space for the number of points indicated by the verb. The
     68          * return value is a pointer to where the points for the verb should be written.
     69          * 'weight' is only used if 'verb' is kConic_Verb
     70          */
     71         SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight = 0) {
     72             SkDEBUGCODE(fPathRef->validate();)
     73             return fPathRef->growForVerb(verb, weight);
     74         }
     75 
     76         /**
     77          * Allocates space for multiple instances of a particular verb and the
     78          * requisite points & weights.
     79          * The return pointer points at the first new point (indexed normally [<i>]).
     80          * If 'verb' is kConic_Verb, 'weights' will return a pointer to the
     81          * space for the conic weights (indexed normally).
     82          */
     83         SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb,
     84                                      int numVbs,
     85                                      SkScalar** weights = NULL) {
     86             return fPathRef->growForRepeatedVerb(verb, numVbs, weights);
     87         }
     88 
     89         /**
     90          * Resets the path ref to a new verb and point count. The new verbs and points are
     91          * uninitialized.
     92          */
     93         void resetToSize(int newVerbCnt, int newPointCnt, int newConicCount) {
     94             fPathRef->resetToSize(newVerbCnt, newPointCnt, newConicCount);
     95         }
     96 
     97         /**
     98          * Gets the path ref that is wrapped in the Editor.
     99          */
    100         SkPathRef* pathRef() { return fPathRef; }
    101 
    102         void setIsOval(bool isOval) { fPathRef->setIsOval(isOval); }
    103 
    104         void setIsRRect(bool isRRect) { fPathRef->setIsRRect(isRRect); }
    105 
    106         void setBounds(const SkRect& rect) { fPathRef->setBounds(rect); }
    107 
    108     private:
    109         SkPathRef* fPathRef;
    110     };
    111 
    112     class SK_API Iter {
    113     public:
    114         Iter();
    115         Iter(const SkPathRef&);
    116 
    117         void setPathRef(const SkPathRef&);
    118 
    119         /** Return the next verb in this iteration of the path. When all
    120             segments have been visited, return kDone_Verb.
    121 
    122             @param  pts The points representing the current verb and/or segment
    123                         This must not be NULL.
    124             @return The verb for the current segment
    125         */
    126         uint8_t next(SkPoint pts[4]);
    127         uint8_t peek() const;
    128 
    129         SkScalar conicWeight() const { return *fConicWeights; }
    130 
    131     private:
    132         const SkPoint*  fPts;
    133         const uint8_t*  fVerbs;
    134         const uint8_t*  fVerbStop;
    135         const SkScalar* fConicWeights;
    136     };
    137 
    138 public:
    139     /**
    140      * Gets a path ref with no verbs or points.
    141      */
    142     static SkPathRef* CreateEmpty();
    143 
    144     /**
    145      *  Returns true if all of the points in this path are finite, meaning there
    146      *  are no infinities and no NaNs.
    147      */
    148     bool isFinite() const {
    149         if (fBoundsIsDirty) {
    150             this->computeBounds();
    151         }
    152         return SkToBool(fIsFinite);
    153     }
    154 
    155     /**
    156      *  Returns a mask, where each bit corresponding to a SegmentMask is
    157      *  set if the path contains 1 or more segments of that type.
    158      *  Returns 0 for an empty path (no segments).
    159      */
    160     uint32_t getSegmentMasks() const { return fSegmentMask; }
    161 
    162     /** Returns true if the path is an oval.
    163      *
    164      * @param rect      returns the bounding rect of this oval. It's a circle
    165      *                  if the height and width are the same.
    166      *
    167      * @return true if this path is an oval.
    168      *              Tracking whether a path is an oval is considered an
    169      *              optimization for performance and so some paths that are in
    170      *              fact ovals can report false.
    171      */
    172     bool isOval(SkRect* rect) const {
    173         if (fIsOval && rect) {
    174             *rect = this->getBounds();
    175         }
    176 
    177         return SkToBool(fIsOval);
    178     }
    179 
    180     bool isRRect(SkRRect* rrect) const {
    181         if (fIsRRect && rrect) {
    182             *rrect = this->getRRect();
    183         }
    184         return SkToBool(fIsRRect);
    185     }
    186 
    187 
    188     bool hasComputedBounds() const {
    189         return !fBoundsIsDirty;
    190     }
    191 
    192     /** Returns the bounds of the path's points. If the path contains 0 or 1
    193         points, the bounds is set to (0,0,0,0), and isEmpty() will return true.
    194         Note: this bounds may be larger than the actual shape, since curves
    195         do not extend as far as their control points.
    196     */
    197     const SkRect& getBounds() const {
    198         if (fBoundsIsDirty) {
    199             this->computeBounds();
    200         }
    201         return fBounds;
    202     }
    203 
    204     SkRRect getRRect() const;
    205 
    206     /**
    207      * Transforms a path ref by a matrix, allocating a new one only if necessary.
    208      */
    209     static void CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst,
    210                                       const SkPathRef& src,
    211                                       const SkMatrix& matrix);
    212 
    213     static SkPathRef* CreateFromBuffer(SkRBuffer* buffer);
    214 
    215     /**
    216      * Rollsback a path ref to zero verbs and points with the assumption that the path ref will be
    217      * repopulated with approximately the same number of verbs and points. A new path ref is created
    218      * only if necessary.
    219      */
    220     static void Rewind(SkAutoTUnref<SkPathRef>* pathRef);
    221 
    222     virtual ~SkPathRef();
    223     int countPoints() const { SkDEBUGCODE(this->validate();) return fPointCnt; }
    224     int countVerbs() const { SkDEBUGCODE(this->validate();) return fVerbCnt; }
    225     int countWeights() const { SkDEBUGCODE(this->validate();) return fConicWeights.count(); }
    226 
    227     /**
    228      * Returns a pointer one beyond the first logical verb (last verb in memory order).
    229      */
    230     const uint8_t* verbs() const { SkDEBUGCODE(this->validate();) return fVerbs; }
    231 
    232     /**
    233      * Returns a const pointer to the first verb in memory (which is the last logical verb).
    234      */
    235     const uint8_t* verbsMemBegin() const { return this->verbs() - fVerbCnt; }
    236 
    237     /**
    238      * Returns a const pointer to the first point.
    239      */
    240     const SkPoint* points() const { SkDEBUGCODE(this->validate();) return fPoints; }
    241 
    242     /**
    243      * Shortcut for this->points() + this->countPoints()
    244      */
    245     const SkPoint* pointsEnd() const { return this->points() + this->countPoints(); }
    246 
    247     const SkScalar* conicWeights() const { SkDEBUGCODE(this->validate();) return fConicWeights.begin(); }
    248     const SkScalar* conicWeightsEnd() const { SkDEBUGCODE(this->validate();) return fConicWeights.end(); }
    249 
    250     /**
    251      * Convenience methods for getting to a verb or point by index.
    252      */
    253     uint8_t atVerb(int index) const {
    254         SkASSERT((unsigned) index < (unsigned) fVerbCnt);
    255         return this->verbs()[~index];
    256     }
    257     const SkPoint& atPoint(int index) const {
    258         SkASSERT((unsigned) index < (unsigned) fPointCnt);
    259         return this->points()[index];
    260     }
    261 
    262     bool operator== (const SkPathRef& ref) const;
    263 
    264     /**
    265      * Writes the path points and verbs to a buffer.
    266      */
    267     void writeToBuffer(SkWBuffer* buffer) const;
    268 
    269     /**
    270      * Gets the number of bytes that would be written in writeBuffer()
    271      */
    272     uint32_t writeSize() const;
    273 
    274     void interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* out) const;
    275 
    276     /**
    277      * Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the
    278      * same ID then they have the same verbs and points. However, two path refs may have the same
    279      * contents but different genIDs.
    280      */
    281     uint32_t genID() const;
    282 
    283     struct GenIDChangeListener {
    284         virtual ~GenIDChangeListener() {}
    285         virtual void onChange() = 0;
    286     };
    287 
    288     void addGenIDChangeListener(GenIDChangeListener* listener);
    289 
    290     SkDEBUGCODE(void validate() const;)
    291 
    292 private:
    293     enum SerializationOffsets {
    294         kIsRRect_SerializationShift = 26,   // requires 1 bit
    295         kIsFinite_SerializationShift = 25,  // requires 1 bit
    296         kIsOval_SerializationShift = 24,    // requires 1 bit
    297         kSegmentMask_SerializationShift = 0 // requires 4 bits
    298     };
    299 
    300     SkPathRef() {
    301         fBoundsIsDirty = true;    // this also invalidates fIsFinite
    302         fPointCnt = 0;
    303         fVerbCnt = 0;
    304         fVerbs = NULL;
    305         fPoints = NULL;
    306         fFreeSpace = 0;
    307         fGenerationID = kEmptyGenID;
    308         fSegmentMask = 0;
    309         fIsOval = false;
    310         fIsRRect = false;
    311         SkDEBUGCODE(fEditorsAttached = 0;)
    312         SkDEBUGCODE(this->validate();)
    313     }
    314 
    315     void copy(const SkPathRef& ref, int additionalReserveVerbs, int additionalReservePoints);
    316 
    317     // Return true if the computed bounds are finite.
    318     static bool ComputePtBounds(SkRect* bounds, const SkPathRef& ref) {
    319         return bounds->setBoundsCheck(ref.points(), ref.countPoints());
    320     }
    321 
    322     // called, if dirty, by getBounds()
    323     void computeBounds() const {
    324         SkDEBUGCODE(this->validate();)
    325         // TODO(mtklein): remove fBoundsIsDirty and fIsFinite,
    326         // using an inverted rect instead of fBoundsIsDirty and always recalculating fIsFinite.
    327         SkASSERT(fBoundsIsDirty);
    328 
    329         fIsFinite = ComputePtBounds(&fBounds, *this);
    330         fBoundsIsDirty = false;
    331     }
    332 
    333     void setBounds(const SkRect& rect) {
    334         SkASSERT(rect.fLeft <= rect.fRight && rect.fTop <= rect.fBottom);
    335         fBounds = rect;
    336         fBoundsIsDirty = false;
    337         fIsFinite = fBounds.isFinite();
    338     }
    339 
    340     /** Makes additional room but does not change the counts or change the genID */
    341     void incReserve(int additionalVerbs, int additionalPoints) {
    342         SkDEBUGCODE(this->validate();)
    343         size_t space = additionalVerbs * sizeof(uint8_t) + additionalPoints * sizeof (SkPoint);
    344         this->makeSpace(space);
    345         SkDEBUGCODE(this->validate();)
    346     }
    347 
    348     /** Resets the path ref with verbCount verbs and pointCount points, all uninitialized. Also
    349      *  allocates space for reserveVerb additional verbs and reservePoints additional points.*/
    350     void resetToSize(int verbCount, int pointCount, int conicCount,
    351                      int reserveVerbs = 0, int reservePoints = 0) {
    352         SkDEBUGCODE(this->validate();)
    353         fBoundsIsDirty = true;      // this also invalidates fIsFinite
    354         fGenerationID = 0;
    355 
    356         fSegmentMask = 0;
    357         fIsOval = false;
    358         fIsRRect = false;
    359 
    360         size_t newSize = sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount;
    361         size_t newReserve = sizeof(uint8_t) * reserveVerbs + sizeof(SkPoint) * reservePoints;
    362         size_t minSize = newSize + newReserve;
    363 
    364         ptrdiff_t sizeDelta = this->currSize() - minSize;
    365 
    366         if (sizeDelta < 0 || static_cast<size_t>(sizeDelta) >= 3 * minSize) {
    367             sk_free(fPoints);
    368             fPoints = NULL;
    369             fVerbs = NULL;
    370             fFreeSpace = 0;
    371             fVerbCnt = 0;
    372             fPointCnt = 0;
    373             this->makeSpace(minSize);
    374             fVerbCnt = verbCount;
    375             fPointCnt = pointCount;
    376             fFreeSpace -= newSize;
    377         } else {
    378             fPointCnt = pointCount;
    379             fVerbCnt = verbCount;
    380             fFreeSpace = this->currSize() - minSize;
    381         }
    382         fConicWeights.setCount(conicCount);
    383         SkDEBUGCODE(this->validate();)
    384     }
    385 
    386     /**
    387      * Increases the verb count by numVbs and point count by the required amount.
    388      * The new points are uninitialized. All the new verbs are set to the specified
    389      * verb. If 'verb' is kConic_Verb, 'weights' will return a pointer to the
    390      * uninitialized conic weights.
    391      */
    392     SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, int numVbs, SkScalar** weights);
    393 
    394     /**
    395      * Increases the verb count 1, records the new verb, and creates room for the requisite number
    396      * of additional points. A pointer to the first point is returned. Any new points are
    397      * uninitialized.
    398      */
    399     SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight);
    400 
    401     /**
    402      * Ensures that the free space available in the path ref is >= size. The verb and point counts
    403      * are not changed.
    404      */
    405     void makeSpace(size_t size) {
    406         SkDEBUGCODE(this->validate();)
    407         ptrdiff_t growSize = size - fFreeSpace;
    408         if (growSize <= 0) {
    409             return;
    410         }
    411         size_t oldSize = this->currSize();
    412         // round to next multiple of 8 bytes
    413         growSize = (growSize + 7) & ~static_cast<size_t>(7);
    414         // we always at least double the allocation
    415         if (static_cast<size_t>(growSize) < oldSize) {
    416             growSize = oldSize;
    417         }
    418         if (growSize < kMinSize) {
    419             growSize = kMinSize;
    420         }
    421         size_t newSize = oldSize + growSize;
    422         // Note that realloc could memcpy more than we need. It seems to be a win anyway. TODO:
    423         // encapsulate this.
    424         fPoints = reinterpret_cast<SkPoint*>(sk_realloc_throw(fPoints, newSize));
    425         size_t oldVerbSize = fVerbCnt * sizeof(uint8_t);
    426         void* newVerbsDst = reinterpret_cast<void*>(
    427                                 reinterpret_cast<intptr_t>(fPoints) + newSize - oldVerbSize);
    428         void* oldVerbsSrc = reinterpret_cast<void*>(
    429                                 reinterpret_cast<intptr_t>(fPoints) + oldSize - oldVerbSize);
    430         memmove(newVerbsDst, oldVerbsSrc, oldVerbSize);
    431         fVerbs = reinterpret_cast<uint8_t*>(reinterpret_cast<intptr_t>(fPoints) + newSize);
    432         fFreeSpace += growSize;
    433         SkDEBUGCODE(this->validate();)
    434     }
    435 
    436     /**
    437      * Private, non-const-ptr version of the public function verbsMemBegin().
    438      */
    439     uint8_t* verbsMemWritable() {
    440         SkDEBUGCODE(this->validate();)
    441         return fVerbs - fVerbCnt;
    442     }
    443 
    444     /**
    445      * Gets the total amount of space allocated for verbs, points, and reserve.
    446      */
    447     size_t currSize() const {
    448         return reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints);
    449     }
    450 
    451     /**
    452      * Called the first time someone calls CreateEmpty to actually create the singleton.
    453      */
    454     friend SkPathRef* sk_create_empty_pathref();
    455 
    456     void setIsOval(bool isOval) { fIsOval = isOval; }
    457 
    458     void setIsRRect(bool isRRect) { fIsRRect = isRRect; }
    459 
    460     // called only by the editor. Note that this is not a const function.
    461     SkPoint* getPoints() {
    462         SkDEBUGCODE(this->validate();)
    463         fIsOval = false;
    464         fIsRRect = false;
    465         return fPoints;
    466     }
    467 
    468     const SkPoint* getPoints() const {
    469         SkDEBUGCODE(this->validate();)
    470         return fPoints;
    471     }
    472 
    473     void callGenIDChangeListeners();
    474 
    475     enum {
    476         kMinSize = 256,
    477     };
    478 
    479     mutable SkRect   fBounds;
    480 
    481     SkPoint*            fPoints; // points to begining of the allocation
    482     uint8_t*            fVerbs; // points just past the end of the allocation (verbs grow backwards)
    483     int                 fVerbCnt;
    484     int                 fPointCnt;
    485     size_t              fFreeSpace; // redundant but saves computation
    486     SkTDArray<SkScalar> fConicWeights;
    487 
    488     enum {
    489         kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs.
    490     };
    491     mutable uint32_t    fGenerationID;
    492     SkDEBUGCODE(int32_t fEditorsAttached;) // assert that only one editor in use at any time.
    493 
    494     SkTDArray<GenIDChangeListener*> fGenIDChangeListeners;  // pointers are owned
    495 
    496     mutable uint8_t  fBoundsIsDirty;
    497     mutable SkBool8  fIsFinite;    // only meaningful if bounds are valid
    498 
    499     SkBool8  fIsOval;
    500     SkBool8  fIsRRect;
    501     uint8_t  fSegmentMask;
    502 
    503     friend class PathRefTest_Private;
    504     friend class ForceIsRRect_Private; // unit test isRRect
    505     typedef SkRefCnt INHERITED;
    506 };
    507 
    508 #endif
    509