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