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