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