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
This commit is contained in:
junov@chromium.org 2012-06-01 21:23:07 +00:00
parent 898e7b568f
commit 4866cc0afb
9 changed files with 144 additions and 31 deletions

View File

@ -267,15 +267,20 @@ public:
SkFactorySet* setFactoryRecorder(SkFactorySet*);
enum Flags {
kCrossProcess_Flag = 0x01,
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
kInlineFactoryNames_Flag = 0x02,
/**
* Instructs the writer to always serialize bitmap pixel data.
*/
kForceFlattenBitmapPixels_Flag = 0x04
};
Flags getFlags() const { return (Flags)fFlags; }
void setFlags(Flags flags) { fFlags = flags; }
uint32_t getFlags() const { return fFlags; }
void setFlags(uint32_t flags) { fFlags = flags; }
bool isCrossProcess() const {
return SkToBool(fFlags & kCrossProcess_Flag);
@ -285,7 +290,7 @@ public:
}
bool persistBitmapPixels() const {
return (fFlags & kCrossProcess_Flag) != 0;
return (fFlags & (kCrossProcess_Flag | kForceFlattenBitmapPixels_Flag)) != 0;
}
bool persistTypeface() const { return (fFlags & kCrossProcess_Flag) != 0; }

View File

@ -12,6 +12,7 @@
#include "SkRefCnt.h"
class SkBitmap;
class SkCanvas;
class SkPicturePlayback;
class SkPictureRecord;
@ -51,7 +52,16 @@ public:
clip-query calls will reflect the path's bounds, not the actual
path.
*/
kUsePathBoundsForClip_RecordingFlag = 0x01
kUsePathBoundsForClip_RecordingFlag = 0x01,
/* When a draw operation is recorded that has a bitmap parameter, it
may be unsafe to defer rendering if source bitmap may be written to
between the time of recording and the time of executing the draw
operation. This flag specifies that SkPicture should serialize a
snapshot of any source bitmaps that reside in RAM and are not
marked as immutable, making the draw operation safe for deferral.
*/
kFlattenMutableNonTexturePixelRefs_RecordingFlag = 0x02
};
/** Returns the canvas that records the drawing commands.
@ -74,6 +84,16 @@ public:
is drawn.
*/
void endRecording();
/** Returns true if any draw commands have been recorded since the last
call to beginRecording.
*/
bool hasRecorded() const;
/** Returns true if a snapshot of the specified bitmap will be flattened
whaen a draw operation using the bitmap is recorded.
*/
bool willFlattenPixelsOnRecord(const SkBitmap&) const;
/** Replays the drawing commands on the specified canvas. This internally
calls endRecording() if that has not already been called.

View File

@ -162,6 +162,14 @@ SkCanvas* SkPicture::beginRecording(int width, int height,
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;

View File

@ -61,7 +61,8 @@ SkRefCnt* SkRefCntPlayback::set(int index, SkRefCnt* obj) {
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 @@ SkFlatData* SkFlatData::Create(SkChunkAlloc* heap, const void* obj,
if (faceRecorder) {
buffer.setTypefaceRecorder(faceRecorder);
}
buffer.setFlags(writeBufferflags);
flattenProc(buffer, obj);
uint32_t size = buffer.size();

View File

@ -168,7 +168,8 @@ public:
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 @@ public:
* 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) {

View File

@ -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 @@ void SkPictureRecord::reset() {
fPathHeap = NULL;
fBitmaps.reset();
fPixelRefDictionary.reset();
fMatrices.reset();
fPaints.reset();
fPictureRefs.unrefAll();
@ -510,7 +512,7 @@ void SkPictureRecord::reset() {
}
void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
addInt(fBitmaps.find(&bitmap, &fRCSet));
addInt(find(bitmap));
}
void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
@ -611,6 +613,42 @@ void SkPictureRecord::addText(const void* text, size_t byteLength) {
///////////////////////////////////////////////////////////////////////////////
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;

View File

@ -86,12 +86,27 @@ public:
}
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 @@ private:
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 @@ public:
private:
SkChunkAlloc fHeap;
SkTDArray<PixelRefDictionaryEntry> fPixelRefDictionary;
SkBitmapDictionary fBitmaps;
SkMatrixDictionary fMatrices;
SkPaintDictionary fPaints;

View File

@ -450,7 +450,8 @@ SkDeferredCanvas::DeferredDevice::DeferredDevice(
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 @@ void SkDeferredCanvas::DeferredDevice::contentsCleared() {
// 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 @@ bool SkDeferredCanvas::DeferredDevice::isFreshFrame() {
}
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::flush() {
}
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

View File

@ -562,17 +562,19 @@ private:
public:
static void TestPictureSerializationRoundTrip(skiatest::Reporter* reporter,
CanvasTestStep* testStep) {
CanvasTestStep* testStep,
uint32_t recordFlags) {
testStep->setAssertMessageFormat(kPictureDrawAssertMessageFormat);
SkPicture referencePicture;
testStep->draw(referencePicture.beginRecording(kWidth, kHeight),
reporter);
testStep->draw(referencePicture.beginRecording(kWidth, kHeight,
recordFlags), reporter);
SkPicture initialPicture;
testStep->draw(initialPicture.beginRecording(kWidth, kHeight),
reporter);
testStep->draw(initialPicture.beginRecording(kWidth, kHeight,
recordFlags), reporter);
testStep->setAssertMessageFormat(kPictureReDrawAssertMessageFormat);
SkPicture roundTripPicture;
initialPicture.draw(roundTripPicture.beginRecording(kWidth, kHeight));
initialPicture.draw(roundTripPicture.beginRecording(kWidth, kHeight,
recordFlags));
SkPictureRecord* referenceRecord = static_cast<SkPictureRecord*>(
referencePicture.getRecordingCanvas());
@ -618,17 +620,18 @@ public:
}
static void TestPictureFlattenedObjectReuse(skiatest::Reporter* reporter,
CanvasTestStep* testStep) {
CanvasTestStep* testStep,
uint32_t recordFlags) {
// Verify that when a test step is executed twice, no extra resources
// are flattened during the second execution
testStep->setAssertMessageFormat(kPictureDrawAssertMessageFormat);
SkPicture referencePicture;
SkCanvas* referenceCanvas = referencePicture.beginRecording(kWidth,
kHeight);
kHeight, recordFlags);
testStep->draw(referenceCanvas, reporter);
SkPicture testPicture;
SkCanvas* testCanvas = testPicture.beginRecording(kWidth,
kHeight);
kHeight, recordFlags);
testStep->draw(testCanvas, reporter);
testStep->setAssertMessageFormat(kPictureSecondDrawAssertMessageFormat);
testStep->draw(testCanvas, reporter);
@ -645,11 +648,13 @@ public:
static void TestPictureStateConsistency(skiatest::Reporter* reporter,
CanvasTestStep* testStep,
const SkCanvas& referenceCanvas) {
const SkCanvas& referenceCanvas,
uint32_t recordFlags) {
// Verify that the recording canvas's state is consistent
// with that of a regular canvas
SkPicture testPicture;
SkCanvas* pictureCanvas = testPicture.beginRecording(kWidth, kHeight);
SkCanvas* pictureCanvas = testPicture.beginRecording(kWidth, kHeight,
recordFlags);
testStep->setAssertMessageFormat(kPictureDrawAssertMessageFormat);
testStep->draw(pictureCanvas, reporter);
testStep->setAssertMessageFormat(kPictureRecoringAssertMessageFormat);
@ -668,7 +673,8 @@ static void TestPictureStateConsistency(skiatest::Reporter* reporter,
// The following test code is commented out because SkPicture is not
// currently expected to preserve state when restarting recording.
/*
SkCanvas* pictureCanvas = testPicture.beginRecording(kWidth, kHeight);
SkCanvas* pictureCanvas = testPicture.beginRecording(kWidth, kHeight,
recordFlags);
testStep->setAssertMessageFormat(kPictureResumeAssertMessageFormat);
AssertCanvasStatesEqual(reporter, pictureCanvas, &referenceCanvas,
testStep);
@ -775,7 +781,9 @@ static void TestOverrideStateConsistency(skiatest::Reporter* reporter,
testStep->setAssertMessageFormat(kCanvasDrawAssertMessageFormat);
testStep->draw(&referenceCanvas, reporter);
TestPictureStateConsistency(reporter, testStep, referenceCanvas);
TestPictureStateConsistency(reporter, testStep, referenceCanvas, 0);
TestPictureStateConsistency(reporter, testStep, referenceCanvas,
SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag);
TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas);
// The following test code is commented out because SkProxyCanvas is
@ -800,9 +808,15 @@ static void TestCanvas(skiatest::Reporter* reporter) {
for (int testStep = 0; testStep < testStepArray().count(); testStep++) {
TestOverrideStateConsistency(reporter, testStepArray()[testStep]);
SkPictureTester::TestPictureSerializationRoundTrip(reporter,
testStepArray()[testStep]);
testStepArray()[testStep], 0);
SkPictureTester::TestPictureSerializationRoundTrip(reporter,
testStepArray()[testStep],
SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag);
SkPictureTester::TestPictureFlattenedObjectReuse(reporter,
testStepArray()[testStep]);
testStepArray()[testStep], 0);
SkPictureTester::TestPictureFlattenedObjectReuse(reporter,
testStepArray()[testStep],
SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag);
}
}