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 "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