SkPictureFlat.h revision 7d3451b1844d8f93892f032bc060212f17173214
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 27enum 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 66 LAST_DRAWTYPE_ENUM = NOOP 67}; 68 69enum DrawVertexFlags { 70 DRAW_VERTICES_HAS_TEXS = 0x01, 71 DRAW_VERTICES_HAS_COLORS = 0x02, 72 DRAW_VERTICES_HAS_INDICES = 0x04 73}; 74 75/////////////////////////////////////////////////////////////////////////////// 76// clipparams are packed in 5 bits 77// doAA:1 | regionOp:4 78 79static inline uint32_t ClipParams_pack(SkRegion::Op op, bool doAA) { 80 unsigned doAABit = doAA ? 1 : 0; 81 return (doAABit << 4) | op; 82} 83 84static inline SkRegion::Op ClipParams_unpackRegionOp(uint32_t packed) { 85 return (SkRegion::Op)(packed & 0xF); 86} 87 88static inline bool ClipParams_unpackDoAA(uint32_t packed) { 89 return SkToBool((packed >> 4) & 1); 90} 91 92/////////////////////////////////////////////////////////////////////////////// 93 94class SkTypefacePlayback { 95public: 96 SkTypefacePlayback(); 97 virtual ~SkTypefacePlayback(); 98 99 int count() const { return fCount; } 100 101 void reset(const SkRefCntSet*); 102 103 void setCount(int count); 104 SkRefCnt* set(int index, SkRefCnt*); 105 106 void setupBuffer(SkOrderedReadBuffer& buffer) const { 107 buffer.setTypefaceArray((SkTypeface**)fArray, fCount); 108 } 109 110protected: 111 int fCount; 112 SkRefCnt** fArray; 113}; 114 115class SkFactoryPlayback { 116public: 117 SkFactoryPlayback(int count) : fCount(count) { 118 fArray = SkNEW_ARRAY(SkFlattenable::Factory, count); 119 } 120 121 ~SkFactoryPlayback() { 122 SkDELETE_ARRAY(fArray); 123 } 124 125 SkFlattenable::Factory* base() const { return fArray; } 126 127 void setupBuffer(SkOrderedReadBuffer& buffer) const { 128 buffer.setFactoryPlayback(fArray, fCount); 129 } 130 131private: 132 int fCount; 133 SkFlattenable::Factory* fArray; 134}; 135 136/////////////////////////////////////////////////////////////////////////////// 137// 138// 139// The following templated classes provide an efficient way to store and compare 140// objects that have been flattened (i.e. serialized in an ordered binary 141// format). 142// 143// SkFlatData: is a simple indexable container for the flattened data 144// which is agnostic to the type of data is is indexing. It is 145// also responsible for flattening/unflattening objects but 146// details of that operation are hidden in the provided procs 147// SkFlatDictionary: is an abstract templated dictionary that maintains a 148// searchable set of SkFlataData objects of type T. 149// SkFlatController: is an interface provided to SkFlatDictionary which handles 150// allocation and unallocation in some cases. It also holds 151// ref count recorders and the like. 152// 153// NOTE: any class that wishes to be used in conjunction with SkFlatDictionary 154// must subclass the dictionary and provide the necessary flattening procs. 155// The end of this header contains dictionary subclasses for some common classes 156// like SkBitmap, SkMatrix, SkPaint, and SkRegion. SkFlatController must also 157// be implemented, or SkChunkFlatController can be used to use an 158// SkChunkAllocator and never do replacements. 159// 160// 161/////////////////////////////////////////////////////////////////////////////// 162 163class SkFlatData; 164 165class SkFlatController : public SkRefCnt { 166public: 167 SK_DECLARE_INST_COUNT(SkFlatController) 168 169 SkFlatController(); 170 virtual ~SkFlatController(); 171 /** 172 * Provide a new block of memory for the SkFlatDictionary to use. 173 */ 174 virtual void* allocThrow(size_t bytes) = 0; 175 176 /** 177 * Unallocate a previously allocated block, returned by allocThrow. 178 * Implementation should at least perform an unallocation if passed the last 179 * pointer returned by allocThrow. If findAndReplace() is intended to be 180 * used, unalloc should also be able to unallocate the SkFlatData that is 181 * provided. 182 */ 183 virtual void unalloc(void* ptr) = 0; 184 185 /** 186 * Used during creation and unflattening of SkFlatData objects. If the 187 * objects being flattened contain bitmaps they are stored in this heap 188 * and the flattenable stores the index to the bitmap on the heap. 189 * This should be set by the protected setBitmapHeap. 190 */ 191 SkBitmapHeap* getBitmapHeap() { return fBitmapHeap; } 192 193 /** 194 * Used during creation of SkFlatData objects. If a typeface recorder is 195 * required to flatten the objects being flattened (i.e. for SkPaints), this 196 * should be set by the protected setTypefaceSet. 197 */ 198 SkRefCntSet* getTypefaceSet() { return fTypefaceSet; } 199 200 /** 201 * Used during unflattening of the SkFlatData objects in the 202 * SkFlatDictionary. Needs to be set by the protected setTypefacePlayback 203 * and needs to be reset to the SkRefCntSet passed to setTypefaceSet. 204 */ 205 SkTypefacePlayback* getTypefacePlayback() { return fTypefacePlayback; } 206 207 /** 208 * Optional factory recorder used during creation of SkFlatData objects. Set 209 * using the protected method setNamedFactorySet. 210 */ 211 SkNamedFactorySet* getNamedFactorySet() { return fFactorySet; } 212 213 /** 214 * Flags to use during creation of SkFlatData objects. Defaults to zero. 215 */ 216 uint32_t getWriteBufferFlags() { return fWriteBufferFlags; } 217 218protected: 219 /** 220 * Set an SkBitmapHeap to be used to store/read SkBitmaps. Ref counted. 221 */ 222 void setBitmapHeap(SkBitmapHeap*); 223 224 /** 225 * Set an SkRefCntSet to be used to store SkTypefaces during flattening. Ref 226 * counted. 227 */ 228 void setTypefaceSet(SkRefCntSet*); 229 230 /** 231 * Set an SkTypefacePlayback to be used to find references to SkTypefaces 232 * during unflattening. Should be reset to the set provided to 233 * setTypefaceSet. 234 */ 235 void setTypefacePlayback(SkTypefacePlayback*); 236 237 /** 238 * Set an SkNamedFactorySet to be used to store Factorys and their 239 * corresponding names during flattening. Ref counted. Returns the same 240 * set as a convenience. 241 */ 242 SkNamedFactorySet* setNamedFactorySet(SkNamedFactorySet*); 243 244 /** 245 * Set the flags to be used during flattening. 246 */ 247 void setWriteBufferFlags(uint32_t flags) { fWriteBufferFlags = flags; } 248 249private: 250 SkBitmapHeap* fBitmapHeap; 251 SkRefCntSet* fTypefaceSet; 252 SkTypefacePlayback* fTypefacePlayback; 253 SkNamedFactorySet* fFactorySet; 254 uint32_t fWriteBufferFlags; 255 256 typedef SkRefCnt INHERITED; 257}; 258 259class SkFlatData { 260public: 261 /** 262 * Compare two SkFlatData ptrs, returning -1, 0, 1 to allow them to be 263 * sorted. 264 * 265 * Note: this assumes that a and b have different sentinel values, either 266 * InCache or AsCandidate, otherwise the loop will go beyond the end of 267 * the buffers. 268 * 269 * dataToCompare() returns 2 fields before the flattened data: 270 * - checksum 271 * - size 272 * This ensures that if we see two blocks of different length, we will 273 * notice that right away, and not read any further. It also ensures that 274 * we see the checksum right away, so that most of the time it is enough 275 * to short-circuit our comparison. 276 */ 277 static int Compare(const SkFlatData* a, const SkFlatData* b) { 278 const uint32_t* stop = a->dataStop(); 279 const uint32_t* a_ptr = a->dataToCompare() - 1; 280 const uint32_t* b_ptr = b->dataToCompare() - 1; 281 // We use -1 above, so we can pre-increment our pointers in the loop 282 while (*++a_ptr == *++b_ptr) {} 283 284 if (a_ptr == stop) { // sentinel 285 SkASSERT(b->dataStop() == b_ptr); 286 return 0; 287 } 288 SkASSERT(a_ptr < a->dataStop()); 289 SkASSERT(b_ptr < b->dataStop()); 290 return (*a_ptr < *b_ptr) ? -1 : 1; 291 } 292 293 int index() const { return fIndex; } 294 const void* data() const { return (const char*)this + sizeof(*this); } 295 void* data() { return (char*)this + sizeof(*this); } 296 // Our data is always 32bit aligned, so we can offer this accessor 297 uint32_t* data32() { return (uint32_t*)this->data(); } 298 // Returns the size of the flattened data. 299 size_t flatSize() const { return fFlatSize; } 300 301 void setSentinelInCache() { 302 this->setSentinel(kInCache_Sentinel); 303 } 304 void setSentinelAsCandidate() { 305 this->setSentinel(kCandidate_Sentinel); 306 } 307 308 uint32_t checksum() const { return fChecksum; } 309 310#ifdef SK_DEBUG_SIZE 311 // returns the logical size of our data. Does not return any sentinel or 312 // padding we might have. 313 size_t size() const { 314 return sizeof(SkFlatData) + fFlatSize; 315 } 316#endif 317 318 static SkFlatData* Create(SkFlatController* controller, const void* obj, int index, 319 void (*flattenProc)(SkOrderedWriteBuffer&, const void*)); 320 321 void unflatten(void* result, 322 void (*unflattenProc)(SkOrderedReadBuffer&, void*), 323 SkBitmapHeap* bitmapHeap = NULL, 324 SkTypefacePlayback* facePlayback = NULL) const; 325 326 // When we purge an entry, we want to reuse an old index for the new entry, 327 // so we expose this setter. 328 void setIndex(int index) { fIndex = index; } 329 330 // for unittesting 331 friend bool operator==(const SkFlatData& a, const SkFlatData& b) { 332 size_t N = (const char*)a.dataStop() - (const char*)a.dataToCompare(); 333 return !memcmp(a.dataToCompare(), b.dataToCompare(), N); 334 } 335 336 // returns true if fTopBot[] has been recorded 337 bool isTopBotWritten() const { 338 return !SkScalarIsNaN(fTopBot[0]); 339 } 340 341 // Returns fTopBot array, so it can be passed to a routine to compute them. 342 // For efficiency, we assert that fTopBot have not been recorded yet. 343 SkScalar* writableTopBot() const { 344 SkASSERT(!this->isTopBotWritten()); 345 return fTopBot; 346 } 347 348 // return the topbot[] after it has been recorded 349 const SkScalar* topBot() const { 350 SkASSERT(this->isTopBotWritten()); 351 return fTopBot; 352 } 353 354private: 355 // This is *not* part of the key for search/sort 356 int fIndex; 357 358 // Cache of paint's FontMetrics fTop,fBottom 359 // initialied to [NaN,NaN] as a sentinel that they have not been recorded yet 360 // 361 // This is *not* part of the key for search/sort 362 mutable SkScalar fTopBot[2]; 363 364 // marks fTopBot[] as unrecorded 365 void setTopBotUnwritten() { 366 this->fTopBot[0] = SK_ScalarNaN; // initial to sentinel values 367 } 368 369 // From here down is the data we look at in the search/sort. We always begin 370 // with the checksum and then length. 371 uint32_t fChecksum; 372 int32_t fFlatSize; // size of flattened data 373 // uint32_t flattenedData[] 374 // uint32_t sentinelValue 375 376 const uint32_t* dataToCompare() const { 377 return (const uint32_t*)&fChecksum; 378 } 379 const uint32_t* dataStop() const { 380 SkASSERT(SkIsAlign4(fFlatSize)); 381 return (const uint32_t*)((const char*)this->data() + fFlatSize); 382 } 383 384 enum { 385 kInCache_Sentinel = 0, 386 kCandidate_Sentinel = ~0U, 387 }; 388 void setSentinel(uint32_t value) { 389 SkASSERT(SkIsAlign4(fFlatSize)); 390 this->data32()[fFlatSize >> 2] = value; 391 } 392}; 393 394template <class T> 395class SkFlatDictionary { 396public: 397 SkFlatDictionary(SkFlatController* controller) 398 : fController(controller) { 399 fFlattenProc = NULL; 400 fUnflattenProc = NULL; 401 SkASSERT(controller); 402 fController->ref(); 403 // set to 1 since returning a zero from find() indicates failure 404 fNextIndex = 1; 405 sk_bzero(fHash, sizeof(fHash)); 406 // index 0 is always empty since it is used as a signal that find failed 407 fIndexedData.push(NULL); 408 } 409 410 virtual ~SkFlatDictionary() { 411 fController->unref(); 412 } 413 414 int count() const { 415 SkASSERT(fIndexedData.count() == fSortedData.count()+1); 416 return fSortedData.count(); 417 } 418 419 const SkFlatData* operator[](int index) const { 420 SkASSERT(index >= 0 && index < fSortedData.count()); 421 return fSortedData[index]; 422 } 423 424 /** 425 * Clears the dictionary of all entries. However, it does NOT free the 426 * memory that was allocated for each entry. 427 */ 428 void reset() { 429 fSortedData.reset(); 430 fIndexedData.rewind(); 431 // index 0 is always empty since it is used as a signal that find failed 432 fIndexedData.push(NULL); 433 fNextIndex = 1; 434 sk_bzero(fHash, sizeof(fHash)); 435 } 436 437 /** 438 * Similar to find. Allows the caller to specify an SkFlatData to replace in 439 * the case of an add. Also tells the caller whether a new SkFlatData was 440 * added and whether the old one was replaced. The parameters added and 441 * replaced are required to be non-NULL. Rather than returning the index of 442 * the entry in the dictionary, it returns the actual SkFlatData. 443 */ 444 const SkFlatData* findAndReplace(const T& element, 445 const SkFlatData* toReplace, bool* added, 446 bool* replaced) { 447 SkASSERT(added != NULL && replaced != NULL); 448 int oldCount = fSortedData.count(); 449 const SkFlatData* flat = this->findAndReturnFlat(element); 450 *added = fSortedData.count() == oldCount + 1; 451 *replaced = false; 452 if (*added && toReplace != NULL) { 453 // First, find the index of the one to replace 454 int indexToReplace = fSortedData.find(toReplace); 455 if (indexToReplace >= 0) { 456 // findAndReturnFlat set the index to fNextIndex and increased 457 // fNextIndex by one. Reuse the index from the one being 458 // replaced and reset fNextIndex to the proper value. 459 int oldIndex = flat->index(); 460 const_cast<SkFlatData*>(flat)->setIndex(toReplace->index()); 461 fIndexedData[toReplace->index()] = flat; 462 fNextIndex--; 463 // Remove from the arrays. 464 fSortedData.remove(indexToReplace); 465 fIndexedData.remove(oldIndex); 466 // Remove from the hash table. 467 int oldHash = ChecksumToHashIndex(toReplace->checksum()); 468 if (fHash[oldHash] == toReplace) { 469 fHash[oldHash] = NULL; 470 } 471 // Delete the actual object. 472 fController->unalloc((void*)toReplace); 473 *replaced = true; 474 SkASSERT(fIndexedData.count() == fSortedData.count()+1); 475 } 476 } 477 return flat; 478 } 479 480 /** 481 * Given an element of type T return its 1-based index in the dictionary. If 482 * the element wasn't previously in the dictionary it is automatically 483 * added. 484 * 485 * To make the Compare function fast, we write a sentinel value at the end 486 * of each block. The blocks in our fSortedData[] all have a 0 sentinel. The 487 * newly created block we're comparing against has a -1 in the sentinel. 488 * 489 * This trick allows Compare to always loop until failure. If it fails on 490 * the sentinal value, we know the blocks are equal. 491 */ 492 int find(const T& element) { 493 return this->findAndReturnFlat(element)->index(); 494 } 495 496 /** 497 * Unflatten the objects and return them in SkTRefArray, or return NULL 498 * if there no objects (instead of an empty array). 499 */ 500 SkTRefArray<T>* unflattenToArray() const { 501 int count = fSortedData.count(); 502 SkTRefArray<T>* array = NULL; 503 if (count > 0) { 504 array = SkTRefArray<T>::Create(count); 505 this->unflattenIntoArray(&array->writableAt(0)); 506 } 507 return array; 508 } 509 510 /** 511 * Unflatten the specific object at the given index 512 */ 513 T* unflatten(int index) const { 514 SkASSERT(fIndexedData.count() == fSortedData.count()+1); 515 const SkFlatData* element = fIndexedData[index]; 516 SkASSERT(index == element->index()); 517 518 T* dst = new T; 519 this->unflatten(dst, element); 520 return dst; 521 } 522 523 const SkFlatData* findAndReturnFlat(const T& element) { 524 SkFlatData* flat = SkFlatData::Create(fController, &element, fNextIndex, fFlattenProc); 525 526 int hashIndex = ChecksumToHashIndex(flat->checksum()); 527 const SkFlatData* candidate = fHash[hashIndex]; 528 if (candidate && !SkFlatData::Compare(flat, candidate)) { 529 fController->unalloc(flat); 530 return candidate; 531 } 532 533 int index = SkTSearch<SkFlatData>((const SkFlatData**) fSortedData.begin(), 534 fSortedData.count(), flat, sizeof(flat), 535 &SkFlatData::Compare); 536 if (index >= 0) { 537 fController->unalloc(flat); 538 fHash[hashIndex] = fSortedData[index]; 539 return fSortedData[index]; 540 } 541 542 index = ~index; 543 *fSortedData.insert(index) = flat; 544 *fIndexedData.insert(flat->index()) = flat; 545 SkASSERT(fSortedData.count() == fNextIndex); 546 fNextIndex++; 547 flat->setSentinelInCache(); 548 fHash[hashIndex] = flat; 549 SkASSERT(fIndexedData.count() == fSortedData.count()+1); 550 return flat; 551 } 552 553protected: 554 void (*fFlattenProc)(SkOrderedWriteBuffer&, const void*); 555 void (*fUnflattenProc)(SkOrderedReadBuffer&, void*); 556 557private: 558 void unflatten(T* dst, const SkFlatData* element) const { 559 element->unflatten(dst, fUnflattenProc, 560 fController->getBitmapHeap(), 561 fController->getTypefacePlayback()); 562 } 563 564 void unflattenIntoArray(T* array) const { 565 const int count = fSortedData.count(); 566 SkASSERT(fIndexedData.count() == fSortedData.count()+1); 567 const SkFlatData* const* iter = fSortedData.begin(); 568 for (int i = 0; i < count; ++i) { 569 const SkFlatData* element = iter[i]; 570 int index = element->index() - 1; 571 SkASSERT((unsigned)index < (unsigned)count); 572 unflatten(&array[index], element); 573 } 574 } 575 576 SkFlatController * const fController; 577 int fNextIndex; 578 579 // SkFlatDictionary has two copies of the data one indexed by the 580 // SkFlatData's index and the other sorted. The sorted data is used 581 // for finding and uniquification while the indexed copy is used 582 // for standard array-style lookups based on the SkFlatData's index 583 // (as in 'unflatten'). 584 SkTDArray<const SkFlatData*> fIndexedData; 585 // fSortedData is sorted by checksum/size/data. 586 SkTDArray<const SkFlatData*> fSortedData; 587 588 enum { 589 // Determined by trying diff values on picture-recording benchmarks 590 // (e.g. PictureRecordBench.cpp), choosing the smallest value that 591 // showed a big improvement. Even better would be to benchmark diff 592 // values on recording representative web-pages or other "real" content. 593 HASH_BITS = 7, 594 HASH_MASK = (1 << HASH_BITS) - 1, 595 HASH_COUNT = 1 << HASH_BITS 596 }; 597 const SkFlatData* fHash[HASH_COUNT]; 598 599 static int ChecksumToHashIndex(uint32_t checksum) { 600 int n = checksum; 601 if (HASH_BITS < 32) { 602 n ^= n >> 16; 603 } 604 if (HASH_BITS < 16) { 605 n ^= n >> 8; 606 } 607 if (HASH_BITS < 8) { 608 n ^= n >> 4; 609 } 610 return n & HASH_MASK; 611 } 612}; 613 614/////////////////////////////////////////////////////////////////////////////// 615// Some common dictionaries are defined here for both reference and convenience 616/////////////////////////////////////////////////////////////////////////////// 617 618template <class T> 619static void SkFlattenObjectProc(SkOrderedWriteBuffer& buffer, const void* obj) { 620 ((T*)obj)->flatten(buffer); 621} 622 623template <class T> 624static void SkUnflattenObjectProc(SkOrderedReadBuffer& buffer, void* obj) { 625 ((T*)obj)->unflatten(buffer); 626} 627 628class SkChunkFlatController : public SkFlatController { 629public: 630 SkChunkFlatController(size_t minSize) 631 : fHeap(minSize) 632 , fTypefaceSet(SkNEW(SkRefCntSet)) { 633 this->setTypefaceSet(fTypefaceSet); 634 this->setTypefacePlayback(&fTypefacePlayback); 635 } 636 637 ~SkChunkFlatController() { 638 fTypefaceSet->unref(); 639 } 640 641 virtual void* allocThrow(size_t bytes) SK_OVERRIDE { 642 return fHeap.allocThrow(bytes); 643 } 644 645 virtual void unalloc(void* ptr) SK_OVERRIDE { 646 (void) fHeap.unalloc(ptr); 647 } 648 649 void setupPlaybacks() const { 650 fTypefacePlayback.reset(fTypefaceSet); 651 } 652 653 void setBitmapStorage(SkBitmapHeap* heap) { 654 this->setBitmapHeap(heap); 655 } 656 657private: 658 SkChunkAlloc fHeap; 659 SkRefCntSet* fTypefaceSet; 660 mutable SkTypefacePlayback fTypefacePlayback; 661}; 662 663class SkBitmapDictionary : public SkFlatDictionary<SkBitmap> { 664public: 665 SkBitmapDictionary(SkFlatController* controller) 666 : SkFlatDictionary<SkBitmap>(controller) { 667 fFlattenProc = &SkFlattenObjectProc<SkBitmap>; 668 fUnflattenProc = &SkUnflattenObjectProc<SkBitmap>; 669 } 670}; 671 672class SkMatrixDictionary : public SkFlatDictionary<SkMatrix> { 673 public: 674 SkMatrixDictionary(SkFlatController* controller) 675 : SkFlatDictionary<SkMatrix>(controller) { 676 fFlattenProc = &flattenMatrix; 677 fUnflattenProc = &unflattenMatrix; 678 } 679 680 static void flattenMatrix(SkOrderedWriteBuffer& buffer, const void* obj) { 681 buffer.getWriter32()->writeMatrix(*(SkMatrix*)obj); 682 } 683 684 static void unflattenMatrix(SkOrderedReadBuffer& buffer, void* obj) { 685 buffer.getReader32()->readMatrix((SkMatrix*)obj); 686 } 687}; 688 689class SkPaintDictionary : public SkFlatDictionary<SkPaint> { 690 public: 691 SkPaintDictionary(SkFlatController* controller) 692 : SkFlatDictionary<SkPaint>(controller) { 693 fFlattenProc = &SkFlattenObjectProc<SkPaint>; 694 fUnflattenProc = &SkUnflattenObjectProc<SkPaint>; 695 } 696}; 697 698class SkRegionDictionary : public SkFlatDictionary<SkRegion> { 699 public: 700 SkRegionDictionary(SkFlatController* controller) 701 : SkFlatDictionary<SkRegion>(controller) { 702 fFlattenProc = &flattenRegion; 703 fUnflattenProc = &unflattenRegion; 704 } 705 706 static void flattenRegion(SkOrderedWriteBuffer& buffer, const void* obj) { 707 buffer.getWriter32()->writeRegion(*(SkRegion*)obj); 708 } 709 710 static void unflattenRegion(SkOrderedReadBuffer& buffer, void* obj) { 711 buffer.getReader32()->readRegion((SkRegion*)obj); 712 } 713}; 714 715#endif 716