scaling API on SkPixmap

BUG=skia:4481

Review URL: https://codereview.chromium.org/1463373002
diff --git a/gm/image.cpp b/gm/image.cpp
index 27cce23..03ed105 100644
--- a/gm/image.cpp
+++ b/gm/image.cpp
@@ -192,3 +192,111 @@
     typedef skiagm::GM INHERITED;
 };
 DEF_GM( return new ImageGM; )
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#include "SkPictureRecorder.h"
+
+static void draw_pixmap(SkCanvas* canvas, const SkPixmap& pmap) {
+    SkBitmap bitmap;
+    bitmap.installPixels(pmap.info(), (void*)pmap.addr(), pmap.rowBytes());
+    canvas->drawBitmap(bitmap, 0, 0, nullptr);
+}
+
+static void show_scaled_pixels(SkCanvas* canvas, SkImage* image) {
+    SkAutoCanvasRestore acr(canvas, true);
+
+    canvas->drawImage(image, 0, 0, nullptr);
+    canvas->translate(110, 10);
+
+    const SkImageInfo info = SkImageInfo::MakeN32Premul(40, 40);
+    SkAutoPixmapStorage storage;
+    storage.alloc(info);
+
+    const SkImage::CachingHint chints[] = {
+        SkImage::kAllow_CachingHint, // SkImage::kDisallow_CachingHint,
+    };
+    const SkFilterQuality qualities[] = {
+        kNone_SkFilterQuality, kLow_SkFilterQuality, kMedium_SkFilterQuality, kHigh_SkFilterQuality,
+    };
+
+    for (auto ch : chints) {
+        canvas->save();
+        for (auto q : qualities) {
+            if (image->scalePixels(storage, q, ch)) {
+                draw_pixmap(canvas, storage);
+            }
+            canvas->translate(70, 0);
+        }
+        canvas->restore();
+        canvas->translate(0, 45);
+    }
+}
+
+static void draw_contents(SkCanvas* canvas) {
+    SkPaint paint;
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(20);
+    canvas->drawCircle(50, 50, 35, paint);
+}
+
+static SkImage* make_raster(const SkImageInfo& info, GrContext*) {
+    SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(info));
+    draw_contents(surface->getCanvas());
+    return surface->newImageSnapshot();
+}
+
+static SkImage* make_picture(const SkImageInfo& info, GrContext*) {
+    SkPictureRecorder recorder;
+    draw_contents(recorder.beginRecording(SkRect::MakeIWH(info.width(), info.height())));
+    SkAutoTUnref<SkPicture> pict(recorder.endRecording());
+    return SkImage::NewFromPicture(pict, info.dimensions(), nullptr, nullptr);
+}
+
+static SkImage* make_codec(const SkImageInfo& info, GrContext*) {
+    SkAutoTUnref<SkImage> image(make_raster(info, nullptr));
+    SkAutoTUnref<SkData> data(image->encode());
+    return SkImage::NewFromEncoded(data);
+}
+
+static SkImage* make_gpu(const SkImageInfo& info, GrContext* ctx) {
+    if (!ctx) { return nullptr; }
+    SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(ctx, SkSurface::kNo_Budgeted, info));
+    draw_contents(surface->getCanvas());
+    return surface->newImageSnapshot();
+}
+
+typedef SkImage* (*ImageMakerProc)(const SkImageInfo&, GrContext*);
+
+class ScalePixelsGM : public skiagm::GM {
+public:
+    ScalePixelsGM() {}
+
+protected:
+    SkString onShortName() override {
+        return SkString("scale-pixels");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(960, 1200);
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        const SkImageInfo info = SkImageInfo::MakeN32Premul(100, 100);
+
+        const ImageMakerProc procs[] = {
+            make_raster, make_picture, make_codec, make_gpu,
+        };
+        for (auto& proc : procs) {
+            SkAutoTUnref<SkImage> image(proc(info, canvas->getGrContext()));
+            if (image) {
+                show_scaled_pixels(canvas, image);
+            }
+            canvas->translate(0, 120);
+        }
+    }
+    
+private:
+    typedef skiagm::GM INHERITED;
+};
+DEF_GM( return new ScalePixelsGM; )
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index 667cf6b..d6b10a8 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -208,6 +208,16 @@
     GrBackendObject getTextureHandle(bool flushPendingGrContextIO) const;
 
     /**
+     *  Hints to image calls where the system might cache computed intermediates (e.g. the results
+     *  of decoding or a read-back from the GPU. Passing kAllow signals that the system's default
+     *  behavior is fine. Passing kDisallow signals that caching should be avoided.
+     */
+     enum CachingHint {
+        kAllow_CachingHint,
+        kDisallow_CachingHint,
+    };
+
+    /**
      *  Copy the pixels from the image into the specified buffer (pixels + rowBytes),
      *  converting them into the requested format (dstInfo). The image pixels are read
      *  starting at the specified (srcX,srcY) location.
@@ -226,9 +236,19 @@
      *  - If the requested colortype/alphatype cannot be converted from the image's types.
      */
     bool readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
-                    int srcX, int srcY) const;
+                    int srcX, int srcY, CachingHint = kAllow_CachingHint) const;
 
-    bool readPixels(const SkPixmap& dst, int srcX, int srcY) const;
+    bool readPixels(const SkPixmap& dst, int srcX, int srcY,
+                    CachingHint = kAllow_CachingHint) const;
+
+    /**
+     *  Copy the pixels from this image into the dst pixmap, converting as needed into dst's
+     *  colortype/alphatype. If the conversion cannot be performed, false is returned.
+     *
+     *  If dst's dimensions differ from the src dimension, the image will be scaled, applying the
+     *  specified filter-quality.
+     */
+    bool scalePixels(const SkPixmap& dst, SkFilterQuality, CachingHint = kAllow_CachingHint) const;
 
     /**
      *  Encode the image's pixels and return the result as a new SkData, which
diff --git a/include/core/SkPixmap.h b/include/core/SkPixmap.h
index faae85e..da97025 100644
--- a/include/core/SkPixmap.h
+++ b/include/core/SkPixmap.h
@@ -9,6 +9,7 @@
 #define SkPixmap_DEFINED
 
 #include "SkColor.h"
+#include "SkFilterQuality.h"
 #include "SkImageInfo.h"
 
 class SkColorTable;
@@ -135,6 +136,15 @@
     }
 
     /**
+     *  Copy the pixels from this pixmap into the dst pixmap, converting as needed into dst's
+     *  colortype/alphatype. If the conversion cannot be performed, false is returned.
+     *
+     *  If dst's dimensions differ from the src dimension, the image will be scaled, applying the
+     *  specified filter-quality.
+     */
+    bool scalePixels(const SkPixmap& dst, SkFilterQuality) const;
+
+    /**
      *  Returns true if pixels were written to (e.g. if colorType is kUnknown_SkColorType, this
      *  will return false). If subset does not intersect the bounds of this pixmap, returns false.
      */
diff --git a/src/core/SkBitmapController.cpp b/src/core/SkBitmapController.cpp
index 5cfa525..233e811 100644
--- a/src/core/SkBitmapController.cpp
+++ b/src/core/SkBitmapController.cpp
@@ -121,8 +121,10 @@
         
         SkASSERT(fResultBitmap.getPixels());
         fResultBitmap.setImmutable();
-        if (SkBitmapCache::AddWH(desc, fResultBitmap)) {
-            provider.notifyAddedToCache();
+        if (!provider.isVolatile()) {
+            if (SkBitmapCache::AddWH(desc, fResultBitmap)) {
+                provider.notifyAddedToCache();
+            }
         }
     }
     
diff --git a/src/core/SkBitmapProvider.cpp b/src/core/SkBitmapProvider.cpp
index cae744b..9cf2e9a 100644
--- a/src/core/SkBitmapProvider.cpp
+++ b/src/core/SkBitmapProvider.cpp
@@ -52,6 +52,14 @@
     }
 }
 
+bool SkBitmapProvider::isVolatile() const {
+    if (fImage) {
+        return false;   // add flag to images?
+    } else {
+        return fBitmap.isVolatile();
+    }
+}
+
 SkBitmapCacheDesc SkBitmapProvider::makeCacheDesc(int w, int h) const {
     return fImage ? SkBitmapCacheDesc::Make(fImage, w, h) : SkBitmapCacheDesc::Make(fBitmap, w, h);
 }
@@ -70,7 +78,7 @@
 
 bool SkBitmapProvider::asBitmap(SkBitmap* bm) const {
     if (fImage) {
-        return as_IB(fImage)->getROPixels(bm);
+        return as_IB(fImage)->getROPixels(bm, SkImage::kAllow_CachingHint);
     } else {
         *bm = fBitmap;
         return true;
diff --git a/src/core/SkBitmapProvider.h b/src/core/SkBitmapProvider.h
index 72964e4..9901c0f 100644
--- a/src/core/SkBitmapProvider.h
+++ b/src/core/SkBitmapProvider.h
@@ -27,6 +27,7 @@
 
     bool validForDrawing() const;
     SkImageInfo info() const;
+    bool isVolatile() const;
 
     SkBitmapCacheDesc makeCacheDesc(int w, int h) const;
     SkBitmapCacheDesc makeCacheDesc() const;
diff --git a/src/core/SkImageCacherator.cpp b/src/core/SkImageCacherator.cpp
index fb06a56..1c97656 100644
--- a/src/core/SkImageCacherator.cpp
+++ b/src/core/SkImageCacherator.cpp
@@ -110,7 +110,8 @@
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-bool SkImageCacherator::tryLockAsBitmap(SkBitmap* bitmap, const SkImage* client) {
+bool SkImageCacherator::tryLockAsBitmap(SkBitmap* bitmap, const SkImage* client,
+                                        SkImage::CachingHint chint) {
     if (SkBitmapCache::Find(fUniqueID, bitmap)) {
         return check_output_bitmap(*bitmap, fUniqueID);
     }
@@ -120,16 +121,18 @@
     }
 
     bitmap->pixelRef()->setImmutableWithID(fUniqueID);
-    SkBitmapCache::Add(fUniqueID, *bitmap);
-    if (client) {
-        as_IB(client)->notifyAddedToCache();
+    if (SkImage::kAllow_CachingHint == chint) {
+        SkBitmapCache::Add(fUniqueID, *bitmap);
+        if (client) {
+            as_IB(client)->notifyAddedToCache();
+        }
     }
-
     return true;
 }
 
-bool SkImageCacherator::lockAsBitmap(SkBitmap* bitmap, const SkImage* client) {
-    if (this->tryLockAsBitmap(bitmap, client)) {
+bool SkImageCacherator::lockAsBitmap(SkBitmap* bitmap, const SkImage* client,
+                                     SkImage::CachingHint chint) {
+    if (this->tryLockAsBitmap(bitmap, client, chint)) {
         return check_output_bitmap(*bitmap, fUniqueID);
     }
 
@@ -160,11 +163,12 @@
     }
 
     bitmap->pixelRef()->setImmutableWithID(fUniqueID);
-    SkBitmapCache::Add(fUniqueID, *bitmap);
-    if (client) {
-        as_IB(client)->notifyAddedToCache();
+    if (SkImage::kAllow_CachingHint == chint) {
+        SkBitmapCache::Add(fUniqueID, *bitmap);
+        if (client) {
+            as_IB(client)->notifyAddedToCache();
+        }
     }
-
     return check_output_bitmap(*bitmap, fUniqueID);
 #else
     return false;
@@ -220,7 +224,7 @@
  *  5. Ask the generator to return RGB(A) data, which the GPU can convert
  */
 GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& key,
-                                          const SkImage* client) {
+                                          const SkImage* client, SkImage::CachingHint chint) {
     // 1. Check the cache for a pre-existing one
     if (key.isValid()) {
         if (GrTexture* tex = ctx->textureProvider()->findAndRefTextureByUniqueKey(key)) {
@@ -260,7 +264,7 @@
 
     // 5. Ask the generator to return RGB(A) data, which the GPU can convert
     SkBitmap bitmap;
-    if (this->tryLockAsBitmap(&bitmap, client)) {
+    if (this->tryLockAsBitmap(&bitmap, client, chint)) {
         GrTexture* tex = GrUploadBitmapToTexture(ctx, bitmap);
         if (tex) {
             return set_key_and_return(tex, key);
@@ -275,10 +279,13 @@
 
 class Cacherator_GrTextureMaker : public GrTextureMaker {
 public:
-    Cacherator_GrTextureMaker(GrContext* context, SkImageCacherator* cacher, const SkImage* client)
+    Cacherator_GrTextureMaker(GrContext* context, SkImageCacherator* cacher, const SkImage* client,
+                              SkImage::CachingHint chint)
         : INHERITED(context, cacher->info().width(), cacher->info().height())
         , fCacher(cacher)
-        , fClient(client) {
+        , fClient(client)
+        , fCachingHint(chint)
+    {
         if (client) {
             GrMakeKeyFromImageID(&fOriginalKey, client->uniqueID(),
                                  SkIRect::MakeWH(this->width(), this->height()));
@@ -291,7 +298,7 @@
     //          GrTexture* generateTextureForParams(const CopyParams&) override;
 
     GrTexture* refOriginalTexture() override {
-        return fCacher->lockTexture(this->context(), fOriginalKey, fClient);
+        return fCacher->lockTexture(this->context(), fOriginalKey, fClient, fCachingHint);
     }
 
     void makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey) override {
@@ -310,23 +317,24 @@
     SkImageCacherator*      fCacher;
     const SkImage*          fClient;
     GrUniqueKey             fOriginalKey;
+    SkImage::CachingHint    fCachingHint;
 
     typedef GrTextureMaker INHERITED;
 };
 
 GrTexture* SkImageCacherator::lockAsTexture(GrContext* ctx, const GrTextureParams& params,
-                                            const SkImage* client) {
+                                            const SkImage* client, SkImage::CachingHint chint) {
     if (!ctx) {
         return nullptr;
     }
 
-    return Cacherator_GrTextureMaker(ctx, this, client).refTextureForParams(params);
+    return Cacherator_GrTextureMaker(ctx, this, client, chint).refTextureForParams(params);
 }
 
 #else
 
 GrTexture* SkImageCacherator::lockAsTexture(GrContext* ctx, const GrTextureParams&,
-                                            const SkImage* client) {
+                                            const SkImage* client, SkImage::CachingHint) {
     return nullptr;
 }
 
diff --git a/src/core/SkImageCacherator.h b/src/core/SkImageCacherator.h
index 48a2814..5ba5422 100644
--- a/src/core/SkImageCacherator.h
+++ b/src/core/SkImageCacherator.h
@@ -36,7 +36,8 @@
      *  If not NULL, the client will be notified (->notifyAddedToCache()) when resources are
      *  added to the cache on its behalf.
      */
-    bool lockAsBitmap(SkBitmap*, const SkImage* client);
+    bool lockAsBitmap(SkBitmap*, const SkImage* client,
+                      SkImage::CachingHint = SkImage::kAllow_CachingHint);
 
     /**
      *  Returns a ref() on the texture produced by this generator. The caller must call unref()
@@ -47,7 +48,8 @@
      *
      *  The caller is responsible for calling texture->unref() when they are done.
      */
-    GrTexture* lockAsTexture(GrContext*, const GrTextureParams&, const SkImage* client);
+    GrTexture* lockAsTexture(GrContext*, const GrTextureParams&, const SkImage* client,
+                             SkImage::CachingHint = SkImage::kAllow_CachingHint);
 
     /**
      *  If the underlying src naturally is represented by an encoded blob (in SkData), this returns
@@ -59,11 +61,12 @@
     SkImageCacherator(SkImageGenerator*, const SkImageInfo&, const SkIPoint&, uint32_t uniqueID);
 
     bool generateBitmap(SkBitmap*);
-    bool tryLockAsBitmap(SkBitmap*, const SkImage*);
+    bool tryLockAsBitmap(SkBitmap*, const SkImage*, SkImage::CachingHint);
 #if SK_SUPPORT_GPU
     // Returns the texture. If the cacherator is generating the texture and wants to cache it,
     // it should use the passed in key (if the key is valid).
-    GrTexture* lockTexture(GrContext*, const GrUniqueKey& key, const SkImage* client);
+    GrTexture* lockTexture(GrContext*, const GrUniqueKey& key, const SkImage* client,
+                           SkImage::CachingHint);
 #endif
 
     class ScopedGenerator {
diff --git a/src/core/SkPixmap.cpp b/src/core/SkPixmap.cpp
index c63bef9..718be88 100644
--- a/src/core/SkPixmap.cpp
+++ b/src/core/SkPixmap.cpp
@@ -205,6 +205,44 @@
     return true;
 }
 
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkSurface.h"
+#include "SkXfermode.h"
+
+bool SkPixmap::scalePixels(const SkPixmap& dst, SkFilterQuality quality) const {
+    // Can't do anthing with empty src or dst
+    if (this->width() <= 0 || this->height() <= 0 || dst.width() <= 0 || dst.height() <= 0) {
+        return false;
+    }
+
+    // no scaling involved?
+    if (dst.width() == this->width() && dst.height() == this->height()) {
+        return this->readPixels(dst);
+    }
+
+    SkBitmap bitmap;
+    // we will only ready from this pixmap, but the bitmap setting takes void*, hence the cast
+    void* readOnlyAddr = const_cast<void*>(this->addr());
+    if (!bitmap.installPixels(this->info(), readOnlyAddr, this->rowBytes())) {
+        return false;
+    }
+    bitmap.setIsVolatile(true); // so we don't try to cache it
+
+    SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterDirect(dst.info(), dst.writable_addr(),
+                                                               dst.rowBytes()));
+    if (!surface) {
+        return false;
+    }
+
+    SkPaint paint;
+    paint.setFilterQuality(quality);
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    surface->getCanvas()->drawBitmapRect(bitmap, SkRect::MakeIWH(dst.width(), dst.height()),
+                                         &paint);
+    return true;
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
 SkAutoPixmapStorage::SkAutoPixmapStorage() : fStorage(nullptr) {}
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
index b96f7d1..fdaadc5 100644
--- a/src/image/SkImage.cpp
+++ b/src/image/SkImage.cpp
@@ -48,12 +48,29 @@
 }
 
 bool SkImage::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
-                           int srcX, int srcY) const {
+                           int srcX, int srcY, CachingHint chint) const {
     SkReadPixelsRec rec(dstInfo, dstPixels, dstRowBytes, srcX, srcY);
     if (!rec.trim(this->width(), this->height())) {
         return false;
     }
-    return as_IB(this)->onReadPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY);
+    return as_IB(this)->onReadPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY, chint);
+}
+
+bool SkImage::scalePixels(const SkPixmap& dst, SkFilterQuality quality, CachingHint chint) const {
+    // Idea: If/when SkImageGenerator supports a native-scaling API (where the generator itself
+    //       can scale more efficiently) we should take advantage of it here.
+    //
+    SkBitmap bm;
+    if (as_IB(this)->getROPixels(&bm, chint)) {
+        bm.lockPixels();
+        SkPixmap pmap;
+        // Note: By calling the pixmap scaler, we never cache the final result, so the chint
+        //       is (currently) only being applied to the getROPixels. If we get a request to
+        //       also attempt to cache the final (scaled) result, we would add that logic here.
+        //
+        return bm.peekPixels(&pmap) && pmap.scalePixels(dst, quality);
+    }
+    return false;
 }
 
 void SkImage::preroll(GrContext* ctx) const {
@@ -286,7 +303,7 @@
 }
 
 bool SkImage_Base::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
-                                int srcX, int srcY) const {
+                                int srcX, int srcY, CachingHint) const {
     if (!raster_canvas_supports(dstInfo)) {
         return false;
     }
@@ -317,8 +334,8 @@
     return false;
 }
 
-bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY) const {
-    return this->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX, srcY);
+bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY, CachingHint chint) const {
+    return this->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX, srcY, chint);
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index 3fe3444..c91430e 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -29,13 +29,13 @@
 
     // Default impl calls onDraw
     virtual bool onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
-                              int srcX, int srcY) const;
+                              int srcX, int srcY, CachingHint) const;
 
     virtual GrTexture* peekTexture() const { return nullptr; }
 
     // return a read-only copy of the pixels. We promise to not modify them,
     // but only inspect them (or encode them).
-    virtual bool getROPixels(SkBitmap*) const = 0;
+    virtual bool getROPixels(SkBitmap*, CachingHint = kAllow_CachingHint) const = 0;
 
     virtual SkImage* onApplyFilter(SkImageFilter*, SkIPoint* offset,
                                    bool forceResultToOriginalSize) const;
diff --git a/src/image/SkImage_Generator.cpp b/src/image/SkImage_Generator.cpp
index 3465289..5031c24 100644
--- a/src/image/SkImage_Generator.cpp
+++ b/src/image/SkImage_Generator.cpp
@@ -21,12 +21,12 @@
         , fCache(cache) // take ownership
     {}
 
-    bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY) const override;
+    bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY, CachingHint) const override;
     const void* onPeekPixels(SkImageInfo*, size_t* /*rowBytes*/) const override;
     SkData* onRefEncoded() const override;
     bool isOpaque() const override { return fCache->info().isOpaque(); }
     SkImage* onNewSubset(const SkIRect&) const override;
-    bool getROPixels(SkBitmap*) const override;
+    bool getROPixels(SkBitmap*, CachingHint) const override;
     GrTexture* asTextureRef(GrContext*, const GrTextureParams&) const override;
     bool onIsLazyGenerated() const override { return true; }
 
@@ -39,9 +39,9 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkImage_Generator::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
-                                     int srcX, int srcY) const {
+                                     int srcX, int srcY, CachingHint chint) const {
     SkBitmap bm;
-    if (this->getROPixels(&bm)) {
+    if (this->getROPixels(&bm, chint)) {
         return bm.readPixels(dstInfo, dstPixels, dstRB, srcX, srcY);
     }
     return false;
@@ -55,8 +55,8 @@
     return fCache->refEncoded();
 }
 
-bool SkImage_Generator::getROPixels(SkBitmap* bitmap) const {
-    return fCache->lockAsBitmap(bitmap, this);
+bool SkImage_Generator::getROPixels(SkBitmap* bitmap, CachingHint chint) const {
+    return fCache->lockAsBitmap(bitmap, this, chint);
 }
 
 GrTexture* SkImage_Generator::asTextureRef(GrContext* ctx, const GrTextureParams& params) const {
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index dd4bee3..346f798 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -45,7 +45,7 @@
     return SkImageInfo::MakeN32(w, h, isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
 }
 
-bool SkImage_Gpu::getROPixels(SkBitmap* dst) const {
+bool SkImage_Gpu::getROPixels(SkBitmap* dst, CachingHint chint) const {
     if (SkBitmapCache::Find(this->uniqueID(), dst)) {
         SkASSERT(dst->getGenerationID() == this->uniqueID());
         SkASSERT(dst->isImmutable());
@@ -62,8 +62,10 @@
     }
 
     dst->pixelRef()->setImmutableWithID(this->uniqueID());
-    SkBitmapCache::Add(this->uniqueID(), *dst);
-    fAddedRasterVersionToCache.store(true);
+    if (kAllow_CachingHint == chint) {
+        SkBitmapCache::Add(this->uniqueID(), *dst);
+        fAddedRasterVersionToCache.store(true);
+    }
     return true;
 }
 
@@ -120,7 +122,7 @@
 }
 
 bool SkImage_Gpu::onReadPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
-                               int srcX, int srcY) const {
+                               int srcX, int srcY, CachingHint) const {
     GrPixelConfig config = SkImageInfo2GrPixelConfig(info.colorType(), info.alphaType(),
                                                      info.profileType());
     uint32_t flags = 0;
diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h
index d5080d7..ff3b12e 100644
--- a/src/image/SkImage_Gpu.h
+++ b/src/image/SkImage_Gpu.h
@@ -35,14 +35,14 @@
         }
     }
 
-    bool getROPixels(SkBitmap*) const override;
+    bool getROPixels(SkBitmap*, CachingHint) const override;
     GrTexture* asTextureRef(GrContext* ctx, const GrTextureParams& params) const override;
     SkImage* onNewSubset(const SkIRect&) const override;
 
     GrTexture* peekTexture() const override { return fTexture; }
     bool isOpaque() const override;
     bool onReadPixels(const SkImageInfo&, void* dstPixels, size_t dstRowBytes,
-                      int srcX, int srcY) const override;
+                      int srcX, int srcY, CachingHint) const override;
     SkImage* onApplyFilter(SkImageFilter*, SkIPoint* offset,
                            bool forceResultToOriginalSize) const override;
 
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
index 9c90ba9..977dc01 100644
--- a/src/image/SkImage_Raster.cpp
+++ b/src/image/SkImage_Raster.cpp
@@ -66,10 +66,10 @@
     SkImage_Raster(const SkImageInfo&, SkData*, size_t rb, SkColorTable*);
     virtual ~SkImage_Raster();
 
-    bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY) const override;
+    bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY, CachingHint) const override;
     const void* onPeekPixels(SkImageInfo*, size_t* /*rowBytes*/) const override;
     SkData* onRefEncoded() const override;
-    bool getROPixels(SkBitmap*) const override;
+    bool getROPixels(SkBitmap*, CachingHint) const override;
     GrTexture* asTextureRef(GrContext*, const GrTextureParams&) const override;
     SkImage* onNewSubset(const SkIRect&) const override;
 
@@ -135,7 +135,7 @@
 SkImage_Raster::~SkImage_Raster() {}
 
 bool SkImage_Raster::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
-                                  int srcX, int srcY) const {
+                                  int srcX, int srcY, CachingHint) const {
     SkBitmap shallowCopy(fBitmap);
     return shallowCopy.readPixels(dstInfo, dstPixels, dstRowBytes, srcX, srcY);
 }
@@ -162,7 +162,7 @@
     return nullptr;
 }
 
-bool SkImage_Raster::getROPixels(SkBitmap* dst) const {
+bool SkImage_Raster::getROPixels(SkBitmap* dst, CachingHint) const {
     *dst = fBitmap;
     return true;
 }