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