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:
parent
898e7b568f
commit
4866cc0afb
@ -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; }
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user