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