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