Adding option to serialize mutable bitmaps in SkPicture

BUG=http://code.google.com/p/chromium/issues/detail?id=115654
REVIEW=http://codereview.appspot.com/6221066/



git-svn-id: http://skia.googlecode.com/svn/trunk@4130 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp
index a3b7396..812ea65 100644
--- a/src/core/SkPicture.cpp
+++ b/src/core/SkPicture.cpp
@@ -162,6 +162,14 @@
     return fRecord;
 }
 
+bool SkPicture::hasRecorded() const {
+    return NULL != fRecord && fRecord->writeStream().size() > 0;
+}
+
+bool SkPicture::willFlattenPixelsOnRecord(const SkBitmap& bitmap) const {
+    return NULL != fRecord && fRecord->shouldFlattenPixels(bitmap);
+}
+
 SkCanvas* SkPicture::getRecordingCanvas() const {
     // will be null if we are not recording
     return fRecord;
diff --git a/src/core/SkPictureFlat.cpp b/src/core/SkPictureFlat.cpp
index edbf4e3..14d912d 100644
--- a/src/core/SkPictureFlat.cpp
+++ b/src/core/SkPictureFlat.cpp
@@ -61,7 +61,8 @@
 
 SkFlatData* SkFlatData::Create(SkChunkAlloc* heap, const void* obj,
         int index, void (*flattenProc)(SkOrderedWriteBuffer&, const void*),
-        SkRefCntSet* refCntRecorder, SkRefCntSet* faceRecorder) {
+        SkRefCntSet* refCntRecorder, SkRefCntSet* faceRecorder,
+        uint32_t writeBufferflags) {
 
     // a buffer of 256 bytes should be sufficient for most paints, regions,
     // and matrices.
@@ -73,6 +74,7 @@
     if (faceRecorder) {
         buffer.setTypefaceRecorder(faceRecorder);
     }
+    buffer.setFlags(writeBufferflags);
 
     flattenProc(buffer, obj);
     uint32_t size = buffer.size();
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index e0a5b4f..832a645 100644
--- a/src/core/SkPictureFlat.h
+++ b/src/core/SkPictureFlat.h
@@ -168,7 +168,8 @@
     static SkFlatData* Create(SkChunkAlloc* heap, const void* obj, int index,
                               void (*flattenProc)(SkOrderedWriteBuffer&, const void*),
                               SkRefCntSet* refCntRecorder = NULL,
-                              SkRefCntSet* faceRecorder = NULL);
+                              SkRefCntSet* faceRecorder = NULL,
+                              uint32_t writeBufferflags = 0);
     void unflatten(void* result,
                    void (*unflattenProc)(SkOrderedReadBuffer&, void*),
                    SkRefCntPlayback* refCntPlayback = NULL,
@@ -208,11 +209,11 @@
      * the element wasn't previously in the dictionary it is automatically added
      */
     int find(const T* element, SkRefCntSet* refCntRecorder = NULL,
-             SkRefCntSet* faceRecorder = NULL) {
+             SkRefCntSet* faceRecorder = NULL, uint32_t writeBufferflags = 0) {
         if (element == NULL)
             return 0;
         SkFlatData* flat = SkFlatData::Create(fHeap, element, fNextIndex,
-                fFlattenProc, refCntRecorder, faceRecorder);
+                fFlattenProc, refCntRecorder, faceRecorder, writeBufferflags);
         int index = SkTSearch<SkFlatData>((const SkFlatData**) fData.begin(),
                 fData.count(), flat, sizeof(flat), &SkFlatData::Compare);
         if (index >= 0) {
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index 422001b..a499ee1 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -7,6 +7,7 @@
  */
 #include "SkPictureRecord.h"
 #include "SkTSearch.h"
+#include "SkPixelRef.h"
 
 #define MIN_WRITER_SIZE 16384
 #define HEAP_BLOCK_SIZE 4096
@@ -495,6 +496,7 @@
     fPathHeap = NULL;
 
     fBitmaps.reset();
+    fPixelRefDictionary.reset();
     fMatrices.reset();
     fPaints.reset();
     fPictureRefs.unrefAll();
@@ -510,7 +512,7 @@
 }
 
 void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
-    addInt(fBitmaps.find(&bitmap, &fRCSet));
+    addInt(find(bitmap));
 }
 
 void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
@@ -611,6 +613,42 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+bool SkPictureRecord::shouldFlattenPixels(const SkBitmap& bitmap) const {
+    return (fRecordFlags &
+        SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag)
+        && !bitmap.isImmutable() && bitmap.pixelRef()
+        && NULL == bitmap.getTexture();
+}
+
+int SkPictureRecord::find(const SkBitmap& bitmap) {
+    int dictionaryIndex = 0;
+    PixelRefDictionaryEntry entry;
+    bool flattenPixels = shouldFlattenPixels(bitmap);
+    if (flattenPixels) {
+        // Flattened bitmap may be very large. First attempt a fast lookup
+        // based on generation ID to avoid unnecessary flattening in
+        // fBitmaps.find()
+        entry.fKey = bitmap.pixelRef()->getGenerationID();
+        dictionaryIndex = 
+            SkTSearch<const PixelRefDictionaryEntry>(fPixelRefDictionary.begin(),
+            fPixelRefDictionary.count(), entry, sizeof(entry));
+        if (dictionaryIndex >= 0) {
+            return fPixelRefDictionary[dictionaryIndex].fIndex;
+        }
+    }
+    
+    uint32_t writeFlags = flattenPixels ?
+        SkFlattenableWriteBuffer::kForceFlattenBitmapPixels_Flag : 0;
+    int index = fBitmaps.find(&bitmap, &fRCSet, NULL, writeFlags);
+
+    if (flattenPixels) {
+        entry.fIndex = index;
+        dictionaryIndex = ~dictionaryIndex;
+        *fPixelRefDictionary.insert(dictionaryIndex) = entry;
+    }
+    return index;
+}
+
 #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 96809bf..4d0e962 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -86,12 +86,27 @@
     }
 
     void reset();
+    void setFlags(uint32_t recordFlags) {
+        fRecordFlags = recordFlags;
+    }
 
     const SkWriter32& writeStream() const {
         return fWriter;
     }
 
+    bool shouldFlattenPixels(const SkBitmap&) const;
 private:
+    struct PixelRefDictionaryEntry {
+        uint32_t fKey; // SkPixelRef GenerationID.
+        uint32_t fIndex; // Index of corresponding flattened bitmap in fBitmaps.
+        bool operator < (const PixelRefDictionaryEntry& other) const {
+            return this->fKey < other.fKey;
+        } 
+        bool operator != (const PixelRefDictionaryEntry& other) const {
+            return this->fKey != other.fKey;
+        } 
+    };
+
     SkTDArray<uint32_t> fRestoreOffsetStack;
     int fFirstSavedLayerIndex;
     enum {
@@ -127,6 +142,8 @@
     void addRegion(const SkRegion& region);
     void addText(const void* text, size_t byteLength);
 
+    int find(const SkBitmap& bitmap);
+
 #ifdef SK_DEBUG_DUMP
 public:
     void dumpMatrices();
@@ -163,6 +180,8 @@
 
 private:
     SkChunkAlloc fHeap;
+
+    SkTDArray<PixelRefDictionaryEntry> fPixelRefDictionary;
     SkBitmapDictionary fBitmaps;
     SkMatrixDictionary fMatrices;
     SkPaintDictionary fPaints;
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
index e965050..ac2b3cc 100644
--- a/src/utils/SkDeferredCanvas.cpp
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -450,7 +450,8 @@
     fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
     fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice));
     fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
-        fImmediateDevice->height(), 0);
+        fImmediateDevice->height(),
+        SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag);
 }
 
 SkDeferredCanvas::DeferredDevice::~DeferredDevice() {
@@ -482,7 +483,8 @@
             // old one, hence purging deferred draw ops.
             fRecordingCanvas = fPicture.beginRecording(
                 fImmediateDevice->width(),
-                fImmediateDevice->height(), 0);
+                fImmediateDevice->height(),
+                SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag);
 
             // Restore pre-purge state
             if (!clipRegion.isEmpty()) {
@@ -506,12 +508,16 @@
 }
 
 void SkDeferredCanvas::DeferredDevice::flushPending() {
+    if (!fPicture.hasRecorded()) {
+        return;
+    }
     if (fDeviceContext) {
         fDeviceContext->prepareForDraw();
     }
     fPicture.draw(fImmediateCanvas);
     fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(), 
-        fImmediateDevice->height(), 0);
+        fImmediateDevice->height(),
+        SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag);
 }
 
 void SkDeferredCanvas::DeferredDevice::flush() {
@@ -520,8 +526,8 @@
 }
 
 void SkDeferredCanvas::DeferredDevice::flushIfNeeded(const SkBitmap& bitmap) {
-    if (bitmap.isImmutable()) {
-        return; // safe to deffer without registering a dependency
+    if (bitmap.isImmutable() || fPicture.willFlattenPixelsOnRecord(bitmap)) {
+        return; // safe to defer.
     }
 
     // For now, drawing a writable bitmap triggers a flush