Adding class SkDeferredCanvas for deferred rendering.
TEST=added a new pass to gm, so all gm tests are run through SkDeferredCanvas
REVIEW=http://codereview.appspot.com/5430058/
git-svn-id: http://skia.googlecode.com/svn/trunk@3059 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
index 0af2933..878b686 100644
--- a/gm/gmmain.cpp
+++ b/gm/gmmain.cpp
@@ -11,6 +11,7 @@
#include "SkColorPriv.h"
#include "SkData.h"
+#include "SkDeferredCanvas.h"
#include "SkDevice.h"
#include "SkGpuCanvas.h"
#include "SkGpuDevice.h"
@@ -268,26 +269,43 @@
static ErrorBitfield generate_image(GM* gm, const ConfigData& gRec,
GrContext* context,
GrRenderTarget* rt,
- SkBitmap* bitmap) {
+ SkBitmap* bitmap,
+ bool deferred) {
SkISize size (gm->getISize());
setup_bitmap(gRec, size, bitmap);
- SkCanvas canvas(*bitmap);
if (gRec.fBackend == kRaster_Backend) {
- invokeGM(gm, &canvas);
+ SkCanvas* canvas;
+ if (deferred) {
+ canvas = new SkDeferredCanvas;
+ canvas->setDevice(new SkDevice(*bitmap))->unref();
+ } else {
+ canvas = new SkCanvas(*bitmap);
+ }
+ SkAutoUnref canvasUnref(canvas);
+ invokeGM(gm, canvas);
+ if (deferred) {
+ canvas->getDevice()->accessBitmap(false); // trigger a flush
+ }
} else { // GPU
if (NULL == context) {
return ERROR_NO_GPU_CONTEXT;
}
- SkGpuCanvas gc(context, rt);
- gc.setDevice(new SkGpuDevice(context, rt))->unref();
- invokeGM(gm, &gc);
+ SkCanvas* gc;
+ if (deferred) {
+ gc = new SkDeferredCanvas;
+ } else {
+ gc = new SkGpuCanvas(context, rt);
+ }
+ SkAutoUnref gcUnref(gc);
+ gc->setDevice(new SkGpuDevice(context, rt))->unref();
+ invokeGM(gm, gc);
// the device is as large as the current rendertarget, so we explicitly
// only readback the amount we expect (in size)
// overwrite our previous allocation
bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.fWidth,
size.fHeight);
- gc.readPixels(bitmap, 0, 0);
+ gc->readPixels(bitmap, 0, 0);
}
return ERROR_NONE;
}
@@ -488,7 +506,8 @@
if (gRec.fBackend == kRaster_Backend ||
gRec.fBackend == kGPU_Backend) {
// Early exit if we can't generate the image.
- ErrorBitfield errors = generate_image(gm, gRec, context, rt, bitmap);
+ ErrorBitfield errors = generate_image(gm, gRec, context, rt, bitmap,
+ false);
if (ERROR_NONE != errors) {
return errors;
}
@@ -506,6 +525,28 @@
"", *bitmap, &document, NULL);
}
+static ErrorBitfield test_deferred_drawing(GM* gm,
+ const ConfigData& gRec,
+ const SkBitmap& comparisonBitmap,
+ const char diffPath [],
+ GrContext* context,
+ GrRenderTarget* rt) {
+ SkDynamicMemoryWStream document;
+
+ if (gRec.fBackend == kRaster_Backend ||
+ gRec.fBackend == kGPU_Backend) {
+ SkBitmap bitmap;
+ // Early exit if we can't generate the image, but this is
+ // expected in some cases, so don't report a test failure.
+ if (!generate_image(gm, gRec, context, rt, &bitmap, true)) {
+ return ERROR_NONE;
+ }
+ return handle_test_results(gm, gRec, NULL, NULL, diffPath,
+ "-deferred", bitmap, NULL, &comparisonBitmap);
+ }
+ return ERROR_NONE;
+}
+
static ErrorBitfield test_picture_playback(GM* gm,
const ConfigData& gRec,
const SkBitmap& comparisonBitmap,
@@ -612,6 +653,7 @@
bool doReplay = true;
bool doSerialize = false;
bool useMesa = false;
+ bool doDeferred = true;
const char* const commandName = argv[0];
char* const* stop = argv + argc;
@@ -637,6 +679,8 @@
doReplay = false;
} else if (strcmp(*argv, "--nopdf") == 0) {
doPDF = false;
+ } else if (strcmp(*argv, "--nodeferred") == 0) {
+ doDeferred = false;
} else if (strcmp(*argv, "--serialize") == 0) {
doSerialize = true;
} else if (strcmp(*argv, "--match") == 0) {
@@ -758,6 +802,14 @@
rt.get(), &forwardRenderedBitmap);
}
+ if (doDeferred && !testErrors &&
+ (kGPU_Backend == gRec[i].fBackend ||
+ kRaster_Backend == gRec[i].fBackend)) {
+ testErrors |= test_deferred_drawing(gm, gRec[i],
+ forwardRenderedBitmap,
+ diffPath, gGrContext, rt.get());
+ }
+
if ((ERROR_NONE == testErrors) && doReplay &&
!(gmFlags & GM::kSkipPicture_Flag)) {
testErrors |= test_picture_playback(gm, gRec[i],
diff --git a/gyp/utils.gyp b/gyp/utils.gyp
index b4b2be3..d8c421d 100644
--- a/gyp/utils.gyp
+++ b/gyp/utils.gyp
@@ -22,6 +22,7 @@
'../include/utils/SkCamera.h',
'../include/utils/SkCubicInterval.h',
'../include/utils/SkCullPoints.h',
+ '../include/utils/SkDeferredCanvas.h',
'../include/utils/SkDumpCanvas.h',
'../include/utils/SkInterpolator.h',
'../include/utils/SkLayer.h',
@@ -43,6 +44,7 @@
'../src/utils/SkColorMatrix.cpp',
'../src/utils/SkCubicInterval.cpp',
'../src/utils/SkCullPoints.cpp',
+ '../src/utils/SkDeferredCanvas.cpp',
'../src/utils/SkDumpCanvas.cpp',
'../src/utils/SkInterpolator.cpp',
'../src/utils/SkLayer.cpp',
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index 24e4141..a6f7c78 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -78,7 +78,7 @@
reference count is incremented. If the canvas was already holding a
device, its reference count is decremented. The new device is returned.
*/
- SkDevice* setDevice(SkDevice* device);
+ virtual SkDevice* setDevice(SkDevice* device);
/**
* saveLayer() can create another device (which is later drawn onto
@@ -278,7 +278,7 @@
/** Returns the number of matrix/clip states on the SkCanvas' private stack.
This will equal # save() calls - # restore() calls.
*/
- int getSaveCount() const;
+ virtual int getSaveCount() const;
/** Efficient way to pop any calls to save() that happened after the save
count reached saveCount. It is an error for saveCount to be less than
@@ -845,7 +845,7 @@
This does not account for the translate in any of the devices.
@return The current matrix on the canvas.
*/
- const SkMatrix& getTotalMatrix() const;
+ virtual const SkMatrix& getTotalMatrix() const;
enum ClipType {
kEmpty_ClipType = 0,
@@ -921,6 +921,12 @@
};
protected:
+ // Returns the canvas to be used by DrawIter. Default implementation
+ // returns this. Subclasses that encapsulate an indirect canvas may
+ // need to overload this method. The impl must keep track of this, as it
+ // is not released or deleted by the caller.
+ virtual SkCanvas* canvasForDrawIter();
+
// all of the drawBitmap variants call this guy
virtual void commonDrawBitmap(const SkBitmap&, const SkIRect*,
const SkMatrix&, const SkPaint& paint);
diff --git a/include/utils/SkDeferredCanvas.h b/include/utils/SkDeferredCanvas.h
new file mode 100644
index 0000000..14951d9
--- /dev/null
+++ b/include/utils/SkDeferredCanvas.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkDeferredCanvas_DEFINED
+#define SkDeferredCanvas_DEFINED
+
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPicture.h"
+#include "SkPixelRef.h"
+
+/** \class SkDeferredCanvas
+ Subclass of SkCanvas that encapsulates an SkPicture for deferred drawing.
+ The main difference between this class and SkPictureRecord (the canvas
+ provided by SkPicture) is that this is a full drop-in replacement for
+ SkCanvas, while SkPictureRecord only supports draw operations.
+ SkDeferredCanvas will transparently trigger the flushing of deferred
+ draw operations when an attempt is made to access the pixel data.
+*/
+class SK_API SkDeferredCanvas : public SkCanvas {
+public:
+ class DeviceContext;
+
+ SkDeferredCanvas();
+
+ /** Construct a canvas with the specified device to draw into.
+ Equivalent to calling default constructor, then setDevice.
+ @param device Specifies a device for the canvas to draw into.
+ */
+ explicit SkDeferredCanvas(SkDevice* device);
+
+ /** Construct a canvas with the specified device to draw into, and
+ * a device context. Equivalent to calling default constructor, then
+ * setDevice.
+ * @param device Specifies a device for the canvas to draw into.
+ * @param deviceContext interface for the device's the graphics context
+ */
+ explicit SkDeferredCanvas(SkDevice* device, DeviceContext* deviceContext);
+
+ virtual ~SkDeferredCanvas();
+
+ /**
+ * Specify a device to be used by this canvas. Calling setDevice will
+ * release the previously set device, if any.
+ *
+ * @param device The device that the canvas will raw into
+ * @return The device argument, for convenience.
+ */
+ virtual SkDevice* setDevice(SkDevice* device);
+
+ /**
+ * Specify a deviceContext to be used by this canvas. Calling
+ * setDeviceContext will release the previously set deviceContext, if any.
+ * A deviceContext must be specified if the device uses a graphics context
+ * that requires some form of state initialization prior to drawing
+ * and/or explicit flushing to synchronize the execution of rendering
+ * operations.
+ * Note: Must be called after the device is set with setDevice.
+ *
+ * @deviceContext interface for the device's the graphics context
+ * @return The deviceContext argument, for convenience.
+ */
+ DeviceContext* setDeviceContext(DeviceContext* deviceContext);
+
+ /**
+ * Enable or disable deferred drawing. When deferral is disabled,
+ * pending draw operations are immediately flushed and from then on,
+ * the SkDeferredCanvas behaves just like a regular SkCanvas.
+ * This method must not be called while the save/restore stack is in use.
+ * @param deferred true/false
+ */
+ void setDeferredDrawing(bool deferred);
+
+ // Overrides of the SkCanvas interface
+ virtual int save(SaveFlags flags) SK_OVERRIDE;
+ virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SaveFlags flags) SK_OVERRIDE;
+ virtual void restore() SK_OVERRIDE;
+ virtual int getSaveCount() const SK_OVERRIDE;
+ virtual bool translate(SkScalar dx, SkScalar dy) SK_OVERRIDE;
+ virtual bool scale(SkScalar sx, SkScalar sy) SK_OVERRIDE;
+ virtual bool rotate(SkScalar degrees) SK_OVERRIDE;
+ virtual bool skew(SkScalar sx, SkScalar sy) SK_OVERRIDE;
+ virtual bool concat(const SkMatrix& matrix) SK_OVERRIDE;
+ virtual void setMatrix(const SkMatrix& matrix) SK_OVERRIDE;
+ virtual const SkMatrix& getTotalMatrix() const SK_OVERRIDE;
+ virtual bool clipRect(const SkRect& rect, SkRegion::Op op,
+ bool doAntiAlias) SK_OVERRIDE;
+ virtual bool clipPath(const SkPath& path, SkRegion::Op op,
+ bool doAntiAlias) SK_OVERRIDE;
+ virtual bool clipRegion(const SkRegion& deviceRgn,
+ SkRegion::Op op) SK_OVERRIDE;
+ virtual void clear(SkColor) SK_OVERRIDE;
+ virtual void drawPaint(const SkPaint& paint) SK_OVERRIDE;
+ virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+ const SkPaint& paint) SK_OVERRIDE;
+ virtual void drawRect(const SkRect& rect, const SkPaint& paint)
+ SK_OVERRIDE;
+ virtual void drawPath(const SkPath& path, const SkPaint& paint)
+ SK_OVERRIDE;
+ virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left,
+ SkScalar top, const SkPaint* paint)
+ SK_OVERRIDE;
+ virtual void drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+ const SkRect& dst, const SkPaint* paint)
+ SK_OVERRIDE;
+
+ virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+ const SkPaint* paint) SK_OVERRIDE;
+ virtual void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+ const SkRect& dst, const SkPaint* paint)
+ SK_OVERRIDE;
+ virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
+ const SkPaint* paint) SK_OVERRIDE;
+ virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+ SkScalar y, const SkPaint& paint) SK_OVERRIDE;
+ virtual void drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint& paint)
+ SK_OVERRIDE;
+ virtual void drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint) SK_OVERRIDE;
+ virtual void drawTextOnPath(const void* text, size_t byteLength,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) SK_OVERRIDE;
+ virtual void drawPicture(SkPicture& picture) SK_OVERRIDE;
+ virtual void drawVertices(VertexMode vmode, int vertexCount,
+ const SkPoint vertices[], const SkPoint texs[],
+ const SkColor colors[], SkXfermode* xmode,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint) SK_OVERRIDE;
+ virtual SkBounder* setBounder(SkBounder* bounder) SK_OVERRIDE;
+ virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter) SK_OVERRIDE;
+
+private:
+ void flushIfNeeded(const SkBitmap& bitmap);
+
+public:
+ class DeviceContext : public SkRefCnt {
+ public:
+ virtual void prepareForDraw() {}
+ virtual void flush() {}
+ };
+
+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() const {return fRecordingCanvas;}
+
+ /**
+ * Returns the immediate (non deferred) canvas.
+ */
+ SkCanvas* immediateCanvas() const {return fImmediateCanvas;}
+
+ /**
+ * Returns the immediate (non deferred) device.
+ */
+ SkDevice* immediateDevice() const {return fImmediateDevice;}
+
+ void flushPending();
+ void flushContext();
+ void purgePending();
+ void flushIfNeeded(const SkBitmap& bitmap);
+
+ 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 void 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
+ {}
+ virtual void gainFocus(SkCanvas*, 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();
+
+ SkPicture fPicture;
+ SkDevice* fImmediateDevice;
+ SkCanvas* fImmediateCanvas;
+ SkCanvas* fRecordingCanvas;
+ DeviceContext* fDeviceContext;
+ };
+
+ DeferredDevice* getDeferredDevice() const;
+
+protected:
+ virtual SkCanvas* canvasForDrawIter();
+
+private:
+ SkCanvas* drawingCanvas() const;
+ bool isFullFrame(const SkRect*, const SkPaint*) const;
+ void validate() const;
+ void init();
+ bool fDeferredDrawing;
+
+ typedef SkCanvas INHERITED;
+};
+
+
+#endif
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 87da90a..9664ffa 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -214,6 +214,7 @@
class SkDrawIter : public SkDraw {
public:
SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
+ canvas = canvas->canvasForDrawIter();
fCanvas = canvas;
canvas->updateDeviceCMCache();
@@ -591,6 +592,10 @@
}
}
+SkCanvas* SkCanvas::canvasForDrawIter() {
+ return this;
+}
+
//////////////////////////////////////////////////////////////////////////////
void SkCanvas::updateDeviceCMCache() {
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
new file mode 100644
index 0000000..e92f1f8
--- /dev/null
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -0,0 +1,625 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDeferredCanvas.h"
+
+#include "SkPaint.h"
+#include "SkShader.h"
+#include "SkColorFilter.h"
+#include "SkDrawFilter.h"
+
+namespace {
+
+bool isPaintOpaque(const SkPaint& paint, const SkBitmap* bmpReplacesShader = NULL) {
+ // TODO: SkXfermode should have a virtual isOpaque method, which would
+ // make it possible to test modes that do not have a Coeff representation.
+ SkXfermode::Coeff srcCoeff, dstCoeff;
+ if (SkXfermode::AsCoeff(paint.getXfermode(), &srcCoeff, &dstCoeff)){
+ switch (dstCoeff) {
+ case SkXfermode::kZero_Coeff:
+ return true;
+ case SkXfermode::kISA_Coeff:
+ if (paint.getAlpha() != 255) {
+ break;
+ }
+ if (bmpReplacesShader) {
+ if (!bmpReplacesShader->isOpaque()) {
+ break;
+ }
+ } else if (paint.getShader() && !paint.getShader()->isOpaque()) {
+ break;
+ }
+ if (paint.getColorFilter() && ((paint.getColorFilter()->getFlags() &
+ SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
+ break;
+ }
+ return true;
+ case SkXfermode::kSA_Coeff:
+ if (paint.getAlpha() != 0) {
+ break;
+ }
+ if (paint.getColorFilter() && ((paint.getColorFilter()->getFlags() &
+ SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
+ break;
+ }
+ return true;
+ case SkXfermode::kSC_Coeff:
+ if (paint.getColor() != 0) { // all components must be 0
+ break;
+ }
+ if (bmpReplacesShader || paint.getShader()) {
+ break;
+ }
+ if (paint.getColorFilter() && ((paint.getColorFilter()->getFlags() &
+ SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
+ break;
+ }
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+} // unnamed namespace
+
+SkDeferredCanvas::SkDeferredCanvas()
+{
+ init();
+}
+
+SkDeferredCanvas::SkDeferredCanvas(SkDevice* device)
+{
+ init();
+ setDevice(device);
+}
+
+SkDeferredCanvas::SkDeferredCanvas(SkDevice* device,
+ DeviceContext* deviceContext)
+{
+ init();
+ setDevice(device);
+ setDeviceContext(deviceContext);
+}
+
+void SkDeferredCanvas::init()
+{
+ fDeferredDrawing = true; // On by default
+}
+
+void SkDeferredCanvas::validate() const
+{
+ SkASSERT(getDevice());
+ SkASSERT(INHERITED::getTotalMatrix().isIdentity());
+}
+
+SkCanvas* SkDeferredCanvas::drawingCanvas() const
+{
+ validate();
+ return fDeferredDrawing ? getDeferredDevice()->recordingCanvas() :
+ getDeferredDevice()->immediateCanvas();
+}
+
+void SkDeferredCanvas::flushIfNeeded(const SkBitmap& bitmap)
+{
+ validate();
+ if (fDeferredDrawing) {
+ getDeferredDevice()->flushIfNeeded(bitmap);
+ }
+}
+
+SkDeferredCanvas::DeferredDevice* SkDeferredCanvas::getDeferredDevice() const
+{
+ return static_cast<SkDeferredCanvas::DeferredDevice*>(getDevice());
+}
+
+void SkDeferredCanvas::setDeferredDrawing(bool val)
+{
+ validate(); // Must set device before calling this method
+ SkASSERT(drawingCanvas()->getSaveCount() == 1);
+ if (val != fDeferredDrawing) {
+ if (fDeferredDrawing) {
+ // Going live.
+ getDeferredDevice()->flushPending();
+ }
+ fDeferredDrawing = val;
+ }
+}
+
+SkDeferredCanvas::~SkDeferredCanvas()
+{
+}
+
+SkDevice* SkDeferredCanvas::setDevice(SkDevice* device)
+{
+ INHERITED::setDevice(SkNEW_ARGS(DeferredDevice, (device)))->unref();
+ return device;
+}
+
+SkDeferredCanvas::DeviceContext* SkDeferredCanvas::setDeviceContext(
+ DeviceContext* deviceContext)
+{
+ DeferredDevice* deferredDevice = getDeferredDevice();
+ SkASSERT(deferredDevice);
+ if (deferredDevice) {
+ deferredDevice->setDeviceContext(deviceContext);
+ }
+ return deviceContext;
+}
+
+bool SkDeferredCanvas::isFullFrame(const SkRect* rect,
+ const SkPaint* paint) const
+{
+ SkCanvas* canvas = drawingCanvas();
+ SkISize canvasSize = getDeviceSize();
+ if (rect) {
+ if (!canvas->getTotalMatrix().rectStaysRect()) {
+ return false; // conservative
+ }
+
+ SkRect transformedRect;
+ canvas->getTotalMatrix().mapRect(&transformedRect, *rect);
+
+ if (paint) {
+ SkPaint::Style paintStyle = paint->getStyle();
+ if (!(paintStyle == SkPaint::kFill_Style ||
+ paintStyle == SkPaint::kStrokeAndFill_Style)) {
+ return false;
+ }
+ if (paint->getMaskFilter() || paint->getLooper()
+ || paint->getPathEffect() || paint->getImageFilter()) {
+ return false; // conservative
+ }
+ }
+
+ // The following test holds with AA enabled, and is conservative
+ // by a 0.5 pixel margin with AA disabled
+ if (transformedRect.fLeft > 0 || transformedRect.fTop > 0 ||
+ transformedRect.fRight < canvasSize.fWidth ||
+ transformedRect.fBottom < canvasSize.fHeight) {
+ return false;
+ }
+ }
+
+ switch (canvas->getClipType()) {
+ case SkCanvas::kRect_ClipType :
+ {
+ SkIRect bounds;
+ canvas->getClipDeviceBounds(&bounds);
+ if (bounds.fLeft > 0 || bounds.fTop > 0 ||
+ bounds.fRight < canvasSize.fWidth ||
+ bounds.fBottom < canvasSize.fHeight)
+ return false;
+ }
+ break;
+ case SkCanvas::kComplex_ClipType :
+ return false; // conservative
+ case SkCanvas::kEmpty_ClipType:
+ default:
+ break;
+ };
+
+ return true;
+}
+
+int SkDeferredCanvas::save(SaveFlags flags)
+{
+ return drawingCanvas()->save(flags);
+}
+
+int SkDeferredCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SaveFlags flags)
+{
+ return drawingCanvas()->saveLayer(bounds, paint, flags);
+}
+
+void SkDeferredCanvas::restore()
+{
+ drawingCanvas()->restore();
+}
+
+int SkDeferredCanvas::getSaveCount() const
+{
+ return drawingCanvas()->getSaveCount();
+}
+
+bool SkDeferredCanvas::translate(SkScalar dx, SkScalar dy)
+{
+ return drawingCanvas()->translate(dx, dy);
+}
+
+bool SkDeferredCanvas::scale(SkScalar sx, SkScalar sy)
+{
+ return drawingCanvas()->scale(sx, sy);
+}
+
+bool SkDeferredCanvas::rotate(SkScalar degrees)
+{
+ return drawingCanvas()->rotate(degrees);
+}
+
+bool SkDeferredCanvas::skew(SkScalar sx, SkScalar sy)
+{
+ return drawingCanvas()->skew(sx, sy);
+}
+
+bool SkDeferredCanvas::concat(const SkMatrix& matrix)
+{
+ return drawingCanvas()->concat(matrix);
+}
+
+void SkDeferredCanvas::setMatrix(const SkMatrix& matrix)
+{
+ drawingCanvas()->setMatrix(matrix);
+}
+
+const SkMatrix& SkDeferredCanvas::getTotalMatrix() const
+{
+ return drawingCanvas()->getTotalMatrix();
+}
+
+bool SkDeferredCanvas::clipRect(const SkRect& rect,
+ SkRegion::Op op,
+ bool doAntiAlias)
+{
+ return drawingCanvas()->clipRect(rect, op, doAntiAlias);
+}
+
+bool SkDeferredCanvas::clipPath(const SkPath& path,
+ SkRegion::Op op,
+ bool doAntiAlias)
+{
+ return drawingCanvas()->clipPath(path, op, doAntiAlias);
+}
+
+bool SkDeferredCanvas::clipRegion(const SkRegion& deviceRgn,
+ SkRegion::Op op)
+{
+ return drawingCanvas()->clipRegion(deviceRgn, op);
+}
+
+void SkDeferredCanvas::clear(SkColor color)
+{
+ // purge pending commands
+ if (fDeferredDrawing) {
+ getDeferredDevice()->purgePending();
+ }
+
+ drawingCanvas()->clear(color);
+}
+
+void SkDeferredCanvas::drawPaint(const SkPaint& paint)
+{
+ if (fDeferredDrawing && isFullFrame(NULL, &paint) && isPaintOpaque(paint)) {
+ getDeferredDevice()->purgePending();
+ }
+
+ drawingCanvas()->drawPaint(paint);
+}
+
+void SkDeferredCanvas::drawPoints(PointMode mode, size_t count,
+ const SkPoint pts[], const SkPaint& paint)
+{
+ drawingCanvas()->drawPoints(mode, count, pts, paint);
+}
+
+void SkDeferredCanvas::drawRect(const SkRect& rect, const SkPaint& paint)
+{
+ if (fDeferredDrawing && isFullFrame(&rect, &paint) && isPaintOpaque(paint)) {
+ getDeferredDevice()->purgePending();
+ }
+
+ drawingCanvas()->drawRect(rect, paint);
+}
+
+void SkDeferredCanvas::drawPath(const SkPath& path, const SkPaint& paint)
+{
+ drawingCanvas()->drawPath(path, paint);
+}
+
+void SkDeferredCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left,
+ SkScalar top, const SkPaint* paint)
+{
+ SkRect bitmapRect = SkRect::MakeXYWH(left, top, bitmap.width(),
+ bitmap.height());
+ if (fDeferredDrawing &&
+ isFullFrame(&bitmapRect, paint) &&
+ isPaintOpaque(*paint, &bitmap)) {
+ getDeferredDevice()->purgePending();
+ }
+
+ drawingCanvas()->drawBitmap(bitmap, left, top, paint);
+ flushIfNeeded(bitmap);
+}
+
+void SkDeferredCanvas::drawBitmapRect(const SkBitmap& bitmap,
+ const SkIRect* src,
+ const SkRect& dst, const SkPaint* paint)
+{
+ if (fDeferredDrawing &&
+ isFullFrame(&dst, paint) &&
+ isPaintOpaque(*paint, &bitmap)) {
+ getDeferredDevice()->purgePending();
+ }
+
+ drawingCanvas()->drawBitmapRect(bitmap, src,
+ dst, paint);
+ flushIfNeeded(bitmap);
+}
+
+
+void SkDeferredCanvas::drawBitmapMatrix(const SkBitmap& bitmap,
+ const SkMatrix& m,
+ const SkPaint* paint)
+{
+ // TODO: reset recording canvas if paint+bitmap is opaque and clip rect
+ // covers canvas entirely and transformed bitmap covers canvas entirely
+ drawingCanvas()->drawBitmapMatrix(bitmap, m, paint);
+ flushIfNeeded(bitmap);
+}
+
+void SkDeferredCanvas::drawBitmapNine(const SkBitmap& bitmap,
+ const SkIRect& center, const SkRect& dst,
+ const SkPaint* paint)
+{
+ // TODO: reset recording canvas if paint+bitmap is opaque and clip rect
+ // covers canvas entirely and dst covers canvas entirely
+ drawingCanvas()->drawBitmapNine(bitmap, center,
+ dst, paint);
+ flushIfNeeded(bitmap);
+}
+
+void SkDeferredCanvas::drawSprite(const SkBitmap& bitmap, int left, int top,
+ const SkPaint* paint)
+{
+ SkRect bitmapRect = SkRect::MakeXYWH(left, top, bitmap.width(),
+ bitmap.height());
+ if (fDeferredDrawing &&
+ isFullFrame(&bitmapRect, paint) &&
+ isPaintOpaque(*paint, &bitmap)) {
+ getDeferredDevice()->purgePending();
+ }
+
+ drawingCanvas()->drawSprite(bitmap, left, top,
+ paint);
+ flushIfNeeded(bitmap);
+}
+
+void SkDeferredCanvas::drawText(const void* text, size_t byteLength,
+ SkScalar x, SkScalar y, const SkPaint& paint)
+{
+ drawingCanvas()->drawText(text, byteLength, x,
+ y, paint);
+}
+
+void SkDeferredCanvas::drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint& paint)
+{
+ drawingCanvas()->drawPosText(text, byteLength,
+ pos, paint);
+}
+
+void SkDeferredCanvas::drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint)
+{
+ drawingCanvas()->drawPosTextH(text, byteLength,
+ xpos, constY,
+ paint);
+}
+
+void SkDeferredCanvas::drawTextOnPath(const void* text, size_t byteLength,
+ const SkPath& path,
+ const SkMatrix* matrix,
+ const SkPaint& paint)
+{
+ drawingCanvas()->drawTextOnPath(text, byteLength,
+ path, matrix,
+ paint);
+}
+
+void SkDeferredCanvas::drawPicture(SkPicture& picture)
+{
+ drawingCanvas()->drawPicture(picture);
+}
+
+void SkDeferredCanvas::drawVertices(VertexMode vmode, int vertexCount,
+ const SkPoint vertices[],
+ const SkPoint texs[],
+ const SkColor colors[], SkXfermode* xmode,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint)
+{
+ drawingCanvas()->drawVertices(vmode, vertexCount,
+ vertices, texs,
+ colors, xmode,
+ indices, indexCount,
+ paint);
+}
+
+SkBounder* SkDeferredCanvas::setBounder(SkBounder* bounder)
+{
+ INHERITED::setBounder(bounder); // So non-virtual getBounder works
+ return drawingCanvas()->setBounder(bounder);
+}
+
+SkDrawFilter* SkDeferredCanvas::setDrawFilter(SkDrawFilter* filter)
+{
+ INHERITED::setDrawFilter(filter); // So non-virtual getDrawFilter works
+ return drawingCanvas()->setDrawFilter(filter);
+}
+
+SkCanvas* SkDeferredCanvas::canvasForDrawIter() {
+ return drawingCanvas();
+}
+
+// SkDeferredCanvas::DeferredDevice
+//------------------------------------
+
+SkDeferredCanvas::DeferredDevice::DeferredDevice(
+ SkDevice* immediateDevice, DeviceContext* deviceContext) :
+ SkDevice(SkBitmap::kNo_Config, immediateDevice->width(),
+ immediateDevice->height(), immediateDevice->isOpaque())
+{
+ fDeviceContext = deviceContext;
+ SkSafeRef(fDeviceContext);
+ fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
+ fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice));
+ SkSafeRef(fImmediateCanvas);
+ fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
+ fImmediateDevice->height(),
+ SkPicture::kUsePathBoundsForClip_RecordingFlag);
+}
+
+SkDeferredCanvas::DeferredDevice::~DeferredDevice()
+{
+ SkSafeUnref(fImmediateCanvas);
+ SkSafeUnref(fDeviceContext);
+}
+
+void SkDeferredCanvas::DeferredDevice::setDeviceContext(
+ DeviceContext* deviceContext)
+{
+ SkRefCnt_SafeAssign(fDeviceContext, deviceContext);
+}
+
+void SkDeferredCanvas::DeferredDevice::purgePending()
+{
+ // TODO: find a way to transfer the state stack and layers
+ // to the new recording canvas. For now, purge only works
+ // with an empty stack.
+ if (fRecordingCanvas->getSaveCount() > 1)
+ return;
+
+ // 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.
+ fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
+ fImmediateDevice->height(),
+ SkPicture::kUsePathBoundsForClip_RecordingFlag);
+
+ // 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();
+ }
+}
+
+void SkDeferredCanvas::DeferredDevice::flushPending()
+{
+ if (fDeviceContext) {
+ fDeviceContext->prepareForDraw();
+ }
+ fPicture.draw(fImmediateCanvas);
+ fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
+ fImmediateDevice->height(),
+ SkPicture::kUsePathBoundsForClip_RecordingFlag);
+}
+
+void SkDeferredCanvas::DeferredDevice::flush()
+{
+ flushPending();
+}
+
+void SkDeferredCanvas::DeferredDevice::flushContext()
+{
+ if (fDeviceContext) {
+ fDeviceContext->flush();
+ }
+}
+
+void SkDeferredCanvas::DeferredDevice::flushIfNeeded(const SkBitmap& bitmap)
+{
+ if (bitmap.isImmutable()) {
+ return; // safe to deffer without registering a dependency
+ }
+
+ // For now, drawing a writable bitmap triggers a flush
+ // TODO: implement read-only semantics and auto buffer duplication on write
+ // in SkBitmap/SkPixelRef, which will make deferral possible in this case.
+ flushPending();
+}
+
+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()
+{
+ 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()) {
+ purgePending();
+ }
+
+ if (SkBitmap::kARGB_8888_Config == bitmap.config() &&
+ SkCanvas::kNative_Premul_Config8888 != config8888 &&
+ kPMColorAlias != config8888) {
+ //Special case config: no deferral
+ flushPending();
+ fImmediateDevice->writePixels(bitmap, x, y, config8888);
+ }
+
+ SkPaint paint;
+ paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+ fRecordingCanvas->drawSprite(bitmap, x, y, &paint);
+ flushIfNeeded(bitmap);
+}
+
+void SkDeferredCanvas::DeferredDevice::onAccessBitmap(SkBitmap* bitmap)
+{
+ SkASSERT(bitmap);
+ flushPending();
+}
+
+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.
+ 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)
+{
+ flushPending();
+ return fImmediateCanvas->readPixels(const_cast<SkBitmap*>(&bitmap),
+ x, y, config8888);
+}