Revert of Revert of Make drawImage a virtual on SkDevice (patchset #1 id:1 of https://codereview.chromium.org/1124003002/)

Reason for revert:
retry now that some fixes (onDrawImage overrides) have landed in chrome/blink

Original issue's description:
> Revert of Make drawImage a virtual on SkDevice (patchset #4 id:60001 of https://codereview.chromium.org/1122643005/)
>
> Reason for revert:
> speculative to see if it unblocks DEPS roll
>
> Original issue's description:
> > Make drawImage a virtual on SkDevice
> >
> > Now with patch for SkDeferredCanvas
> >
> > This reverts commit 119468b71f8f4f45657ab30ead331be665de5a57.
> >
> > BUG=skia:
> >
> > Committed: https://skia.googlesource.com/skia/+/14fe8fd3e53b5e988aac189a8bc3ed28904d85c8
>
> TBR=robertphillips@google.com,mtklein@google.com,reed@google.com
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
> BUG=skia:
>
> Committed: https://skia.googlesource.com/skia/+/3538e3bfe2e00bc1b5b48d977fa7adff64d8c96b

TBR=robertphillips@google.com,mtklein@google.com,reed@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=skia:

Review URL: https://codereview.chromium.org/1126273002
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index 8a219d7..b474687 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -209,6 +209,10 @@
                                 const SkPaint& paint,
                                 SkCanvas::DrawBitmapRectFlags flags) = 0;
 
+    virtual void drawImage(const SkDraw&, const SkImage*, SkScalar x, SkScalar y, const SkPaint&);
+    virtual void drawImageRect(const SkDraw&, const SkImage*, const SkRect* src, const SkRect& dst,
+                               const SkPaint&);
+
     /**
      *  Does not handle text decoration.
      *  Decorations (underline and stike-thru) will be handled by SkCanvas.
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index ec85516..ebc9a29 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -172,19 +172,6 @@
     static uint32_t NextUniqueID();
 
     typedef SkRefCnt INHERITED;
-
-    friend class SkCanvas;
-
-    void draw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const;
-
-    /**
-     *  Draw the image, cropped to the src rect, to the dst rect of a canvas.
-     *  If src is larger than the bounds of the image, the rest of the image is
-     *  filled with transparent black pixels.
-     *
-     *  See SkCanvas::drawBitmapRectToRect for similar behavior.
-     */
-    void drawRect(SkCanvas*, const SkRect* src, const SkRect& dst, const SkPaint*) const;
 };
 
 #endif
diff --git a/include/utils/SkDeferredCanvas.h b/include/utils/SkDeferredCanvas.h
index ac8556f..a833049 100644
--- a/include/utils/SkDeferredCanvas.h
+++ b/include/utils/SkDeferredCanvas.h
@@ -177,12 +177,9 @@
     void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*,
                           DrawBitmapRectFlags flags) override;
-#if 0
-    // rely on conversion to bitmap(for now)
     void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
                          const SkPaint*) override;
-#endif
     void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
                           const SkPaint*) override;
     void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override;
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 3e3f714..34d6aaa 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -1734,12 +1734,15 @@
     this->onDrawPath(path, paint);
 }
 
-void SkCanvas::drawImage(const SkImage* image, SkScalar dx, SkScalar dy, const SkPaint* paint) {
-    this->onDrawImage(image, dx, dy, paint);
+void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
+    this->onDrawImage(image, x, y, paint);
 }
 
 void SkCanvas::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
                              const SkPaint* paint) {
+    if (dst.isEmpty()) {
+        return;
+    }
     this->onDrawImageRect(image, src, dst, paint);
 }
 
@@ -1959,15 +1962,58 @@
     LOOPER_END
 }
 
-void SkCanvas::onDrawImage(const SkImage* image, SkScalar dx, SkScalar dy, const SkPaint* paint) {
+void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) {
     TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImage()");
-    image->draw(this, dx, dy, paint);
+    SkRect bounds = SkRect::MakeXYWH(x, y,
+                                     SkIntToScalar(image->width()), SkIntToScalar(image->height()));
+    if (NULL == paint || paint->canComputeFastBounds()) {
+        if (paint) {
+            paint->computeFastBounds(bounds, &bounds);
+        }
+        if (this->quickReject(bounds)) {
+            return;
+        }
+    }
+    
+    SkLazyPaint lazy;
+    if (NULL == paint) {
+        paint = lazy.init();
+    }
+    
+    LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &bounds)
+    
+    while (iter.next()) {
+        iter.fDevice->drawImage(iter, image, x, y, looper.paint());
+    }
+    
+    LOOPER_END
 }
 
 void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
                                const SkPaint* paint) {
     TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageRect()");
-    image->drawRect(this, src, dst, paint);
+    SkRect storage;
+    const SkRect* bounds = &dst;
+    if (NULL == paint || paint->canComputeFastBounds()) {
+        if (paint) {
+            bounds = &paint->computeFastBounds(dst, &storage);
+        }
+        if (this->quickReject(*bounds)) {
+            return;
+        }
+    }
+    SkLazyPaint lazy;
+    if (NULL == paint) {
+        paint = lazy.init();
+    }
+    
+    LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, bounds)
+    
+    while (iter.next()) {
+        iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint());
+    }
+    
+    LOOPER_END
 }
 
 void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) {
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index 6be9178..37fafd8 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -9,6 +9,7 @@
 #include "SkDeviceProperties.h"
 #include "SkDraw.h"
 #include "SkDrawFilter.h"
+#include "SkImage_Base.h"
 #include "SkMetaData.h"
 #include "SkPatchUtils.h"
 #include "SkPathMeasure.h"
@@ -165,6 +166,24 @@
     }
 }
 
+void SkBaseDevice::drawImage(const SkDraw& draw, const SkImage* image, SkScalar x, SkScalar y,
+                             const SkPaint& paint) {
+    // Default impl : turns everything into raster bitmap
+    SkBitmap bm;
+    if (as_IB(image)->getROPixels(&bm)) {
+        this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint);
+    }
+}
+
+void SkBaseDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const SkRect* src,
+                                 const SkRect& dst, const SkPaint& paint) {
+    // Default impl : turns everything into raster bitmap
+    SkBitmap bm;
+    if (as_IB(image)->getROPixels(&bm)) {
+        this->drawBitmapRect(draw, bm, src, dst, paint, SkCanvas::kNone_DrawBitmapRectFlag);
+    }
+}
+
 bool SkBaseDevice::readPixels(const SkImageInfo& info, void* dstP, size_t rowBytes, int x, int y) {
 #ifdef SK_DEBUG
     SkASSERT(info.width() > 0 && info.height() > 0);
diff --git a/src/core/SkPaintPriv.cpp b/src/core/SkPaintPriv.cpp
index c6957cd..3ced573 100644
--- a/src/core/SkPaintPriv.cpp
+++ b/src/core/SkPaintPriv.cpp
@@ -9,6 +9,7 @@
 
 #include "SkBitmap.h"
 #include "SkColorFilter.h"
+#include "SkImage.h"
 #include "SkPaint.h"
 #include "SkShader.h"
 
@@ -49,3 +50,8 @@
 
     return isPaintOpaque(paint, contentType);
 }
+
+bool isPaintOpaque(const SkPaint* paint, const SkImage* image) {
+    return isPaintOpaque(paint, image->isOpaque() ?
+                         kOpaque_SkPaintBitmapOpacity : kUnknown_SkPaintBitmapOpacity);
+}
diff --git a/src/core/SkPaintPriv.h b/src/core/SkPaintPriv.h
index 88fc4fc..be441b8 100644
--- a/src/core/SkPaintPriv.h
+++ b/src/core/SkPaintPriv.h
@@ -8,11 +8,12 @@
 #ifndef SkPaintPriv_DEFINED
 #define SkPaintPriv_DEFINED
 
-class SkBitmap;
-class SkPaint;
-
 #include "SkTypes.h"
 
+class SkBitmap;
+class SkImage;
+class SkPaint;
+
 enum SkPaintBitmapOpacity {
     // No content replaces the paint's color
     kNoBitmap_SkPaintBitmapOpacity = 0,
@@ -40,6 +41,7 @@
         shader.
     @return true if paint is opaque
 */
-bool isPaintOpaque(const SkPaint* paint,
-                   const SkBitmap* bmpReplacesShader = NULL);
+bool isPaintOpaque(const SkPaint* paint, const SkBitmap* bmpReplacesShader = NULL);
+bool isPaintOpaque(const SkPaint* paint, const SkImage* image);
+
 #endif
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index 53ecdd0..07811fb 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -7,6 +7,7 @@
 
 #include "SkPictureRecord.h"
 #include "SkDevice.h"
+#include "SkImage_Base.h"
 #include "SkPatchUtils.h"
 #include "SkPixelRef.h"
 #include "SkRRect.h"
@@ -563,6 +564,22 @@
     this->validate(initialOffset, size);
 }
 
+void SkPictureRecord::onDrawImage(const SkImage* image, SkScalar x, SkScalar y,
+                                  const SkPaint* paint) {
+    SkBitmap bm;
+    if (as_IB(image)->getROPixels(&bm)) {
+        this->SkPictureRecord::onDrawBitmap(bm, x, y, paint);
+    }
+}
+
+void SkPictureRecord::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
+                                      const SkPaint* paint) {
+    SkBitmap bm;
+    if (as_IB(image)->getROPixels(&bm)) {
+        this->SkPictureRecord::onDrawBitmapRect(bm, src, dst, paint, kNone_DrawBitmapRectFlag);
+    }
+}
+
 void SkPictureRecord::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
                                        const SkRect& dst, const SkPaint* paint) {
     // op + paint index + bitmap id + center + dst rect
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
index 5e2d5a1..7a6fc81 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -184,12 +184,9 @@
     void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*,
                           DrawBitmapRectFlags flags) override;
-#if 0
-    // rely on conversion to bitmap (for now)
     void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
                          const SkPaint*) override;
-#endif
     void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
                           const SkPaint*) override;
     void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override;
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 6ff0538..9518600 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -21,6 +21,7 @@
 #include "SkErrorInternals.h"
 #include "SkGlyphCache.h"
 #include "SkGrTexturePixelRef.h"
+#include "SkImage_Base.h"
 #include "SkImageFilter.h"
 #include "SkLayerInfo.h"
 #include "SkMaskFilter.h"
@@ -1730,6 +1731,47 @@
                                filter, ctx, result, offset);
 }
 
+static SkImageInfo make_info(GrTexture* tex, int w, int h, bool isOpaque) {
+    const GrPixelConfig config = tex->config();
+    SkColorType ct;
+    SkAlphaType at = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
+    if (!GrPixelConfig2ColorAndProfileType(config, &ct, NULL)) {
+        ct = kUnknown_SkColorType;
+    }
+    return SkImageInfo::Make(w, h, ct, at);
+}
+
+static bool wrap_as_bm(const SkImage* image, SkBitmap* bm) {
+    GrTexture* tex = image->getTexture();
+    if (tex) {
+        // TODO: handle the GrTexture directly, and skip GrPixelRef
+        const SkImageInfo info = make_info(tex, image->width(), image->height(), image->isOpaque());
+        bm->setInfo(info);
+        bm->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (info, tex)))->unref();
+    } else {
+        if (!as_IB(image)->getROPixels(bm)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void SkGpuDevice::drawImage(const SkDraw& draw, const SkImage* image, SkScalar x, SkScalar y,
+                            const SkPaint& paint) {
+    SkBitmap bm;
+    if (wrap_as_bm(image, &bm)) {
+        this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint);
+    }
+}
+
+void SkGpuDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const SkRect* src,
+                                const SkRect& dst, const SkPaint& paint) {
+    SkBitmap bm;
+    if (wrap_as_bm(image, &bm)) {
+        this->drawBitmapRect(draw, bm, src, dst, paint, SkCanvas::kNone_DrawBitmapRectFlag);
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 // must be in SkCanvas::VertexMode order
diff --git a/src/gpu/SkGpuDevice.h b/src/gpu/SkGpuDevice.h
index 30cf68d..89959e1 100644
--- a/src/gpu/SkGpuDevice.h
+++ b/src/gpu/SkGpuDevice.h
@@ -115,6 +115,9 @@
                               const SkPaint&) override;
     virtual void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y,
                             const SkPaint&) override;
+    void drawImage(const SkDraw&, const SkImage*, SkScalar x, SkScalar y, const SkPaint&) override;
+    void drawImageRect(const SkDraw&, const SkImage*, const SkRect* src, const SkRect& dst,
+                       const SkPaint&) override;
 
     void flush() override;
 
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index f427755..494e210 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -25,15 +25,6 @@
     return id;
 }
 
-void SkImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
-    as_IB(this)->onDraw(canvas, x, y, paint);
-}
-
-void SkImage::drawRect(SkCanvas* canvas, const SkRect* src, const SkRect& dst,
-                   const SkPaint* paint) const {
-    as_IB(this)->onDrawRect(canvas, src, dst, paint);
-}
-
 const void* SkImage::peekPixels(SkImageInfo* info, size_t* rowBytes) const {
     SkImageInfo infoStorage;
     size_t rowBytesStorage;
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index 512c80c..5daf419 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -36,9 +36,6 @@
 
     const SkSurfaceProps& props() const { return fProps; }
 
-    virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const = 0;
-    virtual void onDrawRect(SkCanvas*, const SkRect* src,
-                                  const SkRect& dst, const SkPaint*) const = 0;
     virtual SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) const = 0;
 
     virtual const void* onPeekPixels(SkImageInfo*, size_t* /*rowBytes*/) const {
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 7ccff17..eec0d19 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -26,15 +26,6 @@
     return SkShader::CreateBitmapShader(fBitmap, tileX, tileY, localMatrix);
 }
 
-void SkImage_Gpu::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
-    canvas->drawBitmap(fBitmap, x, y, paint);
-}
-
-void SkImage_Gpu::onDrawRect(SkCanvas* canvas, const SkRect* src, const SkRect& dst,
-                                   const SkPaint* paint) const {
-    canvas->drawBitmapRectToRect(fBitmap, src, dst, paint);
-}
-
 SkSurface* SkImage_Gpu::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) const {
     GrContext* ctx = this->getTexture()->getContext();
     // TODO: Change signature of onNewSurface to take a budgeted param.
diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h
index a771dda..7b38e60 100644
--- a/src/image/SkImage_Gpu.h
+++ b/src/image/SkImage_Gpu.h
@@ -21,9 +21,6 @@
 
     SkImage_Gpu(const SkBitmap&, int sampleCountForNewSurfaces, SkSurface::Budgeted);
 
-    void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) const override;
-    void onDrawRect(SkCanvas*, const SkRect* src, const SkRect& dst,
-                    const SkPaint*) const override;
     SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) const override;
     GrTexture* onGetTexture() const override;
     bool getROPixels(SkBitmap*) const override;
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
index acf8058..20ae62c 100644
--- a/src/image/SkImage_Raster.cpp
+++ b/src/image/SkImage_Raster.cpp
@@ -53,8 +53,6 @@
     SkImage_Raster(const SkImageInfo&, SkData*, size_t rb, const SkSurfaceProps*);
     virtual ~SkImage_Raster();
 
-    void onDraw(SkCanvas*, SkScalar, SkScalar, const SkPaint*) const override;
-    void onDrawRect(SkCanvas*, const SkRect*, const SkRect&, const SkPaint*) const override;
     SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) const override;
     bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY) const override;
     const void* onPeekPixels(SkImageInfo*, size_t* /*rowBytes*/) const override;
@@ -120,17 +118,6 @@
     return SkShader::CreateBitmapShader(fBitmap, tileX, tileY, localMatrix);
 }
 
-void SkImage_Raster::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) const {
-    SkBitmap shallowCopy(fBitmap);
-    canvas->drawBitmap(shallowCopy, x, y, paint);
-}
-
-void SkImage_Raster::onDrawRect(SkCanvas* canvas, const SkRect* src, const SkRect& dst,
-                                      const SkPaint* paint) const {
-    SkBitmap shallowCopy(fBitmap);
-    canvas->drawBitmapRectToRect(shallowCopy, src, dst, paint);
-}
-
 SkSurface* SkImage_Raster::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) const {
     return SkSurface::NewRaster(info, &props);
 }
diff --git a/src/pipe/SkGPipePriv.h b/src/pipe/SkGPipePriv.h
index 736930b..c3919f6 100644
--- a/src/pipe/SkGPipePriv.h
+++ b/src/pipe/SkGPipePriv.h
@@ -43,6 +43,8 @@
     kDrawBitmapNine_DrawOp,
     kDrawBitmapRectToRect_DrawOp,
     kDrawDRRect_DrawOp,
+    kDrawImage_DrawOp,
+    kDrawImageRect_DrawOp,
     kDrawOval_DrawOp,
     kDrawPaint_DrawOp,
     kDrawPatch_DrawOp,
@@ -79,6 +81,7 @@
     // these are signals to playback, not drawing verbs
     kReportFlags_DrawOp,
     kShareBitmapHeap_DrawOp,
+    kShareImageHeap_DrawOp,
     kDone_DrawOp,
 };
 
@@ -141,6 +144,7 @@
     kDrawVertices_HasIndices_DrawOpFlag  = 1 << 2,
     kDrawVertices_HasXfermode_DrawOpFlag = 1 << 3,
 };
+// These are shared between drawbitmap and drawimage
 enum {
     kDrawBitmap_HasPaint_DrawOpFlag   = 1 << 0,
     // Specific to drawBitmapRect, but needs to be different from HasPaint,
@@ -213,6 +217,22 @@
             && !(flags & SkGPipeWriter::kSharedAddressSpace_Flag));
 }
 
+class SkImageHeap : public SkRefCnt {
+public:
+    SkImageHeap();
+    virtual ~SkImageHeap();
+
+    // slot must be "valid" -- 0 is never valid
+    const SkImage* get(int32_t slot) const;
+    // returns 0 if not found, else returns slot
+    int32_t find(const SkImage*) const;
+    // returns non-zero value for where the image was stored
+    int32_t insert(const SkImage*);
+
+private:
+    SkTDArray<const SkImage*> fArray;
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 
 enum PaintOps {
diff --git a/src/pipe/SkGPipeRead.cpp b/src/pipe/SkGPipeRead.cpp
index f8411f5..917bb50 100644
--- a/src/pipe/SkGPipeRead.cpp
+++ b/src/pipe/SkGPipeRead.cpp
@@ -178,6 +178,10 @@
         this->updateReader();
     }
 
+    void setImageHeap(SkImageHeap* heap) {
+        fImageHeap.reset(SkRef(heap));
+    }
+
     /**
      * Access the shared heap. Only used in the case when bitmaps are not
      * flattened.
@@ -198,6 +202,10 @@
         return id ? fTypefaces[id - 1] : NULL;
     }
 
+    const SkImage* getImage(int32_t slot) const {
+        return fImageHeap->get(slot);
+    }
+
 private:
     void updateReader() {
         if (NULL == fReader) {
@@ -227,6 +235,7 @@
     bool                      fSilent;
     // Only used when sharing bitmaps with the writer.
     SkBitmapHeap*             fSharedHeap;
+    SkAutoTUnref<SkImageHeap> fImageHeap;
     unsigned                  fFlags;
 };
 
@@ -629,6 +638,35 @@
     }
 }
 
+static void drawImage_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32, SkGPipeState* state) {
+    unsigned slot = DrawOp_unpackData(op32);
+    unsigned flags = DrawOp_unpackFlags(op32);
+    bool hasPaint = SkToBool(flags & kDrawBitmap_HasPaint_DrawOpFlag);
+    SkScalar x = reader->readScalar();
+    SkScalar y = reader->readScalar();
+    const SkImage* image = state->getImage(slot);
+    if (state->shouldDraw()) {
+        canvas->drawImage(image, x, y, hasPaint ? &state->paint() : NULL);
+    }
+}
+
+static void drawImageRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                             SkGPipeState* state) {
+    unsigned slot = DrawOp_unpackData(op32);
+    unsigned flags = DrawOp_unpackFlags(op32);
+    bool hasPaint = SkToBool(flags & kDrawBitmap_HasPaint_DrawOpFlag);
+    bool hasSrc = SkToBool(flags & kDrawBitmap_HasSrcRect_DrawOpFlag);
+    const SkRect* src = NULL;
+    if (hasSrc) {
+        src = skip<SkRect>(reader);
+    }
+    const SkRect* dst = skip<SkRect>(reader);
+    const SkImage* image = state->getImage(slot);
+    if (state->shouldDraw()) {
+        canvas->drawImageRect(image, src, *dst, hasPaint ? &state->paint() : NULL);
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static void drawPicture_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
@@ -774,10 +812,14 @@
 }
 
 static void shareBitmapHeap_rp(SkCanvas*, SkReader32* reader, uint32_t,
-                           SkGPipeState* state) {
+                               SkGPipeState* state) {
     state->setSharedHeap(static_cast<SkBitmapHeap*>(reader->readPtr()));
 }
 
+static void shareImageHeap_rp(SkCanvas*, SkReader32* reader, uint32_t, SkGPipeState* state) {
+    state->setImageHeap(static_cast<SkImageHeap*>(reader->readPtr()));
+}
+
 static void done_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState*) {}
 
 typedef void (*ReadProc)(SkCanvas*, SkReader32*, uint32_t op32, SkGPipeState*);
@@ -793,6 +835,8 @@
     drawBitmapNine_rp,
     drawBitmapRect_rp,
     drawDRRect_rp,
+    drawImage_rp,
+    drawImageRect_rp,
     drawOval_rp,
     drawPaint_rp,
     drawPatch_rp,
@@ -828,6 +872,7 @@
 
     reportFlags_rp,
     shareBitmapHeap_rp,
+    shareImageHeap_rp,
     done_rp
 };
 
diff --git a/src/pipe/SkGPipeWrite.cpp b/src/pipe/SkGPipeWrite.cpp
index 83e5c57..2e73be8 100644
--- a/src/pipe/SkGPipeWrite.cpp
+++ b/src/pipe/SkGPipeWrite.cpp
@@ -272,12 +272,9 @@
     void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst, const SkPaint*,
                           DrawBitmapRectFlags flags) override;
-#if 0
-    // rely on decomposition into bitmap (for now)
     void onDrawImage(const SkImage*, SkScalar left, SkScalar top, const SkPaint*) override;
     void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
                          const SkPaint*) override;
-#endif
     void onDrawBitmapNine(const SkBitmap&, const SkIRect& center, const SkRect& dst,
                           const SkPaint*) override;
     void onDrawSprite(const SkBitmap&, int left, int top, const SkPaint*) override;
@@ -300,6 +297,7 @@
 
     SkNamedFactorySet* fFactorySet;
     SkBitmapHeap*      fBitmapHeap;
+    SkImageHeap*       fImageHeap;
     SkGPipeController* fController;
     SkWriter32&        fWriter;
     size_t             fBlockSize; // amount allocated for writer
@@ -348,8 +346,8 @@
 
     // Common code used by drawBitmap*. Behaves differently depending on the
     // type of SkBitmapHeap being used, which is determined by the flags used.
-    bool commonDrawBitmap(const SkBitmap& bm, DrawOps op, unsigned flags,
-                          size_t opBytesNeeded, const SkPaint* paint);
+    bool commonDrawBitmap(const SkBitmap&, DrawOps, unsigned flags, size_t bytes, const SkPaint*);
+    bool commonDrawImage(const SkImage*, DrawOps, unsigned flags, size_t bytes, const SkPaint*);
 
     SkPaint fPaint;
     void writePaint(const SkPaint&);
@@ -462,6 +460,13 @@
         }
     }
     fFlattenableHeap.setBitmapStorage(fBitmapHeap);
+
+    fImageHeap = SkNEW(SkImageHeap);
+    if (this->needOpBytes(sizeof(void*))) {
+        this->writeOp(kShareImageHeap_DrawOp);
+        fWriter.writePtr(static_cast<void*>(fImageHeap));
+    }
+
     this->doNotify();
 }
 
@@ -469,6 +474,7 @@
     this->finish(true);
     SkSafeUnref(fFactorySet);
     SkSafeUnref(fBitmapHeap);
+    SkSafeUnref(fImageHeap);
 }
 
 bool SkGPipeCanvas::needOpBytes(size_t needed) {
@@ -823,6 +829,53 @@
     }
 }
 
+bool SkGPipeCanvas::commonDrawImage(const SkImage* image, DrawOps op, unsigned flags,
+                                    size_t opBytesNeeded, const SkPaint* paint) {
+    if (fDone) {
+        return false;
+    }
+    
+    if (paint != NULL) {
+        flags |= kDrawBitmap_HasPaint_DrawOpFlag;
+        this->writePaint(*paint);
+    }
+    // This needs to run first so its calls to needOpBytes() and its writes
+    // don't interlace with the needOpBytes() and write below.
+    int32_t slot = fImageHeap->insert(image);
+    SkASSERT(slot != 0);
+    if (this->needOpBytes(opBytesNeeded)) {
+        this->writeOp(op, flags, slot);
+        return true;
+    }
+    return false;
+}
+
+void SkGPipeCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y,
+                                const SkPaint* paint) {
+    NOTIFY_SETUP(this);
+    if (this->commonDrawImage(image, kDrawImage_DrawOp, 0, sizeof(SkScalar) * 2, paint)) {
+        fWriter.writeScalar(x);
+        fWriter.writeScalar(y);
+    }
+}
+
+void SkGPipeCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
+                                    const SkPaint* paint) {
+    NOTIFY_SETUP(this);
+    unsigned flags = 0;
+    size_t opBytesNeeded = sizeof(SkRect);  // dst
+    if (src) {
+        flags |= kDrawBitmap_HasSrcRect_DrawOpFlag;
+        opBytesNeeded += sizeof(SkRect);    // src
+    }
+    if (this->commonDrawImage(image, kDrawImageRect_DrawOp, flags, opBytesNeeded, paint)) {
+        if (src) {
+            fWriter.writeRect(*src);
+        }
+        fWriter.writeRect(dst);
+    }
+}
+
 void SkGPipeCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
                                const SkPaint& paint) {
     if (byteLength) {
@@ -1337,3 +1390,34 @@
     fCanvas->unref();
     fCanvas = NULL;
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkImageHeap::SkImageHeap() {}
+
+SkImageHeap::~SkImageHeap() {
+    fArray.unrefAll();
+}
+
+const SkImage* SkImageHeap::get(int32_t slot) const {
+    SkASSERT(slot > 0);
+    return fArray[slot - 1];
+}
+
+int32_t SkImageHeap::find(const SkImage* img) const {
+    int index = fArray.find(img);
+    if (index >= 0) {
+        return index + 1;   // found
+    }
+    return 0;   // not found
+}
+
+int32_t SkImageHeap::insert(const SkImage* img) {
+    int32_t slot = this->find(img);
+    if (slot) {
+        return slot;
+    }
+    *fArray.append() = SkRef(img);
+    return fArray.count();  // slot is always index+1
+}
+
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
index d5cf97b..7e4539a 100644
--- a/src/utils/SkDeferredCanvas.cpp
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -32,10 +32,17 @@
     kSilent_PlaybackMode,
 };
 
-static bool should_draw_immediately(const SkBitmap* bitmap, const SkPaint* paint,
-                                    size_t bitmapSizeThreshold) {
+static uint64_t image_area(const SkImage* image) {
+    return sk_64_mul(image->width(), image->height());
+}
+
+static bool should_draw_immediately(const SkBitmap* bitmap, const SkImage* image,
+                                    const SkPaint* paint, size_t bitmapSizeThreshold) {
     if (bitmap && ((bitmap->getTexture() && !bitmap->isImmutable()) ||
-        (bitmap->getSize() > bitmapSizeThreshold))) {
+                   (bitmap->getSize() > bitmapSizeThreshold))) {
+        return true;
+    }
+    if (image && (image_area(image) > bitmapSizeThreshold)) {
         return true;
     }
     if (paint) {
@@ -202,6 +209,11 @@
     void drawSprite(const SkDraw&, const SkBitmap& bitmap,
                     int x, int y, const SkPaint& paint) override
         {SkASSERT(0);}
+    void drawImage(const SkDraw&, const SkImage*, SkScalar, SkScalar, const SkPaint&) override
+        {SkASSERT(0);}
+    void drawImageRect(const SkDraw&, const SkImage*, const SkRect*, const SkRect&,
+                       const SkPaint&) override
+        {SkASSERT(0);}
     void drawText(const SkDraw&, const void* text, size_t len,
                   SkScalar x, SkScalar y, const SkPaint& paint) override
         {SkASSERT(0);}
@@ -481,11 +493,15 @@
 public:
     AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkBitmap* bitmap,
                               const SkPaint* paint) {
-        this->init(canvas, bitmap, paint);
+        this->init(canvas, bitmap, NULL, paint);
+    }
+    AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkImage* image,
+                              const SkPaint* paint) {
+        this->init(canvas, NULL, image, paint);
     }
 
     AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkPaint* paint) {
-        this->init(canvas, NULL, paint);
+        this->init(canvas, NULL, NULL, paint);
     }
 
     ~AutoImmediateDrawIfNeeded() {
@@ -494,9 +510,10 @@
         }
     }
 private:
-    void init(SkDeferredCanvas& canvas, const SkBitmap* bitmap, const SkPaint* paint) {
+    void init(SkDeferredCanvas& canvas, const SkBitmap* bitmap, const SkImage* image,
+              const SkPaint* paint) {
         if (canvas.isDeferredDrawing() &&
-            should_draw_immediately(bitmap, paint, canvas.getBitmapSizeThreshold())) {
+            should_draw_immediately(bitmap, image, paint, canvas.getBitmapSizeThreshold())) {
             canvas.setDeferredDrawing(false);
             fCanvas = &canvas;
         } else {
@@ -836,6 +853,34 @@
     this->recordedDrawCommand();
 }
 
+
+void SkDeferredCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y,
+                                   const SkPaint* paint) {
+    SkRect bounds = SkRect::MakeXYWH(x, y,
+                                     SkIntToScalar(image->width()), SkIntToScalar(image->height()));
+    if (fDeferredDrawing &&
+        this->isFullFrame(&bounds, paint) &&
+        isPaintOpaque(paint, image)) {
+        this->getDeferredDevice()->skipPendingCommands();
+    }
+    
+    AutoImmediateDrawIfNeeded autoDraw(*this, image, paint);
+    this->drawingCanvas()->drawImage(image, x, y, paint);
+    this->recordedDrawCommand();
+}
+void SkDeferredCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
+                                       const SkPaint* paint) {
+    if (fDeferredDrawing &&
+        this->isFullFrame(&dst, paint) &&
+        isPaintOpaque(paint, image)) {
+        this->getDeferredDevice()->skipPendingCommands();
+    }
+    
+    AutoImmediateDrawIfNeeded autoDraw(*this, image, paint);
+    this->drawingCanvas()->drawImageRect(image, src, dst, paint);
+    this->recordedDrawCommand();
+}
+
 void SkDeferredCanvas::onDrawBitmapNine(const SkBitmap& bitmap,
                                         const SkIRect& center, const SkRect& dst,
                                         const SkPaint* paint) {