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