Revert "Remove copies for wrap modes in GrTextureProducer."

This reverts commit 7fba244ea9d446eb8e4b36f9c3acc161b3db6dcf.

Reason for revert: See if this is blocking the Chrome roll

Original change's description:
> Remove copies for wrap modes in GrTextureProducer.
> 
> GrTextureEffect now supports implementing wrap modes in shaders
> for subsets and texture types with HW sampling restrictions.
> 
> Change-Id: I5c93ade044465e13c5f56f7437fbbe288db0a8a8
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/271056
> Reviewed-by: Greg Daniel <egdaniel@google.com>
> Commit-Queue: Brian Salomon <bsalomon@google.com>

TBR=egdaniel@google.com,bsalomon@google.com

Change-Id: I14397bec8ff4ba165c28faa8f44497f47d865862
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/272522
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/gm/image_pict.cpp b/gm/image_pict.cpp
index ee610f3..28e0bac 100644
--- a/gm/image_pict.cpp
+++ b/gm/image_pict.cpp
@@ -273,8 +273,8 @@
     }
 
     static void draw_as_tex(SkCanvas* canvas, SkImage* image, SkScalar x, SkScalar y) {
-        GrSurfaceProxyView view =
-                as_IB(image)->refView(canvas->getGrContext(), GrSamplerState::Filter::kBilerp);
+        GrSurfaceProxyView view = as_IB(image)->refView(canvas->getGrContext(),
+                                                        GrSamplerState::Filter::kBilerp, nullptr);
         if (!view) {
             // show placeholder if we have no texture
             SkPaint paint;
diff --git a/src/gpu/GrBitmapTextureMaker.cpp b/src/gpu/GrBitmapTextureMaker.cpp
index 957aebb..e3a9eb5 100644
--- a/src/gpu/GrBitmapTextureMaker.cpp
+++ b/src/gpu/GrBitmapTextureMaker.cpp
@@ -122,14 +122,13 @@
     return {};
 }
 
-void GrBitmapTextureMaker::makeMipMappedKey(GrUniqueKey* mipMappedKey) {
+void GrBitmapTextureMaker::makeCopyKey(const CopyParams& copyParams, GrUniqueKey* copyKey) {
     // Destination color space is irrelevant - we always upload the bitmap's contents as-is
     if (fOriginalKey.isValid()) {
-        MakeMipMappedKeyFromOriginalKey(fOriginalKey, mipMappedKey);
+        MakeCopyKeyFromOrigKey(fOriginalKey, copyParams, copyKey);
     }
 }
 
-void GrBitmapTextureMaker::didCacheMipMappedCopy(const GrUniqueKey& mipMappedKey,
-                                                 uint32_t contextUniqueID) {
-    GrInstallBitmapUniqueKeyInvalidator(mipMappedKey, contextUniqueID, fBitmap.pixelRef());
+void GrBitmapTextureMaker::didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) {
+    GrInstallBitmapUniqueKeyInvalidator(copyKey, contextUniqueID, fBitmap.pixelRef());
 }
diff --git a/src/gpu/GrBitmapTextureMaker.h b/src/gpu/GrBitmapTextureMaker.h
index 72475ca..1090a75 100644
--- a/src/gpu/GrBitmapTextureMaker.h
+++ b/src/gpu/GrBitmapTextureMaker.h
@@ -26,8 +26,8 @@
     GrSurfaceProxyView refOriginalTextureProxyView(bool willBeMipped,
                                                    AllowedTexGenType onlyIfFast) override;
 
-    void makeMipMappedKey(GrUniqueKey* mipMappedKey) override;
-    void didCacheMipMappedCopy(const GrUniqueKey& mipMappedKey, uint32_t contextUniqueID) override;
+    void makeCopyKey(const CopyParams& copyParams, GrUniqueKey* copyKey) override;
+    void didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) override;
 
     const SkBitmap     fBitmap;
     const SkBackingFit fFit;
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 0eca489..65829eb 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -45,14 +45,61 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-bool GrGpu::IsACopyNeededForMips(const GrCaps* caps, const GrTextureProxy* texProxy,
-                                 GrSamplerState::Filter filter) {
-    SkASSERT(texProxy);
-    if (filter != GrSamplerState::Filter::kMipMap || texProxy->mipMapped() == GrMipMapped::kYes ||
-        !caps->mipMapSupport()) {
-        return false;
+bool GrGpu::IsACopyNeededForRepeatWrapMode(const GrCaps* caps,
+                                           GrTextureProxy* texProxy,
+                                           SkISize dimensions,
+                                           GrSamplerState::Filter filter,
+                                           GrTextureProducer::CopyParams* copyParams,
+                                           SkScalar scaleAdjust[2]) {
+    if (!caps->npotTextureTileSupport() &&
+        (!SkIsPow2(dimensions.width()) || !SkIsPow2(dimensions.height()))) {
+        SkASSERT(scaleAdjust);
+        copyParams->fDimensions = {SkNextPow2(dimensions.width()), SkNextPow2(dimensions.height())};
+        SkASSERT(scaleAdjust);
+        scaleAdjust[0] = ((SkScalar)copyParams->fDimensions.width()) / dimensions.width();
+        scaleAdjust[1] = ((SkScalar)copyParams->fDimensions.height()) / dimensions.height();
+        switch (filter) {
+        case GrSamplerState::Filter::kNearest:
+            copyParams->fFilter = GrSamplerState::Filter::kNearest;
+            break;
+        case GrSamplerState::Filter::kBilerp:
+        case GrSamplerState::Filter::kMipMap:
+            // We are only ever scaling up so no reason to ever indicate kMipMap.
+            copyParams->fFilter = GrSamplerState::Filter::kBilerp;
+            break;
+        }
+        return true;
     }
-    return SkMipMap::ComputeLevelCount(texProxy->width(), texProxy->height()) > 0;
+
+    if (texProxy) {
+        // If the texture format itself doesn't support repeat wrap mode or mipmapping (and
+        // those capabilities are required) force a copy.
+        if (texProxy->hasRestrictedSampling()) {
+            copyParams->fFilter = GrSamplerState::Filter::kNearest;
+            copyParams->fDimensions = texProxy->dimensions();
+            return true;
+        }
+    }
+
+    return false;
+}
+
+bool GrGpu::IsACopyNeededForMips(const GrCaps* caps, const GrTextureProxy* texProxy,
+                                 GrSamplerState::Filter filter,
+                                 GrTextureProducer::CopyParams* copyParams) {
+    SkASSERT(texProxy);
+    int mipCount = SkMipMap::ComputeLevelCount(texProxy->width(), texProxy->height());
+    bool willNeedMips = GrSamplerState::Filter::kMipMap == filter && caps->mipMapSupport() &&
+            mipCount;
+    // If the texture format itself doesn't support mipmapping (and those capabilities are required)
+    // force a copy.
+    if (willNeedMips && texProxy->mipMapped() == GrMipMapped::kNo) {
+        copyParams->fFilter = GrSamplerState::Filter::kNearest;
+        copyParams->fDimensions = texProxy->dimensions();
+        return true;
+    }
+
+    return false;
 }
 
 static bool validate_texel_levels(SkISize dimensions, GrColorType texelColorType,
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 6969d41..5551e6e 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -594,12 +594,22 @@
     virtual GrStencilAttachment* createStencilAttachmentForRenderTarget(
             const GrRenderTarget*, int width, int height, int numStencilSamples) = 0;
 
+    // Determines whether a texture will need to be rescaled in order to be used with the
+    // GrSamplerState.
+    static bool IsACopyNeededForRepeatWrapMode(const GrCaps*,
+                                               GrTextureProxy* texProxy,
+                                               SkISize dimensions,
+                                               GrSamplerState::Filter,
+                                               GrTextureProducer::CopyParams*,
+                                               SkScalar scaleAdjust[2]);
+
     // Determines whether a texture will need to be copied because the draw requires mips but the
     // texutre doesn't have any. This call should be only checked if IsACopyNeededForTextureParams
     // fails. If the previous call succeeds, then a copy should be done using those params and the
     // mip mapping requirements will be handled there.
     static bool IsACopyNeededForMips(const GrCaps* caps, const GrTextureProxy* texProxy,
-                                     GrSamplerState::Filter filter);
+                                     GrSamplerState::Filter filter,
+                                     GrTextureProducer::CopyParams* copyParams);
 
     void handleDirtyContext() {
         if (fResetBits) {
diff --git a/src/gpu/GrImageTextureMaker.cpp b/src/gpu/GrImageTextureMaker.cpp
index f531fed..8361f6a 100644
--- a/src/gpu/GrImageTextureMaker.cpp
+++ b/src/gpu/GrImageTextureMaker.cpp
@@ -39,14 +39,15 @@
                                         onlyIfFast);
 }
 
-void GrImageTextureMaker::makeMipMappedKey(GrUniqueKey* mipMappedKey) {
+void GrImageTextureMaker::makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey) {
     if (fOriginalKey.isValid() && SkImage::kAllow_CachingHint == fCachingHint) {
         GrUniqueKey cacheKey;
         fImage->makeCacheKeyFromOrigKey(fOriginalKey, &cacheKey);
-        MakeMipMappedKeyFromOriginalKey(cacheKey, mipMappedKey);
+        MakeCopyKeyFromOrigKey(cacheKey, stretch, paramsCopyKey);
     }
 }
 
+
 /////////////////////////////////////////////////////////////////////////////////////////////////
 
 GrYUVAImageTextureMaker::GrYUVAImageTextureMaker(GrContext* context, const SkImage* client,
@@ -74,13 +75,13 @@
     }
 }
 
-void GrYUVAImageTextureMaker::makeMipMappedKey(GrUniqueKey* mipMappedKey) {
+void GrYUVAImageTextureMaker::makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey) {
     // TODO: Do we ever want to disable caching?
     if (fOriginalKey.isValid()) {
         GrUniqueKey cacheKey;
         static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
         GrUniqueKey::Builder builder(&cacheKey, fOriginalKey, kDomain, 0, "Image");
-        MakeMipMappedKeyFromOriginalKey(cacheKey, mipMappedKey);
+        MakeCopyKeyFromOrigKey(cacheKey, stretch, paramsCopyKey);
     }
 }
 
diff --git a/src/gpu/GrImageTextureMaker.h b/src/gpu/GrImageTextureMaker.h
index 87e505d..1bfea5a 100644
--- a/src/gpu/GrImageTextureMaker.h
+++ b/src/gpu/GrImageTextureMaker.h
@@ -22,12 +22,14 @@
                         SkImage::CachingHint chint, bool useDecal = false);
 
 private:
+    // 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(const CopyParams&) override;
     GrSurfaceProxyView refOriginalTextureProxyView(bool willBeMipped,
                                                    AllowedTexGenType onlyIfFast) override;
 
-    void makeMipMappedKey(GrUniqueKey* mipMappedKey) override;
-    void didCacheMipMappedCopy(const GrUniqueKey& mipMappedKey, uint32_t contextUniqueID) override {
-    }
+    void makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey) override;
+    void didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) override {}
 
     const SkImage_Lazy*     fImage;
     GrUniqueKey             fOriginalKey;
@@ -51,9 +53,8 @@
     GrSurfaceProxyView refOriginalTextureProxyView(bool willBeMipped,
                                                    AllowedTexGenType onlyIfFast) override;
 
-    void makeMipMappedKey(GrUniqueKey* mipMappedKey) override;
-    void didCacheMipMappedCopy(const GrUniqueKey& mipMappedKey, uint32_t contextUniqueID) override {
-    }
+    void makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey) override;
+    void didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) override {}
 
     std::unique_ptr<GrFragmentProcessor> createFragmentProcessor(
         const SkMatrix& textureMatrix,
diff --git a/src/gpu/GrTextureAdjuster.cpp b/src/gpu/GrTextureAdjuster.cpp
index 6d4628a..96b0b64 100644
--- a/src/gpu/GrTextureAdjuster.cpp
+++ b/src/gpu/GrTextureAdjuster.cpp
@@ -22,47 +22,65 @@
         , fOriginal(std::move(original))
         , fUniqueID(uniqueID) {}
 
-void GrTextureAdjuster::makeMipMappedKey(GrUniqueKey* mipMappedKey) {
+void GrTextureAdjuster::makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey) {
     // Destination color space is irrelevant - we already have a texture so we're just sub-setting
     GrUniqueKey baseKey;
     GrMakeKeyFromImageID(&baseKey, fUniqueID, SkIRect::MakeSize(this->dimensions()));
-    MakeMipMappedKeyFromOriginalKey(baseKey, mipMappedKey);
+    MakeCopyKeyFromOrigKey(baseKey, params, copyKey);
 }
 
-void GrTextureAdjuster::didCacheMipMappedCopy(const GrUniqueKey& mipMappedKey,
-                                              uint32_t contextUniqueID) {
+void GrTextureAdjuster::didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) {
     // We don't currently have a mechanism for notifications on Images!
 }
 
-GrSurfaceProxyView GrTextureAdjuster::makeMippedCopy() {
+GrSurfaceProxyView GrTextureAdjuster::copy(const CopyParams& copyParams, bool willBeMipped,
+                                           bool copyForMipsOnly) {
     GrProxyProvider* proxyProvider = this->context()->priv().proxyProvider();
 
     GrUniqueKey key;
-    this->makeMipMappedKey(&key);
+    this->makeCopyKey(copyParams, &key);
     sk_sp<GrTextureProxy> cachedCopy;
     const GrSurfaceProxyView& originalView = this->originalProxyView();
     if (key.isValid()) {
         cachedCopy = proxyProvider->findOrCreateProxyByUniqueKey(key, this->colorType());
-        if (cachedCopy) {
-            return {std::move(cachedCopy), originalView.origin(), originalView.swizzle()};
+        if (cachedCopy && (!willBeMipped || GrMipMapped::kYes == cachedCopy->mipMapped())) {
+            // TODO: Once we no longer use CopyOnGpu which can fallback to arbitrary formats and
+            // colorTypes, we can use the swizzle of the originalView.
+            GrSwizzle swizzle = cachedCopy->textureSwizzleDoNotUse();
+            return GrSurfaceProxyView(std::move(cachedCopy), originalView.origin(), swizzle);
         }
     }
 
-    GrSurfaceProxyView copyView = GrCopyBaseMipMapToTextureProxy(
-            this->context(), originalView.proxy(), originalView.origin(), this->colorType());
-    if (!copyView) {
-        return {};
+    GrSurfaceProxyView copyView;
+    if (copyForMipsOnly) {
+        copyView = GrCopyBaseMipMapToTextureProxy(this->context(), originalView.proxy(),
+                                                  originalView.origin(), this->colorType());
+    } else {
+        copyView = CopyOnGpu(this->context(), this->originalProxyViewRef(), this->colorType(),
+                             copyParams, willBeMipped);
     }
-    if (key.isValid()) {
-        SkASSERT(copyView.origin() == originalView.origin());
-        proxyProvider->assignUniqueKeyToProxy(key, copyView.asTextureProxy());
-        this->didCacheMipMappedCopy(key, proxyProvider->contextID());
+    if (copyView.proxy()) {
+        if (key.isValid()) {
+            SkASSERT(copyView.origin() == originalView.origin());
+            if (cachedCopy) {
+                SkASSERT(GrMipMapped::kYes == copyView.asTextureProxy()->mipMapped() &&
+                         GrMipMapped::kNo == cachedCopy->mipMapped());
+                // If we had a cachedProxy, that means there already is a proxy in the cache which
+                // matches the key, but it does not have mip levels and we require them. Thus we
+                // must remove the unique key from that proxy.
+                SkASSERT(cachedCopy->getUniqueKey() == key);
+                proxyProvider->removeUniqueKeyFromProxy(cachedCopy.get());
+            }
+            proxyProvider->assignUniqueKeyToProxy(key, copyView.asTextureProxy());
+            this->didCacheCopy(key, proxyProvider->contextID());
+        }
     }
     return copyView;
 }
 
 GrSurfaceProxyView GrTextureAdjuster::onRefTextureProxyViewForParams(GrSamplerState params,
-                                                                     bool willBeMipped) {
+                                                                     bool willBeMipped,
+                                                                     SkScalar scaleAdjust[2]) {
     if (this->context()->priv().abandoned()) {
         // The texture was abandoned.
         return {};
@@ -74,32 +92,51 @@
     GrSurfaceProxyView view = this->originalProxyViewRef();
     GrTextureProxy* texProxy = view.asTextureProxy();
     SkASSERT(texProxy);
-    if (!GrGpu::IsACopyNeededForMips(this->context()->priv().caps(), texProxy, params.filter())) {
-        return view;
+    CopyParams copyParams;
+
+    bool needsCopyForMipsOnly = false;
+    if (!params.isRepeated() ||
+        !GrGpu::IsACopyNeededForRepeatWrapMode(this->context()->priv().caps(), texProxy,
+                                               texProxy->dimensions(), params.filter(), &copyParams,
+                                               scaleAdjust)) {
+        needsCopyForMipsOnly = GrGpu::IsACopyNeededForMips(this->context()->priv().caps(),
+                                                           texProxy, params.filter(),
+                                                           &copyParams);
+        if (!needsCopyForMipsOnly) {
+            return view;
+        }
     }
 
-    GrSurfaceProxyView copy = this->makeMippedCopy();
-    if (!copy) {
+    GrSurfaceProxyView result = this->copy(copyParams, willBeMipped, needsCopyForMipsOnly);
+    if (!result.proxy() && needsCopyForMipsOnly) {
         // If we were unable to make a copy and we only needed a copy for mips, then we will return
         // the source texture here and require that the GPU backend is able to fall back to using
         // bilerp if mips are required.
         return view;
     }
-    SkASSERT(copy.asTextureProxy());
-    return copy;
+    SkASSERT(result.asTextureProxy());
+    return result;
 }
 
 std::unique_ptr<GrFragmentProcessor> GrTextureAdjuster::createFragmentProcessor(
-        const SkMatrix& textureMatrix,
+        const SkMatrix& origTextureMatrix,
         const SkRect& constraintRect,
         FilterConstraint filterConstraint,
         bool coordsLimitedToConstraintRect,
         const GrSamplerState::Filter* filterOrNullForBicubic) {
-    GrSurfaceProxyView view = this->viewForParams(filterOrNullForBicubic);
-    if (!view) {
+    SkMatrix textureMatrix = origTextureMatrix;
+
+    SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
+    GrSurfaceProxyView view = this->viewForParams(filterOrNullForBicubic, scaleAdjust);
+    if (!view.proxy()) {
         return nullptr;
     }
     SkASSERT(view.asTextureProxy());
+    // If we made a copy then we only copied the contentArea, in which case the new texture is all
+    // content.
+    if (view.proxy() != this->originalProxyView().proxy()) {
+        textureMatrix.postScale(scaleAdjust[0], scaleAdjust[1]);
+    }
 
     SkRect domain;
     DomainMode domainMode =
diff --git a/src/gpu/GrTextureAdjuster.h b/src/gpu/GrTextureAdjuster.h
index d52e517..bb34c5d 100644
--- a/src/gpu/GrTextureAdjuster.h
+++ b/src/gpu/GrTextureAdjuster.h
@@ -33,16 +33,17 @@
                       uint32_t uniqueID, bool useDecal = false);
 
 protected:
-    void makeMipMappedKey(GrUniqueKey* mipMappedKey) override;
-    void didCacheMipMappedCopy(const GrUniqueKey& mipMappedKey, uint32_t contextUniqueID) override;
+    void makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey) override;
+    void didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) override;
 
     const GrSurfaceProxyView& originalProxyView() const { return fOriginal; }
     GrSurfaceProxyView originalProxyViewRef() const { return fOriginal; }
 
 private:
-    GrSurfaceProxyView onRefTextureProxyViewForParams(GrSamplerState, bool willBeMipped) override;
+    GrSurfaceProxyView onRefTextureProxyViewForParams(GrSamplerState, bool willBeMipped,
+                                                      SkScalar scaleAdjust[2]) override;
 
-    GrSurfaceProxyView makeMippedCopy();
+    GrSurfaceProxyView copy(const CopyParams& copyParams, bool willBeMipped, bool copyOnlyForMips);
 
     GrSurfaceProxyView fOriginal;
     uint32_t fUniqueID;
diff --git a/src/gpu/GrTextureMaker.cpp b/src/gpu/GrTextureMaker.cpp
index d9e89c2..17200f2 100644
--- a/src/gpu/GrTextureMaker.cpp
+++ b/src/gpu/GrTextureMaker.cpp
@@ -14,66 +14,102 @@
 #include "src/gpu/GrRecordingContextPriv.h"
 
 GrSurfaceProxyView GrTextureMaker::onRefTextureProxyViewForParams(GrSamplerState params,
-                                                                  bool willBeMipped) {
+                                                                  bool willBeMipped,
+                                                                  SkScalar scaleAdjust[2]) {
     if (this->width() > this->context()->priv().caps()->maxTextureSize() ||
         this->height() > this->context()->priv().caps()->maxTextureSize()) {
         return {};
     }
 
+    CopyParams copyParams;
+
     GrSurfaceProxyView original = this->refOriginalTextureProxyView(willBeMipped,
                                                                     AllowedTexGenType::kCheap);
-    if (!original) {
-        return this->refOriginalTextureProxyView(willBeMipped, AllowedTexGenType::kAny);
-    }
-
-    GrTextureProxy* texProxy = original.asTextureProxy();
-    if (!GrGpu::IsACopyNeededForMips(this->context()->priv().caps(), texProxy, params.filter())) {
-        return original;
+    bool needsCopyForMipsOnly = false;
+    if (original.proxy()) {
+        GrTextureProxy* texProxy = original.asTextureProxy();
+        SkASSERT(texProxy);
+        if (!params.isRepeated() ||
+            !GrGpu::IsACopyNeededForRepeatWrapMode(this->context()->priv().caps(), texProxy,
+                                                   texProxy->dimensions(), params.filter(),
+                                                   &copyParams, scaleAdjust)) {
+            needsCopyForMipsOnly = GrGpu::IsACopyNeededForMips(this->context()->priv().caps(),
+                                                               texProxy, params.filter(),
+                                                               &copyParams);
+            if (!needsCopyForMipsOnly) {
+                return original;
+            }
+        }
+    } else {
+        if (!params.isRepeated() ||
+            !GrGpu::IsACopyNeededForRepeatWrapMode(this->context()->priv().caps(), nullptr,
+                                                   this->dimensions(), params.filter(), &copyParams,
+                                                   scaleAdjust)) {
+            return this->refOriginalTextureProxyView(willBeMipped, AllowedTexGenType::kAny);
+        }
     }
 
     GrProxyProvider* proxyProvider = this->context()->priv().proxyProvider();
 
     GrSurfaceOrigin origOrigin = original.proxy() ? original.origin() : kTopLeft_GrSurfaceOrigin;
-    GrUniqueKey mipMappedKey;
-    this->makeMipMappedKey(&mipMappedKey);
-    if (mipMappedKey.isValid()) {
-        auto cachedProxy =
-                proxyProvider->findOrCreateProxyByUniqueKey(mipMappedKey, this->colorType());
+    GrUniqueKey copyKey;
+    this->makeCopyKey(copyParams, &copyKey);
+    GrSurfaceProxyView cachedView;
+    if (copyKey.isValid()) {
+        auto cachedProxy = proxyProvider->findOrCreateProxyByUniqueKey(copyKey, this->colorType());
         if (cachedProxy) {
-            SkASSERT(cachedProxy->mipMapped() == GrMipMapped::kYes);
-            // TODO: Once we no longer use MakeMipMappedCopy which can fallback to arbitrary formats
-            // and colorTypes, we can use the swizzle of the originalView.
+            GrMipMapped mipped = cachedProxy->mipMapped();
+            // TODO: Once we no longer use CopyOnGpu which can fallback to arbitrary formats and
+            // colorTypes, we can use the swizzle of the originalView.
             GrSwizzle swizzle = cachedProxy->textureSwizzleDoNotUse();
-            return GrSurfaceProxyView(std::move(cachedProxy), origOrigin, swizzle);
+            cachedView = GrSurfaceProxyView(std::move(cachedProxy), origOrigin, swizzle);
+            if (!willBeMipped || GrMipMapped::kYes == mipped) {
+                return cachedView;
+            }
         }
     }
 
     GrSurfaceProxyView source;
-    if (original) {
+    if (original.proxy()) {
         source = std::move(original);
+    } else if (cachedView.proxy()) {
+        source = cachedView;
     } else {
         // Since we will be copying this texture there is no reason to make it mipped
         source = this->refOriginalTextureProxyView(false, AllowedTexGenType::kAny);
-        if (!source) {
-            return {};
-        }
     }
 
+    if (!source.proxy()) {
+        return {};
+    }
     SkASSERT(source.asTextureProxy());
 
-    GrSurfaceProxyView result = MakeMipMappedCopy(this->context(), source, this->colorType());
+    GrSurfaceProxyView result =
+            CopyOnGpu(this->context(), source, this->colorType(), copyParams, willBeMipped);
 
-    if (!result) {
+    if (!result.proxy()) {
         // If we were unable to make a copy and we only needed a copy for mips, then we will return
         // the source texture here and require that the GPU backend is able to fall back to using
         // bilerp if mips are required.
-        return source;
+        if (needsCopyForMipsOnly) {
+            return source;
+        }
+        return {};
     }
 
-    if (mipMappedKey.isValid()) {
+    if (copyKey.isValid()) {
         SkASSERT(result.origin() == origOrigin);
-        proxyProvider->assignUniqueKeyToProxy(mipMappedKey, result.asTextureProxy());
-        this->didCacheMipMappedCopy(mipMappedKey, proxyProvider->contextID());
+        if (cachedView.proxy()) {
+            SkASSERT(GrMipMapped::kYes == result.asTextureProxy()->mipMapped() &&
+                     GrMipMapped::kNo == cachedView.asTextureProxy()->mipMapped());
+            // If we had a cachedProxy, that means there already is a proxy in the cache which
+            // matches the key, but it does not have mip levels and we require them. Thus we must
+            // remove the unique key from that proxy.
+            SkASSERT(cachedView.asTextureProxy()->getUniqueKey() == copyKey);
+            proxyProvider->removeUniqueKeyFromProxy(cachedView.asTextureProxy());
+        }
+        proxyProvider->assignUniqueKeyToProxy(copyKey, result.asTextureProxy());
+        this->didCacheCopy(copyKey, proxyProvider->contextID());
     }
     return result;
 }
@@ -96,10 +132,13 @@
         fmForDetermineDomain = &kBilerp;
     }
 
-    GrSurfaceProxyView view = this->viewForParams(filterOrNullForBicubic);
-    if (!view) {
+    SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
+    GrSurfaceProxyView view = this->viewForParams(filterOrNullForBicubic, scaleAdjust);
+    if (!view.proxy()) {
         return nullptr;
     }
+    SkMatrix adjustedMatrix = textureMatrix;
+    adjustedMatrix.postScale(scaleAdjust[0], scaleAdjust[1]);
 
     SkRect domain;
     DomainMode domainMode =
@@ -107,5 +146,5 @@
                             view.proxy(), fmForDetermineDomain, &domain);
     SkASSERT(kTightCopy_DomainMode != domainMode);
     return this->createFragmentProcessorForDomainAndFilter(
-            std::move(view), textureMatrix, domainMode, domain, filterOrNullForBicubic);
+            std::move(view), adjustedMatrix, domainMode, domain, filterOrNullForBicubic);
 }
diff --git a/src/gpu/GrTextureMaker.h b/src/gpu/GrTextureMaker.h
index 6296964..22aaced 100644
--- a/src/gpu/GrTextureMaker.h
+++ b/src/gpu/GrTextureMaker.h
@@ -40,7 +40,9 @@
                                                            AllowedTexGenType genType) = 0;
 
 private:
-    GrSurfaceProxyView onRefTextureProxyViewForParams(GrSamplerState, bool willBeMipped) override;
+    GrSurfaceProxyView onRefTextureProxyViewForParams(GrSamplerState,
+                                                      bool willBeMipped,
+                                                      SkScalar scaleAdjust[2]) override;
 
     typedef GrTextureProducer INHERITED;
 };
diff --git a/src/gpu/GrTextureProducer.cpp b/src/gpu/GrTextureProducer.cpp
index 55fccc0..0e2f908 100644
--- a/src/gpu/GrTextureProducer.cpp
+++ b/src/gpu/GrTextureProducer.cpp
@@ -21,35 +21,53 @@
 #include "src/gpu/effects/GrTextureDomain.h"
 #include "src/gpu/effects/GrTextureEffect.h"
 
-GrSurfaceProxyView GrTextureProducer::MakeMipMappedCopy(GrRecordingContext* context,
-                                                        GrSurfaceProxyView inputView,
-                                                        GrColorType colorType) {
+GrSurfaceProxyView GrTextureProducer::CopyOnGpu(GrRecordingContext* context,
+                                                GrSurfaceProxyView inputView,
+                                                GrColorType colorType,
+                                                const CopyParams& copyParams,
+                                                bool dstWillRequireMipMaps) {
     SkASSERT(context);
     SkASSERT(inputView.asTextureProxy());
 
-    GrSurfaceProxy* proxy = inputView.proxy();
-    SkRect proxyRect = proxy->getBoundsRect();
+    const SkRect dstRect = SkRect::Make(copyParams.fDimensions);
+    GrMipMapped mipMapped = dstWillRequireMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
 
-    GrSurfaceProxyView view =
-            GrCopyBaseMipMapToTextureProxy(context, proxy, inputView.origin(), colorType);
-    if (view) {
-        return view;
+    GrSurfaceProxy* proxy = inputView.proxy();
+    SkRect localRect = proxy->getBoundsRect();
+
+    bool resizing = false;
+    if (copyParams.fFilter != GrSamplerState::Filter::kNearest) {
+        resizing = localRect.width() != dstRect.width() || localRect.height() != dstRect.height();
+    }
+
+    if (copyParams.fFilter == GrSamplerState::Filter::kNearest && !resizing &&
+        dstWillRequireMipMaps) {
+        GrSurfaceProxyView view = GrCopyBaseMipMapToTextureProxy(context, proxy, inputView.origin(),
+                                                                 colorType);
+        if (view.proxy()) {
+            return view;
+        }
     }
 
     auto copyRTC = GrRenderTargetContext::MakeWithFallback(
-            context, colorType, nullptr, SkBackingFit::kExact, inputView.dimensions(), 1,
-            GrMipMapped::kYes, proxy->isProtected(), inputView.origin());
+            context, colorType, nullptr, SkBackingFit::kExact, copyParams.fDimensions, 1,
+            mipMapped, proxy->isProtected(), inputView.origin());
     if (!copyRTC) {
         return {};
     }
 
+    const auto& caps = *context->priv().caps();
     GrPaint paint;
-    auto fp = GrTextureEffect::Make(std::move(inputView), kUnknown_SkAlphaType, SkMatrix::I(),
-                                    GrSamplerState::Filter::kNearest);
+
+    GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, copyParams.fFilter);
+    auto boundsRect = SkRect::Make(proxy->dimensions());
+    auto fp = GrTextureEffect::MakeSubset(std::move(inputView), kUnknown_SkAlphaType, SkMatrix::I(),
+                                          sampler, boundsRect, localRect, caps);
     paint.addColorFragmentProcessor(std::move(fp));
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
 
-    copyRTC->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), proxyRect);
+    copyRTC->fillRectToRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect,
+                            localRect);
     return copyRTC->readSurfaceView();
 }
 
@@ -200,7 +218,7 @@
 }
 
 GrSurfaceProxyView GrTextureProducer::viewForParams(
-        const GrSamplerState::Filter* filterOrNullForBicubic) {
+        const GrSamplerState::Filter* filterOrNullForBicubic, SkScalar scaleAdjust[2]) {
     GrSamplerState sampler; // Default is nearest + clamp
     if (filterOrNullForBicubic) {
         sampler.setFilterMode(*filterOrNullForBicubic);
@@ -212,29 +230,36 @@
             sampler.setWrapModeY(GrSamplerState::WrapMode::kClampToBorder);
         }
     }
-    return this->viewForParams(sampler);
+    return this->viewForParams(sampler, scaleAdjust);
 }
 
-GrSurfaceProxyView GrTextureProducer::viewForParams(GrSamplerState sampler) {
+GrSurfaceProxyView GrTextureProducer::viewForParams(GrSamplerState sampler,
+                                                    SkScalar scaleAdjust[2]) {
+    // Check that the caller pre-initialized scaleAdjust
+    SkASSERT(!scaleAdjust || (scaleAdjust[0] == 1 && scaleAdjust[1] == 1));
+
     const GrCaps* caps = this->context()->priv().caps();
 
     int mipCount = SkMipMap::ComputeLevelCount(this->width(), this->height());
     bool willBeMipped = GrSamplerState::Filter::kMipMap == sampler.filter() && mipCount &&
                         caps->mipMapSupport();
 
-    auto result = this->onRefTextureProxyViewForParams(sampler, willBeMipped);
-    if (!result) {
-        return {};
-    }
-
-    SkASSERT(result.asTextureProxy());
+    auto result = this->onRefTextureProxyViewForParams(sampler, willBeMipped, scaleAdjust);
 
     // Check to make sure that if we say the texture willBeMipped that the returned texture has mip
     // maps, unless the config is not copyable.
-    SkASSERT(!willBeMipped || result.asTextureProxy()->mipMapped() == GrMipMapped::kYes ||
+    SkASSERT(!result.proxy() || !willBeMipped ||
+             result.asTextureProxy()->mipMapped() == GrMipMapped::kYes ||
              !caps->isFormatCopyable(result.proxy()->backendFormat()));
 
-    SkASSERT(result.proxy()->dimensions() == this->dimensions());
+    SkASSERT(!result.proxy() || result.asTextureProxy());
+
+    SkDEBUGCODE(bool expectNoScale = (sampler.filter() != GrSamplerState::Filter::kMipMap &&
+                                      !sampler.isRepeated()));
+    // Check that the "no scaling expected" case always returns a proxy of the same size as the
+    // producer.
+    SkASSERT(!result.proxy() || !expectNoScale ||
+             result.proxy()->dimensions() == this->dimensions());
 
     return result;
 }
@@ -245,7 +270,7 @@
                                              : GrSamplerState::Filter::kMipMap;
     GrSamplerState sampler(GrSamplerState::WrapMode::kClamp, filter);
 
-    auto result = this->viewForParams(sampler);
+    auto result = this->viewForParams(sampler, nullptr);
 
 #ifdef SK_DEBUG
     const GrCaps* caps = this->context()->priv().caps();
diff --git a/src/gpu/GrTextureProducer.h b/src/gpu/GrTextureProducer.h
index a1a9a02..39944d9 100644
--- a/src/gpu/GrTextureProducer.h
+++ b/src/gpu/GrTextureProducer.h
@@ -32,6 +32,11 @@
  */
 class GrTextureProducer : public SkNoncopyable {
 public:
+    struct CopyParams {
+        GrSamplerState::Filter fFilter;
+        SkISize fDimensions;
+    };
+
     enum FilterConstraint {
         kYes_FilterConstraint,
         kNo_FilterConstraint,
@@ -66,10 +71,21 @@
 
     /**
      *  Returns a texture that is safe for use with the params.
+     *
+     * If the size of the returned texture does not match width()/height() then the contents of the
+     * original may have been scaled to fit the texture or the original may have been copied into
+     * a subrect of the copy. 'scaleAdjust' must be  applied to the normalized texture coordinates
+     * in order to correct for the latter case.
+     *
+     * If the GrSamplerState is known to clamp and use kNearest or kBilerp filter mode then the
+     * proxy will always be unscaled and nullptr can be passed for scaleAdjust. There is a weird
+     * contract that if scaleAdjust is not null it must be initialized to {1, 1} before calling
+     * this method. (TODO: Fix this and make this function always initialize scaleAdjust).
      */
-    GrSurfaceProxyView viewForParams(GrSamplerState);
+    GrSurfaceProxyView viewForParams(GrSamplerState, SkScalar scaleAdjust[2]);
 
-    GrSurfaceProxyView viewForParams(const GrSamplerState::Filter* filterOrNullForBicubic);
+    GrSurfaceProxyView viewForParams(const GrSamplerState::Filter* filterOrNullForBicubic,
+                                     SkScalar scaleAdjust[2]);
 
     /**
      * Returns a texture. If willNeedMips is true then the returned texture is guaranteed to have
@@ -105,29 +121,35 @@
     GrColorType colorType() const { return fImageInfo.colorType(); }
 
     /** Helper for creating a key for a copy from an original key. */
-    static void MakeMipMappedKeyFromOriginalKey(const GrUniqueKey& origKey,
-                                                GrUniqueKey* mipMappedKey) {
-        SkASSERT(!mipMappedKey->isValid());
+    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(mipMappedKey, origKey, kDomain, 0);
+            GrUniqueKey::Builder builder(copyKey, origKey, kDomain, 3);
+            builder[0] = static_cast<uint32_t>(copyParams.fFilter);
+            builder[1] = copyParams.fDimensions.width();
+            builder[2] = copyParams.fDimensions.height();
         }
     }
 
     /**
-     * If we need to make a copy with MIP maps the producer is asked to return a key that identifies
-     * the original conteny + the addition of MIP map levels. If the producer does not want to cache
-     * the copy it can simply leave the key uninitialized.
-     */
-    virtual void makeMipMappedKey(GrUniqueKey* mipMappedKey) = 0;
+    *  If we need to make a copy in order to be compatible with GrTextureParams producer is asked to
+    *  return a key that identifies its original content + the CopyParms parameter. If the producer
+    *  does not want to cache the stretched version (e.g. the producer is volatile), this should
+    *  simply return without initializing the copyKey. If the texture generated by this producer
+    *  depends on the destination color space, then that information should also be incorporated
+    *  in the key.
+    */
+    virtual void makeCopyKey(const CopyParams&, GrUniqueKey* copyKey) = 0;
 
     /**
-     *  If a stretched version of the texture is generated, it may be cached (assuming that
-     *  makeMipMappedKey() returns true). In that case, the maker is notified in case it
-     *  wants to note that for when the maker is destroyed.
-     */
-    virtual void didCacheMipMappedCopy(const GrUniqueKey& mipMappedKey,
-                                       uint32_t contextUniqueID) = 0;
+    *  If a stretched version of the texture is generated, it may be cached (assuming that
+    *  makeCopyKey() 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, uint32_t contextUniqueID) = 0;
 
     enum DomainMode {
         kNoDomain_DomainMode,
@@ -135,11 +157,12 @@
         kTightCopy_DomainMode
     };
 
-    // TODO: Try to remove the draw fallback and directly use GrCopyBaseMipMapToTextureProxy
-    // instead.
-    static GrSurfaceProxyView MakeMipMappedCopy(GrRecordingContext*,
-                                                GrSurfaceProxyView,
-                                                GrColorType colorType);
+    // This can draw to accomplish the copy, thus the recording context is needed
+    static GrSurfaceProxyView CopyOnGpu(GrRecordingContext*,
+                                        GrSurfaceProxyView inputView,
+                                        GrColorType,
+                                        const CopyParams& copyParams,
+                                        bool dstWillRequireMipMaps);
 
     static DomainMode DetermineDomainMode(const SkRect& constraintRect,
                                           FilterConstraint filterConstraint,
@@ -158,8 +181,8 @@
     GrRecordingContext* context() const { return fContext; }
 
 private:
-    virtual GrSurfaceProxyView onRefTextureProxyViewForParams(GrSamplerState,
-                                                              bool willBeMipped) = 0;
+    virtual GrSurfaceProxyView onRefTextureProxyViewForParams(GrSamplerState, bool willBeMipped,
+                                                              SkScalar scaleAdjust[2]) = 0;
 
     GrRecordingContext* fContext;
     const GrImageInfo fImageInfo;
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 9deaff9..d52787e 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -918,13 +918,15 @@
              bitmap.height() <= this->caps()->maxTileSize());
     SkASSERT(!samplerState.isRepeated());
 
-    GrSurfaceProxyView view = GrRefCachedBitmapView(fContext.get(), bitmap, samplerState);
+    SkScalar scales[2] = {1.f, 1.f};
+    GrSurfaceProxyView view = GrRefCachedBitmapView(fContext.get(), bitmap, samplerState, scales);
     if (!view) {
         return;
     }
 
     // Compute a matrix that maps the rect we will draw to the src rect.
     SkMatrix texMatrix = SkMatrix::MakeRectToRect(dstRect, srcRect, SkMatrix::kFill_ScaleToFit);
+    texMatrix.postScale(scales[0], scales[1]);
 
     SkAlphaType srcAlphaType = bitmap.alphaType();
 
@@ -1050,7 +1052,7 @@
         auto filter = paint.getFilterQuality() > kNone_SkFilterQuality
                               ? GrSamplerState::Filter::kBilerp
                               : GrSamplerState::Filter::kNearest;
-        GrSurfaceProxyView clipView = as_IB(clipImage)->refView(this->context(), filter);
+        GrSurfaceProxyView clipView = as_IB(clipImage)->refView(this->context(), filter, nullptr);
         // Fold clip matrix into ctm
         ctm.preConcat(clipMatrix);
         SkMatrix inverseClipMatrix;
@@ -1343,7 +1345,7 @@
 
     auto dstColorSpace = fRenderTargetContext->colorInfo().colorSpace();
     const GrSamplerState::Filter filter = compute_lattice_filter_mode(*paint);
-    auto view = producer->viewForParams(&filter);
+    auto view = producer->viewForParams(&filter, nullptr);
     if (!view) {
         return;
     }
diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp
index 7b2b1a9..edd3469 100644
--- a/src/gpu/SkGpuDevice_drawTexture.cpp
+++ b/src/gpu/SkGpuDevice_drawTexture.cpp
@@ -537,7 +537,7 @@
             uint32_t uniqueID;
             view = image->refPinnedView(this->context(), &uniqueID);
             if (!view) {
-                view = image->refView(this->context(), GrSamplerState::Filter::kBilerp);
+                view = image->refView(this->context(), GrSamplerState::Filter::kBilerp, nullptr);
             }
         }
 
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 0b20bb7..c6fb7b7 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -134,9 +134,9 @@
 }
 
 GrSurfaceProxyView GrRefCachedBitmapView(GrRecordingContext* ctx, const SkBitmap& bitmap,
-                                         GrSamplerState params) {
+                                         GrSamplerState params, SkScalar scaleAdjust[2]) {
     GrBitmapTextureMaker maker(ctx, bitmap, GrBitmapTextureMaker::Cached::kYes);
-    return maker.viewForParams(params);
+    return maker.viewForParams(params, scaleAdjust);
 }
 
 GrSurfaceProxyView GrMakeCachedBitmapProxyView(GrRecordingContext* context, const SkBitmap& bitmap,
diff --git a/src/gpu/SkGr.h b/src/gpu/SkGr.h
index 36daae1..0d97169 100644
--- a/src/gpu/SkGr.h
+++ b/src/gpu/SkGr.h
@@ -163,12 +163,15 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Texture management
 
-/**
- * Returns a view that wraps a texture representing the bitmap that is compatible with the
- * GrSamplerState. The texture is inserted into the cache (unless the bitmap is marked volatile)
- * and can be retrieved again via this function.
+/** Returns a view that wraps a texture representing the bitmap that is compatible with the
+ *  GrSamplerState. The texture is inserted into the cache (unless the bitmap is marked volatile)
+ *  and can be retrieved again via this function.
+ *  The 'scaleAdjust' in/out parameter will be updated to hold any rescaling that needs to be
+ *  performed on the absolute texture coordinates (e.g., if the texture is resized out to
+ *  the next power of two). It can be null if the caller is sure the bitmap won't be resized.
  */
-GrSurfaceProxyView GrRefCachedBitmapView(GrRecordingContext*, const SkBitmap&, GrSamplerState);
+GrSurfaceProxyView GrRefCachedBitmapView(GrRecordingContext*, const SkBitmap&, GrSamplerState,
+                                         SkScalar scaleAdjust[2]);
 
 /**
  * Creates a new texture with mipmap levels and copies the baseProxy into the base layer.
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
index 9dfcc7c..a4abb60 100644
--- a/src/image/SkImage_Base.h
+++ b/src/image/SkImage_Base.h
@@ -64,7 +64,8 @@
     // this call will flatten a SkImage_GpuYUV to a single texture.
     virtual const GrSurfaceProxyView* view(GrRecordingContext*) const { return nullptr; }
 
-    virtual GrSurfaceProxyView refView(GrRecordingContext*, GrSamplerState) const = 0;
+    virtual GrSurfaceProxyView refView(GrRecordingContext*, GrSamplerState,
+                                       SkScalar scaleAdjust[2]) const = 0;
     virtual GrSurfaceProxyView refPinnedView(GrRecordingContext*, uint32_t* uniqueID) const {
         return {};
     }
diff --git a/src/image/SkImage_GpuBase.cpp b/src/image/SkImage_GpuBase.cpp
index 7bd05ba..95002e0 100644
--- a/src/image/SkImage_GpuBase.cpp
+++ b/src/image/SkImage_GpuBase.cpp
@@ -186,8 +186,8 @@
     return sContext->readPixels(dstInfo, dstPixels, dstRB, {srcX, srcY});
 }
 
-GrSurfaceProxyView SkImage_GpuBase::refView(GrRecordingContext* context,
-                                            GrSamplerState params) const {
+GrSurfaceProxyView SkImage_GpuBase::refView(GrRecordingContext* context, GrSamplerState params,
+                                            SkScalar scaleAdjust[2]) const {
     if (!context || !fContext->priv().matches(context)) {
         SkASSERT(0);
         return {};
@@ -195,7 +195,7 @@
 
     GrTextureAdjuster adjuster(fContext.get(), *this->view(context), this->imageInfo().colorInfo(),
                                this->uniqueID());
-    return adjuster.viewForParams(params);
+    return adjuster.viewForParams(params, scaleAdjust);
 }
 
 GrBackendTexture SkImage_GpuBase::onGetBackendTexture(bool flushPendingGrContextIO,
diff --git a/src/image/SkImage_GpuBase.h b/src/image/SkImage_GpuBase.h
index 3f4fa52..05fc48f 100644
--- a/src/image/SkImage_GpuBase.h
+++ b/src/image/SkImage_GpuBase.h
@@ -28,7 +28,8 @@
     bool onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
                       int srcX, int srcY, CachingHint) const override;
 
-    GrSurfaceProxyView refView(GrRecordingContext*, GrSamplerState) const final;
+    GrSurfaceProxyView refView(GrRecordingContext*, GrSamplerState,
+                               SkScalar scaleAdjust[2]) const final;
 
     GrSurfaceProxyView refPinnedView(GrRecordingContext* context, uint32_t* uniqueID) const final {
         *uniqueID = this->uniqueID();
diff --git a/src/image/SkImage_GpuYUVA.cpp b/src/image/SkImage_GpuYUVA.cpp
index 43ab458..eedd399 100644
--- a/src/image/SkImage_GpuYUVA.cpp
+++ b/src/image/SkImage_GpuYUVA.cpp
@@ -92,11 +92,13 @@
     }
 
     for (int i = 0; i < fNumViews; ++i) {
+        GrTextureProducer::CopyParams copyParams;
         int mipCount = SkMipMap::ComputeLevelCount(fViews[i].proxy()->width(),
                                                    fViews[i].proxy()->height());
         if (mipCount && GrGpu::IsACopyNeededForMips(fContext->priv().caps(),
                                                     fViews[i].asTextureProxy(),
-                                                    GrSamplerState::Filter::kMipMap)) {
+                                                    GrSamplerState::Filter::kMipMap,
+                                                    &copyParams)) {
             auto mippedView = GrCopyBaseMipMapToTextureProxy(context, fViews[i].asTextureProxy(),
                                                              fOrigin, fProxyColorTypes[i]);
             if (!mippedView.proxy()) {
diff --git a/src/image/SkImage_Lazy.cpp b/src/image/SkImage_Lazy.cpp
index 50b9041..81a874d 100644
--- a/src/image/SkImage_Lazy.cpp
+++ b/src/image/SkImage_Lazy.cpp
@@ -238,13 +238,14 @@
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #if SK_SUPPORT_GPU
-GrSurfaceProxyView SkImage_Lazy::refView(GrRecordingContext* context, GrSamplerState params) const {
+GrSurfaceProxyView SkImage_Lazy::refView(GrRecordingContext* context, GrSamplerState params,
+                                         SkScalar scaleAdjust[2]) const {
     if (!context) {
         return {};
     }
 
     GrImageTextureMaker textureMaker(context, this, kAllow_CachingHint);
-    return textureMaker.viewForParams(params);
+    return textureMaker.viewForParams(params, scaleAdjust);
 }
 #endif
 
diff --git a/src/image/SkImage_Lazy.h b/src/image/SkImage_Lazy.h
index dce367b..224eaf0 100644
--- a/src/image/SkImage_Lazy.h
+++ b/src/image/SkImage_Lazy.h
@@ -42,7 +42,8 @@
     bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY,
                       CachingHint) const override;
 #if SK_SUPPORT_GPU
-    GrSurfaceProxyView refView(GrRecordingContext*, GrSamplerState) const override;
+    GrSurfaceProxyView refView(GrRecordingContext*, GrSamplerState,
+                               SkScalar scaleAdjust[2]) const override;
     sk_sp<SkCachedData> getPlanes(SkYUVASizeInfo*, SkYUVAIndex[4],
                                   SkYUVColorSpace*, const void* planes[4]) override;
 #endif
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
index e1e8b4c..0764a4d 100644
--- a/src/image/SkImage_Raster.cpp
+++ b/src/image/SkImage_Raster.cpp
@@ -85,7 +85,8 @@
     const SkBitmap* onPeekBitmap() const override { return &fBitmap; }
 
 #if SK_SUPPORT_GPU
-    GrSurfaceProxyView refView(GrRecordingContext*, GrSamplerState) const override;
+    GrSurfaceProxyView refView(GrRecordingContext*, GrSamplerState,
+                               SkScalar scaleAdjust[2]) const override;
 #endif
 
     bool getROPixels(SkBitmap*, CachingHint) const override;
@@ -173,8 +174,8 @@
 }
 
 #if SK_SUPPORT_GPU
-GrSurfaceProxyView SkImage_Raster::refView(GrRecordingContext* context,
-                                           GrSamplerState params) const {
+GrSurfaceProxyView SkImage_Raster::refView(GrRecordingContext* context, GrSamplerState params,
+                                           SkScalar scaleAdjust[2]) const {
     if (!context) {
         return {};
     }
@@ -183,10 +184,10 @@
     if (GrSurfaceProxyView view = this->refPinnedView(context, &uniqueID)) {
         GrTextureAdjuster adjuster(context, std::move(view), fBitmap.info().colorInfo(),
                                    fPinnedUniqueID);
-        return adjuster.viewForParams(params);
+        return adjuster.viewForParams(params, scaleAdjust);
     }
 
-    return GrRefCachedBitmapView(context, fBitmap, params);
+    return GrRefCachedBitmapView(context, fBitmap, params, scaleAdjust);
 }
 #endif
 
@@ -209,7 +210,8 @@
     } else {
         SkASSERT(fPinnedCount == 0);
         SkASSERT(fPinnedUniqueID == 0);
-        fPinnedView = GrRefCachedBitmapView(ctx, fBitmap, GrSamplerState::Filter::kNearest);
+        fPinnedView =
+                GrRefCachedBitmapView(ctx, fBitmap, GrSamplerState::Filter::kNearest, nullptr);
         if (!fPinnedView) {
             return false;
         }
diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp
index 9c31223..b509e0b 100755
--- a/src/shaders/SkImageShader.cpp
+++ b/src/shaders/SkImageShader.cpp
@@ -213,13 +213,16 @@
             fImage->width(), fImage->height(), args.fFilterQuality, *args.fViewMatrix, *lm,
             args.fContext->priv().options().fSharpenMipmappedTextures, &doBicubic);
     GrSamplerState samplerState(wm, textureFilterMode);
-    GrSurfaceProxyView view = as_IB(fImage)->refView(args.fContext, samplerState);
+    SkScalar scaleAdjust[2] = { 1.0f, 1.0f };
+    GrSurfaceProxyView view = as_IB(fImage)->refView(args.fContext, samplerState, scaleAdjust);
     if (!view) {
         return nullptr;
     }
 
     SkAlphaType srcAlphaType = fImage->alphaType();
 
+    lmInverse.postScale(scaleAdjust[0], scaleAdjust[1]);
+
     const auto& caps = *args.fContext->priv().caps();
 
     std::unique_ptr<GrFragmentProcessor> inner;
diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp
index 1a1969a..d908365 100644
--- a/tests/ImageTest.cpp
+++ b/tests/ImageTest.cpp
@@ -970,19 +970,20 @@
             sk_sp<SkImage> refImg(imageMaker(ctx));
 
             // Any context should be able to borrow the texture at this point
-            GrSurfaceProxyView view = as_IB(refImg)->refView(ctx, GrSamplerState::Filter::kNearest);
+            GrSurfaceProxyView view =
+                    as_IB(refImg)->refView(ctx, GrSamplerState::Filter::kNearest, nullptr);
             REPORTER_ASSERT(reporter, view);
 
             // But once it's borrowed, no other context should be able to borrow
             otherTestContext->makeCurrent();
             GrSurfaceProxyView otherView =
-                    as_IB(refImg)->refView(otherCtx, GrSamplerState::Filter::kNearest);
+                    as_IB(refImg)->refView(otherCtx, GrSamplerState::Filter::kNearest, nullptr);
             REPORTER_ASSERT(reporter, !otherView);
 
             // Original context (that's already borrowing) should be okay
             testContext->makeCurrent();
             GrSurfaceProxyView viewSecondRef =
-                    as_IB(refImg)->refView(ctx, GrSamplerState::Filter::kNearest);
+                    as_IB(refImg)->refView(ctx, GrSamplerState::Filter::kNearest, nullptr);
             REPORTER_ASSERT(reporter, viewSecondRef);
 
             // Release first ref from the original context
@@ -991,7 +992,7 @@
             // We released one proxy but not the other from the current borrowing context. Make sure
             // a new context is still not able to borrow the texture.
             otherTestContext->makeCurrent();
-            otherView = as_IB(refImg)->refView(otherCtx, GrSamplerState::Filter::kNearest);
+            otherView = as_IB(refImg)->refView(otherCtx, GrSamplerState::Filter::kNearest, nullptr);
             REPORTER_ASSERT(reporter, !otherView);
 
             // Release second ref from the original context
@@ -1000,7 +1001,7 @@
 
             // Now we should be able to borrow the texture from the other context
             otherTestContext->makeCurrent();
-            otherView = as_IB(refImg)->refView(otherCtx, GrSamplerState::Filter::kNearest);
+            otherView = as_IB(refImg)->refView(otherCtx, GrSamplerState::Filter::kNearest, nullptr);
             REPORTER_ASSERT(reporter, otherView);
 
             // Release everything
@@ -1041,7 +1042,8 @@
             sk_sp<SkImage> image = SkImage::MakeCrossContextFromPixmap(ctx, pixmap, false);
             REPORTER_ASSERT(reporter, image);
 
-            GrSurfaceProxyView view = as_IB(image)->refView(ctx, GrSamplerState::Filter::kNearest);
+            GrSurfaceProxyView view =
+                    as_IB(image)->refView(ctx, GrSamplerState::Filter::kNearest, nullptr);
             REPORTER_ASSERT(reporter, view);
 
             bool expectAlpha = kAlpha_8_SkColorType == ct;