Revert of Rewrite GrTextureMaker to disentangle bitmap case from base class and give GPU object a say in what… (patchset #4 id:60001 of https://codereview.chromium.org/1409163002/ )

Reason for revert:
breaking nanobench

Original issue's description:
> Rewrite GrTextureMaker to disentangle bitmap case from base class and give GPU object a say in what copying needs to be done.
>
> Committed: https://skia.googlesource.com/skia/+/fcffaf22d697f06f903c3193308f9dc54a959f79

TBR=reed@google.com,bsalomon@google.com
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true

Review URL: https://codereview.chromium.org/1409923003
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
index d0a3e91..b86e5d4 100644
--- a/gyp/gpu.gypi
+++ b/gyp/gpu.gypi
@@ -178,8 +178,6 @@
       '<(skia_src_path)/gpu/GrTextContext.cpp',
       '<(skia_src_path)/gpu/GrTextContext.h',
       '<(skia_src_path)/gpu/GrTexture.cpp',
-      '<(skia_src_path)/gpu/GrTextureParamsAdjuster.h',
-      '<(skia_src_path)/gpu/GrTextureParamsAdjuster.cpp',
       '<(skia_src_path)/gpu/GrTextureProvider.cpp',
       '<(skia_src_path)/gpu/GrTexturePriv.h',
       '<(skia_src_path)/gpu/GrTextureAccess.cpp',
diff --git a/include/gpu/GrResourceKey.h b/include/gpu/GrResourceKey.h
index 9958cfc..6cfa90f 100644
--- a/include/gpu/GrResourceKey.h
+++ b/include/gpu/GrResourceKey.h
@@ -138,8 +138,8 @@
 
     friend class TestResource; // For unit test to access kMetaDataCnt.
 
-    // bmp textures require 5 uint32_t values.
-    SkAutoSTMalloc<kMetaDataCnt + 5, uint32_t> fKey;
+    // bmp textures require 4 uint32_t values.
+    SkAutoSTMalloc<kMetaDataCnt + 4, uint32_t> fKey;
 };
 
 /**
diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h
index 17625c4..591d3d8 100644
--- a/include/gpu/SkGr.h
+++ b/include/gpu/SkGr.h
@@ -65,11 +65,9 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-/** Returns a texture representing the bitmap that is compatible with the GrTextureParams. The
-    texture is inserted into the cache (unless the bitmap is marked volatile) and can be
-    retrieved again via this function. */
-GrTexture* GrRefCachedBitmapTexture(GrContext*, const SkBitmap&, const GrTextureParams&);
 
+GrTexture* GrRefCachedBitmapTexture(GrContext*, const SkBitmap&, const GrTextureParams&);
+    
 // TODO: Move SkImageInfo2GrPixelConfig to SkGrPriv.h (requires cleanup to SkWindow its subclasses).
 GrPixelConfig SkImageInfo2GrPixelConfig(SkColorType, SkAlphaType, SkColorProfileType);
 
diff --git a/src/core/SkImageCacherator.cpp b/src/core/SkImageCacherator.cpp
index f59be32..cd5ae57 100644
--- a/src/core/SkImageCacherator.cpp
+++ b/src/core/SkImageCacherator.cpp
@@ -204,9 +204,7 @@
 };
 
 static GrTexture* set_key_and_return(GrTexture* tex, const GrUniqueKey& key) {
-    if (key.isValid()) {
-        tex->resourcePriv().setUniqueKey(key);
-    }
+    tex->resourcePriv().setUniqueKey(key);
     return tex;
 }
 
@@ -219,16 +217,24 @@
  *  4. Ask the generator to return YUV planes, which the GPU can convert
  *  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) {
-    // 1. Check the cache for a pre-existing one
-    if (key.isValid()) {
-        if (GrTexture* tex = ctx->textureProvider()->findAndRefTextureByUniqueKey(key)) {
-            return tex;
-        }
+GrTexture* SkImageCacherator::lockUnstretchedTexture(GrContext* ctx, const SkImage* client) {
+    // textures (at least the texture-key) only support 16bit dimensions, so abort early
+    // if we're too big.
+    if (fInfo.width() > 0xFFFF || fInfo.height() > 0xFFFF) {
+        return nullptr;
     }
 
-    // 2. Ask the generator to natively create one
+    GrUniqueKey key;
+    const GrTextureParams& noStretchParams = GrTextureParams::ClampNoFilter();
+    GrMakeKeyFromImageID(&key, fUniqueID, SkIRect::MakeWH(fInfo.width(), fInfo.height()),
+                         *ctx->caps(), noStretchParams);
+
+    // 1. Check the cache for a pre-existing one
+    if (GrTexture* tex = ctx->textureProvider()->findAndRefTextureByUniqueKey(key)) {
+        return tex;
+    }
+
+    // 2. Ask the genreator to natively create one
     {
         ScopedGenerator generator(this);
         SkIRect subset = SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), fInfo.width(), fInfo.height());
@@ -261,62 +267,54 @@
     // 5. Ask the generator to return RGB(A) data, which the GPU can convert
     SkBitmap bitmap;
     if (this->tryLockAsBitmap(&bitmap, client)) {
-        GrTexture* tex = GrUploadBitmapToTexture(ctx, bitmap);
-        if (tex) {
-            return set_key_and_return(tex, key);
-        }
+        return GrRefCachedBitmapTexture(ctx, bitmap, noStretchParams);
     }
     return nullptr;
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-#include "GrTextureParamsAdjuster.h"
+#include "GrTextureMaker.h"
 
-class Cacherator_GrTextureParamsAdjuster : public GrTextureParamsAdjuster {
+class Cacherator_GrTextureMaker : public GrTextureMaker {
 public:
-    Cacherator_GrTextureParamsAdjuster(SkImageCacherator* cacher, const SkImage* client)
+    Cacherator_GrTextureMaker(SkImageCacherator* cacher, const SkImage* client,
+                              const GrUniqueKey& unstretchedKey)
         : INHERITED(cacher->info().width(), cacher->info().height())
         , fCacher(cacher)
         , fClient(client)
-    {
-        if (client) {
-            GrMakeKeyFromImageID(&fOriginalKey, client->uniqueID(),
-                                 SkIRect::MakeWH(this->width(), this->height()));
-        }
-    }
+        , fUnstretchedKey(unstretchedKey)
+    {}
 
 protected:
     // TODO: consider overriding this, for the case where the underlying generator might be
     //       able to efficiently produce a "stretched" texture natively (e.g. picture-backed)
-    //    GrTexture* generateTextureForParams(GrContext*, const SkGrStretch&) override;
+//    GrTexture* onGenerateStretchedTexture(GrContext*, const SkGrStretch&) override;
 
-    GrTexture* refOriginalTexture(GrContext* ctx) override {
-        return fCacher->lockTexture(ctx, fOriginalKey, fClient);
+    GrTexture* onRefUnstretchedTexture(GrContext* ctx) override {
+        return fCacher->lockUnstretchedTexture(ctx, fClient);
     }
 
-    void makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey) override {
-        if (fOriginalKey.isValid()) {
-            MakeCopyKeyFromOrigKey(fOriginalKey, stretch, paramsCopyKey);
-        }
+    bool onMakeStretchedKey(const SkGrStretch& stretch, GrUniqueKey* stretchedKey) override {
+        return GrMakeStretchedKey(fUnstretchedKey, stretch, stretchedKey);
     }
 
-    void didCacheCopy(const GrUniqueKey& copyKey) override {
+    void onNotifyStretchCached(const GrUniqueKey& stretchedKey) override {
         if (fClient) {
             as_IB(fClient)->notifyAddedToCache();
         }
     }
 
-    bool getROBitmap(SkBitmap* bitmap) override {
+    bool onGetROBitmap(SkBitmap* bitmap) override {
         return fCacher->lockAsBitmap(bitmap, fClient);
     }
 
 private:
     SkImageCacherator*      fCacher;
     const SkImage*          fClient;
-    GrUniqueKey             fOriginalKey;
+    const GrUniqueKey       fUnstretchedKey;
 
-    typedef GrTextureParamsAdjuster INHERITED;
+    typedef GrTextureMaker INHERITED;
 };
 
 GrTexture* SkImageCacherator::lockAsTexture(GrContext* ctx, const GrTextureParams& params,
@@ -325,7 +323,12 @@
         return nullptr;
     }
 
-    return Cacherator_GrTextureParamsAdjuster(this, client).refTextureForParams(ctx, params);
+    GrUniqueKey key;
+    GrMakeKeyFromImageID(&key, this->uniqueID(),
+                         SkIRect::MakeWH(this->info().width(), this->info().height()),
+                         *ctx->caps(), GrTextureParams::ClampNoFilter());
+
+    return Cacherator_GrTextureMaker(this, client, key).refCachedTexture(ctx, params);
 }
 
 #else
diff --git a/src/core/SkImageCacherator.h b/src/core/SkImageCacherator.h
index ac112d7..7508f34 100644
--- a/src/core/SkImageCacherator.h
+++ b/src/core/SkImageCacherator.h
@@ -13,8 +13,7 @@
 #include "SkTemplates.h"
 
 class GrContext;
-class GrTextureParams;
-class GrUniqueKey;
+class GrTextureParams; 
 class SkBitmap;
 class SkImage;
 
@@ -61,9 +60,7 @@
     bool generateBitmap(SkBitmap*);
     bool tryLockAsBitmap(SkBitmap*, const SkImage*);
 #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* lockUnstretchedTexture(GrContext*, const SkImage* client);
 #endif
 
     class ScopedGenerator {
@@ -86,7 +83,7 @@
     const SkIPoint      fOrigin;
     const uint32_t      fUniqueID;
 
-    friend class Cacherator_GrTextureParamsAdjuster;
+    friend class Cacherator_GrTextureMaker;
 };
 
 #endif
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 48fc740..96728f3 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -55,37 +55,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-bool GrGpu::makeCopyForTextureParams(int width, int height, const GrTextureParams& textureParams,
-                                     GrTextureParamsAdjuster::CopyParams* copyParams) const {
-    bool doCopy = false;
-    const GrCaps& caps = *this->caps();
-    if (textureParams.isTiled() && !caps.npotTextureTileSupport() &&
-        (!SkIsPow2(width) || !SkIsPow2(height))) {
-        doCopy = true;
-        copyParams->fWidth = GrNextPow2(SkTMax(width, caps.minTextureSize()));
-        copyParams->fHeight = GrNextPow2(SkTMax(height, caps.minTextureSize()));
-    } else if (width < caps.minTextureSize() || height < caps.minTextureSize()) {
-        // The small texture issues appear to be with tiling. Hence it seems ok to scale
-        // them up using the GPU. If issues persist we may need to CPU-stretch.
-        doCopy = true;
-        copyParams->fWidth = SkTMax(width, caps.minTextureSize());
-        copyParams->fHeight = SkTMax(height, caps.minTextureSize());
-    }
-    if (doCopy) {
-        switch (textureParams.filterMode()) {
-            case GrTextureParams::kNone_FilterMode:
-                copyParams->fFilter = GrTextureParams::kNone_FilterMode;
-                break;
-            case GrTextureParams::kBilerp_FilterMode:
-            case GrTextureParams::kMipMap_FilterMode:
-                // We are only ever scaling up so no reason to ever indicate kMipMap.
-                copyParams->fFilter = GrTextureParams::kBilerp_FilterMode;
-                break;
-        }
-    }
-    return doCopy;
-}
-
 static GrSurfaceOrigin resolve_origin(GrSurfaceOrigin origin, bool renderTarget) {
     // By default, GrRenderTargets are GL's normal orientation so that they
     // can be drawn to by the outside world without the client having
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 4714d36..d8e5681 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -11,7 +11,6 @@
 #include "GrPipelineBuilder.h"
 #include "GrProgramDesc.h"
 #include "GrStencil.h"
-#include "GrTextureParamsAdjuster.h"
 #include "GrXferProcessor.h"
 #include "SkPath.h"
 
@@ -379,13 +378,6 @@
     // clears target's entire stencil buffer to 0
     virtual void clearStencil(GrRenderTarget* target) = 0;
 
-
-    // Determines whether a copy of a texture must be made in order to be compatible with
-    // a given GrTextureParams. If so, the width, height and filter used for the copy are
-    // output via the CopyParams.
-    bool makeCopyForTextureParams(int width, int height, const GrTextureParams&,
-                                 GrTextureParamsAdjuster::CopyParams*) const;
-
     // This is only to be used in GL-specific tests.
     virtual const GrGLContext* glContextForTesting() const { return nullptr; }
 
diff --git a/src/gpu/GrTextureMaker.h b/src/gpu/GrTextureMaker.h
new file mode 100644
index 0000000..47141bd
--- /dev/null
+++ b/src/gpu/GrTextureMaker.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrTextureMaker_DEFINED
+#define GrTextureMaker_DEFINED
+
+#include "SkGrPriv.h"
+
+class GrContext;
+class GrTexture;
+class GrTextureParams;
+class GrUniqueKey;
+class SkBitmap;
+
+class GrTextureMaker {
+public:
+    GrTextureMaker(int width, int height) : fWidth(width), fHeight(height) {}
+    virtual ~GrTextureMaker() {}
+
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+
+    GrTexture* refCachedTexture(GrContext*, const GrTextureParams&);
+
+protected:
+    /**
+     *  Return the maker's "original" unstretched texture. It is the responsibility of the maker
+     *  to make this efficient ... if the texture is being generated, the maker must handle
+     *  caching it.
+     */
+    virtual GrTexture* onRefUnstretchedTexture(GrContext*) = 0;
+
+    /**
+     *  If we need to stretch the maker's original texture, the maker is asked to return a key
+     *  that identifies its origianl + the stretch parameter. If the maker does not want to cache
+     *  the stretched version (e.g. the maker is volatile), this should ignore the key parameter
+     *  and return false.
+     */
+    virtual bool onMakeStretchedKey(const SkGrStretch&, GrUniqueKey* stretchedKey) = 0;
+
+    /**
+     *  Return a new (uncached) texture that is the stretch of the maker's original.
+     *
+     *  The base-class handles general logic for this, and only needs access to the following
+     *  methods:
+     *  - onRefUnstretchedTexture()
+     *  - onGetROBitmap()
+     *
+     *  Subclass may override this if they can handle stretching more efficiently.
+     */
+    virtual GrTexture* onGenerateStretchedTexture(GrContext*, const SkGrStretch&);
+
+    /**
+     *  If a stretched version of the texture is generated, it may be cached (assuming that
+     *  onMakeStretchedKey() returns true). In that case, the maker is notified in case it
+     *  wants to note that for when the maker is destroyed.
+     */
+    virtual void onNotifyStretchCached(const GrUniqueKey& stretchedKey) = 0;
+
+    /**
+     *  Some GPUs are unreliable w/ very small texture sizes. If we run into that case, this
+     *  method will be called (in service of onGenerateStretchedTexture) to return a raster version
+     *  of the original texture.
+     */
+    virtual bool onGetROBitmap(SkBitmap*) = 0;
+
+private:
+    const int fWidth;
+    const int fHeight;
+};
+
+#endif
diff --git a/src/gpu/GrTextureParamsAdjuster.cpp b/src/gpu/GrTextureParamsAdjuster.cpp
deleted file mode 100644
index adeec45..0000000
--- a/src/gpu/GrTextureParamsAdjuster.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "GrTextureParamsAdjuster.h"
-
-#include "GrCaps.h"
-#include "GrContext.h"
-#include "GrDrawContext.h"
-#include "GrGpu.h"
-#include "GrTexture.h"
-#include "GrTextureParams.h"
-#include "GrTextureProvider.h"
-#include "SkCanvas.h"
-#include "SkGr.h"
-#include "SkGrPriv.h"
-
-typedef GrTextureParamsAdjuster::CopyParams CopyParams;
-
-static GrTexture* copy_on_gpu(GrTexture* inputTexture, const CopyParams& copyParams) {
-    GrContext* context = inputTexture->getContext();
-    SkASSERT(context);
-    const GrCaps* caps = context->caps();
-
-    // Either it's a cache miss or the original wasn't cached to begin with.
-    GrSurfaceDesc rtDesc = inputTexture->desc();
-    rtDesc.fFlags =  rtDesc.fFlags | kRenderTarget_GrSurfaceFlag;
-    rtDesc.fWidth  = copyParams.fWidth;
-    rtDesc.fHeight = copyParams.fHeight;
-    rtDesc.fConfig = GrMakePixelConfigUncompressed(rtDesc.fConfig);
-
-    // If the config isn't renderable try converting to either A8 or an 32 bit config. Otherwise,
-    // fail.
-    if (!caps->isConfigRenderable(rtDesc.fConfig, false)) {
-        if (GrPixelConfigIsAlphaOnly(rtDesc.fConfig)) {
-            if (caps->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
-                rtDesc.fConfig = kAlpha_8_GrPixelConfig;
-            } else if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) {
-                rtDesc.fConfig = kSkia8888_GrPixelConfig;
-            } else {
-                return nullptr;
-            }
-        } else if (kRGB_GrColorComponentFlags ==
-                   (kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) {
-            if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) {
-                rtDesc.fConfig = kSkia8888_GrPixelConfig;
-            } else {
-                return nullptr;
-            }
-        } else {
-            return nullptr;
-        }
-    }
-
-    SkAutoTUnref<GrTexture> copy(context->textureProvider()->createTexture(rtDesc, true));
-    if (!copy) {
-        return nullptr;
-    }
-
-    GrPaint paint;
-
-    // If filtering is not desired then we want to ensure all texels in the resampled image are
-    // copies of texels from the original.
-    GrTextureParams params(SkShader::kClamp_TileMode, copyParams.fFilter);
-    paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params);
-
-    SkRect rect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight));
-    SkRect localRect = SkRect::MakeWH(1.f, 1.f);
-
-    SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(copy->asRenderTarget()));
-    if (!drawContext) {
-        return nullptr;
-    }
-
-    drawContext->drawNonAARectToRect(GrClip::WideOpen(), paint, SkMatrix::I(), rect, localRect);
-    return copy.detach();
-}
-
-static SkBitmap copy_on_cpu(const SkBitmap& bmp, const CopyParams& copyParams) {
-    SkBitmap stretched;
-    stretched.allocN32Pixels(copyParams.fWidth, copyParams.fHeight);
-    SkCanvas canvas(stretched);
-    SkPaint paint;
-    switch (copyParams.fFilter) {
-        case GrTextureParams::kNone_FilterMode:
-            paint.setFilterQuality(kNone_SkFilterQuality);
-            break;
-        case GrTextureParams::kBilerp_FilterMode:
-            paint.setFilterQuality(kLow_SkFilterQuality);
-            break;
-        case GrTextureParams::kMipMap_FilterMode:
-            paint.setFilterQuality(kMedium_SkFilterQuality);
-            break;
-    }
-    SkRect dstRect = SkRect::MakeWH(SkIntToScalar(copyParams.fWidth),
-                                    SkIntToScalar(copyParams.fHeight));
-    canvas.drawBitmapRect(bmp, dstRect, &paint);
-    return stretched;
-}
-
-GrTexture* GrTextureParamsAdjuster::refTextureForParams(GrContext* ctx,
-                                                        const GrTextureParams& params) {
-    CopyParams copyParams;
-    if (!ctx->getGpu()->makeCopyForTextureParams(this->width(), this->height(), params,
-                                                 &copyParams)) {
-        return this->refOriginalTexture(ctx);
-    }
-    GrUniqueKey copyKey;
-    this->makeCopyKey(copyParams, &copyKey);
-    if (copyKey.isValid()) {
-        GrTexture* result = ctx->textureProvider()->findAndRefTextureByUniqueKey(copyKey);
-        if (result) {
-            return result;
-        }
-    }
-
-    GrTexture* result = this->generateTextureForParams(ctx, copyParams);
-    if (!result) {
-        return nullptr;
-    }
-
-    if (copyKey.isValid()) {
-        ctx->textureProvider()->assignUniqueKeyToTexture(copyKey, result);
-        this->didCacheCopy(copyKey);
-    }
-    return result;
-}
-
-GrTexture* GrTextureParamsAdjuster::generateTextureForParams(GrContext* ctx,
-                                                             const CopyParams& copyParams) {
-    if (this->width() < ctx->caps()->minTextureSize() ||
-        this->height() < ctx->caps()->minTextureSize())
-    {
-        // we can't trust our ability to use HW to perform the stretch, so we request
-        // a raster instead, and perform the stretch on the CPU.
-        SkBitmap bitmap;
-        if (!this->getROBitmap(&bitmap)) {
-            return nullptr;
-        }
-        SkBitmap stretchedBmp = copy_on_cpu(bitmap, copyParams);
-        return GrUploadBitmapToTexture(ctx, bitmap);
-    } else {
-        SkAutoTUnref<GrTexture> original(this->refOriginalTexture(ctx));
-        if (!original) {
-            return nullptr;
-        }
-        return copy_on_gpu(original, copyParams);
-    }
-}
diff --git a/src/gpu/GrTextureParamsAdjuster.h b/src/gpu/GrTextureParamsAdjuster.h
deleted file mode 100644
index e1aa80e..0000000
--- a/src/gpu/GrTextureParamsAdjuster.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrTextureMaker_DEFINED
-#define GrTextureMaker_DEFINED
-
-#include "GrTextureParams.h"
-#include "GrResourceKey.h"
-
-class GrContext;
-class GrTexture;
-class GrTextureParams;
-class GrUniqueKey;
-class SkBitmap;
-
-/**
- * Different GPUs and API extensions have different requirements with respect to what texture
- * sampling parameters may be used with textures of various types. This class facilitates making
- * texture compatible with a given GrTextureParams. It abstracts the source of the original data
- * which may be an already existing texture, CPU pixels, a codec, ... so that various sources can
- * be used with common code that scales or copies the data to make it compatible with a
- * GrTextureParams.
- */
-class GrTextureParamsAdjuster {
-public:
-    struct CopyParams {
-        GrTextureParams::FilterMode fFilter;
-        int                         fWidth;
-        int                         fHeight;
-    };
-
-    GrTextureParamsAdjuster(int width, int height) : fWidth(width), fHeight(height) {}
-    virtual ~GrTextureParamsAdjuster() {}
-
-    int width() const { return fWidth; }
-    int height() const { return fHeight; }
-
-    /** Returns a texture that is safe for use with the params */
-    GrTexture* refTextureForParams(GrContext*, const GrTextureParams&);
-
-protected:
-    /**
-     *  Return the maker's "original" texture. It is the responsibility of the maker
-     *  to make this efficient ... if the texture is being generated, the maker must handle
-     *  caching it (if desired).
-     */
-    virtual GrTexture* refOriginalTexture(GrContext*) = 0;
-
-    /**
-     *  If we need to copy the maker's original texture, the maker is asked to return a key
-     *  that identifies its original + the CopyParms parameter. If the maker does not want to cache
-     *  the stretched version (e.g. the maker is volatile), this should simply return without
-     *  initializing the copyKey.
-     */
-    virtual void makeCopyKey(const CopyParams&, GrUniqueKey* copyKey) = 0;
-
-    /**
-     *  Return a new (uncached) texture that is the stretch of the maker's original.
-     *
-     *  The base-class handles general logic for this, and only needs access to the following
-     *  methods:
-     *  - onRefOriginalTexture()
-     *  - onGetROBitmap()
-     *
-     *  Subclass may override this if they can handle creating the texture more directly than
-     *  by copying.
-     */
-    virtual GrTexture* generateTextureForParams(GrContext*, const CopyParams&);
-
-    /**
-     *  If a stretched version of the texture is generated, it may be cached (assuming that
-     *  onMakeParamsKey() returns true). In that case, the maker is notified in case it
-     *  wants to note that for when the maker is destroyed.
-     */
-    virtual void didCacheCopy(const GrUniqueKey& copyKey) = 0;
-
-    /**
-     *  Some GPUs are unreliable w/ very small texture sizes. If we run into that case, this
-     *  method will be called (in service of onGenerateParamsTexture) to return a raster version
-     *  of the original texture.
-     */
-    virtual bool getROBitmap(SkBitmap*) = 0;
-
-    /** Helper for creating a key for a copy from an original key. */
-    static void MakeCopyKeyFromOrigKey(const GrUniqueKey& origKey,
-                                       const CopyParams& copyParams,
-                                       GrUniqueKey* copyKey) {
-        SkASSERT(!copyKey->isValid());
-        if (origKey.isValid()) {
-            static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
-            GrUniqueKey::Builder builder(copyKey, origKey, kDomain, 3);
-            builder[0] = copyParams.fFilter;
-            builder[1] = copyParams.fWidth;
-            builder[2] = copyParams.fHeight;
-        }
-    }
-
-private:
-    const int fWidth;
-    const int fHeight;
-};
-
-#endif
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 42922b3..d844496 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -5,13 +5,13 @@
  * found in the LICENSE file.
  */
 
+#include "GrTextureMaker.h"
 
 #include "SkGr.h"
 
 #include "GrCaps.h"
 #include "GrContext.h"
-#include "GrTextureParamsAdjuster.h"
-#include "GrGpuResourcePriv.h"
+#include "GrDrawContext.h"
 #include "GrXferProcessor.h"
 #include "GrYUVProvider.h"
 
@@ -48,17 +48,64 @@
     return desc;
 }
 
-void GrMakeKeyFromImageID(GrUniqueKey* key, uint32_t imageID, const SkIRect& imageBounds) {
-    SkASSERT(key);
-    SkASSERT(imageID);
-    SkASSERT(!imageBounds.isEmpty());
-    static const GrUniqueKey::Domain kImageIDDomain = GrUniqueKey::GenerateDomain();
-    GrUniqueKey::Builder builder(key, kImageIDDomain, 5);
+static void get_stretch(const GrCaps& caps, int width, int height,
+                        const GrTextureParams& params, SkGrStretch* stretch) {
+    stretch->fType = SkGrStretch::kNone_Type;
+    bool doStretch = false;
+    if (params.isTiled() && !caps.npotTextureTileSupport() &&
+        (!SkIsPow2(width) || !SkIsPow2(height))) {
+        doStretch = true;
+        stretch->fWidth = GrNextPow2(SkTMax(width, caps.minTextureSize()));
+        stretch->fHeight = GrNextPow2(SkTMax(height, caps.minTextureSize()));
+    } else if (width < caps.minTextureSize() || height < caps.minTextureSize()) {
+        // The small texture issues appear to be with tiling. Hence it seems ok to scale them
+        // up using the GPU. If issues persist we may need to CPU-stretch.
+        doStretch = true;
+        stretch->fWidth = SkTMax(width, caps.minTextureSize());
+        stretch->fHeight = SkTMax(height, caps.minTextureSize());
+    }
+    if (doStretch) {
+        switch (params.filterMode()) {
+            case GrTextureParams::kNone_FilterMode:
+                stretch->fType = SkGrStretch::kNearest_Type;
+                break;
+            case GrTextureParams::kBilerp_FilterMode:
+            case GrTextureParams::kMipMap_FilterMode:
+                stretch->fType = SkGrStretch::kBilerp_Type;
+                break;
+        }
+    } else {
+        stretch->fWidth = -1;
+        stretch->fHeight = -1;
+        stretch->fType = SkGrStretch::kNone_Type;
+    }
+}
+
+static void make_unstretched_key(GrUniqueKey* key, uint32_t imageID, const SkIRect& subset) {
+    SkASSERT(SkIsU16(subset.width()));
+    SkASSERT(SkIsU16(subset.height()));
+
+    static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
+    GrUniqueKey::Builder builder(key, kDomain, 4);
     builder[0] = imageID;
-    builder[1] = imageBounds.fLeft;
-    builder[2] = imageBounds.fTop;
-    builder[3] = imageBounds.fRight;
-    builder[4] = imageBounds.fBottom;
+    builder[1] = subset.x();
+    builder[2] = subset.y();
+    builder[3] = subset.width() | (subset.height() << 16);
+}
+
+void GrMakeKeyFromImageID(GrUniqueKey* key, uint32_t imageID, const SkIRect& subset,
+                          const GrCaps& caps, const GrTextureParams& params) {
+    SkGrStretch stretch;
+    get_stretch(caps, subset.width(), subset.height(), params, &stretch);
+    if (SkGrStretch::kNone_Type != stretch.fType) {
+        GrUniqueKey tmpKey;
+        make_unstretched_key(&tmpKey, imageID, subset);
+        if (!GrMakeStretchedKey(tmpKey, stretch, key)) {
+            *key = tmpKey;
+        }
+    } else {
+        make_unstretched_key(key, imageID, subset);
+    }
 }
 
 GrPixelConfig GrIsCompressedTextureDataSupported(GrContext* ctx, SkData* data,
@@ -103,19 +150,16 @@
     return kUnknown_GrPixelConfig;
 }
 
-//////////////////////////////////////////////////////////////////////////////
+/*  Fill out buffer with the compressed format Ganesh expects from a colortable
+ based bitmap. [palette (colortable) + indices].
 
-/**
- * Fill out buffer with the compressed format Ganesh expects from a colortable
- * based bitmap. [palette (colortable) + indices].
- *
- * At the moment Ganesh only supports 8bit version. If Ganesh allowed we others
- * we could detect that the colortable.count is <= 16, and then repack the
- * indices as nibbles to save RAM, but it would take more time (i.e. a lot
- * slower than memcpy), so skipping that for now.
- *
- * Ganesh wants a full 256 palette entry, even though Skia's ctable is only as big
- * as the colortable.count says it is.
+ At the moment Ganesh only supports 8bit version. If Ganesh allowed we others
+ we could detect that the colortable.count is <= 16, and then repack the
+ indices as nibbles to save RAM, but it would take more time (i.e. a lot
+ slower than memcpy), so skipping that for now.
+
+ Ganesh wants a full 256 palette entry, even though Skia's ctable is only as big
+ as the colortable.count says it is.
  */
 static void build_index8_data(void* buffer, const SkBitmap& bitmap) {
     SkASSERT(kIndex_8_SkColorType == bitmap.colorType());
@@ -163,7 +207,130 @@
     }
 }
 
-/**
+////////////////////////////////////////////////////////////////////////////////
+
+
+bool GrMakeStretchedKey(const GrUniqueKey& origKey, const SkGrStretch& stretch,
+                        GrUniqueKey* stretchedKey) {
+    if (origKey.isValid() && SkGrStretch::kNone_Type != stretch.fType) {
+        uint32_t width = SkToU16(stretch.fWidth);
+        uint32_t height = SkToU16(stretch.fHeight);
+        static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
+        GrUniqueKey::Builder builder(stretchedKey, origKey, kDomain, 2);
+        builder[0] = stretch.fType;
+        builder[1] = width | (height << 16);
+        builder.finish();
+        return true;
+    }
+    SkASSERT(!stretchedKey->isValid());
+    return false;
+}
+
+namespace {
+
+// When the SkPixelRef genID changes, invalidate a corresponding GrResource described by key.
+class BitmapInvalidator : public SkPixelRef::GenIDChangeListener {
+public:
+    explicit BitmapInvalidator(const GrUniqueKey& key) : fMsg(key) {}
+private:
+    GrUniqueKeyInvalidatedMessage fMsg;
+
+    void onChange() override {
+        SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
+    }
+};
+
+}  // namespace
+
+
+GrTexture* GrCreateTextureForPixels(GrContext* ctx,
+                                    const GrUniqueKey& optionalKey,
+                                    GrSurfaceDesc desc,
+                                    SkPixelRef* pixelRefForInvalidationNotification,
+                                    const void* pixels,
+                                    size_t rowBytes) {
+    GrTexture* result = ctx->textureProvider()->createTexture(desc, true, pixels, rowBytes);
+    if (result && optionalKey.isValid()) {
+        if (pixelRefForInvalidationNotification) {
+            BitmapInvalidator* listener = new BitmapInvalidator(optionalKey);
+            pixelRefForInvalidationNotification->addGenIDChangeListener(listener);
+        }
+        ctx->textureProvider()->assignUniqueKeyToTexture(optionalKey, result);
+    }
+    return result;
+}
+
+// creates a new texture that is the input texture scaled up. If optionalKey is valid it will be
+// set on the new texture. stretch controls whether the scaling is done using nearest or bilerp
+// filtering and the size to stretch the texture to.
+GrTexture* stretch_texture(GrTexture* inputTexture, const SkGrStretch& stretch,
+                           SkPixelRef* pixelRef,
+                           const GrUniqueKey& optionalKey) {
+    SkASSERT(SkGrStretch::kNone_Type != stretch.fType);
+
+    GrContext* context = inputTexture->getContext();
+    SkASSERT(context);
+    const GrCaps* caps = context->caps();
+
+    // Either it's a cache miss or the original wasn't cached to begin with.
+    GrSurfaceDesc rtDesc = inputTexture->desc();
+    rtDesc.fFlags =  rtDesc.fFlags | kRenderTarget_GrSurfaceFlag;
+    rtDesc.fWidth  = stretch.fWidth;
+    rtDesc.fHeight = stretch.fHeight;
+    rtDesc.fConfig = GrMakePixelConfigUncompressed(rtDesc.fConfig);
+
+    // If the config isn't renderable try converting to either A8 or an 32 bit config. Otherwise,
+    // fail.
+    if (!caps->isConfigRenderable(rtDesc.fConfig, false)) {
+        if (GrPixelConfigIsAlphaOnly(rtDesc.fConfig)) {
+            if (caps->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
+                rtDesc.fConfig = kAlpha_8_GrPixelConfig;
+            } else if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) {
+                rtDesc.fConfig = kSkia8888_GrPixelConfig;
+            } else {
+                return nullptr;
+            }
+        } else if (kRGB_GrColorComponentFlags ==
+                   (kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) {
+            if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) {
+                rtDesc.fConfig = kSkia8888_GrPixelConfig;
+            } else {
+                return nullptr;
+            }
+        } else {
+            return nullptr;
+        }
+    }
+
+    SkAutoTUnref<GrTexture> stretched(GrCreateTextureForPixels(context, optionalKey, rtDesc,
+                                                               pixelRef, nullptr,0));
+    if (!stretched) {
+        return nullptr;
+    }
+    GrPaint paint;
+
+    // If filtering is not desired then we want to ensure all texels in the resampled image are
+    // copies of texels from the original.
+    GrTextureParams params(SkShader::kClamp_TileMode,
+                           SkGrStretch::kBilerp_Type == stretch.fType ?
+                              GrTextureParams::kBilerp_FilterMode :
+                              GrTextureParams::kNone_FilterMode);
+    paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params);
+
+    SkRect rect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight));
+    SkRect localRect = SkRect::MakeWH(1.f, 1.f);
+
+    SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(stretched->asRenderTarget()));
+    if (!drawContext) {
+        return nullptr;
+    }
+
+    drawContext->drawNonAARectToRect(GrClip::WideOpen(), paint, SkMatrix::I(), rect, localRect);
+
+    return stretched.detach();
+}
+
+/*
  *  Once we have made SkImages handle all lazy/deferred/generated content, the YUV apis will
  *  be gone from SkPixelRef, and we can remove this subclass entirely.
  */
@@ -183,22 +350,33 @@
     }
 };
 
-static GrTexture* create_texture_from_yuv(GrContext* ctx, const SkBitmap& bm,
-                                          const GrSurfaceDesc& desc) {
+static GrTexture* load_yuv_texture(GrContext* ctx, const GrUniqueKey& optionalKey,
+                                   const SkBitmap& bm, const GrSurfaceDesc& desc) {
     // Subsets are not supported, the whole pixelRef is loaded when using YUV decoding
     SkPixelRef* pixelRef = bm.pixelRef();
     if ((nullptr == pixelRef) ||
-        (pixelRef->info().width() != bm.info().width()) ||
+        (pixelRef->info().width()  != bm.info().width()) ||
         (pixelRef->info().height() != bm.info().height())) {
         return nullptr;
     }
 
+    const bool useCache = optionalKey.isValid();
     PixelRef_GrYUVProvider provider(pixelRef);
+    GrTexture* texture = provider.refAsTexture(ctx, desc, useCache);
+    if (!texture) {
+        return nullptr;
+    }
 
-    return provider.refAsTexture(ctx, desc, !bm.isVolatile());
+    if (useCache) {
+        BitmapInvalidator* listener = new BitmapInvalidator(optionalKey);
+        pixelRef->addGenIDChangeListener(listener);
+        ctx->textureProvider()->assignUniqueKeyToTexture(optionalKey, texture);
+    }
+    return texture;
 }
 
-static GrTexture* load_etc1_texture(GrContext* ctx, const SkBitmap &bm, GrSurfaceDesc desc) {
+static GrTexture* load_etc1_texture(GrContext* ctx, const GrUniqueKey& optionalKey,
+                                    const SkBitmap &bm, GrSurfaceDesc desc) {
     SkAutoTUnref<SkData> data(bm.pixelRef()->refEncodedData());
     if (!data) {
         return nullptr;
@@ -211,19 +389,19 @@
         return nullptr;
     }
 
-    return ctx->textureProvider()->createTexture(desc, true, startOfTexData, 0);
+    return GrCreateTextureForPixels(ctx, optionalKey, desc, bm.pixelRef(), startOfTexData, 0);
 }
 
-GrTexture* GrUploadBitmapToTexture(GrContext* ctx, const SkBitmap& bmp) {
-    SkASSERT(!bmp.getTexture());
-
-    if (bmp.width() < ctx->caps()->minTextureSize() ||
-        bmp.height() < ctx->caps()->minTextureSize()) {
+static GrTexture* create_unstretched_bitmap_texture(GrContext* ctx,
+                                                    const SkBitmap& origBitmap,
+                                                    const GrUniqueKey& optionalKey) {
+    if (origBitmap.width() < ctx->caps()->minTextureSize() ||
+        origBitmap.height() < ctx->caps()->minTextureSize()) {
         return nullptr;
     }
-
     SkBitmap tmpBitmap;
-    const SkBitmap* bitmap = &bmp;
+
+    const SkBitmap* bitmap = &origBitmap;
 
     GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bitmap->info());
     const GrCaps* caps = ctx->caps();
@@ -233,14 +411,14 @@
             size_t imageSize = GrCompressedFormatDataSize(kIndex_8_GrPixelConfig,
                                                           bitmap->width(), bitmap->height());
             SkAutoMalloc storage(imageSize);
-            build_index8_data(storage.get(), bmp);
+            build_index8_data(storage.get(), origBitmap);
 
             // our compressed data will be trimmed, so pass width() for its
             // "rowBytes", since they are the same now.
-            return ctx->textureProvider()->createTexture(desc, true, storage.get(),
-                                                         bitmap->width());
+            return GrCreateTextureForPixels(ctx, optionalKey, desc, origBitmap.pixelRef(),
+                                            storage.get(), bitmap->width());
         } else {
-            bmp.copyTo(&tmpBitmap, kN32_SkColorType);
+            origBitmap.copyTo(&tmpBitmap, kN32_SkColorType);
             // now bitmap points to our temp, which has been promoted to 32bits
             bitmap = &tmpBitmap;
             desc.fConfig = SkImageInfo2GrPixelConfig(bitmap->info());
@@ -250,16 +428,13 @@
         // compressed data on 'refEncodedData' and upload it. Probably not good, since if
         // the bitmap has available pixels, then they might not be what the decompressed
         // data is.
-
-        // Really?? We aren't doing this with YUV.
-
-        GrTexture *texture = load_etc1_texture(ctx, *bitmap, desc);
+        GrTexture *texture = load_etc1_texture(ctx, optionalKey, *bitmap, desc);
         if (texture) {
             return texture;
         }
     }
 
-    GrTexture *texture = create_texture_from_yuv(ctx, *bitmap, desc);
+    GrTexture *texture = load_yuv_texture(ctx, optionalKey, *bitmap, desc);
     if (texture) {
         return texture;
     }
@@ -269,90 +444,83 @@
         return nullptr;
     }
 
-    return ctx->textureProvider()->createTexture(desc, true, bitmap->getPixels(),
-                                                 bitmap->rowBytes());
+    return GrCreateTextureForPixels(ctx, optionalKey, desc, origBitmap.pixelRef(),
+                                    bitmap->getPixels(), bitmap->rowBytes());
 }
 
+static SkBitmap stretch_on_cpu(const SkBitmap& bmp, const SkGrStretch& stretch) {
+    SkBitmap stretched;
+    stretched.allocN32Pixels(stretch.fWidth, stretch.fHeight);
+    SkCanvas canvas(stretched);
+    SkPaint paint;
+    switch (stretch.fType) {
+        case SkGrStretch::kNearest_Type:
+            paint.setFilterQuality(kNone_SkFilterQuality);
+            break;
+        case SkGrStretch::kBilerp_Type:
+            paint.setFilterQuality(kLow_SkFilterQuality);
+            break;
+        case SkGrStretch::kNone_Type:
+            SkDEBUGFAIL("Shouldn't get here.");
+            break;
+    }
+    SkRect dstRect = SkRect::MakeWH(SkIntToScalar(stretch.fWidth), SkIntToScalar(stretch.fHeight));
+    canvas.drawBitmapRect(bmp, dstRect, &paint);
+    return stretched;
+}
 
-////////////////////////////////////////////////////////////////////////////////
-
-class Bitmap_GrTextureParamsAdjuster : public GrTextureParamsAdjuster {
+class Bitmap_GrTextureMaker : public GrTextureMaker {
 public:
-    Bitmap_GrTextureParamsAdjuster(const SkBitmap& bitmap)
+    Bitmap_GrTextureMaker(const SkBitmap& bitmap)
         : INHERITED(bitmap.width(), bitmap.height())
         , fBitmap(bitmap)
-    {
-        if (!bitmap.isVolatile()) {
-            SkIPoint origin = bitmap.pixelRefOrigin();
-            SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, bitmap.width(),
-                                               bitmap.height());
-            GrMakeKeyFromImageID(&fOriginalKey, bitmap.pixelRef()->getGenerationID(), subset);
-        }
-    }
+    {}
 
 protected:
-    GrTexture* refOriginalTexture(GrContext* ctx) {
+    GrTexture* onRefUnstretchedTexture(GrContext* ctx) override {
         GrTexture* tex = fBitmap.getTexture();
         if (tex) {
             return SkRef(tex);
         }
 
-        if (fOriginalKey.isValid()) {
-            tex = ctx->textureProvider()->findAndRefTextureByUniqueKey(fOriginalKey);
-            if (tex) {
-                return tex;
-            }
-        }
+        GrUniqueKey unstretchedKey;
+        make_unstretched_key(&unstretchedKey, fBitmap.getGenerationID(), fBitmap.getSubset());
 
-        tex = GrUploadBitmapToTexture(ctx, fBitmap);
-        if (tex) {
-            tex->resourcePriv().setUniqueKey(fOriginalKey);
-            InstallInvalidator(fOriginalKey, fBitmap.pixelRef());
+        GrTexture* result = ctx->textureProvider()->findAndRefTextureByUniqueKey(unstretchedKey);
+        if (result) {
+            return result;
         }
-        return tex;
+        return create_unstretched_bitmap_texture(ctx, fBitmap, unstretchedKey);
     }
 
-    void makeCopyKey(const CopyParams& copyParams, GrUniqueKey* copyKey) override {
-        if (fOriginalKey.isValid()) {
-            MakeCopyKeyFromOrigKey(fOriginalKey, copyParams, copyKey);
+    bool onMakeStretchedKey(const SkGrStretch& stretch, GrUniqueKey* stretchedKey) override {
+        if (fBitmap.isVolatile()) {
+            return false;
         }
+
+        GrUniqueKey unstretchedKey;
+        make_unstretched_key(&unstretchedKey, fBitmap.getGenerationID(), fBitmap.getSubset());
+        return GrMakeStretchedKey(unstretchedKey, stretch, stretchedKey);
     }
 
-    void didCacheCopy(const GrUniqueKey& copyKey) override {
-        InstallInvalidator(copyKey, fBitmap.pixelRef());
+    void onNotifyStretchCached(const GrUniqueKey& stretchedKey) override {
+        fBitmap.pixelRef()->addGenIDChangeListener(new BitmapInvalidator(stretchedKey));
     }
 
-    bool getROBitmap(SkBitmap* bitmap) override {
-        SkASSERT(!fBitmap.getTexture());
+    bool onGetROBitmap(SkBitmap* bitmap) override {
         *bitmap = fBitmap;
         return true;
     }
 
 private:
-    static void InstallInvalidator(const GrUniqueKey& key, SkPixelRef* pixelRef) {
-        class Invalidator : public SkPixelRef::GenIDChangeListener {
-        public:
-            explicit Invalidator(const GrUniqueKey& key) : fMsg(key) {}
-        private:
-            GrUniqueKeyInvalidatedMessage fMsg;
+    const SkBitmap fBitmap;
 
-            void onChange() override {
-                SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg);
-            }
-        };
-        Invalidator* listener = new Invalidator(key);
-        pixelRef->addGenIDChangeListener(listener);
-    }
-
-    const SkBitmap  fBitmap;
-    GrUniqueKey     fOriginalKey;
-
-    typedef GrTextureParamsAdjuster INHERITED;
+    typedef GrTextureMaker INHERITED;
 };
 
 GrTexture* GrRefCachedBitmapTexture(GrContext* ctx, const SkBitmap& bitmap,
                                     const GrTextureParams& params) {
-    return Bitmap_GrTextureParamsAdjuster(bitmap).refTextureForParams(ctx, params);
+    return Bitmap_GrTextureMaker(bitmap).refCachedTexture(ctx, params);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -424,6 +592,7 @@
     return true;
 }
 
+
 ////////////////////////////////////////////////////////////////////////////////////////////////
 
 static inline bool blend_requires_shader(const SkXfermode::Mode mode, bool primitiveIsSrc) {
@@ -673,3 +842,54 @@
     }
     return textureFilterMode;
 }
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+GrTexture* GrTextureMaker::refCachedTexture(GrContext* ctx, const GrTextureParams& params) {
+    SkGrStretch stretch;
+    get_stretch(*ctx->caps(), this->width(), this->height(), params, &stretch);
+
+    if (SkGrStretch::kNone_Type == stretch.fType) {
+        return this->onRefUnstretchedTexture(ctx);
+    }
+
+    GrUniqueKey stretchedKey;
+    if (this->onMakeStretchedKey(stretch, &stretchedKey)) {
+        GrTexture* result = ctx->textureProvider()->findAndRefTextureByUniqueKey(stretchedKey);
+        if (result) {
+            return result;
+        }
+    }
+
+    GrTexture* result = this->onGenerateStretchedTexture(ctx, stretch);
+    if (!result) {
+        return nullptr;
+    }
+
+    if (stretchedKey.isValid()) {
+        ctx->textureProvider()->assignUniqueKeyToTexture(stretchedKey, result);
+        this->onNotifyStretchCached(stretchedKey);
+    }
+    return result;
+}
+
+GrTexture* GrTextureMaker::onGenerateStretchedTexture(GrContext* ctx, const SkGrStretch& stretch) {
+    if (this->width() < ctx->caps()->minTextureSize() ||
+        this->height() < ctx->caps()->minTextureSize())
+    {
+        // we can't trust our ability to use HW to perform the stretch, so we request
+        // a raster instead, and perform the stretch on the CPU.
+        SkBitmap bitmap;
+        if (!this->onGetROBitmap(&bitmap)) {
+            return nullptr;
+        }
+        SkBitmap stretchedBmp = stretch_on_cpu(bitmap, stretch);
+        return create_unstretched_bitmap_texture(ctx, stretchedBmp, GrUniqueKey());
+    } else {
+        SkAutoTUnref<GrTexture> unstretched(this->onRefUnstretchedTexture(ctx));
+        if (!unstretched) {
+            return nullptr;
+        }
+        return stretch_texture(unstretched, stretch, nullptr, GrUniqueKey());
+    }
+}
diff --git a/src/gpu/SkGrPriv.h b/src/gpu/SkGrPriv.h
index 5935309..c92e580 100644
--- a/src/gpu/SkGrPriv.h
+++ b/src/gpu/SkGrPriv.h
@@ -26,18 +26,34 @@
 class SkPixelRef;
 struct SkIRect;
 
+struct SkGrStretch {
+    enum Type {
+        kNone_Type,
+        kBilerp_Type,
+        kNearest_Type
+    } fType;
+    int fWidth;
+    int fHeight;
+};
+
 /**
  *  Our key includes the offset, width, and height so that bitmaps created by extractSubset()
  *  are unique.
  *
- *  The imageID is in the shared namespace (see SkNextID::ImageID())
+ *  The imageID is in the shared namespace (see SkNextID::ImageID()
  *      - SkBitmap/SkPixelRef
  *      - SkImage
  *      - SkImageGenerator
  *
  *  Note: width/height must fit in 16bits for this impl.
  */
-void GrMakeKeyFromImageID(GrUniqueKey* key, uint32_t imageID, const SkIRect& imageBounds);
+void GrMakeKeyFromImageID(GrUniqueKey* key, uint32_t imageID, const SkIRect& imageBounds,
+                          const GrCaps&, const GrTextureParams&);
+
+/**
+ *  Given an "unstretched" key, and a stretch rec, produce a stretched key.
+ */
+bool GrMakeStretchedKey(const GrUniqueKey& origKey, const SkGrStretch&, GrUniqueKey* stretchedKey);
 
 /** Converts an SkPaint to a GrPaint for a given GrContext. The matrix is required in order
     to convert the SkShader (if any) on the SkPaint. The primitive itself has no color. */
@@ -87,24 +103,21 @@
 bool GrPixelConfig2ColorAndProfileType(GrPixelConfig, SkColorType*, SkColorProfileType*);
 
 /**
- *  If the compressed data in the SkData is supported (as a texture format, this returns
- *  the pixel-config that should be used, and sets outStartOfDataToUpload to the ptr into
- *  the data where the actual raw data starts (skipping any header bytes).
- *
- *  If the compressed data is not supported, this returns kUnknown_GrPixelConfig, and
- *  ignores outStartOfDataToUpload.
- */
+*  If the compressed data in the SkData is supported (as a texture format, this returns
+*  the pixel-config that should be used, and sets outStartOfDataToUpload to the ptr into
+*  the data where the actual raw data starts (skipping any header bytes).
+*
+*  If the compressed data is not supported, this returns kUnknown_GrPixelConfig, and
+*  ignores outStartOfDataToUpload.
+*/
 GrPixelConfig GrIsCompressedTextureDataSupported(GrContext* ctx, SkData* data,
                                                  int expectedW, int expectedH,
                                                  const void** outStartOfDataToUpload);
 
+GrTexture* GrCreateTextureForPixels(GrContext*, const GrUniqueKey& optionalKey, GrSurfaceDesc,
+                                    SkPixelRef* pixelRefForInvalidationNotificationOrNull,
+                                    const void* pixels, size_t rowBytesOrZero);
 
-/**
- * Creates a new texture for the bitmap. Does not concern itself with cache keys or texture params.
- * The bitmap must have CPU-accessible pixels. Attempts to take advantage of faster paths for
- * compressed textures and yuv planes.
- */
-GrTexture* GrUploadBitmapToTexture(GrContext*, const SkBitmap&);
 
 //////////////////////////////////////////////////////////////////////////////
 
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 039bbdd..67d1b8c 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -10,7 +10,7 @@
 #include "GrCaps.h"
 #include "GrContext.h"
 #include "GrDrawContext.h"
-#include "GrTextureParamsAdjuster.h"
+#include "GrTextureMaker.h"
 #include "effects/GrYUVtoRGBEffect.h"
 #include "SkCanvas.h"
 #include "SkGpuDevice.h"
@@ -64,51 +64,56 @@
     return true;
 }
 
-static void make_raw_texture_stretched_key(uint32_t imageID,
-                                           const GrTextureParamsAdjuster::CopyParams& params,
+static void make_raw_texture_stretched_key(uint32_t imageID, const SkGrStretch& stretch,
                                            GrUniqueKey* stretchedKey) {
+    SkASSERT(SkGrStretch::kNone_Type != stretch.fType);
+
+    uint32_t width = SkToU16(stretch.fWidth);
+    uint32_t height = SkToU16(stretch.fHeight);
+
     static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
-    GrUniqueKey::Builder builder(stretchedKey, kDomain, 4);
+    GrUniqueKey::Builder builder(stretchedKey, kDomain, 3);
     builder[0] = imageID;
-    builder[1] = params.fFilter;
-    builder[2] = params.fWidth;
-    builder[3] = params.fHeight;
+    builder[1] = stretch.fType;
+    builder[2] = width | (height << 16);
+    builder.finish();
 }
 
-class Texture_GrTextureParamsAdjuster : public GrTextureParamsAdjuster {
+class Texture_GrTextureMaker : public GrTextureMaker {
 public:
-    Texture_GrTextureParamsAdjuster(const SkImage* image, GrTexture* unstretched)
+    Texture_GrTextureMaker(const SkImage* image, GrTexture* unstretched)
         : INHERITED(image->width(), image->height())
         , fImage(image)
-        , fOriginal(unstretched)
+        , fUnstretched(unstretched)
     {}
 
 protected:
-    GrTexture* refOriginalTexture(GrContext* ctx) override {
-        return SkRef(fOriginal);
+    GrTexture* onRefUnstretchedTexture(GrContext* ctx) override {
+        return SkRef(fUnstretched);
     }
 
-    void makeCopyKey(const CopyParams& copyParams, GrUniqueKey* copyKey) override {
-        make_raw_texture_stretched_key(fImage->uniqueID(), copyParams, copyKey);
+    bool onMakeStretchedKey(const SkGrStretch& stretch, GrUniqueKey* stretchedKey) override {
+        make_raw_texture_stretched_key(fImage->uniqueID(), stretch, stretchedKey);
+        return stretchedKey->isValid();
     }
 
-    void didCacheCopy(const GrUniqueKey& copyKey) override {
+    void onNotifyStretchCached(const GrUniqueKey& stretchedKey) override {
         as_IB(fImage)->notifyAddedToCache();
     }
 
-    bool getROBitmap(SkBitmap* bitmap) override {
+    bool onGetROBitmap(SkBitmap* bitmap) override {
         return as_IB(fImage)->getROPixels(bitmap);
     }
 
 private:
     const SkImage*  fImage;
-    GrTexture*      fOriginal;
+    GrTexture*      fUnstretched;
 
-    typedef GrTextureParamsAdjuster INHERITED;
+    typedef GrTextureMaker INHERITED;
 };
 
 GrTexture* SkImage_Gpu::asTextureRef(GrContext* ctx, const GrTextureParams& params) const {
-    return Texture_GrTextureParamsAdjuster(this, fTexture).refTextureForParams(ctx, params);
+    return Texture_GrTextureMaker(this, fTexture).refCachedTexture(ctx, params);
 }
 
 bool SkImage_Gpu::isOpaque() const {
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
index 9c90ba9..b78818d 100644
--- a/src/image/SkImage_Raster.cpp
+++ b/src/image/SkImage_Raster.cpp
@@ -173,6 +173,20 @@
         return nullptr;
     }
 
+    // textures (at least the texture-key) only support 16bit dimensions, so abort early
+    // if we're too big.
+    if (fBitmap.width() > 0xFFFF || fBitmap.height() > 0xFFFF) {
+        return nullptr;
+    }
+
+    GrUniqueKey key;
+    GrMakeKeyFromImageID(&key, fBitmap.getGenerationID(),
+                         SkIRect::MakeWH(fBitmap.width(), fBitmap.height()),
+                         *ctx->caps(), params);
+
+    if (GrTexture* tex = ctx->textureProvider()->findAndRefTextureByUniqueKey(key)) {
+        return tex;
+    }
     return GrRefCachedBitmapTexture(ctx, fBitmap, params);
 #endif