aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar junov@chromium.org <junov@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-08-07 16:48:22 +0000
committerGravatar junov@chromium.org <junov@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-08-07 16:48:22 +0000
commit88e29146c1efc5ff8eec06076c9dce12684f2c11 (patch)
treedf238dc7d1814e2a183e585ef3045804ba698ec3
parent53e845e7e79774ed5ad7c57d69e06b91fad31387 (diff)
Moving DeferredDevice and DeferredGPipeController classes out of the deferred canvas API header
Review URL: https://codereview.appspot.com/6449104 git-svn-id: http://skia.googlecode.com/svn/trunk@4989 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--include/utils/SkDeferredCanvas.h194
-rw-r--r--src/utils/SkDeferredCanvas.cpp668
-rw-r--r--tests/CanvasTest.cpp73
-rw-r--r--tests/DeferredCanvasTest.cpp29
4 files changed, 482 insertions, 482 deletions
diff --git a/include/utils/SkDeferredCanvas.h b/include/utils/SkDeferredCanvas.h
index 01a71140fa..67ef2aecc6 100644
--- a/include/utils/SkDeferredCanvas.h
+++ b/include/utils/SkDeferredCanvas.h
@@ -9,11 +9,9 @@
#define SkDeferredCanvas_DEFINED
#include "SkCanvas.h"
-#include "SkDevice.h"
#include "SkPixelRef.h"
-#include "SkGPipe.h"
-#include "SkChunkAlloc.h"
+class DeferredDevice;
/** \class SkDeferredCanvas
Subclass of SkCanvas that encapsulates an SkPicture or SkGPipe for deferred
@@ -80,7 +78,19 @@ public:
/**
* Returns true if deferred drawing is currenlty enabled.
*/
- bool isDeferredDrawing();
+ bool isDeferredDrawing() const;
+
+ /**
+ * Returns true if the canvas contains a fresh frame. A frame is
+ * considered fresh when its content do not depend on the contents
+ * of the previous frame. For example, if a canvas is cleared before
+ * drawing each frame, the frames will all be considered fresh.
+ * A frame is defined as the graphics image produced by as a result
+ * of all the canvas draws operation executed between two successive
+ * calls to isFreshFrame. The result of isFreshFrame is computed
+ * conservatively, so it may report false negatives.
+ */
+ bool isFreshFrame() const;
/**
* Specify the maximum number of bytes to be allocated for the purpose
@@ -177,188 +187,18 @@ public:
};
protected:
- class DeferredPipeController : public SkGPipeController {
- public:
- DeferredPipeController();
- void setPlaybackCanvas(SkCanvas*);
- virtual ~DeferredPipeController();
- virtual void* requestBlock(size_t minRequest, size_t* actual) SK_OVERRIDE;
- virtual void notifyWritten(size_t bytes) SK_OVERRIDE;
- void playback();
- void reset();
- bool hasRecorded() const { return fAllocator.blockCount() != 0; }
- size_t storageAllocatedForRecording() const { return fAllocator.totalCapacity(); }
- private:
- enum {
- kMinBlockSize = 4096
- };
- struct PipeBlock {
- PipeBlock(void* block, size_t size) { fBlock = block, fSize = size; }
- void* fBlock;
- size_t fSize;
- };
- void* fBlock;
- size_t fBytesWritten;
- SkChunkAlloc fAllocator;
- SkTDArray<PipeBlock> fBlockList;
- SkGPipeReader fReader;
- };
-
-public:
- class DeferredDevice : public SkDevice {
- public:
- /**
- * Constructor
- * @param immediateDevice device to be drawn to when flushing
- * deferred operations
- * @param deviceContext callback interface for managing graphics
- * context state, can be NULL.
- */
- DeferredDevice(SkDevice* immediateDevice,
- DeviceContext* deviceContext = NULL);
- ~DeferredDevice();
-
- /**
- * Sets the device context to be use with the device.
- * @param deviceContext callback interface for managing graphics
- * context state, can be NULL.
- */
- void setDeviceContext(DeviceContext* deviceContext);
-
- /**
- * Returns the recording canvas.
- */
- SkCanvas* recordingCanvas();
-
- /**
- * Returns the immediate (non deferred) canvas.
- */
- SkCanvas* immediateCanvas() const {return fImmediateCanvas;}
-
- /**
- * Returns the immediate (non deferred) device.
- */
- SkDevice* immediateDevice() const {return fImmediateDevice;}
-
- /**
- * Returns true if an opaque draw operation covering the entire canvas
- * was performed since the last call to isFreshFrame().
- */
- bool isFreshFrame();
-
- size_t storageAllocatedForRecording() const;
- size_t freeMemoryIfPossible(size_t bytesToFree);
- void flushPending();
- void contentsCleared();
- void setMaxRecordingStorage(size_t);
-
- virtual uint32_t getDeviceCapabilities() SK_OVERRIDE;
- virtual int width() const SK_OVERRIDE;
- virtual int height() const SK_OVERRIDE;
- virtual SkGpuRenderTarget* accessRenderTarget() SK_OVERRIDE;
-
- virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config,
- int width, int height,
- bool isOpaque,
- Usage usage) SK_OVERRIDE;
-
- virtual void writePixels(const SkBitmap& bitmap, int x, int y,
- SkCanvas::Config8888 config8888) SK_OVERRIDE;
-
- protected:
- virtual const SkBitmap& onAccessBitmap(SkBitmap*) SK_OVERRIDE;
- virtual bool onReadPixels(const SkBitmap& bitmap,
- int x, int y,
- SkCanvas::Config8888 config8888) SK_OVERRIDE;
-
- // The following methods are no-ops on a deferred device
- virtual bool filterTextFlags(const SkPaint& paint, TextFlags*)
- SK_OVERRIDE
- {return false;}
- virtual void setMatrixClip(const SkMatrix&, const SkRegion&,
- const SkClipStack&) SK_OVERRIDE
- {}
-
- // None of the following drawing methods should ever get called on the
- // deferred device
- virtual void clear(SkColor color)
- {SkASSERT(0);}
- virtual void drawPaint(const SkDraw&, const SkPaint& paint)
- {SkASSERT(0);}
- virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
- size_t count, const SkPoint[],
- const SkPaint& paint)
- {SkASSERT(0);}
- virtual void drawRect(const SkDraw&, const SkRect& r,
- const SkPaint& paint)
- {SkASSERT(0);}
- virtual void drawPath(const SkDraw&, const SkPath& path,
- const SkPaint& paint,
- const SkMatrix* prePathMatrix = NULL,
- bool pathIsMutable = false)
- {SkASSERT(0);}
- virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
- const SkIRect* srcRectOrNull,
- const SkMatrix& matrix, const SkPaint& paint)
- {SkASSERT(0);}
- virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
- int x, int y, const SkPaint& paint)
- {SkASSERT(0);}
- virtual void drawText(const SkDraw&, const void* text, size_t len,
- SkScalar x, SkScalar y, const SkPaint& paint)
- {SkASSERT(0);}
- virtual void drawPosText(const SkDraw&, const void* text, size_t len,
- const SkScalar pos[], SkScalar constY,
- int scalarsPerPos, const SkPaint& paint)
- {SkASSERT(0);}
- virtual void drawTextOnPath(const SkDraw&, const void* text,
- size_t len, const SkPath& path,
- const SkMatrix* matrix,
- const SkPaint& paint)
- {SkASSERT(0);}
- virtual void drawPosTextOnPath(const SkDraw& draw, const void* text,
- size_t len, const SkPoint pos[],
- const SkPaint& paint,
- const SkPath& path,
- const SkMatrix* matrix)
- {SkASSERT(0);}
- virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode,
- int vertexCount, const SkPoint verts[],
- const SkPoint texs[], const SkColor colors[],
- SkXfermode* xmode, const uint16_t indices[],
- int indexCount, const SkPaint& paint)
- {SkASSERT(0);}
- virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
- const SkPaint&)
- {SkASSERT(0);}
- private:
- virtual void flush();
-
- void endRecording();
- void beginRecording();
-
- DeferredPipeController fPipeController;
- SkGPipeWriter fPipeWriter;
- SkDevice* fImmediateDevice;
- SkCanvas* fImmediateCanvas;
- SkCanvas* fRecordingCanvas;
- DeviceContext* fDeviceContext;
- bool fFreshFrame;
- size_t fMaxRecordingStorageBytes;
- };
-
- DeferredDevice* getDeferredDevice() const;
-
-protected:
virtual SkCanvas* canvasForDrawIter();
+ DeferredDevice* getDeferredDevice() const;
private:
SkCanvas* drawingCanvas() const;
+ SkCanvas* immediateCanvas() const;
bool isFullFrame(const SkRect*, const SkPaint*) const;
void validate() const;
void init();
bool fDeferredDrawing;
+ friend class SkDeferredCanvasTester; // for unit testing
typedef SkCanvas INHERITED;
};
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
index 731e80b2ce..6259f949ee 100644
--- a/src/utils/SkDeferredCanvas.cpp
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -8,10 +8,13 @@
#include "SkDeferredCanvas.h"
-#include "SkPaint.h"
-#include "SkShader.h"
+#include "SkChunkAlloc.h"
#include "SkColorFilter.h"
+#include "SkDevice.h"
#include "SkDrawFilter.h"
+#include "SkGPipe.h"
+#include "SkPaint.h"
+#include "SkShader.h"
SK_DEFINE_INST_COUNT(SkDeferredCanvas::DeviceContext)
@@ -139,6 +142,400 @@ bool isPaintOpaque(const SkPaint* paint,
} // unnamed namespace
+//-----------------------------------------------------------------------------
+// DeferredPipeController
+//-----------------------------------------------------------------------------
+
+class DeferredPipeController : public SkGPipeController {
+public:
+ DeferredPipeController();
+ void setPlaybackCanvas(SkCanvas*);
+ virtual ~DeferredPipeController();
+ virtual void* requestBlock(size_t minRequest, size_t* actual) SK_OVERRIDE;
+ virtual void notifyWritten(size_t bytes) SK_OVERRIDE;
+ void playback();
+ void reset();
+ bool hasRecorded() const { return fAllocator.blockCount() != 0; }
+ size_t storageAllocatedForRecording() const { return fAllocator.totalCapacity(); }
+private:
+ enum {
+ kMinBlockSize = 4096
+ };
+ struct PipeBlock {
+ PipeBlock(void* block, size_t size) { fBlock = block, fSize = size; }
+ void* fBlock;
+ size_t fSize;
+ };
+ void* fBlock;
+ size_t fBytesWritten;
+ SkChunkAlloc fAllocator;
+ SkTDArray<PipeBlock> fBlockList;
+ SkGPipeReader fReader;
+};
+
+DeferredPipeController::DeferredPipeController() :
+ fAllocator(kMinBlockSize) {
+ fBlock = NULL;
+ fBytesWritten = 0;
+}
+
+DeferredPipeController::~DeferredPipeController() {
+ fAllocator.reset();
+}
+
+void DeferredPipeController::setPlaybackCanvas(SkCanvas* canvas) {
+ fReader.setCanvas(canvas);
+}
+
+void* DeferredPipeController::requestBlock(size_t minRequest, size_t *actual) {
+ if (fBlock) {
+ // Save the previous block for later
+ PipeBlock previousBloc(fBlock, fBytesWritten);
+ fBlockList.push(previousBloc);
+ }
+ int32_t blockSize = SkMax32(minRequest, kMinBlockSize);
+ fBlock = fAllocator.allocThrow(blockSize);
+ fBytesWritten = 0;
+ *actual = blockSize;
+ return fBlock;
+}
+
+void DeferredPipeController::notifyWritten(size_t bytes) {
+ fBytesWritten += bytes;
+}
+
+void DeferredPipeController::playback() {
+
+ for (int currentBlock = 0; currentBlock < fBlockList.count(); currentBlock++ ) {
+ fReader.playback(fBlockList[currentBlock].fBlock, fBlockList[currentBlock].fSize);
+ }
+ fBlockList.reset();
+
+ if (fBlock) {
+ fReader.playback(fBlock, fBytesWritten);
+ fBlock = NULL;
+ }
+
+ // Release all allocated blocks
+ fAllocator.reset();
+}
+
+void DeferredPipeController::reset() {
+ fBlockList.reset();
+ fBlock = NULL;
+ fAllocator.reset();
+}
+
+//-----------------------------------------------------------------------------
+// DeferredDevice
+//-----------------------------------------------------------------------------
+
+class DeferredDevice : public SkDevice {
+public:
+ DeferredDevice(SkDevice* immediateDevice,
+ SkDeferredCanvas::DeviceContext* deviceContext = NULL);
+ ~DeferredDevice();
+
+ void setDeviceContext(SkDeferredCanvas::DeviceContext* deviceContext);
+ SkCanvas* recordingCanvas();
+ SkCanvas* immediateCanvas() const {return fImmediateCanvas;}
+ SkDevice* immediateDevice() const {return fImmediateDevice;}
+ bool isFreshFrame();
+ size_t storageAllocatedForRecording() const;
+ size_t freeMemoryIfPossible(size_t bytesToFree);
+ void flushPending();
+ void contentsCleared();
+ void setMaxRecordingStorage(size_t);
+
+ virtual uint32_t getDeviceCapabilities() SK_OVERRIDE;
+ virtual int width() const SK_OVERRIDE;
+ virtual int height() const SK_OVERRIDE;
+ virtual SkGpuRenderTarget* accessRenderTarget() SK_OVERRIDE;
+
+ virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config,
+ int width, int height,
+ bool isOpaque,
+ Usage usage) SK_OVERRIDE;
+
+ virtual void writePixels(const SkBitmap& bitmap, int x, int y,
+ SkCanvas::Config8888 config8888) SK_OVERRIDE;
+
+protected:
+ virtual const SkBitmap& onAccessBitmap(SkBitmap*) SK_OVERRIDE;
+ virtual bool onReadPixels(const SkBitmap& bitmap,
+ int x, int y,
+ SkCanvas::Config8888 config8888) SK_OVERRIDE;
+
+ // The following methods are no-ops on a deferred device
+ virtual bool filterTextFlags(const SkPaint& paint, TextFlags*)
+ SK_OVERRIDE
+ {return false;}
+ virtual void setMatrixClip(const SkMatrix&, const SkRegion&,
+ const SkClipStack&) SK_OVERRIDE
+ {}
+
+ // None of the following drawing methods should ever get called on the
+ // deferred device
+ virtual void clear(SkColor color)
+ {SkASSERT(0);}
+ virtual void drawPaint(const SkDraw&, const SkPaint& paint)
+ {SkASSERT(0);}
+ virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
+ size_t count, const SkPoint[],
+ const SkPaint& paint)
+ {SkASSERT(0);}
+ virtual void drawRect(const SkDraw&, const SkRect& r,
+ const SkPaint& paint)
+ {SkASSERT(0);}
+ virtual void drawPath(const SkDraw&, const SkPath& path,
+ const SkPaint& paint,
+ const SkMatrix* prePathMatrix = NULL,
+ bool pathIsMutable = false)
+ {SkASSERT(0);}
+ virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+ const SkIRect* srcRectOrNull,
+ const SkMatrix& matrix, const SkPaint& paint)
+ {SkASSERT(0);}
+ virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
+ int x, int y, const SkPaint& paint)
+ {SkASSERT(0);}
+ virtual void drawText(const SkDraw&, const void* text, size_t len,
+ SkScalar x, SkScalar y, const SkPaint& paint)
+ {SkASSERT(0);}
+ virtual void drawPosText(const SkDraw&, const void* text, size_t len,
+ const SkScalar pos[], SkScalar constY,
+ int scalarsPerPos, const SkPaint& paint)
+ {SkASSERT(0);}
+ virtual void drawTextOnPath(const SkDraw&, const void* text,
+ size_t len, const SkPath& path,
+ const SkMatrix* matrix,
+ const SkPaint& paint)
+ {SkASSERT(0);}
+ virtual void drawPosTextOnPath(const SkDraw& draw, const void* text,
+ size_t len, const SkPoint pos[],
+ const SkPaint& paint,
+ const SkPath& path,
+ const SkMatrix* matrix)
+ {SkASSERT(0);}
+ virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode,
+ int vertexCount, const SkPoint verts[],
+ const SkPoint texs[], const SkColor colors[],
+ SkXfermode* xmode, const uint16_t indices[],
+ int indexCount, const SkPaint& paint)
+ {SkASSERT(0);}
+ virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
+ const SkPaint&)
+ {SkASSERT(0);}
+private:
+ virtual void flush();
+
+ void endRecording();
+ void beginRecording();
+
+ DeferredPipeController fPipeController;
+ SkGPipeWriter fPipeWriter;
+ SkDevice* fImmediateDevice;
+ SkCanvas* fImmediateCanvas;
+ SkCanvas* fRecordingCanvas;
+ SkDeferredCanvas::DeviceContext* fDeviceContext;
+ bool fFreshFrame;
+ size_t fMaxRecordingStorageBytes;
+};
+
+DeferredDevice::DeferredDevice(
+ SkDevice* immediateDevice, SkDeferredCanvas::DeviceContext* deviceContext) :
+ SkDevice(SkBitmap::kNo_Config, immediateDevice->width(),
+ immediateDevice->height(), immediateDevice->isOpaque())
+ , fFreshFrame(true) {
+
+ fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes;
+ fDeviceContext = deviceContext;
+ SkSafeRef(fDeviceContext);
+ fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
+ fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice));
+ fPipeController.setPlaybackCanvas(fImmediateCanvas);
+ this->beginRecording();
+}
+
+DeferredDevice::~DeferredDevice() {
+ this->flushPending();
+ SkSafeUnref(fImmediateCanvas);
+ SkSafeUnref(fDeviceContext);
+}
+
+void DeferredDevice::setMaxRecordingStorage(size_t maxStorage) {
+ fMaxRecordingStorageBytes = maxStorage;
+ this->recordingCanvas(); // Accessing the recording canvas applies the new limit.
+}
+
+void DeferredDevice::endRecording() {
+ fPipeWriter.endRecording();
+ fPipeController.reset();
+ fRecordingCanvas = NULL;
+}
+
+void DeferredDevice::beginRecording() {
+ fRecordingCanvas = fPipeWriter.startRecording(&fPipeController, 0);
+}
+
+void DeferredDevice::setDeviceContext(
+ SkDeferredCanvas::DeviceContext* deviceContext) {
+ SkRefCnt_SafeAssign(fDeviceContext, deviceContext);
+}
+
+void DeferredDevice::contentsCleared() {
+ if (!fRecordingCanvas->isDrawingToLayer()) {
+ fFreshFrame = true;
+
+ // TODO: find a way to transfer the state stack and layers
+ // to the new recording canvas. For now, purging only works
+ // with an empty stack.
+ if (fRecordingCanvas->getSaveCount() == 0) {
+
+ // Save state that is trashed by the purge
+ SkDrawFilter* drawFilter = fRecordingCanvas->getDrawFilter();
+ SkSafeRef(drawFilter); // So that it survives the purge
+ SkMatrix matrix = fRecordingCanvas->getTotalMatrix();
+ SkRegion clipRegion = fRecordingCanvas->getTotalClip();
+
+ // beginRecording creates a new recording canvas and discards the
+ // old one, hence purging deferred draw ops.
+ this->endRecording();
+ this->beginRecording();
+
+ // Restore pre-purge state
+ if (!clipRegion.isEmpty()) {
+ fRecordingCanvas->clipRegion(clipRegion,
+ SkRegion::kReplace_Op);
+ }
+ if (!matrix.isIdentity()) {
+ fRecordingCanvas->setMatrix(matrix);
+ }
+ if (drawFilter) {
+ fRecordingCanvas->setDrawFilter(drawFilter)->unref();
+ }
+ }
+ }
+}
+
+bool DeferredDevice::isFreshFrame() {
+ bool ret = fFreshFrame;
+ fFreshFrame = false;
+ return ret;
+}
+
+void DeferredDevice::flushPending() {
+ if (!fPipeController.hasRecorded()) {
+ return;
+ }
+ if (fDeviceContext) {
+ fDeviceContext->prepareForDraw();
+ }
+
+ fPipeWriter.flushRecording(true);
+ fPipeController.playback();
+}
+
+void DeferredDevice::flush() {
+ this->flushPending();
+ fImmediateCanvas->flush();
+}
+
+size_t DeferredDevice::freeMemoryIfPossible(size_t bytesToFree) {
+ return fPipeWriter.freeMemoryIfPossible(bytesToFree);
+}
+
+size_t DeferredDevice::storageAllocatedForRecording() const {
+ return (fPipeController.storageAllocatedForRecording()
+ + fPipeWriter.storageAllocatedForRecording());
+}
+
+SkCanvas* DeferredDevice::recordingCanvas() {
+ size_t storageAllocated = this->storageAllocatedForRecording();
+ if (storageAllocated > fMaxRecordingStorageBytes) {
+ // First, attempt to reduce cache without flushing
+ size_t tryFree = storageAllocated - fMaxRecordingStorageBytes;
+ if (this->freeMemoryIfPossible(tryFree) < tryFree) {
+ // Flush is necessary to free more space.
+ this->flushPending();
+ // Free as much as possible to avoid oscillating around fMaxRecordingStorageBytes
+ // which could cause a high flushing frequency.
+ this->freeMemoryIfPossible(~0);
+ }
+ }
+ return fRecordingCanvas;
+}
+
+uint32_t DeferredDevice::getDeviceCapabilities() {
+ return fImmediateDevice->getDeviceCapabilities();
+}
+
+int DeferredDevice::width() const {
+ return fImmediateDevice->width();
+}
+
+int DeferredDevice::height() const {
+ return fImmediateDevice->height();
+}
+
+SkGpuRenderTarget* DeferredDevice::accessRenderTarget() {
+ this->flushPending();
+ return fImmediateDevice->accessRenderTarget();
+}
+
+void DeferredDevice::writePixels(const SkBitmap& bitmap,
+ int x, int y, SkCanvas::Config8888 config8888) {
+
+ if (x <= 0 && y <= 0 && (x + bitmap.width()) >= width() &&
+ (y + bitmap.height()) >= height()) {
+ this->contentsCleared();
+ }
+
+ if (SkBitmap::kARGB_8888_Config == bitmap.config() &&
+ SkCanvas::kNative_Premul_Config8888 != config8888 &&
+ kPMColorAlias != config8888) {
+ //Special case config: no deferral
+ this->flushPending();
+ fImmediateDevice->writePixels(bitmap, x, y, config8888);
+ return;
+ }
+
+ SkPaint paint;
+ paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+ if (shouldDrawImmediately(&bitmap, NULL)) {
+ this->flushPending();
+ fImmediateCanvas->drawSprite(bitmap, x, y, &paint);
+ } else {
+ this->recordingCanvas()->drawSprite(bitmap, x, y, &paint);
+ }
+}
+
+const SkBitmap& DeferredDevice::onAccessBitmap(SkBitmap*) {
+ this->flushPending();
+ return fImmediateDevice->accessBitmap(false);
+}
+
+SkDevice* DeferredDevice::onCreateCompatibleDevice(
+ SkBitmap::Config config, int width, int height, bool isOpaque,
+ Usage usage) {
+
+ // Save layer usage not supported, and not required by SkDeferredCanvas.
+ SkASSERT(usage != kSaveLayer_Usage);
+ // Create a compatible non-deferred device.
+ SkAutoTUnref<SkDevice> compatibleDevice
+ (fImmediateDevice->createCompatibleDevice(config, width, height,
+ isOpaque));
+ return SkNEW_ARGS(DeferredDevice, (compatibleDevice, fDeviceContext));
+}
+
+bool DeferredDevice::onReadPixels(
+ const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888) {
+ this->flushPending();
+ return fImmediateCanvas->readPixels(const_cast<SkBitmap*>(&bitmap),
+ x, y, config8888);
+}
+
+
SkDeferredCanvas::SkDeferredCanvas() {
this->init();
}
@@ -182,8 +579,13 @@ SkCanvas* SkDeferredCanvas::drawingCanvas() const {
this->getDeferredDevice()->immediateCanvas();
}
-SkDeferredCanvas::DeferredDevice* SkDeferredCanvas::getDeferredDevice() const {
- return static_cast<SkDeferredCanvas::DeferredDevice*>(this->getDevice());
+SkCanvas* SkDeferredCanvas::immediateCanvas() const {
+ this->validate();
+ return this->getDeferredDevice()->immediateCanvas();
+}
+
+DeferredDevice* SkDeferredCanvas::getDeferredDevice() const {
+ return static_cast<DeferredDevice*>(this->getDevice());
}
void SkDeferredCanvas::setDeferredDrawing(bool val) {
@@ -197,10 +599,14 @@ void SkDeferredCanvas::setDeferredDrawing(bool val) {
}
}
-bool SkDeferredCanvas::isDeferredDrawing() {
+bool SkDeferredCanvas::isDeferredDrawing() const {
return fDeferredDrawing;
}
+bool SkDeferredCanvas::isFreshFrame() const {
+ return this->getDeferredDevice()->isFreshFrame();
+}
+
SkDeferredCanvas::~SkDeferredCanvas() {
}
@@ -505,255 +911,3 @@ SkDrawFilter* SkDeferredCanvas::setDrawFilter(SkDrawFilter* filter) {
SkCanvas* SkDeferredCanvas::canvasForDrawIter() {
return this->drawingCanvas();
}
-
-// SkDeferredCanvas::DeferredPipeController
-//-------------------------------------------
-
-SkDeferredCanvas::DeferredPipeController::DeferredPipeController() :
- fAllocator(kMinBlockSize) {
- fBlock = NULL;
- fBytesWritten = 0;
-}
-
-SkDeferredCanvas::DeferredPipeController::~DeferredPipeController() {
- fAllocator.reset();
-}
-
-void SkDeferredCanvas::DeferredPipeController::setPlaybackCanvas(SkCanvas* canvas) {
- fReader.setCanvas(canvas);
-}
-
-void* SkDeferredCanvas::DeferredPipeController::requestBlock(size_t minRequest, size_t *actual) {
- if (fBlock) {
- // Save the previous block for later
- PipeBlock previousBloc(fBlock, fBytesWritten);
- fBlockList.push(previousBloc);
- }
- int32_t blockSize = SkMax32(minRequest, kMinBlockSize);
- fBlock = fAllocator.allocThrow(blockSize);
- fBytesWritten = 0;
- *actual = blockSize;
- return fBlock;
-}
-
-void SkDeferredCanvas::DeferredPipeController::notifyWritten(size_t bytes) {
- fBytesWritten += bytes;
-}
-
-void SkDeferredCanvas::DeferredPipeController::playback() {
-
- for (int currentBlock = 0; currentBlock < fBlockList.count(); currentBlock++ ) {
- fReader.playback(fBlockList[currentBlock].fBlock, fBlockList[currentBlock].fSize);
- }
- fBlockList.reset();
-
- if (fBlock) {
- fReader.playback(fBlock, fBytesWritten);
- fBlock = NULL;
- }
-
- // Release all allocated blocks
- fAllocator.reset();
-}
-
-void SkDeferredCanvas::DeferredPipeController::reset() {
- fBlockList.reset();
- fBlock = NULL;
- fAllocator.reset();
-}
-
-// SkDeferredCanvas::DeferredDevice
-//------------------------------------
-
-SkDeferredCanvas::DeferredDevice::DeferredDevice(
- SkDevice* immediateDevice, DeviceContext* deviceContext) :
- SkDevice(SkBitmap::kNo_Config, immediateDevice->width(),
- immediateDevice->height(), immediateDevice->isOpaque())
- , fFreshFrame(true) {
-
- fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes;
- fDeviceContext = deviceContext;
- SkSafeRef(fDeviceContext);
- fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
- fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice));
- fPipeController.setPlaybackCanvas(fImmediateCanvas);
- this->beginRecording();
-}
-
-SkDeferredCanvas::DeferredDevice::~DeferredDevice() {
- this->flushPending();
- SkSafeUnref(fImmediateCanvas);
- SkSafeUnref(fDeviceContext);
-}
-
-void SkDeferredCanvas::DeferredDevice::setMaxRecordingStorage(size_t maxStorage) {
- fMaxRecordingStorageBytes = maxStorage;
- this->recordingCanvas(); // Accessing the recording canvas applies the new limit.
-}
-
-void SkDeferredCanvas::DeferredDevice::endRecording() {
- fPipeWriter.endRecording();
- fPipeController.reset();
- fRecordingCanvas = NULL;
-}
-
-void SkDeferredCanvas::DeferredDevice::beginRecording() {
- fRecordingCanvas = fPipeWriter.startRecording(&fPipeController, 0);
-}
-
-void SkDeferredCanvas::DeferredDevice::setDeviceContext(
- DeviceContext* deviceContext) {
- SkRefCnt_SafeAssign(fDeviceContext, deviceContext);
-}
-
-void SkDeferredCanvas::DeferredDevice::contentsCleared() {
- if (!fRecordingCanvas->isDrawingToLayer()) {
- fFreshFrame = true;
-
- // TODO: find a way to transfer the state stack and layers
- // to the new recording canvas. For now, purging only works
- // with an empty stack.
- if (fRecordingCanvas->getSaveCount() == 0) {
-
- // Save state that is trashed by the purge
- SkDrawFilter* drawFilter = fRecordingCanvas->getDrawFilter();
- SkSafeRef(drawFilter); // So that it survives the purge
- SkMatrix matrix = fRecordingCanvas->getTotalMatrix();
- SkRegion clipRegion = fRecordingCanvas->getTotalClip();
-
- // beginRecording creates a new recording canvas and discards the
- // old one, hence purging deferred draw ops.
- this->endRecording();
- this->beginRecording();
-
- // Restore pre-purge state
- if (!clipRegion.isEmpty()) {
- fRecordingCanvas->clipRegion(clipRegion,
- SkRegion::kReplace_Op);
- }
- if (!matrix.isIdentity()) {
- fRecordingCanvas->setMatrix(matrix);
- }
- if (drawFilter) {
- fRecordingCanvas->setDrawFilter(drawFilter)->unref();
- }
- }
- }
-}
-
-bool SkDeferredCanvas::DeferredDevice::isFreshFrame() {
- bool ret = fFreshFrame;
- fFreshFrame = false;
- return ret;
-}
-
-void SkDeferredCanvas::DeferredDevice::flushPending() {
- if (!fPipeController.hasRecorded()) {
- return;
- }
- if (fDeviceContext) {
- fDeviceContext->prepareForDraw();
- }
-
- fPipeWriter.flushRecording(true);
- fPipeController.playback();
-}
-
-void SkDeferredCanvas::DeferredDevice::flush() {
- this->flushPending();
- fImmediateCanvas->flush();
-}
-
-size_t SkDeferredCanvas::DeferredDevice::freeMemoryIfPossible(size_t bytesToFree) {
- return fPipeWriter.freeMemoryIfPossible(bytesToFree);
-}
-
-size_t SkDeferredCanvas::DeferredDevice::storageAllocatedForRecording() const {
- return (fPipeController.storageAllocatedForRecording()
- + fPipeWriter.storageAllocatedForRecording());
-}
-
-SkCanvas* SkDeferredCanvas::DeferredDevice::recordingCanvas() {
- size_t storageAllocated = this->storageAllocatedForRecording();
- if (storageAllocated > fMaxRecordingStorageBytes) {
- // First, attempt to reduce cache without flushing
- size_t tryFree = storageAllocated - fMaxRecordingStorageBytes;
- if (this->freeMemoryIfPossible(tryFree) < tryFree) {
- // Flush is necessary to free more space.
- this->flushPending();
- // Free as much as possible to avoid oscillating around fMaxRecordingStorageBytes
- // which could cause a high flushing frequency.
- this->freeMemoryIfPossible(~0);
- }
- }
- return fRecordingCanvas;
-}
-
-uint32_t SkDeferredCanvas::DeferredDevice::getDeviceCapabilities() {
- return fImmediateDevice->getDeviceCapabilities();
-}
-
-int SkDeferredCanvas::DeferredDevice::width() const {
- return fImmediateDevice->width();
-}
-
-int SkDeferredCanvas::DeferredDevice::height() const {
- return fImmediateDevice->height();
-}
-
-SkGpuRenderTarget* SkDeferredCanvas::DeferredDevice::accessRenderTarget() {
- this->flushPending();
- return fImmediateDevice->accessRenderTarget();
-}
-
-void SkDeferredCanvas::DeferredDevice::writePixels(const SkBitmap& bitmap,
- int x, int y, SkCanvas::Config8888 config8888) {
-
- if (x <= 0 && y <= 0 && (x + bitmap.width()) >= width() &&
- (y + bitmap.height()) >= height()) {
- this->contentsCleared();
- }
-
- if (SkBitmap::kARGB_8888_Config == bitmap.config() &&
- SkCanvas::kNative_Premul_Config8888 != config8888 &&
- kPMColorAlias != config8888) {
- //Special case config: no deferral
- this->flushPending();
- fImmediateDevice->writePixels(bitmap, x, y, config8888);
- return;
- }
-
- SkPaint paint;
- paint.setXfermodeMode(SkXfermode::kSrc_Mode);
- if (shouldDrawImmediately(&bitmap, NULL)) {
- this->flushPending();
- fImmediateCanvas->drawSprite(bitmap, x, y, &paint);
- } else {
- this->recordingCanvas()->drawSprite(bitmap, x, y, &paint);
- }
-}
-
-const SkBitmap& SkDeferredCanvas::DeferredDevice::onAccessBitmap(SkBitmap*) {
- this->flushPending();
- return fImmediateDevice->accessBitmap(false);
-}
-
-SkDevice* SkDeferredCanvas::DeferredDevice::onCreateCompatibleDevice(
- SkBitmap::Config config, int width, int height, bool isOpaque,
- Usage usage) {
-
- // Save layer usage not supported, and not required by SkDeferredCanvas.
- SkASSERT(usage != kSaveLayer_Usage);
- // Create a compatible non-deferred device.
- SkAutoTUnref<SkDevice> compatibleDevice
- (fImmediateDevice->createCompatibleDevice(config, width, height,
- isOpaque));
- return SkNEW_ARGS(DeferredDevice, (compatibleDevice, fDeviceContext));
-}
-
-bool SkDeferredCanvas::DeferredDevice::onReadPixels(
- const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888) {
- this->flushPending();
- return fImmediateCanvas->readPixels(const_cast<SkBitmap*>(&bitmap),
- x, y, config8888);
-}
diff --git a/tests/CanvasTest.cpp b/tests/CanvasTest.cpp
index 4347edcdf6..866bf0ca22 100644
--- a/tests/CanvasTest.cpp
+++ b/tests/CanvasTest.cpp
@@ -741,39 +741,44 @@ public:
}
};
-static void TestDeferredCanvasStateConsistency(
- skiatest::Reporter* reporter,
- CanvasTestStep* testStep,
- const SkCanvas& referenceCanvas) {
-
- SkBitmap deferredStore;
- createBitmap(&deferredStore, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
- SkDevice deferredDevice(deferredStore);
- SkDeferredCanvas deferredCanvas(&deferredDevice);
- testStep->setAssertMessageFormat(kDeferredDrawAssertMessageFormat);
- testStep->draw(&deferredCanvas, reporter);
- testStep->setAssertMessageFormat(kDeferredPreFlushAssertMessageFormat);
- AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas,
- testStep);
-
- deferredCanvas.flush();
- testStep->setAssertMessageFormat(
- kDeferredPostFlushPlaybackAssertMessageFormat);
- AssertCanvasStatesEqual(reporter,
- deferredCanvas.getDeferredDevice()->immediateCanvas(),
- &referenceCanvas, testStep);
-
- // Verified that deferred canvas state is not affected by flushing
- // pending draw operations
-
- // The following test code is commented out because it currently fails.
- // Issue: http://code.google.com/p/skia/issues/detail?id=496
- /*
- testStep->setAssertMessageFormat(kDeferredPostFlushAssertMessageFormat);
- AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas,
- testStep);
- */
-}
+// The following class groups static functions that need to access
+// the privates members of SkDeferredCanvas
+class SkDeferredCanvasTester {
+public:
+ static void TestDeferredCanvasStateConsistency(
+ skiatest::Reporter* reporter,
+ CanvasTestStep* testStep,
+ const SkCanvas& referenceCanvas) {
+
+ SkBitmap deferredStore;
+ createBitmap(&deferredStore, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+ SkDevice deferredDevice(deferredStore);
+ SkDeferredCanvas deferredCanvas(&deferredDevice);
+ testStep->setAssertMessageFormat(kDeferredDrawAssertMessageFormat);
+ testStep->draw(&deferredCanvas, reporter);
+ testStep->setAssertMessageFormat(kDeferredPreFlushAssertMessageFormat);
+ AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas,
+ testStep);
+
+ deferredCanvas.flush();
+ testStep->setAssertMessageFormat(
+ kDeferredPostFlushPlaybackAssertMessageFormat);
+ AssertCanvasStatesEqual(reporter,
+ deferredCanvas.immediateCanvas(),
+ &referenceCanvas, testStep);
+
+ // Verified that deferred canvas state is not affected by flushing
+ // pending draw operations
+
+ // The following test code is commented out because it currently fails.
+ // Issue: http://code.google.com/p/skia/issues/detail?id=496
+ /*
+ testStep->setAssertMessageFormat(kDeferredPostFlushAssertMessageFormat);
+ AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas,
+ testStep);
+ */
+ }
+};
// unused
static void TestProxyCanvasStateConsistency(
@@ -849,7 +854,7 @@ static void TestOverrideStateConsistency(skiatest::Reporter* reporter,
testStep->setAssertMessageFormat(kCanvasDrawAssertMessageFormat);
testStep->draw(&referenceCanvas, reporter);
- TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas);
+ SkDeferredCanvasTester::TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas);
// The following test code is disabled because SkProxyCanvas is
// missing a lot of virtual overrides on get* methods, which are used
diff --git a/tests/DeferredCanvasTest.cpp b/tests/DeferredCanvasTest.cpp
index 5e5e425e12..69ca75ad69 100644
--- a/tests/DeferredCanvasTest.cpp
+++ b/tests/DeferredCanvasTest.cpp
@@ -8,6 +8,7 @@
#include "Test.h"
#include "SkBitmap.h"
#include "SkDeferredCanvas.h"
+#include "SkDevice.h"
#include "SkShader.h"
static const int gWidth = 2;
@@ -63,25 +64,25 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
SkDeferredCanvas canvas(&device);
// verify that frame is intially fresh
- REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
+ REPORTER_ASSERT(reporter, canvas.isFreshFrame());
// no clearing op since last call to isFreshFrame -> not fresh
- REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
+ REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
// Verify that clear triggers a fresh frame
canvas.clear(0x00000000);
- REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
+ REPORTER_ASSERT(reporter, canvas.isFreshFrame());
// Verify that clear with saved state triggers a fresh frame
canvas.save(SkCanvas::kMatrixClip_SaveFlag);
canvas.clear(0x00000000);
canvas.restore();
- REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
+ REPORTER_ASSERT(reporter, canvas.isFreshFrame());
// Verify that clear within a layer does NOT trigger a fresh frame
canvas.saveLayer(NULL, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
canvas.clear(0x00000000);
canvas.restore();
- REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
+ REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
// Verify that a clear with clipping triggers a fresh frame
// (clear is not affected by clipping)
@@ -89,7 +90,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
canvas.clipRect(partialRect, SkRegion::kIntersect_Op, false);
canvas.clear(0x00000000);
canvas.restore();
- REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
+ REPORTER_ASSERT(reporter, canvas.isFreshFrame());
// Verify that full frame rects with different forms of opaque paint
// trigger frames to be marked as fresh
@@ -98,7 +99,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
paint.setStyle( SkPaint::kFill_Style );
paint.setAlpha( 255 );
canvas.drawRect(fullRect, paint);
- REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
+ REPORTER_ASSERT(reporter, canvas.isFreshFrame());
}
{
SkPaint paint;
@@ -110,7 +111,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
paint.setShader(shader)->unref();
canvas.drawRect(fullRect, paint);
- REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
+ REPORTER_ASSERT(reporter, canvas.isFreshFrame());
}
// Verify that full frame rects with different forms of non-opaque paint
@@ -120,7 +121,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
paint.setStyle( SkPaint::kFill_Style );
paint.setAlpha( 254 );
canvas.drawRect(fullRect, paint);
- REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
+ REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
}
{
SkPaint paint;
@@ -132,7 +133,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
paint.setShader(shader)->unref();
canvas.drawRect(fullRect, paint);
- REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
+ REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
}
// Verify that incomplete coverage does not trigger a fresh frame
@@ -141,7 +142,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
paint.setStyle(SkPaint::kFill_Style);
paint.setAlpha(255);
canvas.drawRect(partialRect, paint);
- REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
+ REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
}
// Verify that incomplete coverage due to clipping does not trigger a fresh
@@ -153,7 +154,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
paint.setStyle(SkPaint::kFill_Style);
paint.setAlpha(255);
canvas.drawRect(fullRect, paint);
- REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
+ REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
}
// Verify that stroked rect does not trigger a fresh frame
@@ -162,7 +163,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
paint.setStyle( SkPaint::kStroke_Style );
paint.setAlpha( 255 );
canvas.drawRect(fullRect, paint);
- REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
+ REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
}
// Verify kSrcMode triggers a fresh frame even with transparent color
@@ -172,7 +173,7 @@ static void TestDeferredCanvasFreshFrame(skiatest::Reporter* reporter) {
paint.setAlpha( 100 );
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
canvas.drawRect(fullRect, paint);
- REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
+ REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
}
}