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 47 ~Editor() { SkDEBUGCODE(sk_atomic_dec(&fPathRef->fEditorsAttached);) } 48 49 /** 50 * Returns the array of points. 51 */ 52 SkPoint* points() { return fPathRef->getPoints(); } 53 const SkPoint* points() const { return fPathRef->points(); } 54 55 /** 56 * Gets the ith point. Shortcut for this->points() + i 57 */ 58 SkPoint* atPoint(int i) { 59 SkASSERT((unsigned) i < (unsigned) fPathRef->fPointCnt); 60 return this->points() + i; 61 }; 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 */ 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 */ 101 SkPathRef* pathRef() { return fPathRef; } 102 103 void setIsOval(bool isOval) { fPathRef->setIsOval(isOval); } 104 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 */ 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 */ 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 */ 145 bool isOval(SkRect* rect) const { 146 if (fIsOval && rect) { 147 *rect = getBounds(); 148 } 149 150 return SkToBool(fIsOval); 151 } 152 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 */ 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 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 198 int countPoints() const { SkDEBUGCODE(this->validate();) return fPointCnt; } 199 int countVerbs() const { SkDEBUGCODE(this->validate();) return fVerbCnt; } 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 */ 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 */ 210 const uint8_t* verbsMemBegin() const { return this->verbs() - fVerbCnt; } 211 212 /** 213 * Returns a const pointer to the first point. 214 */ 215 const SkPoint* points() const { SkDEBUGCODE(this->validate();) return fPoints; } 216 217 /** 218 * Shortcut for this->points() + this->countPoints() 219 */ 220 const SkPoint* pointsEnd() const { return this->points() + this->countPoints(); } 221 222 const SkScalar* conicWeights() const { SkDEBUGCODE(this->validate();) return fConicWeights.begin(); } 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 */ 228 uint8_t atVerb(int index) const { 229 SkASSERT((unsigned) index < (unsigned) fVerbCnt); 230 return this->verbs()[~index]; 231 } 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 SkDEBUGCODE(void validate() const;) 257 258 private: 259 enum SerializationOffsets { 260 kIsFinite_SerializationShift = 25, // requires 1 bit 261 kIsOval_SerializationShift = 24, // requires 1 bit 262 kSegmentMask_SerializationShift = 0 // requires 4 bits 263 }; 264 265 SkPathRef() { 266 fBoundsIsDirty = true; // this also invalidates fIsFinite 267 fPointCnt = 0; 268 fVerbCnt = 0; 269 fVerbs = NULL; 270 fPoints = NULL; 271 fFreeSpace = 0; 272 fGenerationID = kEmptyGenID; 273 fSegmentMask = 0; 274 fIsOval = false; 275 SkDEBUGCODE(fEditorsAttached = 0;) 276 SkDEBUGCODE(this->validate();) 277 } 278 279 void copy(const SkPathRef& ref, int additionalReserveVerbs, int additionalReservePoints); 280 281 // Return true if the computed bounds are finite. 282 static bool ComputePtBounds(SkRect* bounds, const SkPathRef& ref) { 283 int count = ref.countPoints(); 284 if (count <= 1) { // we ignore just 1 point (moveto) 285 bounds->setEmpty(); 286 return count ? ref.points()->isFinite() : true; 287 } else { 288 return bounds->setBoundsCheck(ref.points(), count); 289 } 290 } 291 292 // called, if dirty, by getBounds() 293 void computeBounds() const { 294 SkDEBUGCODE(this->validate();) 295 // TODO(mtklein): remove fBoundsIsDirty and fIsFinite, 296 // using an inverted rect instead of fBoundsIsDirty and always recalculating fIsFinite. 297 SkASSERT(fBoundsIsDirty); 298 299 fIsFinite = ComputePtBounds(&fBounds, *this); 300 fBoundsIsDirty = false; 301 } 302 303 void setBounds(const SkRect& rect) { 304 SkASSERT(rect.fLeft <= rect.fRight && rect.fTop <= rect.fBottom); 305 fBounds = rect; 306 fBoundsIsDirty = false; 307 fIsFinite = fBounds.isFinite(); 308 } 309 310 /** Makes additional room but does not change the counts or change the genID */ 311 void incReserve(int additionalVerbs, int additionalPoints) { 312 SkDEBUGCODE(this->validate();) 313 size_t space = additionalVerbs * sizeof(uint8_t) + additionalPoints * sizeof (SkPoint); 314 this->makeSpace(space); 315 SkDEBUGCODE(this->validate();) 316 } 317 318 /** Resets the path ref with verbCount verbs and pointCount points, all uninitialized. Also 319 * allocates space for reserveVerb additional verbs and reservePoints additional points.*/ 320 void resetToSize(int verbCount, int pointCount, int conicCount, 321 int reserveVerbs = 0, int reservePoints = 0) { 322 SkDEBUGCODE(this->validate();) 323 fBoundsIsDirty = true; // this also invalidates fIsFinite 324 fGenerationID = 0; 325 326 fSegmentMask = 0; 327 fIsOval = false; 328 329 size_t newSize = sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount; 330 size_t newReserve = sizeof(uint8_t) * reserveVerbs + sizeof(SkPoint) * reservePoints; 331 size_t minSize = newSize + newReserve; 332 333 ptrdiff_t sizeDelta = this->currSize() - minSize; 334 335 if (sizeDelta < 0 || static_cast<size_t>(sizeDelta) >= 3 * minSize) { 336 sk_free(fPoints); 337 fPoints = NULL; 338 fVerbs = NULL; 339 fFreeSpace = 0; 340 fVerbCnt = 0; 341 fPointCnt = 0; 342 this->makeSpace(minSize); 343 fVerbCnt = verbCount; 344 fPointCnt = pointCount; 345 fFreeSpace -= newSize; 346 } else { 347 fPointCnt = pointCount; 348 fVerbCnt = verbCount; 349 fFreeSpace = this->currSize() - minSize; 350 } 351 fConicWeights.setCount(conicCount); 352 SkDEBUGCODE(this->validate();) 353 } 354 355 /** 356 * Increases the verb count by numVbs and point count by the required amount. 357 * The new points are uninitialized. All the new verbs are set to the specified 358 * verb. If 'verb' is kConic_Verb, 'weights' will return a pointer to the 359 * uninitialized conic weights. 360 */ 361 SkPoint* growForRepeatedVerb(int /*SkPath::Verb*/ verb, int numVbs, SkScalar** weights); 362 363 /** 364 * Increases the verb count 1, records the new verb, and creates room for the requisite number 365 * of additional points. A pointer to the first point is returned. Any new points are 366 * uninitialized. 367 */ 368 SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight); 369 370 /** 371 * Ensures that the free space available in the path ref is >= size. The verb and point counts 372 * are not changed. 373 */ 374 void makeSpace(size_t size) { 375 SkDEBUGCODE(this->validate();) 376 ptrdiff_t growSize = size - fFreeSpace; 377 if (growSize <= 0) { 378 return; 379 } 380 size_t oldSize = this->currSize(); 381 // round to next multiple of 8 bytes 382 growSize = (growSize + 7) & ~static_cast<size_t>(7); 383 // we always at least double the allocation 384 if (static_cast<size_t>(growSize) < oldSize) { 385 growSize = oldSize; 386 } 387 if (growSize < kMinSize) { 388 growSize = kMinSize; 389 } 390 size_t newSize = oldSize + growSize; 391 // Note that realloc could memcpy more than we need. It seems to be a win anyway. TODO: 392 // encapsulate this. 393 fPoints = reinterpret_cast<SkPoint*>(sk_realloc_throw(fPoints, newSize)); 394 size_t oldVerbSize = fVerbCnt * sizeof(uint8_t); 395 void* newVerbsDst = reinterpret_cast<void*>( 396 reinterpret_cast<intptr_t>(fPoints) + newSize - oldVerbSize); 397 void* oldVerbsSrc = reinterpret_cast<void*>( 398 reinterpret_cast<intptr_t>(fPoints) + oldSize - oldVerbSize); 399 memmove(newVerbsDst, oldVerbsSrc, oldVerbSize); 400 fVerbs = reinterpret_cast<uint8_t*>(reinterpret_cast<intptr_t>(fPoints) + newSize); 401 fFreeSpace += growSize; 402 SkDEBUGCODE(this->validate();) 403 } 404 405 /** 406 * Private, non-const-ptr version of the public function verbsMemBegin(). 407 */ 408 uint8_t* verbsMemWritable() { 409 SkDEBUGCODE(this->validate();) 410 return fVerbs - fVerbCnt; 411 } 412 413 /** 414 * Gets the total amount of space allocated for verbs, points, and reserve. 415 */ 416 size_t currSize() const { 417 return reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints); 418 } 419 420 /** 421 * Called the first time someone calls CreateEmpty to actually create the singleton. 422 */ 423 friend SkPathRef* sk_create_empty_pathref(); 424 425 void setIsOval(bool isOval) { fIsOval = isOval; } 426 427 SkPoint* getPoints() { 428 SkDEBUGCODE(this->validate();) 429 fIsOval = false; 430 return fPoints; 431 } 432 433 enum { 434 kMinSize = 256, 435 }; 436 437 mutable SkRect fBounds; 438 mutable uint8_t fBoundsIsDirty; 439 mutable SkBool8 fIsFinite; // only meaningful if bounds are valid 440 441 SkBool8 fIsOval; 442 uint8_t fSegmentMask; 443 444 SkPoint* fPoints; // points to begining of the allocation 445 uint8_t* fVerbs; // points just past the end of the allocation (verbs grow backwards) 446 int fVerbCnt; 447 int fPointCnt; 448 size_t fFreeSpace; // redundant but saves computation 449 SkTDArray<SkScalar> fConicWeights; 450 451 enum { 452 kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs. 453 }; 454 mutable uint32_t fGenerationID; 455 SkDEBUGCODE(int32_t fEditorsAttached;) // assert that only one editor in use at any time. 456 457 friend class PathRefTest_Private; 458 typedef SkRefCnt INHERITED; 459 }; 460 461 #endif 462