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