1 2 /* 3 * Copyright 2011 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 #ifndef SkPictureFlat_DEFINED 9 #define SkPictureFlat_DEFINED 10 11 //#define SK_DEBUG_SIZE 12 13 #include "SkChunkAlloc.h" 14 #include "SkBitmap.h" 15 #include "SkBitmapHeap.h" 16 #include "SkOrderedReadBuffer.h" 17 #include "SkOrderedWriteBuffer.h" 18 #include "SkPicture.h" 19 #include "SkPtrRecorder.h" 20 #include "SkMatrix.h" 21 #include "SkPaint.h" 22 #include "SkPath.h" 23 #include "SkRegion.h" 24 #include "SkTRefArray.h" 25 #include "SkTSearch.h" 26 27 enum DrawType { 28 UNUSED, 29 CLIP_PATH, 30 CLIP_REGION, 31 CLIP_RECT, 32 CLIP_RRECT, 33 CONCAT, 34 DRAW_BITMAP, 35 DRAW_BITMAP_MATRIX, 36 DRAW_BITMAP_NINE, 37 DRAW_BITMAP_RECT_TO_RECT, 38 DRAW_CLEAR, 39 DRAW_DATA, 40 DRAW_OVAL, 41 DRAW_PAINT, 42 DRAW_PATH, 43 DRAW_PICTURE, 44 DRAW_POINTS, 45 DRAW_POS_TEXT, 46 DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT 47 DRAW_POS_TEXT_H, 48 DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H 49 DRAW_RECT, 50 DRAW_RRECT, 51 DRAW_SPRITE, 52 DRAW_TEXT, 53 DRAW_TEXT_ON_PATH, 54 DRAW_TEXT_TOP_BOTTOM, // fast variant of DRAW_TEXT 55 DRAW_VERTICES, 56 RESTORE, 57 ROTATE, 58 SAVE, 59 SAVE_LAYER, 60 SCALE, 61 SET_MATRIX, 62 SKEW, 63 TRANSLATE, 64 NOOP, 65 BEGIN_COMMENT_GROUP, 66 COMMENT, 67 END_COMMENT_GROUP, 68 69 LAST_DRAWTYPE_ENUM = END_COMMENT_GROUP 70 }; 71 72 // In the 'match' method, this constant will match any flavor of DRAW_BITMAP* 73 static const int kDRAW_BITMAP_FLAVOR = LAST_DRAWTYPE_ENUM+1; 74 75 enum DrawVertexFlags { 76 DRAW_VERTICES_HAS_TEXS = 0x01, 77 DRAW_VERTICES_HAS_COLORS = 0x02, 78 DRAW_VERTICES_HAS_INDICES = 0x04 79 }; 80 81 /////////////////////////////////////////////////////////////////////////////// 82 // clipparams are packed in 5 bits 83 // doAA:1 | regionOp:4 84 85 static inline uint32_t ClipParams_pack(SkRegion::Op op, bool doAA) { 86 unsigned doAABit = doAA ? 1 : 0; 87 return (doAABit << 4) | op; 88 } 89 90 static inline SkRegion::Op ClipParams_unpackRegionOp(uint32_t packed) { 91 return (SkRegion::Op)(packed & 0xF); 92 } 93 94 static inline bool ClipParams_unpackDoAA(uint32_t packed) { 95 return SkToBool((packed >> 4) & 1); 96 } 97 98 /////////////////////////////////////////////////////////////////////////////// 99 100 class SkTypefacePlayback { 101 public: 102 SkTypefacePlayback(); 103 virtual ~SkTypefacePlayback(); 104 105 int count() const { return fCount; } 106 107 void reset(const SkRefCntSet*); 108 109 void setCount(int count); 110 SkRefCnt* set(int index, SkRefCnt*); 111 112 void setupBuffer(SkOrderedReadBuffer& buffer) const { 113 buffer.setTypefaceArray((SkTypeface**)fArray, fCount); 114 } 115 116 protected: 117 int fCount; 118 SkRefCnt** fArray; 119 }; 120 121 class SkFactoryPlayback { 122 public: 123 SkFactoryPlayback(int count) : fCount(count) { 124 fArray = SkNEW_ARRAY(SkFlattenable::Factory, count); 125 } 126 127 ~SkFactoryPlayback() { 128 SkDELETE_ARRAY(fArray); 129 } 130 131 SkFlattenable::Factory* base() const { return fArray; } 132 133 void setupBuffer(SkOrderedReadBuffer& buffer) const { 134 buffer.setFactoryPlayback(fArray, fCount); 135 } 136 137 private: 138 int fCount; 139 SkFlattenable::Factory* fArray; 140 }; 141 142 /////////////////////////////////////////////////////////////////////////////// 143 // 144 // 145 // The following templated classes provide an efficient way to store and compare 146 // objects that have been flattened (i.e. serialized in an ordered binary 147 // format). 148 // 149 // SkFlatData: is a simple indexable container for the flattened data 150 // which is agnostic to the type of data is is indexing. It is 151 // also responsible for flattening/unflattening objects but 152 // details of that operation are hidden in the provided procs 153 // SkFlatDictionary: is an abstract templated dictionary that maintains a 154 // searchable set of SkFlatData objects of type T. 155 // SkFlatController: is an interface provided to SkFlatDictionary which handles 156 // allocation (and unallocation in some cases). It also holds 157 // ref count recorders and the like. 158 // 159 // NOTE: any class that wishes to be used in conjunction with SkFlatDictionary 160 // must subclass the dictionary and provide the necessary flattening procs. 161 // The end of this header contains dictionary subclasses for some common classes 162 // like SkBitmap, SkMatrix, SkPaint, and SkRegion. SkFlatController must also 163 // be implemented, or SkChunkFlatController can be used to use an 164 // SkChunkAllocator and never do replacements. 165 // 166 // 167 /////////////////////////////////////////////////////////////////////////////// 168 169 class SkFlatData; 170 171 class SkFlatController : public SkRefCnt { 172 public: 173 SK_DECLARE_INST_COUNT(SkFlatController) 174 175 SkFlatController(); 176 virtual ~SkFlatController(); 177 /** 178 * Return a new block of memory for the SkFlatDictionary to use. 179 * This memory is owned by the controller and has the same lifetime unless you 180 * call unalloc(), in which case it may be freed early. 181 */ 182 virtual void* allocThrow(size_t bytes) = 0; 183 184 /** 185 * Hint that this block, which was allocated with allocThrow, is no longer needed. 186 * The implementation may choose to free this memory any time beteween now and destruction. 187 */ 188 virtual void unalloc(void* ptr) = 0; 189 190 /** 191 * Used during creation and unflattening of SkFlatData objects. If the 192 * objects being flattened contain bitmaps they are stored in this heap 193 * and the flattenable stores the index to the bitmap on the heap. 194 * This should be set by the protected setBitmapHeap. 195 */ 196 SkBitmapHeap* getBitmapHeap() { return fBitmapHeap; } 197 198 /** 199 * Used during creation of SkFlatData objects. If a typeface recorder is 200 * required to flatten the objects being flattened (i.e. for SkPaints), this 201 * should be set by the protected setTypefaceSet. 202 */ 203 SkRefCntSet* getTypefaceSet() { return fTypefaceSet; } 204 205 /** 206 * Used during unflattening of the SkFlatData objects in the 207 * SkFlatDictionary. Needs to be set by the protected setTypefacePlayback 208 * and needs to be reset to the SkRefCntSet passed to setTypefaceSet. 209 */ 210 SkTypefacePlayback* getTypefacePlayback() { return fTypefacePlayback; } 211 212 /** 213 * Optional factory recorder used during creation of SkFlatData objects. Set 214 * using the protected method setNamedFactorySet. 215 */ 216 SkNamedFactorySet* getNamedFactorySet() { return fFactorySet; } 217 218 /** 219 * Flags to use during creation of SkFlatData objects. Defaults to zero. 220 */ 221 uint32_t getWriteBufferFlags() { return fWriteBufferFlags; } 222 223 protected: 224 /** 225 * Set an SkBitmapHeap to be used to store/read SkBitmaps. Ref counted. 226 */ 227 void setBitmapHeap(SkBitmapHeap*); 228 229 /** 230 * Set an SkRefCntSet to be used to store SkTypefaces during flattening. Ref 231 * counted. 232 */ 233 void setTypefaceSet(SkRefCntSet*); 234 235 /** 236 * Set an SkTypefacePlayback to be used to find references to SkTypefaces 237 * during unflattening. Should be reset to the set provided to 238 * setTypefaceSet. 239 */ 240 void setTypefacePlayback(SkTypefacePlayback*); 241 242 /** 243 * Set an SkNamedFactorySet to be used to store Factorys and their 244 * corresponding names during flattening. Ref counted. Returns the same 245 * set as a convenience. 246 */ 247 SkNamedFactorySet* setNamedFactorySet(SkNamedFactorySet*); 248 249 /** 250 * Set the flags to be used during flattening. 251 */ 252 void setWriteBufferFlags(uint32_t flags) { fWriteBufferFlags = flags; } 253 254 private: 255 SkBitmapHeap* fBitmapHeap; 256 SkRefCntSet* fTypefaceSet; 257 SkTypefacePlayback* fTypefacePlayback; 258 SkNamedFactorySet* fFactorySet; 259 uint32_t fWriteBufferFlags; 260 261 typedef SkRefCnt INHERITED; 262 }; 263 264 class SkFlatData { 265 public: 266 /** 267 * Compare two SkFlatData ptrs, returning -1, 0, 1 to allow them to be 268 * sorted. 269 * 270 * Note: this assumes that a and b have different sentinel values, either 271 * InCache or AsCandidate, otherwise the loop will go beyond the end of 272 * the buffers. 273 * 274 * dataToCompare() returns 2 fields before the flattened data: 275 * - checksum 276 * - size 277 * This ensures that if we see two blocks of different length, we will 278 * notice that right away, and not read any further. It also ensures that 279 * we see the checksum right away, so that most of the time it is enough 280 * to short-circuit our comparison. 281 */ 282 static int Compare(const SkFlatData& a, const SkFlatData& b) { 283 const uint32_t* stop = a.dataStop(); 284 const uint32_t* a_ptr = a.dataToCompare() - 1; 285 const uint32_t* b_ptr = b.dataToCompare() - 1; 286 // We use -1 above, so we can pre-increment our pointers in the loop 287 while (*++a_ptr == *++b_ptr) {} 288 289 if (a_ptr == stop) { // sentinel 290 SkASSERT(b.dataStop() == b_ptr); 291 return 0; 292 } 293 SkASSERT(a_ptr < a.dataStop()); 294 SkASSERT(b_ptr < b.dataStop()); 295 return (*a_ptr < *b_ptr) ? -1 : 1; 296 } 297 298 // Adapts Compare to be used with SkTSearch 299 static bool Less(const SkFlatData& a, const SkFlatData& b) { 300 return Compare(a, b) < 0; 301 } 302 303 int index() const { return fIndex; } 304 const void* data() const { return (const char*)this + sizeof(*this); } 305 void* data() { return (char*)this + sizeof(*this); } 306 // Our data is always 32bit aligned, so we can offer this accessor 307 uint32_t* data32() { return (uint32_t*)this->data(); } 308 // Returns the size of the flattened data. 309 size_t flatSize() const { return fFlatSize; } 310 311 void setSentinelInCache() { 312 this->setSentinel(kInCache_Sentinel); 313 } 314 void setSentinelAsCandidate() { 315 this->setSentinel(kCandidate_Sentinel); 316 } 317 318 uint32_t checksum() const { return fChecksum; } 319 320 #ifdef SK_DEBUG_SIZE 321 // returns the logical size of our data. Does not return any sentinel or 322 // padding we might have. 323 size_t size() const { 324 return sizeof(SkFlatData) + fFlatSize; 325 } 326 #endif 327 328 static SkFlatData* Create(SkFlatController* controller, const void* obj, int index, 329 void (*flattenProc)(SkOrderedWriteBuffer&, const void*)); 330 331 void unflatten(void* result, 332 void (*unflattenProc)(SkOrderedReadBuffer&, void*), 333 SkBitmapHeap* bitmapHeap = NULL, 334 SkTypefacePlayback* facePlayback = NULL) const; 335 336 // When we purge an entry, we want to reuse an old index for the new entry, 337 // so we expose this setter. 338 void setIndex(int index) { fIndex = index; } 339 340 // for unittesting 341 friend bool operator==(const SkFlatData& a, const SkFlatData& b) { 342 size_t N = (const char*)a.dataStop() - (const char*)a.dataToCompare(); 343 return !memcmp(a.dataToCompare(), b.dataToCompare(), N); 344 } 345 346 // returns true if fTopBot[] has been recorded 347 bool isTopBotWritten() const { 348 return !SkScalarIsNaN(fTopBot[0]); 349 } 350 351 // Returns fTopBot array, so it can be passed to a routine to compute them. 352 // For efficiency, we assert that fTopBot have not been recorded yet. 353 SkScalar* writableTopBot() const { 354 SkASSERT(!this->isTopBotWritten()); 355 return fTopBot; 356 } 357 358 // return the topbot[] after it has been recorded 359 const SkScalar* topBot() const { 360 SkASSERT(this->isTopBotWritten()); 361 return fTopBot; 362 } 363 364 private: 365 // This is *not* part of the key for search/sort 366 int fIndex; 367 368 // Cache of paint's FontMetrics fTop,fBottom 369 // initialied to [NaN,NaN] as a sentinel that they have not been recorded yet 370 // 371 // This is *not* part of the key for search/sort 372 mutable SkScalar fTopBot[2]; 373 374 // marks fTopBot[] as unrecorded 375 void setTopBotUnwritten() { 376 this->fTopBot[0] = SK_ScalarNaN; // initial to sentinel values 377 } 378 379 // From here down is the data we look at in the search/sort. We always begin 380 // with the checksum and then length. 381 uint32_t fChecksum; 382 int32_t fFlatSize; // size of flattened data 383 // uint32_t flattenedData[] 384 // uint32_t sentinelValue 385 386 const uint32_t* dataToCompare() const { 387 return (const uint32_t*)&fChecksum; 388 } 389 const uint32_t* dataStop() const { 390 SkASSERT(SkIsAlign4(fFlatSize)); 391 return (const uint32_t*)((const char*)this->data() + fFlatSize); 392 } 393 394 enum { 395 kInCache_Sentinel = 0, 396 kCandidate_Sentinel = ~0U, 397 }; 398 void setSentinel(uint32_t value) { 399 SkASSERT(SkIsAlign4(fFlatSize)); 400 this->data32()[fFlatSize >> 2] = value; 401 } 402 403 // This does not modify the payload flat data, in case it's already been written. 404 void stampHeaderAndSentinel(int index, int32_t size); 405 template <class T> friend class SkFlatDictionary; // For stampHeaderAndSentinel(). 406 }; 407 408 template <class T> 409 class SkFlatDictionary { 410 static const size_t kWriteBufferGrowthBytes = 1024; 411 412 public: 413 SkFlatDictionary(SkFlatController* controller, size_t scratchSizeGuess = 0) 414 : fFlattenProc(NULL) 415 , fUnflattenProc(NULL) 416 , fController(SkRef(controller)) 417 , fScratchSize(scratchSizeGuess) 418 , fScratch(AllocScratch(fScratchSize)) 419 , fWriteBuffer(kWriteBufferGrowthBytes) 420 , fWriteBufferReady(false) 421 , fNextIndex(1) { // set to 1 since returning a zero from find() indicates failure 422 sk_bzero(fHash, sizeof(fHash)); 423 // index 0 is always empty since it is used as a signal that find failed 424 fIndexedData.push(NULL); 425 } 426 427 ~SkFlatDictionary() { 428 sk_free(fScratch); 429 } 430 431 int count() const { 432 SkASSERT(fIndexedData.count() == fSortedData.count()+1); 433 return fSortedData.count(); 434 } 435 436 const SkFlatData* operator[](int index) const { 437 SkASSERT(index >= 0 && index < fSortedData.count()); 438 return fSortedData[index]; 439 } 440 441 /** 442 * Clears the dictionary of all entries. However, it does NOT free the 443 * memory that was allocated for each entry. 444 */ 445 void reset() { 446 fSortedData.reset(); 447 fIndexedData.rewind(); 448 // index 0 is always empty since it is used as a signal that find failed 449 fIndexedData.push(NULL); 450 fNextIndex = 1; 451 sk_bzero(fHash, sizeof(fHash)); 452 } 453 454 /** 455 * Similar to find. Allows the caller to specify an SkFlatData to replace in 456 * the case of an add. Also tells the caller whether a new SkFlatData was 457 * added and whether the old one was replaced. The parameters added and 458 * replaced are required to be non-NULL. Rather than returning the index of 459 * the entry in the dictionary, it returns the actual SkFlatData. 460 */ 461 const SkFlatData* findAndReplace(const T& element, 462 const SkFlatData* toReplace, bool* added, 463 bool* replaced) { 464 SkASSERT(added != NULL && replaced != NULL); 465 int oldCount = fSortedData.count(); 466 const SkFlatData* flat = this->findAndReturnFlat(element); 467 *added = fSortedData.count() == oldCount + 1; 468 *replaced = false; 469 if (*added && toReplace != NULL) { 470 // First, find the index of the one to replace 471 int indexToReplace = fSortedData.find(toReplace); 472 if (indexToReplace >= 0) { 473 // findAndReturnFlat set the index to fNextIndex and increased 474 // fNextIndex by one. Reuse the index from the one being 475 // replaced and reset fNextIndex to the proper value. 476 int oldIndex = flat->index(); 477 const_cast<SkFlatData*>(flat)->setIndex(toReplace->index()); 478 fIndexedData[toReplace->index()] = flat; 479 fNextIndex--; 480 // Remove from the arrays. 481 fSortedData.remove(indexToReplace); 482 fIndexedData.remove(oldIndex); 483 // Remove from the hash table. 484 int oldHash = ChecksumToHashIndex(toReplace->checksum()); 485 if (fHash[oldHash] == toReplace) { 486 fHash[oldHash] = NULL; 487 } 488 // Delete the actual object. 489 fController->unalloc((void*)toReplace); 490 *replaced = true; 491 SkASSERT(fIndexedData.count() == fSortedData.count()+1); 492 } 493 } 494 return flat; 495 } 496 497 /** 498 * Given an element of type T return its 1-based index in the dictionary. If 499 * the element wasn't previously in the dictionary it is automatically 500 * added. 501 * 502 * To make the Compare function fast, we write a sentinel value at the end 503 * of each block. The blocks in our fSortedData[] all have a 0 sentinel. The 504 * newly created block we're comparing against has a -1 in the sentinel. 505 * 506 * This trick allows Compare to always loop until failure. If it fails on 507 * the sentinal value, we know the blocks are equal. 508 */ 509 int find(const T& element) { 510 return this->findAndReturnFlat(element)->index(); 511 } 512 513 /** 514 * Unflatten the objects and return them in SkTRefArray, or return NULL 515 * if there no objects (instead of an empty array). 516 */ 517 SkTRefArray<T>* unflattenToArray() const { 518 int count = fSortedData.count(); 519 SkTRefArray<T>* array = NULL; 520 if (count > 0) { 521 array = SkTRefArray<T>::Create(count); 522 this->unflattenIntoArray(&array->writableAt(0)); 523 } 524 return array; 525 } 526 527 /** 528 * Unflatten the specific object at the given index 529 */ 530 T* unflatten(int index) const { 531 SkASSERT(fIndexedData.count() == fSortedData.count()+1); 532 const SkFlatData* element = fIndexedData[index]; 533 SkASSERT(index == element->index()); 534 535 T* dst = new T; 536 this->unflatten(dst, element); 537 return dst; 538 } 539 540 const SkFlatData* findAndReturnFlat(const T& element) { 541 // Only valid until the next call to resetScratch(). 542 const SkFlatData& scratch = this->resetScratch(element, fNextIndex); 543 544 // See if we have it in the hash? 545 const int hashIndex = ChecksumToHashIndex(scratch.checksum()); 546 const SkFlatData* candidate = fHash[hashIndex]; 547 if (candidate != NULL && SkFlatData::Compare(scratch, *candidate) == 0) { 548 return candidate; 549 } 550 551 // See if we have it at all? 552 const int index = SkTSearch<const SkFlatData, SkFlatData::Less>(fSortedData.begin(), 553 fSortedData.count(), 554 &scratch, 555 sizeof(&scratch)); 556 if (index >= 0) { 557 // Found. Update hash before we return. 558 fHash[hashIndex] = fSortedData[index]; 559 return fSortedData[index]; 560 } 561 562 // We don't have it. Add it. 563 SkFlatData* detached = this->detachScratch(); 564 // detached will live beyond the next call to resetScratch(), but is owned by fController. 565 *fSortedData.insert(~index) = detached; // SkTSearch returned bit-not of where to insert. 566 *fIndexedData.insert(detached->index()) = detached; 567 fHash[hashIndex] = detached; 568 569 SkASSERT(detached->index() == fNextIndex); 570 SkASSERT(fSortedData.count() == fNextIndex); 571 SkASSERT(fIndexedData.count() == fNextIndex+1); 572 fNextIndex++; 573 574 return detached; 575 } 576 577 protected: 578 void (*fFlattenProc)(SkOrderedWriteBuffer&, const void*); 579 void (*fUnflattenProc)(SkOrderedReadBuffer&, void*); 580 581 private: 582 // Layout: [ SkFlatData header, 20 bytes ] [ data ..., 4-byte aligned ] [ sentinel, 4 bytes] 583 static size_t SizeWithPadding(size_t flatDataSize) { 584 SkASSERT(SkIsAlign4(flatDataSize)); 585 return sizeof(SkFlatData) + flatDataSize + sizeof(uint32_t); 586 } 587 588 // Allocate a new scratch SkFlatData. Must be sk_freed. 589 static SkFlatData* AllocScratch(size_t scratchSize) { 590 return (SkFlatData*) sk_malloc_throw(SizeWithPadding(scratchSize)); 591 } 592 593 // We have to delay fWriteBuffer's initialization until its first use; fController might not 594 // be fully set up by the time we get it in the constructor. 595 void lazyWriteBufferInit() { 596 if (fWriteBufferReady) { 597 return; 598 } 599 // Without a bitmap heap, we'll flatten bitmaps into paints. That's never what you want. 600 SkASSERT(fController->getBitmapHeap() != NULL); 601 fWriteBuffer.setBitmapHeap(fController->getBitmapHeap()); 602 fWriteBuffer.setTypefaceRecorder(fController->getTypefaceSet()); 603 fWriteBuffer.setNamedFactoryRecorder(fController->getNamedFactorySet()); 604 fWriteBuffer.setFlags(fController->getWriteBufferFlags()); 605 fWriteBufferReady = true; 606 } 607 608 // This reference is valid only until the next call to resetScratch() or detachScratch(). 609 const SkFlatData& resetScratch(const T& element, int index) { 610 this->lazyWriteBufferInit(); 611 612 // Flatten element into fWriteBuffer (using fScratch as storage). 613 fWriteBuffer.reset(fScratch->data(), fScratchSize); 614 fFlattenProc(fWriteBuffer, &element); 615 const size_t bytesWritten = fWriteBuffer.bytesWritten(); 616 617 // If all the flattened bytes fit into fScratch, we can skip a call to writeToMemory. 618 if (!fWriteBuffer.wroteOnlyToStorage()) { 619 SkASSERT(bytesWritten > fScratchSize); 620 // It didn't all fit. Copy into a larger replacement SkFlatData. 621 // We can't just realloc because it might move the pointer and confuse writeToMemory. 622 SkFlatData* larger = AllocScratch(bytesWritten); 623 fWriteBuffer.writeToMemory(larger->data()); 624 625 // Carry on with this larger scratch to minimize the likelihood of future resizing. 626 sk_free(fScratch); 627 fScratchSize = bytesWritten; 628 fScratch = larger; 629 } 630 631 // The data is in fScratch now, but we need to stamp its header and trailing sentinel. 632 fScratch->stampHeaderAndSentinel(index, bytesWritten); 633 return *fScratch; 634 } 635 636 // This result is owned by fController and lives as long as it does (unless unalloc'd). 637 SkFlatData* detachScratch() { 638 // Allocate a new SkFlatData exactly big enough to hold our current scratch. 639 // We use the controller for this allocation to extend the allocation's lifetime and allow 640 // the controller to do whatever memory management it wants. 641 const size_t paddedSize = SizeWithPadding(fScratch->flatSize()); 642 SkFlatData* detached = (SkFlatData*)fController->allocThrow(paddedSize); 643 644 // Copy scratch into the new SkFlatData, setting the sentinel for cache storage. 645 memcpy(detached, fScratch, paddedSize); 646 detached->setSentinelInCache(); 647 648 // We can now reuse fScratch, and detached will live until fController dies. 649 return detached; 650 } 651 652 void unflatten(T* dst, const SkFlatData* element) const { 653 element->unflatten(dst, fUnflattenProc, 654 fController->getBitmapHeap(), 655 fController->getTypefacePlayback()); 656 } 657 658 void unflattenIntoArray(T* array) const { 659 const int count = fSortedData.count(); 660 SkASSERT(fIndexedData.count() == fSortedData.count()+1); 661 const SkFlatData* const* iter = fSortedData.begin(); 662 for (int i = 0; i < count; ++i) { 663 const SkFlatData* element = iter[i]; 664 int index = element->index() - 1; 665 SkASSERT((unsigned)index < (unsigned)count); 666 unflatten(&array[index], element); 667 } 668 } 669 670 SkAutoTUnref<SkFlatController> fController; 671 size_t fScratchSize; // How many bytes fScratch has allocated for data itself. 672 SkFlatData* fScratch; // Owned, must be freed with sk_free. 673 SkOrderedWriteBuffer fWriteBuffer; 674 bool fWriteBufferReady; 675 676 // SkFlatDictionary has two copies of the data one indexed by the 677 // SkFlatData's index and the other sorted. The sorted data is used 678 // for finding and uniquification while the indexed copy is used 679 // for standard array-style lookups based on the SkFlatData's index 680 // (as in 'unflatten'). 681 int fNextIndex; 682 SkTDArray<const SkFlatData*> fIndexedData; 683 // fSortedData is sorted by checksum/size/data. 684 SkTDArray<const SkFlatData*> fSortedData; 685 686 enum { 687 // Determined by trying diff values on picture-recording benchmarks 688 // (e.g. PictureRecordBench.cpp), choosing the smallest value that 689 // showed a big improvement. Even better would be to benchmark diff 690 // values on recording representative web-pages or other "real" content. 691 HASH_BITS = 7, 692 HASH_MASK = (1 << HASH_BITS) - 1, 693 HASH_COUNT = 1 << HASH_BITS 694 }; 695 const SkFlatData* fHash[HASH_COUNT]; 696 697 static int ChecksumToHashIndex(uint32_t checksum) { 698 int n = checksum; 699 if (HASH_BITS < 32) { 700 n ^= n >> 16; 701 } 702 if (HASH_BITS < 16) { 703 n ^= n >> 8; 704 } 705 if (HASH_BITS < 8) { 706 n ^= n >> 4; 707 } 708 return n & HASH_MASK; 709 } 710 }; 711 712 /////////////////////////////////////////////////////////////////////////////// 713 // Some common dictionaries are defined here for both reference and convenience 714 /////////////////////////////////////////////////////////////////////////////// 715 716 template <class T> 717 static void SkFlattenObjectProc(SkOrderedWriteBuffer& buffer, const void* obj) { 718 ((T*)obj)->flatten(buffer); 719 } 720 721 template <class T> 722 static void SkUnflattenObjectProc(SkOrderedReadBuffer& buffer, void* obj) { 723 ((T*)obj)->unflatten(buffer); 724 } 725 726 class SkChunkFlatController : public SkFlatController { 727 public: 728 SkChunkFlatController(size_t minSize) 729 : fHeap(minSize) 730 , fTypefaceSet(SkNEW(SkRefCntSet)) 731 , fLastAllocated(NULL) { 732 this->setTypefaceSet(fTypefaceSet); 733 this->setTypefacePlayback(&fTypefacePlayback); 734 } 735 736 virtual void* allocThrow(size_t bytes) SK_OVERRIDE { 737 fLastAllocated = fHeap.allocThrow(bytes); 738 return fLastAllocated; 739 } 740 741 virtual void unalloc(void* ptr) SK_OVERRIDE { 742 // fHeap can only free a pointer if it was the last one allocated. Otherwise, we'll just 743 // have to wait until fHeap is destroyed. 744 if (ptr == fLastAllocated) (void)fHeap.unalloc(ptr); 745 } 746 747 void setupPlaybacks() const { 748 fTypefacePlayback.reset(fTypefaceSet.get()); 749 } 750 751 void setBitmapStorage(SkBitmapHeap* heap) { 752 this->setBitmapHeap(heap); 753 } 754 755 private: 756 SkChunkAlloc fHeap; 757 SkAutoTUnref<SkRefCntSet> fTypefaceSet; 758 void* fLastAllocated; 759 mutable SkTypefacePlayback fTypefacePlayback; 760 }; 761 762 class SkMatrixDictionary : public SkFlatDictionary<SkMatrix> { 763 public: 764 // All matrices fit in 36 bytes. 765 SkMatrixDictionary(SkFlatController* controller) 766 : SkFlatDictionary<SkMatrix>(controller, 36) { 767 fFlattenProc = &flattenMatrix; 768 fUnflattenProc = &unflattenMatrix; 769 } 770 771 static void flattenMatrix(SkOrderedWriteBuffer& buffer, const void* obj) { 772 buffer.getWriter32()->writeMatrix(*(SkMatrix*)obj); 773 } 774 775 static void unflattenMatrix(SkOrderedReadBuffer& buffer, void* obj) { 776 buffer.getReader32()->readMatrix((SkMatrix*)obj); 777 } 778 }; 779 780 class SkPaintDictionary : public SkFlatDictionary<SkPaint> { 781 public: 782 // The largest paint across ~60 .skps was 500 bytes. 783 SkPaintDictionary(SkFlatController* controller) 784 : SkFlatDictionary<SkPaint>(controller, 512) { 785 fFlattenProc = &SkFlattenObjectProc<SkPaint>; 786 fUnflattenProc = &SkUnflattenObjectProc<SkPaint>; 787 } 788 }; 789 790 class SkRegionDictionary : public SkFlatDictionary<SkRegion> { 791 public: 792 SkRegionDictionary(SkFlatController* controller) 793 : SkFlatDictionary<SkRegion>(controller) { 794 fFlattenProc = &flattenRegion; 795 fUnflattenProc = &unflattenRegion; 796 } 797 798 static void flattenRegion(SkOrderedWriteBuffer& buffer, const void* obj) { 799 buffer.getWriter32()->writeRegion(*(SkRegion*)obj); 800 } 801 802 static void unflattenRegion(SkOrderedReadBuffer& buffer, void* obj) { 803 buffer.getReader32()->readRegion((SkRegion*)obj); 804 } 805 }; 806 807 #endif 808