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);
+}