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