Refactor dictionaries for use by entities other than just SkPicture
Review URL: https://codereview.appspot.com/6101043

git-svn-id: http://skia.googlecode.com/svn/trunk@4077 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkOrderedWriteBuffer.h b/include/core/SkOrderedWriteBuffer.h
index 51daf5c..947877b 100644
--- a/include/core/SkOrderedWriteBuffer.h
+++ b/include/core/SkOrderedWriteBuffer.h
@@ -22,6 +22,8 @@
                          size_t storageSize);
     virtual ~SkOrderedWriteBuffer() {}
 
+    SkWriter32* getWriter32() { return &fWriter; }
+
     // deprecated naming convention that will be removed after callers are updated
     virtual bool writeBool(bool value) { return fWriter.writeBool(value); }
     virtual void writeInt(int32_t value) { fWriter.writeInt(value); }
diff --git a/src/core/SkPictureFlat.cpp b/src/core/SkPictureFlat.cpp
index 68a6651..edbf4e3 100644
--- a/src/core/SkPictureFlat.cpp
+++ b/src/core/SkPictureFlat.cpp
@@ -14,216 +14,7 @@
 #include "SkShader.h"
 #include "SkTypeface.h"
 #include "SkXfermode.h"
-#include "SkOrderedReadBuffer.h"
-#include "SkOrderedWriteBuffer.h"
 
-SkFlatData* SkFlatData::Alloc(SkChunkAlloc* heap, int32_t size, int index) {
-    SkFlatData* result = (SkFlatData*) heap->allocThrow(size + sizeof(SkFlatData));
-    result->fIndex = index;
-    result->fAllocSize = size + sizeof(result->fAllocSize);
-    return result;
-}
-
-SkFlatBitmap* SkFlatBitmap::Flatten(SkChunkAlloc* heap, const SkBitmap& bitmap,
-                                    int index, SkRefCntSet* rec) {
-    SkOrderedWriteBuffer buffer(1024);
-    buffer.setRefCntRecorder(rec);
-    
-    bitmap.flatten(buffer);
-    size_t size = buffer.size();
-    SkFlatBitmap* result = (SkFlatBitmap*) INHERITED::Alloc(heap, size, index);
-    buffer.flatten(result->fBitmapData);
-    return result;
-}
-
-SkFlatMatrix* SkFlatMatrix::Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index) {
-    size_t size = matrix.flatten(NULL);
-    SkFlatMatrix* result = (SkFlatMatrix*) INHERITED::Alloc(heap, size, index);
-    matrix.flatten(&result->fMatrixData);
-    return result;
-}
-
-#ifdef SK_DEBUG_DUMP
-void SkFlatMatrix::dump() const {
-    const SkMatrix* matrix = (const SkMatrix*) fMatrixData;
-    char pBuffer[DUMP_BUFFER_SIZE];
-    char* bufferPtr = pBuffer;
-    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-    "matrix: ");
-    SkScalar scaleX = matrix->getScaleX();
-    SkMatrix defaultMatrix;
-    defaultMatrix.reset();
-    if (scaleX != defaultMatrix.getScaleX())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "scaleX:%g ", SkScalarToFloat(scaleX));
-    SkScalar scaleY = matrix->getScaleY();
-    if (scaleY != defaultMatrix.getScaleY())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "scaleY:%g ", SkScalarToFloat(scaleY));
-    SkScalar skewX = matrix->getSkewX();
-    if (skewX != defaultMatrix.getSkewX())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "skewX:%g ", SkScalarToFloat(skewX));
-    SkScalar skewY = matrix->getSkewY();
-    if (skewY != defaultMatrix.getSkewY())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "skewY:%g ", SkScalarToFloat(skewY));
-    SkScalar translateX = matrix->getTranslateX();
-    if (translateX != defaultMatrix.getTranslateX())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "translateX:%g ", SkScalarToFloat(translateX));
-    SkScalar translateY = matrix->getTranslateY();
-    if (translateY != defaultMatrix.getTranslateY())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "translateY:%g ", SkScalarToFloat(translateY));
-    SkScalar perspX = matrix->getPerspX();
-    if (perspX != defaultMatrix.getPerspX())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "perspX:%g ", SkFractToFloat(perspX));
-    SkScalar perspY = matrix->getPerspY();
-    if (perspY != defaultMatrix.getPerspY())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "perspY:%g ", SkFractToFloat(perspY));
-    SkDebugf("%s\n", pBuffer);
-}
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkFlatPaint* SkFlatPaint::Flatten(SkChunkAlloc* heap, const SkPaint& paint,
-                                  int index, SkRefCntSet* rec,
-                                  SkRefCntSet* faceRecorder) {
-    intptr_t storage[256];
-    SkOrderedWriteBuffer buffer(4*sizeof(SkPaint), storage, sizeof(storage));
-
-    buffer.setRefCntRecorder(rec);
-    buffer.setTypefaceRecorder(faceRecorder);
-
-    paint.flatten(buffer);
-    uint32_t size = buffer.size();
-    SkFlatPaint* result = (SkFlatPaint*) INHERITED::Alloc(heap, size, index);
-    buffer.flatten(&result->fPaintData);
-    return result;
-}
-    
-void SkFlatPaint::Read(const void* storage, SkPaint* paint,
-                   SkRefCntPlayback* rcp, SkTypefacePlayback* facePlayback) {
-    SkOrderedReadBuffer buffer(storage, 1024*1024);
-    if (rcp) {
-        rcp->setupBuffer(buffer);
-    }
-    if (facePlayback) {
-        facePlayback->setupBuffer(buffer);
-    }
-    paint->unflatten(buffer);
-}
-
-#ifdef SK_DEBUG_DUMP
-void SkFlatPaint::dump() const {
-    SkPaint defaultPaint;
-    SkFlattenableReadBuffer buffer(fPaintData);
-    SkTypeface* typeface = (SkTypeface*) buffer.readPtr();
-    char pBuffer[DUMP_BUFFER_SIZE];
-    char* bufferPtr = pBuffer;
-    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-        "paint: ");
-    if (typeface != defaultPaint.getTypeface())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "typeface:%p ", typeface);
-    SkScalar textSize = buffer.readScalar();
-    if (textSize != defaultPaint.getTextSize())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "textSize:%g ", SkScalarToFloat(textSize));
-    SkScalar textScaleX = buffer.readScalar();
-    if (textScaleX != defaultPaint.getTextScaleX())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "textScaleX:%g ", SkScalarToFloat(textScaleX));
-    SkScalar textSkewX = buffer.readScalar();
-    if (textSkewX != defaultPaint.getTextSkewX())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "textSkewX:%g ", SkScalarToFloat(textSkewX));
-    const SkPathEffect* pathEffect = (const SkPathEffect*) buffer.readFlattenable();
-    if (pathEffect != defaultPaint.getPathEffect())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "pathEffect:%p ", pathEffect);
-    SkDELETE(pathEffect);
-    const SkShader* shader = (const SkShader*) buffer.readFlattenable();
-    if (shader != defaultPaint.getShader())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "shader:%p ", shader);
-    SkDELETE(shader);
-    const SkXfermode* xfermode = (const SkXfermode*) buffer.readFlattenable();
-    if (xfermode != defaultPaint.getXfermode())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "xfermode:%p ", xfermode);
-    SkDELETE(xfermode);
-    const SkMaskFilter* maskFilter = (const SkMaskFilter*) buffer.readFlattenable();
-    if (maskFilter != defaultPaint.getMaskFilter())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "maskFilter:%p ", maskFilter);
-    SkDELETE(maskFilter);
-    const SkColorFilter* colorFilter = (const SkColorFilter*) buffer.readFlattenable();
-    if (colorFilter != defaultPaint.getColorFilter())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "colorFilter:%p ", colorFilter);
-    SkDELETE(colorFilter);
-    const SkRasterizer* rasterizer = (const SkRasterizer*) buffer.readFlattenable();
-    if (rasterizer != defaultPaint.getRasterizer())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "rasterizer:%p ", rasterizer);
-    SkDELETE(rasterizer);
-    const SkDrawLooper* drawLooper = (const SkDrawLooper*) buffer.readFlattenable();
-    if (drawLooper != defaultPaint.getLooper())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "drawLooper:%p ", drawLooper);
-    SkDELETE(drawLooper);
-    unsigned color = buffer.readU32();
-    if (color != defaultPaint.getColor())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "color:0x%x ", color);
-    SkScalar strokeWidth = buffer.readScalar();
-    if (strokeWidth != defaultPaint.getStrokeWidth())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "strokeWidth:%g ", SkScalarToFloat(strokeWidth));
-    SkScalar strokeMiter = buffer.readScalar();
-    if (strokeMiter != defaultPaint.getStrokeMiter())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "strokeMiter:%g ", SkScalarToFloat(strokeMiter));
-    unsigned flags = buffer.readU16();
-    if (flags != defaultPaint.getFlags())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "flags:0x%x ", flags);
-    int align = buffer.readU8();
-    if (align != defaultPaint.getTextAlign())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "align:0x%x ", align);
-    int strokeCap = buffer.readU8();
-    if (strokeCap != defaultPaint.getStrokeCap())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "strokeCap:0x%x ", strokeCap);
-    int strokeJoin = buffer.readU8();
-    if (strokeJoin != defaultPaint.getStrokeJoin())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "align:0x%x ", strokeJoin);
-    int style = buffer.readU8();
-    if (style != defaultPaint.getStyle())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "style:0x%x ", style);
-    int textEncoding = buffer.readU8();
-    if (textEncoding != defaultPaint.getTextEncoding())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "textEncoding:0x%x ", textEncoding);
-    SkDebugf("%s\n", pBuffer);
-}
-#endif
-
-SkFlatRegion* SkFlatRegion::Flatten(SkChunkAlloc* heap, const SkRegion& region, int index) {
-    uint32_t size = region.flatten(NULL);
-    SkFlatRegion* result = (SkFlatRegion*) INHERITED::Alloc(heap, size, index);
-    region.flatten(&result->fRegionData);
-    return result;
-}
-    
 ///////////////////////////////////////////////////////////////////////////////
 
 SkRefCntPlayback::SkRefCntPlayback() : fCount(0), fArray(NULL) {}
@@ -266,3 +57,49 @@
     return obj;
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
+SkFlatData* SkFlatData::Create(SkChunkAlloc* heap, const void* obj,
+        int index, void (*flattenProc)(SkOrderedWriteBuffer&, const void*),
+        SkRefCntSet* refCntRecorder, SkRefCntSet* faceRecorder) {
+
+    // a buffer of 256 bytes should be sufficient for most paints, regions,
+    // and matrices.
+    intptr_t storage[256];
+    SkOrderedWriteBuffer buffer(256, storage, sizeof(storage));
+    if (refCntRecorder) {
+        buffer.setRefCntRecorder(refCntRecorder);
+    }
+    if (faceRecorder) {
+        buffer.setTypefaceRecorder(faceRecorder);
+    }
+
+    flattenProc(buffer, obj);
+    uint32_t size = buffer.size();
+
+    // allocate the enough memory to hold both SkFlatData and the serialized
+    // contents
+    SkFlatData* result = (SkFlatData*) heap->allocThrow(size + sizeof(SkFlatData));
+    result->fIndex = index;
+    result->fAllocSize = size;
+
+    // put the serialized contents into the data section of the new allocation
+    buffer.flatten(result->data());
+    return result;
+}
+
+void SkFlatData::unflatten(void* result,
+        void (*unflattenProc)(SkOrderedReadBuffer&, void*),
+        SkRefCntPlayback* refCntPlayback,
+        SkTypefacePlayback* facePlayback) const {
+
+    SkOrderedReadBuffer buffer(this->data(), fAllocSize);
+    if (refCntPlayback) {
+        refCntPlayback->setupBuffer(buffer);
+    }
+    if (facePlayback) {
+        facePlayback->setupBuffer(buffer);
+    }
+    unflattenProc(buffer, result);
+    SkASSERT(fAllocSize == (int32_t)buffer.offset());
+}
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index 10432c2..e0a5b4f 100644
--- a/src/core/SkPictureFlat.h
+++ b/src/core/SkPictureFlat.h
@@ -11,11 +11,13 @@
 #include "SkChunkAlloc.h"
 #include "SkBitmap.h"
 #include "SkOrderedReadBuffer.h"
+#include "SkOrderedWriteBuffer.h"
 #include "SkPicture.h"
 #include "SkMatrix.h"
 #include "SkPaint.h"
 #include "SkPath.h"
 #include "SkRegion.h"
+#include "SkTSearch.h"
 
 enum DrawType {
     UNUSED,
@@ -127,112 +129,193 @@
     SkFlattenable::Factory* fArray;
 };
 
+///////////////////////////////////////////////////////////////////////////////
+//
+//
+// The following templated classes provide an efficient way to store and compare
+// objects that have been flattened (i.e. serialized in an ordered binary
+// format).
+//
+// SkFlatData:       is a simple indexable container for the flattened data
+//                   which is agnostic to the type of data is is indexing. It is
+//                   also responsible for flattening/unflattening objects but
+//                   details of that operation are hidden in the provided procs
+// SkFlatDictionary: is a abstract templated dictionary that maintains a
+//                   searchable set of SkFlataData objects of type T.
+//
+// NOTE: any class that wishes to be used in conjunction with SkFlatDictionary
+// must subclass the dictionary and provide the necessary flattening procs.
+// The end of this header contains dictionary subclasses for some common classes
+// like SkBitmap, SkMatrix, SkPaint, and SkRegion.
+//
+//
+///////////////////////////////////////////////////////////////////////////////
+
 class SkFlatData {
 public:
+
     static int Compare(const SkFlatData* a, const SkFlatData* b) {
-        return memcmp(&a->fAllocSize, &b->fAllocSize, a->fAllocSize);
+        return memcmp(a->data(), b->data(), a->fAllocSize);
     }
     
     int index() const { return fIndex; }
+    void* data() const { return (char*)this + sizeof(*this); }
     
 #ifdef SK_DEBUG_SIZE
-    size_t size() const { return sizeof(fIndex) + fAllocSize; }
+    size_t size() const { return sizeof(SkFlatData) + fAllocSize; }
 #endif
 
-protected:
-    static SkFlatData* Alloc(SkChunkAlloc* heap, int32_t size, int index);
-    
+    static SkFlatData* Create(SkChunkAlloc* heap, const void* obj, int index,
+                              void (*flattenProc)(SkOrderedWriteBuffer&, const void*),
+                              SkRefCntSet* refCntRecorder = NULL,
+                              SkRefCntSet* faceRecorder = NULL);
+    void unflatten(void* result,
+                   void (*unflattenProc)(SkOrderedReadBuffer&, void*),
+                   SkRefCntPlayback* refCntPlayback = NULL,
+                   SkTypefacePlayback* facePlayback = NULL) const;
+
+private:
     int fIndex;
     int32_t fAllocSize;
 };
 
-class SkFlatBitmap : public SkFlatData {
+template <class T>
+class SkFlatDictionary {
 public:
-    static SkFlatBitmap* Flatten(SkChunkAlloc*, const SkBitmap&, int index,
-                                 SkRefCntSet*);
+    SkFlatDictionary(SkChunkAlloc* heap) {
+        fFlattenProc = NULL;
+        fUnflattenProc = NULL;
+        fHeap = heap;
+        // set to 1 since returning a zero from find() indicates failure
+        fNextIndex = 1;
+    }
 
-    void unflatten(SkBitmap* bitmap, SkRefCntPlayback* rcp) const {
-        SkOrderedReadBuffer buffer(fBitmapData, fAllocSize);
-        if (rcp) {
-            rcp->setupBuffer(buffer);
+    int count() const { return fData.count(); }
+
+    const SkFlatData*  operator[](int index) const {
+        SkASSERT(index >= 0 && index < fData.count());
+        return fData[index];
+    }
+
+    /**
+     * Clears the dictionary of all entries. However, it does NOT free the
+     * memory that was allocated for each entry.
+     */
+    void reset() { fData.reset(); fNextIndex = 1; }
+
+    /**
+     * Given an element of type T it returns its index in the dictionary. If
+     * the element wasn't previously in the dictionary it is automatically added
+     */
+    int find(const T* element, SkRefCntSet* refCntRecorder = NULL,
+             SkRefCntSet* faceRecorder = NULL) {
+        if (element == NULL)
+            return 0;
+        SkFlatData* flat = SkFlatData::Create(fHeap, element, fNextIndex,
+                fFlattenProc, refCntRecorder, faceRecorder);
+        int index = SkTSearch<SkFlatData>((const SkFlatData**) fData.begin(),
+                fData.count(), flat, sizeof(flat), &SkFlatData::Compare);
+        if (index >= 0) {
+            (void)fHeap->unalloc(flat);
+            return fData[index]->index();
         }
-        bitmap->unflatten(buffer);
+        index = ~index;
+        *fData.insert(index) = flat;
+        SkASSERT(fData.count() == fNextIndex);
+        return fNextIndex++;
     }
 
-#ifdef SK_DEBUG_VALIDATE
-    void validate() const {
-        // to be written
+    /**
+     * Given a pointer to a array of type T we allocate the array and fill it
+     * with the unflattened dictionary contents. The return value is the size of
+     * the allocated array.
+     */
+    int unflattenDictionary(T*& array, SkRefCntPlayback* refCntPlayback = NULL,
+            SkTypefacePlayback* facePlayback = NULL) const {
+        int elementCount = fData.count();
+        if (elementCount > 0) {
+            array = SkNEW_ARRAY(T, elementCount);
+            for (const SkFlatData** elementPtr = fData.begin();
+                    elementPtr != fData.end(); elementPtr++) {
+                const SkFlatData* element = *elementPtr;
+                int index = element->index() - 1;
+                element->unflatten(&array[index], fUnflattenProc,
+                                   refCntPlayback, facePlayback);
+            }
+        }
+        return elementCount;
     }
-#endif
+
+protected:
+    void (*fFlattenProc)(SkOrderedWriteBuffer&, const void*);
+    void (*fUnflattenProc)(SkOrderedReadBuffer&, void*);
 
 private:
-    char fBitmapData[1];
-    typedef SkFlatData INHERITED;
+    SkChunkAlloc* fHeap;
+    int fNextIndex;
+    SkTDArray<const SkFlatData*> fData;
 };
 
-class SkFlatMatrix : public SkFlatData {
+///////////////////////////////////////////////////////////////////////////////
+// Some common dictionaries are defined here for both reference and convenience
+///////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+static void SkFlattenObjectProc(SkOrderedWriteBuffer& buffer, const void* obj) {
+    ((T*)obj)->flatten(buffer);
+}
+
+template <class T>
+static void SkUnflattenObjectProc(SkOrderedReadBuffer& buffer, void* obj) {
+    ((T*)obj)->unflatten(buffer);
+}
+
+class SkBitmapDictionary : public SkFlatDictionary<SkBitmap> {
 public:
-    static SkFlatMatrix* Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index);
-
-    void unflatten(SkMatrix* result) const {
-        result->unflatten(fMatrixData);
+    SkBitmapDictionary(SkChunkAlloc* heap) : SkFlatDictionary<SkBitmap>(heap) {
+        fFlattenProc = &SkFlattenObjectProc<SkBitmap>;
+        fUnflattenProc = &SkUnflattenObjectProc<SkBitmap>;
     }
-
-#ifdef SK_DEBUG_DUMP
-    void dump() const;
-#endif
-
-#ifdef SK_DEBUG_VALIDATE
-    void validate() const {
-        // to be written
-    }
-#endif
-
-private:
-    char fMatrixData[1];
-    typedef SkFlatData INHERITED;
 };
 
-class SkFlatPaint : public SkFlatData {
-public:
-    static SkFlatPaint* Flatten(SkChunkAlloc* heap, const SkPaint& paint,
-                                int index, SkRefCntSet*,
-                                SkRefCntSet* faceRecorder);
-    
-    void unflatten(SkPaint* result, SkRefCntPlayback* rcp,
-                   SkTypefacePlayback* facePlayback) const {
-        Read(fPaintData, result, rcp, facePlayback);
+class SkMatrixDictionary : public SkFlatDictionary<SkMatrix> {
+ public:
+    SkMatrixDictionary(SkChunkAlloc* heap) : SkFlatDictionary<SkMatrix>(heap) {
+        fFlattenProc = &flattenMatrix;
+        fUnflattenProc = &unflattenMatrix;
     }
-    
-    static void Read(const void* storage, SkPaint* paint, SkRefCntPlayback*,
-                     SkTypefacePlayback* facePlayback);
 
-#ifdef SK_DEBUG_DUMP
-    void dump() const;
-#endif
-    
-private:
-    char fPaintData[1];
-    typedef SkFlatData INHERITED;
+    static void flattenMatrix(SkOrderedWriteBuffer& buffer, const void* obj) {
+        buffer.getWriter32()->writeMatrix(*(SkMatrix*)obj);
+    }
+
+    static void unflattenMatrix(SkOrderedReadBuffer& buffer, void* obj) {
+        buffer.getReader32()->readMatrix((SkMatrix*)obj);
+    }
 };
 
-class SkFlatRegion : public SkFlatData {
-public:
-    static SkFlatRegion* Flatten(SkChunkAlloc* heap, const SkRegion& region, int index);
-    
-    void unflatten(SkRegion* result) const {
-        result->unflatten(fRegionData);
+class SkPaintDictionary : public SkFlatDictionary<SkPaint> {
+ public:
+    SkPaintDictionary(SkChunkAlloc* heap) : SkFlatDictionary<SkPaint>(heap) {
+        fFlattenProc = &SkFlattenObjectProc<SkPaint>;
+        fUnflattenProc = &SkUnflattenObjectProc<SkPaint>;
+    }
+};
+
+class SkRegionDictionary : public SkFlatDictionary<SkRegion> {
+ public:
+    SkRegionDictionary(SkChunkAlloc* heap) : SkFlatDictionary<SkRegion>(heap) {
+        fFlattenProc = &flattenRegion;
+        fUnflattenProc = &unflattenRegion;
     }
 
-#ifdef SK_DEBUG_VALIDATE
-    void validate() const {
-        // to be written
+    static void flattenRegion(SkOrderedWriteBuffer& buffer, const void* obj) {
+        buffer.getWriter32()->writeRegion(*(SkRegion*)obj);
     }
-#endif
 
-private:
-    char fRegionData[1];
-    typedef SkFlatData INHERITED;
+    static void unflattenRegion(SkOrderedReadBuffer& buffer, void* obj) {
+        buffer.getReader32()->readRegion((SkRegion*)obj);
+    }
 };
 
 #endif
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index b3eb147..d2cf4db 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -77,41 +77,15 @@
     fRCPlayback.reset(&record.fRCSet);
     fTFPlayback.reset(&record.fTFSet);
 
-    const SkTDArray<const SkFlatBitmap* >& bitmaps = record.getBitmaps();
-    fBitmapCount = bitmaps.count();
-    if (fBitmapCount > 0) {
-        fBitmaps = SkNEW_ARRAY(SkBitmap, fBitmapCount);
-        for (const SkFlatBitmap** flatBitmapPtr = bitmaps.begin();
-             flatBitmapPtr != bitmaps.end(); flatBitmapPtr++) {
-            const SkFlatBitmap* flatBitmap = *flatBitmapPtr;
-            int index = flatBitmap->index() - 1;
-            flatBitmap->unflatten(&fBitmaps[index], &fRCPlayback);
-        }
-    }
+    fBitmapCount = record.getBitmaps().unflattenDictionary(fBitmaps, &fRCPlayback);
+    fMatrixCount = record.getMatrices().unflattenDictionary(fMatrices);
+    fPaintCount = record.getPaints().unflattenDictionary(fPaints, &fRCPlayback, &fTFPlayback);
+    fRegionCount = record.getRegions().unflattenDictionary(fRegions);
 
-    const SkTDArray<const SkFlatMatrix* >& matrices = record.getMatrices();
-    fMatrixCount = matrices.count();
-    if (fMatrixCount > 0) {
-        fMatrices = SkNEW_ARRAY(SkMatrix, fMatrixCount);
-        for (const SkFlatMatrix** matrixPtr = matrices.begin();
-             matrixPtr != matrices.end(); matrixPtr++) {
-            const SkFlatMatrix* flatMatrix = *matrixPtr;
-            flatMatrix->unflatten(&fMatrices[flatMatrix->index() - 1]);
-        }
-    }
-
-    const SkTDArray<const SkFlatPaint* >& paints = record.getPaints();
-    fPaintCount = paints.count();
-    if (fPaintCount > 0) {
-        fPaints = SkNEW_ARRAY(SkPaint, fPaintCount);
-        for (const SkFlatPaint** flatPaintPtr = paints.begin();
-             flatPaintPtr != paints.end(); flatPaintPtr++) {
-            const SkFlatPaint* flatPaint = *flatPaintPtr;
-            int index = flatPaint->index() - 1;
-            SkASSERT((unsigned)index < (unsigned)fPaintCount);
-            flatPaint->unflatten(&fPaints[index], &fRCPlayback, &fTFPlayback);
-        }
-    }
+    SkASSERT(fBitmapCount == record.getBitmaps().count());
+    SkASSERT(fMatrixCount == record.getMatrices().count());
+    SkASSERT(fPaintCount == record.getPaints().count());
+    SkASSERT(fRegionCount == record.getRegions().count());
 
     fPathHeap = record.fPathHeap;
     SkSafeRef(fPathHeap);
@@ -126,17 +100,6 @@
         }
     }
 
-    const SkTDArray<const SkFlatRegion* >& regions = record.getRegions();
-    fRegionCount = regions.count();
-    if (fRegionCount > 0) {
-        fRegions = SkNEW_ARRAY(SkRegion, fRegionCount);
-        for (const SkFlatRegion** flatRegionPtr = regions.begin();
-             flatRegionPtr != regions.end(); flatRegionPtr++) {
-            const SkFlatRegion* flatRegion = *flatRegionPtr;
-            flatRegion->unflatten(&fRegions[flatRegion->index() - 1]);
-        }
-    }
-
 #ifdef SK_DEBUG_SIZE
     int overall = fPlayback->size(&overallBytes);
     bitmaps = fPlayback->bitmaps(&bitmapBytes);
@@ -349,11 +312,7 @@
 
     writeTagSize(buffer, PICT_REGION_TAG, fRegionCount);
     for (i = 0; i < fRegionCount; i++) {
-        uint32_t size = fRegions[i].flatten(NULL);
-        buffer.write32(size);
-        SkAutoSMalloc<512> storage(size);
-        fRegions[i].flatten(storage.get());
-        buffer.writePad(storage.get(), size);
+        buffer.getWriter32()->writeRegion(fRegions[i]);
     }
 
     // now we can write to the stream again
@@ -460,10 +419,7 @@
     fRegionCount = readTagSize(buffer, PICT_REGION_TAG);
     fRegions = SkNEW_ARRAY(SkRegion, fRegionCount);
     for (i = 0; i < fRegionCount; i++) {
-        uint32_t bufferSize = buffer.readU32();
-        SkDEBUGCODE(uint32_t bytes =)
-            fRegions[i].unflatten(buffer.skip(bufferSize));
-        SkASSERT(bufferSize == bytes);
+        buffer.getReader32()->readRegion(&fRegions[i]);
     }
 }
 
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index fdeec3f..422001b 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -12,8 +12,13 @@
 #define HEAP_BLOCK_SIZE 4096
 
 SkPictureRecord::SkPictureRecord(uint32_t flags) :
-        fHeap(HEAP_BLOCK_SIZE), fWriter(MIN_WRITER_SIZE), fRecordFlags(flags) {
-    fBitmapIndex = fMatrixIndex = fPaintIndex = fRegionIndex = 1;
+        fHeap(HEAP_BLOCK_SIZE),
+        fBitmaps(&fHeap),
+        fMatrices(&fHeap),
+        fPaints(&fHeap),
+        fRegions(&fHeap),
+        fWriter(MIN_WRITER_SIZE),
+        fRecordFlags(flags) {
 #ifdef SK_DEBUG_SIZE
     fPointBytes = fRectBytes = fTextBytes = 0;
     fPointWrites = fRectWrites = fTextWrites = 0;
@@ -505,7 +510,7 @@
 }
 
 void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
-    addInt(find(fBitmaps, bitmap));
+    addInt(fBitmaps.find(&bitmap, &fRCSet));
 }
 
 void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
@@ -513,7 +518,7 @@
 }
 
 void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
-    addInt(find(fMatrices, matrix));
+    addInt(fMatrices.find(matrix));
 }
 
 void SkPictureRecord::addPaint(const SkPaint& paint) {
@@ -521,7 +526,7 @@
 }
 
 void SkPictureRecord::addPaintPtr(const SkPaint* paint) {
-    addInt(find(fPaints, paint));
+    addInt(fPaints.find(paint, &fRCSet, &fTFSet));
 }
 
 void SkPictureRecord::addPath(const SkPath& path) {
@@ -589,7 +594,7 @@
 }
 
 void SkPictureRecord::addRegion(const SkRegion& region) {
-    addInt(find(fRegions, region));
+    addInt(fRegions.find(&region));
 }
 
 void SkPictureRecord::addText(const void* text, size_t byteLength) {
@@ -606,85 +611,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-int SkPictureRecord::find(SkTDArray<const SkFlatBitmap* >& bitmaps, const SkBitmap& bitmap) {
-    SkFlatBitmap* flat = SkFlatBitmap::Flatten(&fHeap, bitmap, fBitmapIndex,
-                                               &fRCSet);
-    int index = SkTSearch<SkFlatData>((const SkFlatData**) bitmaps.begin(),
-        bitmaps.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
-    if (index >= 0) {
-        (void)fHeap.unalloc(flat);
-        return bitmaps[index]->index();
-    }
-    index = ~index;
-    *bitmaps.insert(index) = flat;
-    return fBitmapIndex++;
-}
-
-int SkPictureRecord::find(SkTDArray<const SkFlatMatrix* >& matrices, const SkMatrix* matrix) {
-    if (matrix == NULL)
-        return 0;
-    SkFlatMatrix* flat = SkFlatMatrix::Flatten(&fHeap, *matrix, fMatrixIndex);
-    int index = SkTSearch<SkFlatData>((const SkFlatData**) matrices.begin(),
-        matrices.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
-    if (index >= 0) {
-        (void)fHeap.unalloc(flat);
-        return matrices[index]->index();
-    }
-    index = ~index;
-    *matrices.insert(index) = flat;
-    return fMatrixIndex++;
-}
-
-int SkPictureRecord::find(SkTDArray<const SkFlatPaint* >& paints, const SkPaint* paint) {
-    if (paint == NULL) {
-        return 0;
-    }
-
-    SkFlatPaint* flat = SkFlatPaint::Flatten(&fHeap, *paint, fPaintIndex,
-                                             &fRCSet, &fTFSet);
-    int index = SkTSearch<SkFlatData>((const SkFlatData**) paints.begin(),
-        paints.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
-    if (index >= 0) {
-        (void)fHeap.unalloc(flat);
-        return paints[index]->index();
-    }
-
-    index = ~index;
-    *paints.insert(index) = flat;
-    return fPaintIndex++;
-}
-
-int SkPictureRecord::find(SkTDArray<const SkFlatRegion* >& regions, const SkRegion& region) {
-    SkFlatRegion* flat = SkFlatRegion::Flatten(&fHeap, region, fRegionIndex);
-    int index = SkTSearch<SkFlatData>((const SkFlatData**) regions.begin(),
-        regions.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
-    if (index >= 0) {
-        (void)fHeap.unalloc(flat);
-        return regions[index]->index();
-    }
-    index = ~index;
-    *regions.insert(index) = flat;
-    return fRegionIndex++;
-}
-
-#ifdef SK_DEBUG_DUMP
-void SkPictureRecord::dumpMatrices() {
-    int count = fMatrices.count();
-    SkMatrix defaultMatrix;
-    defaultMatrix.reset();
-    for (int index = 0; index < count; index++) {
-        const SkFlatMatrix* flatMatrix = fMatrices[index];
-        flatMatrix->dump();
-    }
-}
-
-void SkPictureRecord::dumpPaints() {
-    int count = fPaints.count();
-    for (int index = 0; index < count; index++)
-        fPaints[index]->dump();
-}
-#endif
-
 #ifdef SK_DEBUG_SIZE
 size_t SkPictureRecord::size() const {
     size_t result = 0;
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
index dfddffe..96809bf 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -69,19 +69,19 @@
 
     void addFontMetricsTopBottom(const SkPaint& paint, SkScalar minY, SkScalar maxY);
 
-    const SkTDArray<const SkFlatBitmap* >& getBitmaps() const {
+    const SkBitmapDictionary& getBitmaps() const {
         return fBitmaps;
     }
-    const SkTDArray<const SkFlatMatrix* >& getMatrices() const {
+    const SkMatrixDictionary& getMatrices() const {
         return fMatrices;
     }
-    const SkTDArray<const SkFlatPaint* >& getPaints() const {
+    const SkPaintDictionary& getPaints() const {
         return fPaints;
     }
     const SkTDArray<SkPicture* >& getPictureRefs() const {
         return fPictureRefs;
     }
-    const SkTDArray<const SkFlatRegion* >& getRegions() const {
+    const SkRegionDictionary& getRegions() const {
         return fRegions;
     }
 
@@ -127,13 +127,6 @@
     void addRegion(const SkRegion& region);
     void addText(const void* text, size_t byteLength);
 
-    int find(SkTDArray<const SkFlatBitmap* >& bitmaps,
-                   const SkBitmap& bitmap);
-    int find(SkTDArray<const SkFlatMatrix* >& matrices,
-                   const SkMatrix* matrix);
-    int find(SkTDArray<const SkFlatPaint* >& paints, const SkPaint* paint);
-    int find(SkTDArray<const SkFlatRegion* >& regions, const SkRegion& region);
-
 #ifdef SK_DEBUG_DUMP
 public:
     void dumpMatrices();
@@ -170,14 +163,11 @@
 
 private:
     SkChunkAlloc fHeap;
-    int fBitmapIndex;
-    SkTDArray<const SkFlatBitmap* > fBitmaps;
-    int fMatrixIndex;
-    SkTDArray<const SkFlatMatrix* > fMatrices;
-    int fPaintIndex;
-    SkTDArray<const SkFlatPaint* > fPaints;
-    int fRegionIndex;
-    SkTDArray<const SkFlatRegion* > fRegions;
+    SkBitmapDictionary fBitmaps;
+    SkMatrixDictionary fMatrices;
+    SkPaintDictionary fPaints;
+    SkRegionDictionary fRegions;
+
     SkPathHeap* fPathHeap;  // reference counted
     SkWriter32 fWriter;
 
diff --git a/src/effects/Sk2DPathEffect.cpp b/src/effects/Sk2DPathEffect.cpp
index 3f8c998..e19cdeb 100644
--- a/src/effects/Sk2DPathEffect.cpp
+++ b/src/effects/Sk2DPathEffect.cpp
@@ -74,18 +74,11 @@
 
 void Sk2DPathEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
-    char storage[SkMatrix::kMaxFlattenSize];
-    uint32_t size = fMatrix.flatten(storage);
-    buffer.write32(size);
-    buffer.write(storage, size);
+    buffer.writeMatrix(fMatrix);
 }
 
 Sk2DPathEffect::Sk2DPathEffect(SkFlattenableReadBuffer& buffer) {
-    char storage[SkMatrix::kMaxFlattenSize];
-    uint32_t size = buffer.readS32();
-    SkASSERT(size <= sizeof(storage));
-    buffer.read(storage, size);
-    fMatrix.unflatten(storage);
+    buffer.readMatrix(&fMatrix);
     fMatrixIsInvertible = fMatrix.invert(&fInverse);
 }
 
diff --git a/src/effects/SkGroupShape.cpp b/src/effects/SkGroupShape.cpp
index 83a2043..8ec87c4 100644
--- a/src/effects/SkGroupShape.cpp
+++ b/src/effects/SkGroupShape.cpp
@@ -92,13 +92,9 @@
     const Rec* stop = fList.end();
     while (rec < stop) {
         buffer.writeFlattenable(rec->fShape);
+        buffer.writeBool(rec->fMatrixRef);
         if (rec->fMatrixRef) {
-            char storage[SkMatrix::kMaxFlattenSize];
-            uint32_t size = rec->fMatrixRef->flatten(storage);
-            buffer.write32(size);
-            buffer.writePad(storage, size);
-        } else {
-            buffer.write32(0);
+            buffer.writeMatrix(*rec->fMatrixRef);
         }
         rec += 1;
     }
@@ -109,12 +105,10 @@
     for (int i = 0; i < count; i++) {
         SkShape* shape = reinterpret_cast<SkShape*>(buffer.readFlattenable());
         SkMatrixRef* mr = NULL;
-        uint32_t size = buffer.readS32();
-        if (size) {
-            char storage[SkMatrix::kMaxFlattenSize];
-            buffer.read(storage, SkAlign4(size));
+        bool hasMatrix = buffer.readBool();
+        if (hasMatrix) {
             mr = SkNEW(SkMatrixRef);
-            mr->unflatten(storage);
+            buffer.readMatrix(mr);
         }
         if (shape) {
             this->appendShape(shape, mr)->unref();
diff --git a/tests/CanvasTest.cpp b/tests/CanvasTest.cpp
index 3efbdea..814386e 100644
--- a/tests/CanvasTest.cpp
+++ b/tests/CanvasTest.cpp
@@ -583,14 +583,14 @@
 
         // Verify that deserialization-serialization round trip conserves all
         // data by comparing referenceRecord to roundTripRecord
-        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fBitmapIndex ==
-            roundTripRecord->fBitmapIndex, testStep->assertMessage());
-        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fMatrixIndex ==
-            roundTripRecord->fMatrixIndex, testStep->assertMessage());
-        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fPaintIndex ==
-            roundTripRecord->fPaintIndex, testStep->assertMessage());
-        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fRegionIndex ==
-            roundTripRecord->fRegionIndex, testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fBitmaps.count() ==
+            roundTripRecord->fBitmaps.count(), testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fMatrices.count() ==
+            roundTripRecord->fMatrices.count(), testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fPaints.count() ==
+            roundTripRecord->fPaints.count(), testStep->assertMessage());
+        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fRegions.count() ==
+            roundTripRecord->fRegions.count(), testStep->assertMessage());
         char referenceBuffer[kMaxPictureBufferSize];
         SkMemoryWStream referenceStream(referenceBuffer,
             kMaxPictureBufferSize);