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 "SkBitmap.h" 14 #include "SkBitmapHeap.h" 15 #include "SkChecksum.h" 16 #include "SkChunkAlloc.h" 17 #include "SkMatrix.h" 18 #include "SkOrderedReadBuffer.h" 19 #include "SkOrderedWriteBuffer.h" 20 #include "SkPaint.h" 21 #include "SkPath.h" 22 #include "SkPicture.h" 23 #include "SkPtrRecorder.h" 24 #include "SkRegion.h" 25 #include "SkTDynamicHash.h" 26 #include "SkTRefArray.h" 27 #include "SkTSearch.h" 28 29 enum DrawType { 30 UNUSED, 31 CLIP_PATH, 32 CLIP_REGION, 33 CLIP_RECT, 34 CLIP_RRECT, 35 CONCAT, 36 DRAW_BITMAP, 37 DRAW_BITMAP_MATRIX, 38 DRAW_BITMAP_NINE, 39 DRAW_BITMAP_RECT_TO_RECT, 40 DRAW_CLEAR, 41 DRAW_DATA, 42 DRAW_OVAL, 43 DRAW_PAINT, 44 DRAW_PATH, 45 DRAW_PICTURE, 46 DRAW_POINTS, 47 DRAW_POS_TEXT, 48 DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT 49 DRAW_POS_TEXT_H, 50 DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H 51 DRAW_RECT, 52 DRAW_RRECT, 53 DRAW_SPRITE, 54 DRAW_TEXT, 55 DRAW_TEXT_ON_PATH, 56 DRAW_TEXT_TOP_BOTTOM, // fast variant of DRAW_TEXT 57 DRAW_VERTICES, 58 RESTORE, 59 ROTATE, 60 SAVE, 61 SAVE_LAYER, 62 SCALE, 63 SET_MATRIX, 64 SKEW, 65 TRANSLATE, 66 NOOP, 67 BEGIN_COMMENT_GROUP, 68 COMMENT, 69 END_COMMENT_GROUP, 70 71 LAST_DRAWTYPE_ENUM = END_COMMENT_GROUP 72 }; 73 74 // In the 'match' method, this constant will match any flavor of DRAW_BITMAP* 75 static const int kDRAW_BITMAP_FLAVOR = LAST_DRAWTYPE_ENUM+1; 76 77 enum DrawVertexFlags { 78 DRAW_VERTICES_HAS_TEXS = 0x01, 79 DRAW_VERTICES_HAS_COLORS = 0x02, 80 DRAW_VERTICES_HAS_INDICES = 0x04 81 }; 82 83 /////////////////////////////////////////////////////////////////////////////// 84 // clipparams are packed in 5 bits 85 // doAA:1 | regionOp:4 86 87 static inline uint32_t ClipParams_pack(SkRegion::Op op, bool doAA) { 88 unsigned doAABit = doAA ? 1 : 0; 89 return (doAABit << 4) | op; 90 } 91 92 static inline SkRegion::Op ClipParams_unpackRegionOp(uint32_t packed) { 93 return (SkRegion::Op)(packed & 0xF); 94 } 95 96 static inline bool ClipParams_unpackDoAA(uint32_t packed) { 97 return SkToBool((packed >> 4) & 1); 98 } 99 100 /////////////////////////////////////////////////////////////////////////////// 101 102 class SkTypefacePlayback { 103 public: 104 SkTypefacePlayback(); 105 virtual ~SkTypefacePlayback(); 106 107 int count() const { return fCount; } 108 109 void reset(const SkRefCntSet*); 110 111 void setCount(int count); 112 SkRefCnt* set(int index, SkRefCnt*); 113 114 void setupBuffer(SkOrderedReadBuffer& buffer) const { 115 buffer.setTypefaceArray((SkTypeface**)fArray, fCount); 116 } 117 118 protected: 119 int fCount; 120 SkRefCnt** fArray; 121 }; 122 123 class SkFactoryPlayback { 124 public: 125 SkFactoryPlayback(int count) : fCount(count) { 126 fArray = SkNEW_ARRAY(SkFlattenable::Factory, count); 127 } 128 129 ~SkFactoryPlayback() { 130 SkDELETE_ARRAY(fArray); 131 } 132 133 SkFlattenable::Factory* base() const { return fArray; } 134 135 void setupBuffer(SkOrderedReadBuffer& buffer) const { 136 buffer.setFactoryPlayback(fArray, fCount); 137 } 138 139 private: 140 int fCount; 141 SkFlattenable::Factory* fArray; 142 }; 143 144 /////////////////////////////////////////////////////////////////////////////// 145 // 146 // 147 // The following templated classes provide an efficient way to store and compare 148 // objects that have been flattened (i.e. serialized in an ordered binary 149 // format). 150 // 151 // SkFlatData: is a simple indexable container for the flattened data 152 // which is agnostic to the type of data is is indexing. It is 153 // also responsible for flattening/unflattening objects but 154 // details of that operation are hidden in the provided procs 155 // SkFlatDictionary: is an abstract templated dictionary that maintains a 156 // searchable set of SkFlatData objects of type T. 157 // SkFlatController: is an interface provided to SkFlatDictionary which handles 158 // allocation (and unallocation in some cases). It also holds 159 // ref count recorders and the like. 160 // 161 // NOTE: any class that wishes to be used in conjunction with SkFlatDictionary 162 // must subclass the dictionary and provide the necessary flattening procs. 163 // The end of this header contains dictionary subclasses for some common classes 164 // like SkBitmap, SkMatrix, SkPaint, and SkRegion. SkFlatController must also 165 // be implemented, or SkChunkFlatController can be used to use an 166 // SkChunkAllocator and never do replacements. 167 // 168 // 169 /////////////////////////////////////////////////////////////////////////////// 170 171 class SkFlatData; 172 173 class SkFlatController : public SkRefCnt { 174 public: 175 SK_DECLARE_INST_COUNT(SkFlatController) 176 177 SkFlatController(); 178 virtual ~SkFlatController(); 179 /** 180 * Return a new block of memory for the SkFlatDictionary to use. 181 * This memory is owned by the controller and has the same lifetime unless you 182 * call unalloc(), in which case it may be freed early. 183 */ 184 virtual void* allocThrow(size_t bytes) = 0; 185 186 /** 187 * Hint that this block, which was allocated with allocThrow, is no longer needed. 188 * The implementation may choose to free this memory any time beteween now and destruction. 189 */ 190 virtual void unalloc(void* ptr) = 0; 191 192 /** 193 * Used during creation and unflattening of SkFlatData objects. If the 194 * objects being flattened contain bitmaps they are stored in this heap 195 * and the flattenable stores the index to the bitmap on the heap. 196 * This should be set by the protected setBitmapHeap. 197 */ 198 SkBitmapHeap* getBitmapHeap() { return fBitmapHeap; } 199 200 /** 201 * Used during creation of SkFlatData objects. If a typeface recorder is 202 * required to flatten the objects being flattened (i.e. for SkPaints), this 203 * should be set by the protected setTypefaceSet. 204 */ 205 SkRefCntSet* getTypefaceSet() { return fTypefaceSet; } 206 207 /** 208 * Used during unflattening of the SkFlatData objects in the 209 * SkFlatDictionary. Needs to be set by the protected setTypefacePlayback 210 * and needs to be reset to the SkRefCntSet passed to setTypefaceSet. 211 */ 212 SkTypefacePlayback* getTypefacePlayback() { return fTypefacePlayback; } 213 214 /** 215 * Optional factory recorder used during creation of SkFlatData objects. Set 216 * using the protected method setNamedFactorySet. 217 */ 218 SkNamedFactorySet* getNamedFactorySet() { return fFactorySet; } 219 220 /** 221 * Flags to use during creation of SkFlatData objects. Defaults to zero. 222 */ 223 uint32_t getWriteBufferFlags() { return fWriteBufferFlags; } 224 225 protected: 226 /** 227 * Set an SkBitmapHeap to be used to store/read SkBitmaps. Ref counted. 228 */ 229 void setBitmapHeap(SkBitmapHeap*); 230 231 /** 232 * Set an SkRefCntSet to be used to store SkTypefaces during flattening. Ref 233 * counted. 234 */ 235 void setTypefaceSet(SkRefCntSet*); 236 237 /** 238 * Set an SkTypefacePlayback to be used to find references to SkTypefaces 239 * during unflattening. Should be reset to the set provided to 240 * setTypefaceSet. 241 */ 242 void setTypefacePlayback(SkTypefacePlayback*); 243 244 /** 245 * Set an SkNamedFactorySet to be used to store Factorys and their 246 * corresponding names during flattening. Ref counted. Returns the same 247 * set as a convenience. 248 */ 249 SkNamedFactorySet* setNamedFactorySet(SkNamedFactorySet*); 250 251 /** 252 * Set the flags to be used during flattening. 253 */ 254 void setWriteBufferFlags(uint32_t flags) { fWriteBufferFlags = flags; } 255 256 private: 257 SkBitmapHeap* fBitmapHeap; 258 SkRefCntSet* fTypefaceSet; 259 SkTypefacePlayback* fTypefacePlayback; 260 SkNamedFactorySet* fFactorySet; 261 uint32_t fWriteBufferFlags; 262 263 typedef SkRefCnt INHERITED; 264 }; 265 266 class SkFlatData { 267 public: 268 // Flatten obj into an SkFlatData with this index. controller owns the SkFlatData*. 269 static SkFlatData* Create(SkFlatController* controller, 270 const void* obj, 271 int index, 272 void (*flattenProc)(SkOrderedWriteBuffer&, const void*)); 273 274 // Unflatten this into result, using bitmapHeap and facePlayback for bitmaps and fonts if given. 275 void unflatten(void* result, 276 void (*unflattenProc)(SkOrderedReadBuffer&, void*), 277 SkBitmapHeap* bitmapHeap = NULL, 278 SkTypefacePlayback* facePlayback = NULL) const; 279 280 // Do these contain the same data? Ignores index() and topBot(). 281 bool operator==(const SkFlatData& that) const { 282 if (this->checksum() != that.checksum() || this->flatSize() != that.flatSize()) { 283 return false; 284 } 285 return memcmp(this->data(), that.data(), this->flatSize()) == 0; 286 } 287 288 int index() const { return fIndex; } 289 const uint8_t* data() const { return (const uint8_t*)this + sizeof(*this); } 290 size_t flatSize() const { return fFlatSize; } 291 uint32_t checksum() const { return fChecksum; } 292 293 // Returns true if fTopBot[] has been recorded. 294 bool isTopBotWritten() const { 295 return !SkScalarIsNaN(fTopBot[0]); 296 } 297 298 // Returns fTopBot array, so it can be passed to a routine to compute them. 299 // For efficiency, we assert that fTopBot have not been recorded yet. 300 SkScalar* writableTopBot() const { 301 SkASSERT(!this->isTopBotWritten()); 302 return fTopBot; 303 } 304 305 // Return the topbot[] after it has been recorded. 306 const SkScalar* topBot() const { 307 SkASSERT(this->isTopBotWritten()); 308 return fTopBot; 309 } 310 311 private: 312 // For SkTDynamicHash. 313 static const SkFlatData& Identity(const SkFlatData& flat) { return flat; } 314 static uint32_t Hash(const SkFlatData& flat) { return flat.checksum(); } 315 static bool Equal(const SkFlatData& a, const SkFlatData& b) { return a == b; } 316 317 void setIndex(int index) { fIndex = index; } 318 uint8_t* data() { return (uint8_t*)this + sizeof(*this); } 319 320 // This assumes the payload flat data has already been written and does not modify it. 321 void stampHeader(int index, int32_t size) { 322 SkASSERT(SkIsAlign4(size)); 323 fIndex = index; 324 fFlatSize = size; 325 fTopBot[0] = SK_ScalarNaN; // Mark as unwritten. 326 fChecksum = SkChecksum::Compute((uint32_t*)this->data(), size); 327 } 328 329 int fIndex; 330 int32_t fFlatSize; 331 uint32_t fChecksum; 332 mutable SkScalar fTopBot[2]; // Cache of FontMetrics fTop, fBottom. Starts as [NaN,?]. 333 // uint32_t flattenedData[] implicitly hangs off the end. 334 335 template <class T> friend class SkFlatDictionary; 336 }; 337 338 template <class T> 339 class SkFlatDictionary { 340 static const size_t kWriteBufferGrowthBytes = 1024; 341 342 public: 343 SkFlatDictionary(SkFlatController* controller, size_t scratchSizeGuess = 0) 344 : fFlattenProc(NULL) 345 , fUnflattenProc(NULL) 346 , fController(SkRef(controller)) 347 , fScratchSize(scratchSizeGuess) 348 , fScratch(AllocScratch(fScratchSize)) 349 , fWriteBuffer(kWriteBufferGrowthBytes) 350 , fWriteBufferReady(false) { 351 this->reset(); 352 } 353 354 /** 355 * Clears the dictionary of all entries. However, it does NOT free the 356 * memory that was allocated for each entry (that's owned by controller). 357 */ 358 void reset() { 359 fIndexedData.rewind(); 360 // TODO(mtklein): There's no reason to have the index start from 1. Clean this up. 361 // index 0 is always empty since it is used as a signal that find failed 362 fIndexedData.push(NULL); 363 fNextIndex = 1; 364 } 365 366 ~SkFlatDictionary() { 367 sk_free(fScratch); 368 } 369 370 int count() const { 371 SkASSERT(fIndexedData.count() == fNextIndex); 372 SkASSERT(fHash.count() == fNextIndex - 1); 373 return fNextIndex - 1; 374 } 375 376 // For testing only. Index is zero-based. 377 const SkFlatData* operator[](int index) { 378 return fIndexedData[index+1]; 379 } 380 381 /** 382 * Given an element of type T return its 1-based index in the dictionary. If 383 * the element wasn't previously in the dictionary it is automatically 384 * added. 385 * 386 */ 387 int find(const T& element) { 388 return this->findAndReturnFlat(element)->index(); 389 } 390 391 /** 392 * Similar to find. Allows the caller to specify an SkFlatData to replace in 393 * the case of an add. Also tells the caller whether a new SkFlatData was 394 * added and whether the old one was replaced. The parameters added and 395 * replaced are required to be non-NULL. Rather than returning the index of 396 * the entry in the dictionary, it returns the actual SkFlatData. 397 */ 398 const SkFlatData* findAndReplace(const T& element, 399 const SkFlatData* toReplace, 400 bool* added, 401 bool* replaced) { 402 SkASSERT(added != NULL && replaced != NULL); 403 404 const int oldCount = this->count(); 405 SkFlatData* flat = this->findAndReturnMutableFlat(element); 406 *added = this->count() > oldCount; 407 408 // If we don't want to replace anything, we're done. 409 if (!*added || toReplace == NULL) { 410 *replaced = false; 411 return flat; 412 } 413 414 // If we don't have the thing to replace, we're done. 415 const SkFlatData* found = fHash.find(*toReplace); 416 if (found == NULL) { 417 *replaced = false; 418 return flat; 419 } 420 421 // findAndReturnMutableFlat gave us index (fNextIndex-1), but we'll use the old one. 422 fIndexedData.remove(flat->index()); 423 fNextIndex--; 424 flat->setIndex(found->index()); 425 fIndexedData[flat->index()] = flat; 426 427 // findAndReturnMutableFlat already called fHash.add(), so we just clean up the old entry. 428 fHash.remove(*found); 429 fController->unalloc((void*)found); 430 SkASSERT(this->count() == oldCount); 431 432 *replaced = true; 433 return flat; 434 } 435 436 /** 437 * Unflatten the objects and return them in SkTRefArray, or return NULL 438 * if there no objects. Caller takes ownership of result. 439 */ 440 SkTRefArray<T>* unflattenToArray() const { 441 const int count = this->count(); 442 if (count == 0) { 443 return NULL; 444 } 445 SkTRefArray<T>* array = SkTRefArray<T>::Create(count); 446 for (int i = 0; i < count; i++) { 447 this->unflatten(&array->writableAt(i), fIndexedData[i+1]); 448 } 449 return array; 450 } 451 452 /** 453 * Unflatten the specific object at the given index. 454 * Caller takes ownership of the result. 455 */ 456 T* unflatten(int index) const { 457 const SkFlatData* element = fIndexedData[index]; 458 SkASSERT(index == element->index()); 459 460 T* dst = new T; 461 this->unflatten(dst, element); 462 return dst; 463 } 464 465 /** 466 * Find or insert a flattened version of element into the dictionary. 467 * Caller does not take ownership of the result. This will not return NULL. 468 */ 469 const SkFlatData* findAndReturnFlat(const T& element) { 470 return this->findAndReturnMutableFlat(element); 471 } 472 473 protected: 474 void (*fFlattenProc)(SkOrderedWriteBuffer&, const void*); 475 void (*fUnflattenProc)(SkOrderedReadBuffer&, void*); 476 477 private: 478 // Layout: [ SkFlatData header, 20 bytes ] [ data ..., 4-byte aligned ] 479 static size_t SizeWithPadding(size_t flatDataSize) { 480 SkASSERT(SkIsAlign4(flatDataSize)); 481 return sizeof(SkFlatData) + flatDataSize; 482 } 483 484 // Allocate a new scratch SkFlatData. Must be sk_freed. 485 static SkFlatData* AllocScratch(size_t scratchSize) { 486 return (SkFlatData*) sk_malloc_throw(SizeWithPadding(scratchSize)); 487 } 488 489 // We have to delay fWriteBuffer's initialization until its first use; fController might not 490 // be fully set up by the time we get it in the constructor. 491 void lazyWriteBufferInit() { 492 if (fWriteBufferReady) { 493 return; 494 } 495 // Without a bitmap heap, we'll flatten bitmaps into paints. That's never what you want. 496 SkASSERT(fController->getBitmapHeap() != NULL); 497 fWriteBuffer.setBitmapHeap(fController->getBitmapHeap()); 498 fWriteBuffer.setTypefaceRecorder(fController->getTypefaceSet()); 499 fWriteBuffer.setNamedFactoryRecorder(fController->getNamedFactorySet()); 500 fWriteBuffer.setFlags(fController->getWriteBufferFlags()); 501 fWriteBufferReady = true; 502 } 503 504 // As findAndReturnFlat, but returns a mutable pointer for internal use. 505 SkFlatData* findAndReturnMutableFlat(const T& element) { 506 // Only valid until the next call to resetScratch(). 507 const SkFlatData& scratch = this->resetScratch(element, fNextIndex); 508 509 SkFlatData* candidate = fHash.find(scratch); 510 if (candidate != NULL) return candidate; 511 512 SkFlatData* detached = this->detachScratch(); 513 fHash.add(detached); 514 *fIndexedData.insert(fNextIndex) = detached; 515 fNextIndex++; 516 return detached; 517 } 518 519 // This reference is valid only until the next call to resetScratch() or detachScratch(). 520 const SkFlatData& resetScratch(const T& element, int index) { 521 this->lazyWriteBufferInit(); 522 523 // Flatten element into fWriteBuffer (using fScratch as storage). 524 fWriteBuffer.reset(fScratch->data(), fScratchSize); 525 fFlattenProc(fWriteBuffer, &element); 526 const size_t bytesWritten = fWriteBuffer.bytesWritten(); 527 528 // If all the flattened bytes fit into fScratch, we can skip a call to writeToMemory. 529 if (!fWriteBuffer.wroteOnlyToStorage()) { 530 SkASSERT(bytesWritten > fScratchSize); 531 // It didn't all fit. Copy into a larger replacement SkFlatData. 532 // We can't just realloc because it might move the pointer and confuse writeToMemory. 533 SkFlatData* larger = AllocScratch(bytesWritten); 534 fWriteBuffer.writeToMemory(larger->data()); 535 536 // Carry on with this larger scratch to minimize the likelihood of future resizing. 537 sk_free(fScratch); 538 fScratchSize = bytesWritten; 539 fScratch = larger; 540 } 541 542 // The data is in fScratch now but we need to stamp its header. 543 fScratch->stampHeader(index, bytesWritten); 544 return *fScratch; 545 } 546 547 // This result is owned by fController and lives as long as it does (unless unalloc'd). 548 SkFlatData* detachScratch() { 549 // Allocate a new SkFlatData exactly big enough to hold our current scratch. 550 // We use the controller for this allocation to extend the allocation's lifetime and allow 551 // the controller to do whatever memory management it wants. 552 const size_t paddedSize = SizeWithPadding(fScratch->flatSize()); 553 SkFlatData* detached = (SkFlatData*)fController->allocThrow(paddedSize); 554 555 // Copy scratch into the new SkFlatData. 556 memcpy(detached, fScratch, paddedSize); 557 558 // We can now reuse fScratch, and detached will live until fController dies. 559 return detached; 560 } 561 562 void unflatten(T* dst, const SkFlatData* element) const { 563 element->unflatten(dst, 564 fUnflattenProc, 565 fController->getBitmapHeap(), 566 fController->getTypefacePlayback()); 567 } 568 569 // All SkFlatData* stored in fIndexedData and fHash are owned by the controller. 570 SkAutoTUnref<SkFlatController> fController; 571 size_t fScratchSize; // How many bytes fScratch has allocated for data itself. 572 SkFlatData* fScratch; // Owned, must be freed with sk_free. 573 SkOrderedWriteBuffer fWriteBuffer; 574 bool fWriteBufferReady; 575 576 // We map between SkFlatData and a 1-based integer index. 577 int fNextIndex; 578 579 // For index -> SkFlatData. fIndexedData[0] is always NULL. 580 SkTDArray<const SkFlatData*> fIndexedData; 581 582 // For SkFlatData -> cached SkFlatData, which has index(). 583 SkTDynamicHash<SkFlatData, SkFlatData, 584 SkFlatData::Identity, SkFlatData::Hash, SkFlatData::Equal> fHash; 585 }; 586 587 /////////////////////////////////////////////////////////////////////////////// 588 // Some common dictionaries are defined here for both reference and convenience 589 /////////////////////////////////////////////////////////////////////////////// 590 591 template <class T> 592 static void SkFlattenObjectProc(SkOrderedWriteBuffer& buffer, const void* obj) { 593 ((T*)obj)->flatten(buffer); 594 } 595 596 template <class T> 597 static void SkUnflattenObjectProc(SkOrderedReadBuffer& buffer, void* obj) { 598 ((T*)obj)->unflatten(buffer); 599 } 600 601 class SkChunkFlatController : public SkFlatController { 602 public: 603 SkChunkFlatController(size_t minSize) 604 : fHeap(minSize) 605 , fTypefaceSet(SkNEW(SkRefCntSet)) 606 , fLastAllocated(NULL) { 607 this->setTypefaceSet(fTypefaceSet); 608 this->setTypefacePlayback(&fTypefacePlayback); 609 } 610 611 virtual void* allocThrow(size_t bytes) SK_OVERRIDE { 612 fLastAllocated = fHeap.allocThrow(bytes); 613 return fLastAllocated; 614 } 615 616 virtual void unalloc(void* ptr) SK_OVERRIDE { 617 // fHeap can only free a pointer if it was the last one allocated. Otherwise, we'll just 618 // have to wait until fHeap is destroyed. 619 if (ptr == fLastAllocated) (void)fHeap.unalloc(ptr); 620 } 621 622 void setupPlaybacks() const { 623 fTypefacePlayback.reset(fTypefaceSet.get()); 624 } 625 626 void setBitmapStorage(SkBitmapHeap* heap) { 627 this->setBitmapHeap(heap); 628 } 629 630 private: 631 SkChunkAlloc fHeap; 632 SkAutoTUnref<SkRefCntSet> fTypefaceSet; 633 void* fLastAllocated; 634 mutable SkTypefacePlayback fTypefacePlayback; 635 }; 636 637 class SkMatrixDictionary : public SkFlatDictionary<SkMatrix> { 638 public: 639 // All matrices fit in 36 bytes. 640 SkMatrixDictionary(SkFlatController* controller) 641 : SkFlatDictionary<SkMatrix>(controller, 36) { 642 fFlattenProc = &flattenMatrix; 643 fUnflattenProc = &unflattenMatrix; 644 } 645 646 static void flattenMatrix(SkOrderedWriteBuffer& buffer, const void* obj) { 647 buffer.getWriter32()->writeMatrix(*(SkMatrix*)obj); 648 } 649 650 static void unflattenMatrix(SkOrderedReadBuffer& buffer, void* obj) { 651 buffer.getReader32()->readMatrix((SkMatrix*)obj); 652 } 653 }; 654 655 class SkPaintDictionary : public SkFlatDictionary<SkPaint> { 656 public: 657 // The largest paint across ~60 .skps was 500 bytes. 658 SkPaintDictionary(SkFlatController* controller) 659 : SkFlatDictionary<SkPaint>(controller, 512) { 660 fFlattenProc = &SkFlattenObjectProc<SkPaint>; 661 fUnflattenProc = &SkUnflattenObjectProc<SkPaint>; 662 } 663 }; 664 665 class SkRegionDictionary : public SkFlatDictionary<SkRegion> { 666 public: 667 SkRegionDictionary(SkFlatController* controller) 668 : SkFlatDictionary<SkRegion>(controller) { 669 fFlattenProc = &flattenRegion; 670 fUnflattenProc = &unflattenRegion; 671 } 672 673 static void flattenRegion(SkOrderedWriteBuffer& buffer, const void* obj) { 674 buffer.getWriter32()->writeRegion(*(SkRegion*)obj); 675 } 676 677 static void unflattenRegion(SkOrderedReadBuffer& buffer, void* obj) { 678 buffer.getReader32()->readRegion((SkRegion*)obj); 679 } 680 }; 681 682 #endif 683