Pipe factory names independently from the flattenables using them.
authorscroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 1 Aug 2012 19:34:20 +0000 (19:34 +0000)
committerscroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 1 Aug 2012 19:34:20 +0000 (19:34 +0000)
Avoids an issue where a flattenable written twice might be written
differently (the first time the flat data may have a name, whereas
the second time it will have an index).

Also add a test which confirms that identical flattenables will have
the same SkFlatData representation.

BUG=https://code.google.com/p/skia/issues/detail?id=721
TEST=FlatDataTest.cpp

Review URL: https://codereview.appspot.com/6431057

git-svn-id: http://skia.googlecode.com/svn/trunk@4896 2bbb7eff-a529-9590-31e7-b0007b416f81

12 files changed:
gyp/tests.gyp
include/core/SkFlattenable.h
include/pipe/SkGPipe.h
src/core/SkFlattenable.cpp
src/core/SkOrderedReadBuffer.cpp
src/core/SkOrderedWriteBuffer.cpp
src/core/SkPictureFlat.cpp
src/core/SkPictureFlat.h
src/pipe/SkGPipePriv.h
src/pipe/SkGPipeRead.cpp
src/pipe/SkGPipeWrite.cpp
tests/FlatDataTest.cpp [new file with mode: 0644]

index 8ea4ee8..b30a749 100644 (file)
@@ -40,6 +40,7 @@
         '../tests/DrawTextTest.cpp',
         '../tests/EmptyPathTest.cpp',
         '../tests/FillPathTest.cpp',
+        '../tests/FlatDataTest.cpp',
         '../tests/FlateTest.cpp',
         '../tests/FontHostStreamTest.cpp',
         '../tests/FontHostTest.cpp',
index 1828b42..bbd4a80 100644 (file)
@@ -191,8 +191,8 @@ public:
 
     /**
      *  Call this with an initially empty array, so the reader can cache each
-     *  factory it sees by name. Used by the pipe code in conjunction with
-     *  the writer's kInlineFactoryNames_Flag.
+     *  factory it sees by name. Used by the pipe code in combination with
+     *  setNamedFactoryRecorder.
      */
     void setFactoryArray(SkTDArray<SkFlattenable::Factory>* array) {
         fFactoryTDArray = array;
@@ -241,6 +241,33 @@ protected:
 
 class SkFactorySet : public SkTPtrSet<SkFlattenable::Factory> {};
 
+/**
+ * Similar to SkFactorySet, but only allows Factorys that have registered names.
+ * Also has a function to return the next added Factory's name.
+ */
+class SkNamedFactorySet : public SkRefCnt {
+public:
+    SkNamedFactorySet();
+
+    /**
+     * Find the specified Factory in the set. If it is not already in the set,
+     * and has registered its name, add it to the set, and return its index.
+     * If the Factory has no registered name, return 0.
+     */
+    uint32_t find(SkFlattenable::Factory);
+
+    /**
+     * If new Factorys have been added to the set, return the name of the first
+     * Factory added after the Factory name returned by the last call to this
+     * function.
+     */
+    const char* getNextAddedFactoryName();
+private:
+    int                    fNextAddedFactory;
+    SkFactorySet           fFactorySet;
+    SkTDArray<const char*> fNames;
+};
+
 class SkFlattenableWriteBuffer {
 public:
     SkFlattenableWriteBuffer();
@@ -285,14 +312,12 @@ public:
     SkFactorySet* getFactoryRecorder() const { return fFactorySet; }
     SkFactorySet* setFactoryRecorder(SkFactorySet*);
 
+    SkNamedFactorySet* getNamedFactoryRecorder() const { return fNamedFactorySet; }
+    SkNamedFactorySet* setNamedFactoryRecorder(SkNamedFactorySet*);
+
     enum Flags {
         kCrossProcess_Flag               = 0x01,
         /**
-         *  Instructs the writer to inline Factory names as there are seen the
-         *  first time (after that we store an index). The pipe code uses this.
-         */
-        kInlineFactoryNames_Flag         = 0x02,
-        /**
          *  Instructs the writer to always serialize bitmap pixel data.
          */
         kForceFlattenBitmapPixels_Flag   = 0x04
@@ -304,9 +329,6 @@ public:
     bool isCrossProcess() const {
         return SkToBool(fFlags & kCrossProcess_Flag);
     }
-    bool inlineFactoryNames() const {
-        return SkToBool(fFlags & kInlineFactoryNames_Flag);
-    }
 
     bool persistBitmapPixels() const {
         return (fFlags & (kCrossProcess_Flag | kForceFlattenBitmapPixels_Flag)) != 0;
@@ -322,10 +344,12 @@ protected:
         obj->flatten(buffer);
     }
 
-    uint32_t        fFlags;
-    SkRefCntSet*    fTFSet;
-    SkRefCntSet*    fRCSet;
-    SkFactorySet*   fFactorySet;
+    uint32_t           fFlags;
+    SkRefCntSet*       fTFSet;
+    SkRefCntSet*       fRCSet;
+    SkFactorySet*      fFactorySet;
+    SkNamedFactorySet* fNamedFactorySet;
+
 };
 
 #endif
index a1f425c..0a908d0 100644 (file)
@@ -128,7 +128,6 @@ public:
 
 private:
     SkGPipeCanvas* fCanvas;
-    SkFactorySet*  fFactorySet;
     SkWriter32     fWriter;
 };
 
index edfc5ae..60c04b1 100644 (file)
@@ -46,17 +46,43 @@ SkFlattenableReadBuffer::SkFlattenableReadBuffer() {
 
 ///////////////////////////////////////////////////////////////////////////////
 
+SkNamedFactorySet::SkNamedFactorySet() : fNextAddedFactory(0) {}
+
+uint32_t SkNamedFactorySet::find(SkFlattenable::Factory factory) {
+    uint32_t index = fFactorySet.find(factory);
+    if (index > 0) {
+        return index;
+    }
+    const char* name = SkFlattenable::FactoryToName(factory);
+    if (NULL == name) {
+        return 0;
+    }
+    *fNames.append() = name;
+    return fFactorySet.add(factory);
+}
+
+const char* SkNamedFactorySet::getNextAddedFactoryName() {
+    if (fNextAddedFactory < fNames.count()) {
+        return fNames[fNextAddedFactory++];
+    }
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 SkFlattenableWriteBuffer::SkFlattenableWriteBuffer() {
     fFlags = (Flags)0;
     fRCSet = NULL;
     fTFSet = NULL;
     fFactorySet = NULL;
+    fNamedFactorySet = NULL;
 }
 
 SkFlattenableWriteBuffer::~SkFlattenableWriteBuffer() {
     SkSafeUnref(fRCSet);
     SkSafeUnref(fTFSet);
     SkSafeUnref(fFactorySet);
+    SkSafeUnref(fNamedFactorySet);
 }
 
 SkRefCntSet* SkFlattenableWriteBuffer::setRefCntRecorder(SkRefCntSet* rec) {
@@ -71,6 +97,20 @@ SkRefCntSet* SkFlattenableWriteBuffer::setTypefaceRecorder(SkRefCntSet* rec) {
 
 SkFactorySet* SkFlattenableWriteBuffer::setFactoryRecorder(SkFactorySet* rec) {
     SkRefCnt_SafeAssign(fFactorySet, rec);
+    if (fNamedFactorySet != NULL) {
+        fNamedFactorySet->unref();
+        fNamedFactorySet = NULL;
+    }
+    return rec;
+}
+
+SkNamedFactorySet* SkFlattenableWriteBuffer::setNamedFactoryRecorder(
+        SkNamedFactorySet* rec) {
+    SkRefCnt_SafeAssign(fNamedFactorySet, rec);
+    if (fFactorySet != NULL) {
+        fFactorySet->unref();
+        fFactorySet = NULL;
+    }
     return rec;
 }
 
index f3ba485..df94907 100644 (file)
@@ -45,32 +45,16 @@ SkFlattenable* SkOrderedReadBuffer::readFlattenable() {
         if (0 == index) {
             return NULL; // writer failed to give us the flattenable
         }
-        index = -index; // we stored the negative of the index
         index -= 1;     // we stored the index-base-1
         SkASSERT(index < fFactoryCount);
         factory = fFactoryArray[index];
     } else if (fFactoryTDArray) {
-        const int32_t* peek = (const int32_t*)fReader.peek();
-        if (*peek <= 0) {
-            int32_t index = fReader.readU32();
-            if (0 == index) {
-                return NULL; // writer failed to give us the flattenable
-            }
-            index = -index; // we stored the negative of the index
-            index -= 1;     // we stored the index-base-1
-            factory = (*fFactoryTDArray)[index];
-        } else {
-            const char* name = fReader.readString();
-            factory = SkFlattenable::NameToFactory(name);
-            if (factory) {
-                SkASSERT(fFactoryTDArray->find(factory) < 0);
-                *fFactoryTDArray->append() = factory;
-            } else {
-//                SkDebugf("can't find factory for [%s]\n", name);
-            }
-            // if we didn't find a factory, that's our failure, not the writer's,
-            // so we fall through, so we can skip the sizeRecorded data.
+        int32_t index = fReader.readU32();
+        if (0 == index) {
+            return NULL; // writer failed to give us the flattenable
         }
+        index -= 1;     // we stored the index-base-1
+        factory = (*fFactoryTDArray)[index];
     } else {
         factory = (SkFlattenable::Factory)readFunctionPtr();
         if (NULL == factory) {
index 9c43033..fdfb6e3 100644 (file)
@@ -23,8 +23,7 @@ void SkOrderedWriteBuffer::writeFlattenable(SkFlattenable* flattenable) {
     /*
      *  If we have a factoryset, then the first 32bits tell us...
      *       0: failure to write the flattenable
-     *      <0: we store the negative of the (1-based) index
-     *      >0: the length of the name
+     *      >0: (1-based) index into the SkFactorySet or SkNamedFactorySet
      *  If we don't have a factoryset, then the first "ptr" is either the
      *  factory, or null for failure.
      *
@@ -37,7 +36,7 @@ void SkOrderedWriteBuffer::writeFlattenable(SkFlattenable* flattenable) {
         factory = flattenable->getFactory();
     }
     if (NULL == factory) {
-        if (fFactorySet) {
+        if (fFactorySet != NULL || fNamedFactorySet != NULL) {
             this->write32(0);
         } else {
             this->writeFunctionPtr(NULL);
@@ -53,31 +52,17 @@ void SkOrderedWriteBuffer::writeFlattenable(SkFlattenable* flattenable) {
      *      resolve the function-ptrs into strings for its reader. SkPicture
      *      does exactly this, by writing a table of names (matching the indices)
      *      up front in its serialized form.
-     *  3.  names : Reuse fFactorySet to store indices, but only after we've
-     *      written the name the first time. SkGPipe uses this technique, as it
-     *      doesn't require the reader to be told to know the table of names
-     *      up front.
+     *  3.  index into fNamedFactorySet. fNamedFactorySet will also store the
+     *      name. SkGPipe uses this technique so it can write the name to its
+     *      stream before writing the flattenable.
      */
     if (fFactorySet) {
-        if (this->inlineFactoryNames()) {
-            int index = fFactorySet->find(factory);
-            if (index) {
-                // we write the negative of the index, to distinguish it from
-                // the length of a string
-                this->write32(-index);
-            } else {
-                const char* name = SkFlattenable::FactoryToName(factory);
-                if (NULL == name) {
-                    this->write32(0);
-                    return;
-                }
-                this->writeString(name);
-                index = fFactorySet->add(factory);
-            }
-        } else {
-            // we write the negative of the index, to distinguish it from
-            // the length of a string
-            this->write32(-(int)fFactorySet->add(factory));
+        this->write32(fFactorySet->add(factory));
+    } else if (fNamedFactorySet) {
+        int32_t index = fNamedFactorySet->find(factory);
+        this->write32(index);
+        if (0 == index) {
+            return;
         }
     } else {
         this->writeFunctionPtr((void*)factory);
index 763ed60..b0c02cc 100644 (file)
@@ -63,7 +63,7 @@ SkRefCnt* SkRefCntPlayback::set(int index, SkRefCnt* obj) {
 SkFlatData* SkFlatData::Create(SkFlatController* controller, const void* obj,
         int index, void (*flattenProc)(SkOrderedWriteBuffer&, const void*),
         SkRefCntSet* refCntRecorder, SkRefCntSet* faceRecorder,
-        uint32_t writeBufferflags, SkFactorySet* fset) {
+        uint32_t writeBufferflags, SkNamedFactorySet* fset) {
     // a buffer of 256 bytes should be sufficient for most paints, regions,
     // and matrices.
     intptr_t storage[256];
@@ -75,7 +75,7 @@ SkFlatData* SkFlatData::Create(SkFlatController* controller, const void* obj,
         buffer.setTypefaceRecorder(faceRecorder);
     }
     buffer.setFlags(writeBufferflags);
-    buffer.setFactoryRecorder(fset);
+    buffer.setNamedFactoryRecorder(fset);
     
     flattenProc(buffer, obj);
     uint32_t size = buffer.size();
index bd3c4f8..ddcb100 100644 (file)
@@ -175,6 +175,7 @@ public:
      * provided.
      */
     virtual void unalloc(void* ptr) = 0;
+
 };
 
 class SkFlatData {
@@ -241,7 +242,7 @@ public:
                               SkRefCntSet* refCntRecorder = NULL,
                               SkRefCntSet* faceRecorder = NULL,
                               uint32_t writeBufferflags = 0,
-                              SkFactorySet* fset = NULL);
+                              SkNamedFactorySet* fset = NULL);
 
     void unflatten(void* result,
                    void (*unflattenProc)(SkOrderedReadBuffer&, void*),
@@ -291,7 +292,7 @@ class SkFlatDictionary {
 public:
     SkFlatDictionary(SkFlatController* controller, SkRefCntSet* refSet = NULL,
                      SkRefCntSet* typeFaceSet = NULL,
-                     SkFactorySet* factorySet = NULL)
+                     SkNamedFactorySet* factorySet = NULL)
     : fController(controller), fRefSet(refSet), fTypefaceSet(typeFaceSet)
     , fFactorySet(factorySet) {
         fFlattenProc = NULL;
@@ -444,7 +445,7 @@ private:
     SkTDArray<const SkFlatData*> fData;
     SkRefCntSet*                 fRefSet;
     SkRefCntSet*                 fTypefaceSet;
-    SkFactorySet*                fFactorySet;
+    SkNamedFactorySet*           fFactorySet;
 
     const SkFlatData* findAndReturnFlat(const T& element,
                                         uint32_t writeBufferflags) {
@@ -540,7 +541,7 @@ class SkBitmapDictionary : public SkFlatDictionary<SkBitmap> {
 public:
     SkBitmapDictionary(SkFlatController* controller, SkRefCntSet* refSet = NULL,
                        SkRefCntSet* typefaceSet = NULL,
-                       SkFactorySet* factorySet = NULL)
+                       SkNamedFactorySet* factorySet = NULL)
     : SkFlatDictionary<SkBitmap>(controller, refSet, typefaceSet, factorySet) {
         fFlattenProc = &SkFlattenObjectProc<SkBitmap>;
         fUnflattenProc = &SkUnflattenObjectProc<SkBitmap>;
index d9a36e9..1eb0604 100644 (file)
@@ -70,6 +70,7 @@ enum DrawOps {
     kDef_Typeface_DrawOp,
     kDef_Flattenable_DrawOp,
     kDef_Bitmap_DrawOp,
+    kDef_Factory_DrawOp,
 
     // these are signals to playback, not drawing verbs
     kReportFlags_DrawOp,
index 41650b1..a8cba27 100644 (file)
@@ -104,6 +104,14 @@ public:
         }
     }
 
+    void defFactory(const char* name) {
+        SkFlattenable::Factory factory = SkFlattenable::NameToFactory(name);
+        if (factory) {
+            SkASSERT(fFactoryArray.find(factory) < 0);
+            *fFactoryArray.append() = factory;
+        }
+    }
+
     void addBitmap(int index) {
         index--;
         SkBitmap* bm;
@@ -551,6 +559,11 @@ static void def_Bitmap_rp(SkCanvas*, SkReader32*, uint32_t op32,
     state->addBitmap(index);
 }
 
+static void def_Factory_rp(SkCanvas*, SkReader32* reader, uint32_t,
+                           SkGPipeState* state) {
+    state->defFactory(reader->readString());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static void skip_rp(SkCanvas*, SkReader32* reader, uint32_t op32, SkGPipeState*) {
@@ -605,6 +618,7 @@ static const ReadProc gReadTable[] = {
     def_Typeface_rp,
     def_PaintFlat_rp,
     def_Bitmap_rp,
+    def_Factory_rp,
 
     reportflags_rp,
     done_rp
index 9efc0b5..75f1aff 100644 (file)
 #include "SkOrderedWriteBuffer.h"
 #include "SkPictureFlat.h"
 
+static bool isCrossProcess(uint32_t flags) {
+    return SkToBool(flags & SkGPipeWriter::kCrossProcess_Flag);
+}
+
 static SkFlattenable* get_paintflat(const SkPaint& paint, unsigned paintFlat) {
     SkASSERT(paintFlat < kCount_PaintFlats);
     switch (paintFlat) {
@@ -61,7 +65,9 @@ static size_t writeTypeface(SkWriter32* writer, SkTypeface* typeface) {
 
 class FlattenableHeap : public SkFlatController {
 public:
-    FlattenableHeap(int numFlatsToKeep) : fNumFlatsToKeep(numFlatsToKeep) {}
+    FlattenableHeap(int numFlatsToKeep)
+        : fNumFlatsToKeep(numFlatsToKeep) {
+    }
 
     ~FlattenableHeap() {
         fPointers.freeAll();
@@ -132,7 +138,7 @@ const SkFlatData* FlattenableHeap::flatToReplace() const {
 
 class FlatDictionary : public SkFlatDictionary<SkFlattenable> {
 public:
-    FlatDictionary(FlattenableHeap* heap, SkFactorySet* factorySet)
+    FlatDictionary(FlattenableHeap* heap, SkNamedFactorySet* factorySet)
             : SkFlatDictionary<SkFlattenable>(heap, NULL, NULL, factorySet) {
         fFlattenProc = &flattenFlattenableProc;
         // No need to define fUnflattenProc since the writer will never
@@ -365,7 +371,7 @@ BitmapInfo* SharedHeap::bitmapToReplace(const SkBitmap& bm) const {
 
 class SkGPipeCanvas : public SkCanvas {
 public:
-    SkGPipeCanvas(SkGPipeController*, SkWriter32*, SkFactorySet*, uint32_t flags);
+    SkGPipeCanvas(SkGPipeController*, SkWriter32*, uint32_t flags);
     virtual ~SkGPipeCanvas();
 
     void finish() {
@@ -439,16 +445,17 @@ private:
     enum {
         kNoSaveLayer = -1,
     };
-    int         fFirstSaveLayerStackLevel;
-    SharedHeap  fSharedHeap;
+    SkNamedFactorySet* fFactorySet;
+    int                fFirstSaveLayerStackLevel;
+    SharedHeap         fSharedHeap;
     SkGPipeController* fController;
-    SkWriter32& fWriter;
-    size_t      fBlockSize; // amount allocated for writer
-    size_t      fBytesNotified;
-    bool        fDone;
-    uint32_t    fFlags;
+    SkWriter32&        fWriter;
+    size_t             fBlockSize; // amount allocated for writer
+    size_t             fBytesNotified;
+    bool               fDone;
+    uint32_t           fFlags;
 
-    SkRefCntSet fTypefaceSet;
+    SkRefCntSet        fTypefaceSet;
 
     uint32_t getTypefaceID(SkTypeface*);
 
@@ -472,6 +479,10 @@ private:
         }
     }
 
+    // Should be called after any calls to an SkFlatDictionary::findAndReplace
+    // if a new SkFlatData was added when in cross process mode
+    void flattenFactoryNames();
+
     // These are only used when in cross process, but with no shared address
     // space, so bitmaps are flattened.
     FlattenableHeap    fBitmapHeap;
@@ -509,18 +520,32 @@ private:
     typedef SkCanvas INHERITED;
 };
 
+void SkGPipeCanvas::flattenFactoryNames() {
+    const char* name;
+    while ((name = fFactorySet->getNextAddedFactoryName()) != NULL) {
+        size_t len = strlen(name);
+        if (this->needOpBytes(len)) {
+            this->writeOp(kDef_Factory_DrawOp);
+            fWriter.writeString(name, len);
+        }
+    }
+}
+
 int SkGPipeCanvas::flattenToIndex(const SkBitmap & bitmap) {
     SkASSERT(shouldFlattenBitmaps(fFlags));
-    uint32_t flags = SkFlattenableWriteBuffer::kInlineFactoryNames_Flag
-        | SkFlattenableWriteBuffer::kCrossProcess_Flag;
+    uint32_t flags = SkFlattenableWriteBuffer::kCrossProcess_Flag;
     bool added, replaced;
     const SkFlatData* flat = fBitmapDictionary.findAndReplace(
         bitmap, flags, fBitmapHeap.flatToReplace(), &added, &replaced);
 
     int index = flat->index();
-    if (added && this->needOpBytes(flat->flatSize())) {
-        this->writeOp(kDef_Bitmap_DrawOp, 0, index);
-        fWriter.write(flat->data(), flat->flatSize());
+    if (added) {
+        this->flattenFactoryNames();
+        size_t flatSize = flat->flatSize();
+        if (this->needOpBytes(flatSize)) {
+            this->writeOp(kDef_Bitmap_DrawOp, 0, index);
+            fWriter.write(flat->data(), flatSize);
+        }
     }
     return index;
 }
@@ -533,9 +558,8 @@ int SkGPipeCanvas::flattenToIndex(SkFlattenable* obj, PaintFlats paintflat) {
     }
 
     uint32_t writeBufferFlags;
-    if (SkToBool(fFlags & SkGPipeWriter::kCrossProcess_Flag)) {
-        writeBufferFlags = (SkFlattenableWriteBuffer::kInlineFactoryNames_Flag
-                            | SkFlattenableWriteBuffer::kCrossProcess_Flag);
+    if (isCrossProcess(fFlags)) {
+        writeBufferFlags =  SkFlattenableWriteBuffer::kCrossProcess_Flag;
     } else {
         // Needed for bitmap shaders.
         writeBufferFlags = SkFlattenableWriteBuffer::kForceFlattenBitmapPixels_Flag;
@@ -545,9 +569,15 @@ int SkGPipeCanvas::flattenToIndex(SkFlattenable* obj, PaintFlats paintflat) {
     const SkFlatData* flat = fFlatDictionary.findAndReplace(
             *obj, writeBufferFlags, fFlattenableHeap.flatToReplace(), &added, &replaced);
     int index = flat->index();
-    if (added && this->needOpBytes(flat->flatSize())) {
-        this->writeOp(kDef_Flattenable_DrawOp, paintflat, index);
-        fWriter.write(flat->data(), flat->flatSize());
+    if (added) {
+        if (isCrossProcess(fFlags)) {
+            this->flattenFactoryNames();
+        }
+        size_t flatSize = flat->flatSize();
+        if (this->needOpBytes(flatSize)) {
+            this->writeOp(kDef_Flattenable_DrawOp, paintflat, index);
+            fWriter.write(flat->data(), flatSize);
+        }
     }
     if (replaced) {
         index = ~index;
@@ -562,11 +592,15 @@ int SkGPipeCanvas::flattenToIndex(SkFlattenable* obj, PaintFlats paintflat) {
 #define FLATTENABLES_TO_KEEP 10
 
 SkGPipeCanvas::SkGPipeCanvas(SkGPipeController* controller,
-                             SkWriter32* writer, SkFactorySet* fset, uint32_t flags)
-: fSharedHeap(!(flags & SkGPipeWriter::kCrossProcess_Flag), controller->numberOfReaders())
-, fWriter(*writer), fFlags(flags)
-, fBitmapHeap(BITMAPS_TO_KEEP), fBitmapDictionary(&fBitmapHeap, NULL, NULL, fset)
-, fFlattenableHeap(FLATTENABLES_TO_KEEP), fFlatDictionary(&fFlattenableHeap, fset) {
+                             SkWriter32* writer, uint32_t flags)
+: fFactorySet(isCrossProcess(flags) ? SkNEW(SkNamedFactorySet) : NULL)
+, fSharedHeap(!isCrossProcess(flags), controller->numberOfReaders())
+, fWriter(*writer)
+, fFlags(flags)
+, fBitmapHeap(BITMAPS_TO_KEEP)
+, fBitmapDictionary(&fBitmapHeap, NULL, NULL, fFactorySet)
+, fFlattenableHeap(FLATTENABLES_TO_KEEP)
+, fFlatDictionary(&fFlattenableHeap, fFactorySet) {
     fController = controller;
     fDone = false;
     fBlockSize = 0; // need first block from controller
@@ -589,6 +623,7 @@ SkGPipeCanvas::SkGPipeCanvas(SkGPipeController* controller,
 
 SkGPipeCanvas::~SkGPipeCanvas() {
     this->finish();
+    SkSafeUnref(fFactorySet);
 }
 
 bool SkGPipeCanvas::needOpBytes(size_t needed) {
@@ -1196,7 +1231,7 @@ void SkGPipeCanvas::writePaint(const SkPaint& paint) {
     }
 
     if (!SkTypeface::Equal(base.getTypeface(), paint.getTypeface())) {
-        if (SkToBool(fFlags & SkGPipeWriter::kCrossProcess_Flag)) {
+        if (isCrossProcess(fFlags)) {
             uint32_t id = this->getTypefaceID(paint.getTypeface());
             *ptr++ = PaintOp_packOpData(kTypeface_PaintOp, id);
         } else if (this->needOpBytes(sizeof(void*))) {
@@ -1255,23 +1290,18 @@ void SkGPipeController::setCanvas(SkGPipeCanvas* canvas) {
 ///////////////////////////////////////////////////////////////////////////////
 
 SkGPipeWriter::SkGPipeWriter()
-: fFactorySet(SkNEW(SkFactorySet))
-, fWriter(0) {
+: fWriter(0) {
     fCanvas = NULL;
 }
 
 SkGPipeWriter::~SkGPipeWriter() {
     this->endRecording();
-    fFactorySet->unref();
 }
 
 SkCanvas* SkGPipeWriter::startRecording(SkGPipeController* controller, uint32_t flags) {
     if (NULL == fCanvas) {
         fWriter.reset(NULL, 0);
-        fFactorySet->reset();
-        fCanvas = SkNEW_ARGS(SkGPipeCanvas, (controller, &fWriter,
-                                             (flags & kCrossProcess_Flag) ?
-                                             fFactorySet : NULL, flags));
+        fCanvas = SkNEW_ARGS(SkGPipeCanvas, (controller, &fWriter, flags));
     }
     controller->setCanvas(fCanvas);
     return fCanvas;
diff --git a/tests/FlatDataTest.cpp b/tests/FlatDataTest.cpp
new file mode 100644 (file)
index 0000000..755d8cd
--- /dev/null
@@ -0,0 +1,85 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkColorFilter.h"
+#include "SkGradientShader.h"
+#include "SkPaint.h"
+#include "SkPictureFlat.h"
+#include "SkShader.h"
+#include "SkXfermode.h"
+#include "Test.h"
+
+static void flattenFlattenableProc(SkOrderedWriteBuffer& buffer,
+                                   const void* obj) {
+    buffer.writeFlattenable((SkFlattenable*)obj);
+}
+
+/**
+ * Verify that two SkFlatData objects that created from the same object are
+ * identical when using an SkNamedFactorySet.
+ * @param reporter Object to report failures.
+ * @param obj Flattenable object to be flattened.
+ * @param flattenProc Function that flattens objects with the same type as obj.
+ */
+static void testCreate(skiatest::Reporter* reporter, const void* obj,
+                       void (*flattenProc)(SkOrderedWriteBuffer&, const void*)) {
+    SkChunkFlatController controller(1024);
+    SkNamedFactorySet factorySet;
+    // No need to delete data because that will be taken care of by the
+    // controller.
+    SkFlatData* data1 = SkFlatData::Create(&controller, obj, 0, flattenProc,
+                                           NULL, NULL, 0, &factorySet);
+    data1->setSentinelInCache();
+    SkFlatData* data2 = SkFlatData::Create(&controller, obj, 0, flattenProc,
+                                           NULL, NULL, 0, &factorySet);
+    data2->setSentinelAsCandidate();
+    REPORTER_ASSERT(reporter, SkFlatData::Compare(data1, data2) == 0);
+}
+
+static void Tests(skiatest::Reporter* reporter) {
+    // Test flattening SkShader
+    SkPoint points[2];
+    points[0].set(0, 0);
+    points[1].set(SkIntToScalar(20), SkIntToScalar(20));
+    SkColor colors[2];
+    colors[0] = SK_ColorRED;
+    colors[1] = SK_ColorBLUE;
+    SkShader* shader = SkGradientShader::CreateLinear(points, colors, NULL,
+                                            2, SkShader::kRepeat_TileMode);
+    SkAutoUnref aur(shader);
+    testCreate(reporter, shader, flattenFlattenableProc);
+
+    // Test SkBitmap
+    {
+        SkBitmap bm;
+        bm.setConfig(SkBitmap::kARGB_8888_Config, 50, 50);
+        bm.allocPixels();
+        SkCanvas canvas(bm);
+        SkPaint paint;
+        paint.setShader(shader);
+        canvas.drawPaint(paint);
+        testCreate(reporter, &bm, &SkFlattenObjectProc<SkBitmap>);
+    }
+
+    // Test SkColorFilter
+    SkColorFilter* cf = SkColorFilter::CreateLightingFilter(SK_ColorBLUE,
+                                                            SK_ColorRED);
+    SkAutoUnref aurcf(cf);
+    testCreate(reporter, cf, &flattenFlattenableProc);
+
+    // Test SkXfermode
+    SkXfermode* xfer = SkXfermode::Create(SkXfermode::kDstOver_Mode);
+    SkAutoUnref aurxf(xfer);
+    testCreate(reporter, xfer, &flattenFlattenableProc);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("FlatData", FlatDataClass, Tests)