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 "SkMatrix.h" 13 #include "SkPoint.h" 14 #include "SkRect.h" 15 #include "SkRefCnt.h" 16 #include "SkTDArray.h" 17 #include <stddef.h> // ptrdiff_t 18 19 class SkRBuffer; 20 class SkWBuffer; 21 22 /** 23 * Holds the path verbs and points. It is versioned by a generation ID. None of its public methods 24 * modify the contents. To modify or append to the verbs/points wrap the SkPathRef in an 25 * SkPathRef::Editor object. Installing the editor resets the generation ID. It also performs 26 * copy-on-write if the SkPathRef is shared by multiple SkPaths. The caller passes the Editor's 27 * constructor a SkAutoTUnref, which may be updated to point to a new SkPathRef after the editor's 28 * constructor returns. 29 * 30 * The points and verbs are stored in a single allocation. The points are at the begining of the 31 * allocation while the verbs are stored at end of the allocation, in reverse order. Thus the points 32 * and verbs both grow into the middle of the allocation until the meet. To access verb i in the 33 * verb array use ref.verbs()[~i] (because verbs() returns a pointer just beyond the first 34 * logical verb or the last verb in memory). 35 */ 36 37 class SK_API SkPathRef : public ::SkRefCnt { 38 public: 39 SK_DECLARE_INST_COUNT(SkPathRef); 40 41 class Editor { 42 public: 43 Editor(SkAutoTUnref<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)103 void setIsOval(bool isOval) { fPathRef->setIsOval(isOval); } 104 setBounds(const SkRect & rect)105 void setBounds(const SkRect& rect) { fPathRef->setBounds(rect); } 106 107 private: 108 SkPathRef* fPathRef; 109 }; 110 111 public: 112 /** 113 * Gets a path ref with no verbs or points. 114 */ 115 static SkPathRef* CreateEmpty(); 116 117 /** 118 * Returns true if all of the points in this path are finite, meaning there 119 * are no infinities and no NaNs. 120 */ isFinite()121 bool isFinite() const { 122 if (fBoundsIsDirty) { 123 this->computeBounds(); 124 } 125 return SkToBool(fIsFinite); 126 } 127 128 /** 129 * Returns a mask, where each bit corresponding to a SegmentMask is 130 * set if the path contains 1 or more segments of that type. 131 * Returns 0 for an empty path (no segments). 132 */ getSegmentMasks()133 uint32_t getSegmentMasks() const { return fSegmentMask; } 134 135 /** Returns true if the path is an oval. 136 * 137 * @param rect returns the bounding rect of this oval. It's a circle 138 * if the height and width are the same. 139 * 140 * @return true if this path is an oval. 141 * Tracking whether a path is an oval is considered an 142 * optimization for performance and so some paths that are in 143 * fact ovals can report false. 144 */ isOval(SkRect * rect)145 bool isOval(SkRect* rect) const { 146 if (fIsOval && NULL != rect) { 147 *rect = getBounds(); 148 } 149 150 return SkToBool(fIsOval); 151 } 152 hasComputedBounds()153 bool hasComputedBounds() const { 154 return !fBoundsIsDirty; 155 } 156 157 /** Returns the bounds of the path's points. If the path contains 0 or 1 158 points, the bounds is set to (0,0,0,0), and isEmpty() will return true. 159 Note: this bounds may be larger than the actual shape, since curves 160 do not extend as far as their control points. 161 */ getBounds()162 const SkRect& getBounds() const { 163 if (fBoundsIsDirty) { 164 this->computeBounds(); 165 } 166 return fBounds; 167 } 168 169 /** 170 * Transforms a path ref by a matrix, allocating a new one only if necessary. 171 */ 172 static void CreateTransformedCopy(SkAutoTUnref<SkPathRef>* dst, 173 const SkPathRef& src, 174 const SkMatrix& matrix); 175 176 static SkPathRef* CreateFromBuffer(SkRBuffer* buffer); 177 178 /** 179 * Rollsback a path ref to zero verbs and points with the assumption that the path ref will be 180 * repopulated with approximately the same number of verbs and points. A new path ref is created 181 * only if necessary. 182 */ 183 static void Rewind(SkAutoTUnref<SkPathRef>* pathRef); 184 ~SkPathRef()185 virtual ~SkPathRef() { 186 SkDEBUGCODE(this->validate();) 187 sk_free(fPoints); 188 189 SkDEBUGCODE(fPoints = NULL;) 190 SkDEBUGCODE(fVerbs = NULL;) 191 SkDEBUGCODE(fVerbCnt = 0x9999999;) 192 SkDEBUGCODE(fPointCnt = 0xAAAAAAA;) 193 SkDEBUGCODE(fPointCnt = 0xBBBBBBB;) 194 SkDEBUGCODE(fGenerationID = 0xEEEEEEEE;) 195 SkDEBUGCODE(fEditorsAttached = 0x7777777;) 196 } 197 countPoints()198 int countPoints() const { SkDEBUGCODE(this->validate();) return fPointCnt; } countVerbs()199 int countVerbs() const { SkDEBUGCODE(this->validate();) return fVerbCnt; } countWeights()200 int countWeights() const { SkDEBUGCODE(this->validate();) return fConicWeights.count(); } 201 202 /** 203 * Returns a pointer one beyond the first logical verb (last verb in memory order). 204 */ verbs()205 const uint8_t* verbs() const { SkDEBUGCODE(this->validate();) return fVerbs; } 206 207 /** 208 * Returns a const pointer to the first verb in memory (which is the last logical verb). 209 */ verbsMemBegin()210 const uint8_t* verbsMemBegin() const { return this->verbs() - fVerbCnt; } 211 212 /** 213 * Returns a const pointer to the first point. 214 */ points()215 const SkPoint* points() const { SkDEBUGCODE(this->validate();) return fPoints; } 216 217 /** 218 * Shortcut for this->points() + this->countPoints() 219 */ pointsEnd()220 const SkPoint* pointsEnd() const { return this->points() + this->countPoints(); } 221 conicWeights()222 const SkScalar* conicWeights() const { SkDEBUGCODE(this->validate();) return fConicWeights.begin(); } conicWeightsEnd()223 const SkScalar* conicWeightsEnd() const { SkDEBUGCODE(this->validate();) return fConicWeights.end(); } 224 225 /** 226 * Convenience methods for getting to a verb or point by index. 227 */ atVerb(int index)228 uint8_t atVerb(int index) const { 229 SkASSERT((unsigned) index < (unsigned) fVerbCnt); 230 return this->verbs()[~index]; 231 } atPoint(int index)232 const SkPoint& atPoint(int index) const { 233 SkASSERT((unsigned) index < (unsigned) fPointCnt); 234 return this->points()[index]; 235 } 236 237 bool operator== (const SkPathRef& ref) const; 238 239 /** 240 * Writes the path points and verbs to a buffer. 241 */ 242 void writeToBuffer(SkWBuffer* buffer) const; 243 244 /** 245 * Gets the number of bytes that would be written in writeBuffer() 246 */ 247 uint32_t writeSize() const; 248 249 /** 250 * Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the 251 * same ID then they have the same verbs and points. However, two path refs may have the same 252 * contents but different genIDs. 253 */ 254 uint32_t genID() const; 255 256 private: 257 enum SerializationOffsets { 258 kIsFinite_SerializationShift = 25, // requires 1 bit 259 kIsOval_SerializationShift = 24, // requires 1 bit 260 kSegmentMask_SerializationShift = 0 // requires 4 bits 261 }; 262 SkPathRef()263 SkPathRef() { 264 fBoundsIsDirty = true; // this also invalidates fIsFinite 265 fPointCnt = 0; 266 fVerbCnt = 0; 267 fVerbs = NULL; 268 fPoints = NULL; 269 fFreeSpace = 0; 270 fGenerationID = kEmptyGenID; 271 fSegmentMask = 0; 272 fIsOval = false; 273 SkDEBUGCODE(fEditorsAttached = 0;) 274 SkDEBUGCODE(this->validate();) 275 } 276 277 void copy(const SkPathRef& ref, int additionalReserveVerbs, int additionalReservePoints); 278 279 // Return true if the computed bounds are finite. ComputePtBounds(SkRect * bounds,const SkPathRef & ref)280 static bool ComputePtBounds(SkRect* bounds, const SkPathRef& ref) { 281 int count = ref.countPoints(); 282 if (count <= 1) { // we ignore just 1 point (moveto) 283 bounds->setEmpty(); 284 return count ? ref.points()->isFinite() : true; 285 } else { 286 return bounds->setBoundsCheck(ref.points(), count); 287 } 288 } 289 290 // called, if dirty, by getBounds() computeBounds()291 void computeBounds() const { 292 SkDEBUGCODE(this->validate();) 293 SkASSERT(fBoundsIsDirty); 294 295 fIsFinite = ComputePtBounds(&fBounds, *this); 296 fBoundsIsDirty = false; 297 } 298 setBounds(const SkRect & rect)299 void setBounds(const SkRect& rect) { 300 SkASSERT(rect.fLeft <= rect.fRight && rect.fTop <= rect.fBottom); 301 fBounds = rect; 302 fBoundsIsDirty = false; 303 fIsFinite = fBounds.isFinite(); 304 } 305 306 /** Makes additional room but does not change the counts or change the genID */ incReserve(int additionalVerbs,int additionalPoints)307 void incReserve(int additionalVerbs, int additionalPoints) { 308 SkDEBUGCODE(this->validate();) 309 size_t space = additionalVerbs * sizeof(uint8_t) + additionalPoints * sizeof (SkPoint); 310 this->makeSpace(space); 311 SkDEBUGCODE(this->validate();) 312 } 313 314 /** Resets the path ref with verbCount verbs and pointCount points, all uninitialized. Also 315 * allocates space for reserveVerb additional verbs and reservePoints additional points.*/ 316 void resetToSize(int verbCount, int pointCount, int conicCount, 317 int reserveVerbs = 0, int reservePoints = 0) { 318 SkDEBUGCODE(this->validate();) 319 fBoundsIsDirty = true; // this also invalidates fIsFinite 320 fGenerationID = 0; 321 322 fSegmentMask = 0; 323 fIsOval = false; 324 325 size_t newSize = sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount; 326 size_t newReserve = sizeof(uint8_t) * reserveVerbs + sizeof(SkPoint) * reservePoints; 327 size_t minSize = newSize + newReserve; 328 329 ptrdiff_t sizeDelta = this->currSize() - minSize; 330 331 if (sizeDelta < 0 || static_cast<size_t>(sizeDelta) >= 3 * minSize) { 332 sk_free(fPoints); 333 fPoints = NULL; 334 fVerbs = NULL; 335 fFreeSpace = 0; 336 fVerbCnt = 0; 337 fPointCnt = 0; 338 this->makeSpace(minSize); 339 fVerbCnt = verbCount; 340 fPointCnt = pointCount; 341 fFreeSpace -= newSize; 342 } else { 343 fPointCnt = pointCount; 344 fVerbCnt = verbCount; 345 fFreeSpace = this->currSize() - minSize; 346 } 347 fConicWeights.setCount(conicCount); 348 SkDEBUGCODE(this->validate();) 349 } 350 351 /** 352 * Increases the verb count by numVbs and point count by the required amount. 353 * The new points are uninitialized. All the new verbs are set to the specified 354 * verb. If 'verb' is kConic_Verb, 'weights' will return a pointer to the 355 * uninitialized conic weights. 356 */ 357 SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, int numVbs, SkScalar** weights); 358 359 /** 360 * Increases the verb count 1, records the new verb, and creates room for the requisite number 361 * of additional points. A pointer to the first point is returned. Any new points are 362 * uninitialized. 363 */ 364 SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight); 365 366 /** 367 * Ensures that the free space available in the path ref is >= size. The verb and point counts 368 * are not changed. 369 */ makeSpace(size_t size)370 void makeSpace(size_t size) { 371 SkDEBUGCODE(this->validate();) 372 ptrdiff_t growSize = size - fFreeSpace; 373 if (growSize <= 0) { 374 return; 375 } 376 size_t oldSize = this->currSize(); 377 // round to next multiple of 8 bytes 378 growSize = (growSize + 7) & ~static_cast<size_t>(7); 379 // we always at least double the allocation 380 if (static_cast<size_t>(growSize) < oldSize) { 381 growSize = oldSize; 382 } 383 if (growSize < kMinSize) { 384 growSize = kMinSize; 385 } 386 size_t newSize = oldSize + growSize; 387 // Note that realloc could memcpy more than we need. It seems to be a win anyway. TODO: 388 // encapsulate this. 389 fPoints = reinterpret_cast<SkPoint*>(sk_realloc_throw(fPoints, newSize)); 390 size_t oldVerbSize = fVerbCnt * sizeof(uint8_t); 391 void* newVerbsDst = reinterpret_cast<void*>( 392 reinterpret_cast<intptr_t>(fPoints) + newSize - oldVerbSize); 393 void* oldVerbsSrc = reinterpret_cast<void*>( 394 reinterpret_cast<intptr_t>(fPoints) + oldSize - oldVerbSize); 395 memmove(newVerbsDst, oldVerbsSrc, oldVerbSize); 396 fVerbs = reinterpret_cast<uint8_t*>(reinterpret_cast<intptr_t>(fPoints) + newSize); 397 fFreeSpace += growSize; 398 SkDEBUGCODE(this->validate();) 399 } 400 401 /** 402 * Private, non-const-ptr version of the public function verbsMemBegin(). 403 */ verbsMemWritable()404 uint8_t* verbsMemWritable() { 405 SkDEBUGCODE(this->validate();) 406 return fVerbs - fVerbCnt; 407 } 408 409 /** 410 * Gets the total amount of space allocated for verbs, points, and reserve. 411 */ currSize()412 size_t currSize() const { 413 return reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints); 414 } 415 416 SkDEBUGCODE(void validate() const;) 417 418 /** 419 * Called the first time someone calls CreateEmpty to actually create the singleton. 420 */ 421 static SkPathRef* CreateEmptyImpl(); 422 setIsOval(bool isOval)423 void setIsOval(bool isOval) { fIsOval = isOval; } 424 getPoints()425 SkPoint* getPoints() { 426 SkDEBUGCODE(this->validate();) 427 fIsOval = false; 428 return fPoints; 429 } 430 431 enum { 432 kMinSize = 256, 433 }; 434 435 mutable SkRect fBounds; 436 uint8_t fSegmentMask; 437 mutable uint8_t fBoundsIsDirty; 438 mutable SkBool8 fIsFinite; // only meaningful if bounds are valid 439 mutable SkBool8 fIsOval; 440 441 SkPoint* fPoints; // points to begining of the allocation 442 uint8_t* fVerbs; // points just past the end of the allocation (verbs grow backwards) 443 int fVerbCnt; 444 int fPointCnt; 445 size_t fFreeSpace; // redundant but saves computation 446 SkTDArray<SkScalar> fConicWeights; 447 448 enum { 449 kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs. 450 }; 451 mutable uint32_t fGenerationID; 452 SkDEBUGCODE(int32_t fEditorsAttached;) // assert that only one editor in use at any time. 453 454 friend class PathRefTest_Private; 455 typedef SkRefCnt INHERITED; 456 }; 457 458 #endif 459