Remove create function in proxyProvider that takes a raster SkImage.

Instead in proxyProvider we just have a create a bitmap call which
does no special fallback or logic. All the callers now go through
GrBitmapTextureMaker which handles and special fallbacks or caching
support that we need.

Change-Id: I71bb896cc78f64f9d6d54b54af2490d48e0f5af5
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/266842
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/gm/fpcoordinateoverride.cpp b/gm/fpcoordinateoverride.cpp
index b3ae4d6..6202a61 100644
--- a/gm/fpcoordinateoverride.cpp
+++ b/gm/fpcoordinateoverride.cpp
@@ -18,6 +18,7 @@
 #include "include/core/SkTileMode.h"
 #include "include/core/SkTypes.h"
 #include "include/gpu/GrContext.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrCoordTransform.h"
@@ -81,8 +82,8 @@
 
     SkBitmap bmp;
     GetResourceAsBitmap("images/mandrill_512_q075.jpg", &bmp);
-    GrProxyProvider* proxyProvider = ctx->priv().proxyProvider();
-    sk_sp<GrTextureProxy> texture = proxyProvider->createProxyFromBitmap(bmp, GrMipMapped::kNo);
+    GrBitmapTextureMaker maker(ctx, bmp);
+    auto [texture, grCT] = maker.refTextureProxy(GrMipMapped::kNo);
     std::unique_ptr<GrFragmentProcessor> imgFP =
             GrTextureEffect::Make(texture, bmp.alphaType(), SkMatrix());
     auto fp = std::unique_ptr<GrFragmentProcessor>(new SampleCoordEffect(std::move(imgFP)));
diff --git a/gm/texturedomaineffect.cpp b/gm/texturedomaineffect.cpp
index ff4dfec..be8a2b0 100644
--- a/gm/texturedomaineffect.cpp
+++ b/gm/texturedomaineffect.cpp
@@ -19,6 +19,7 @@
 #include "include/effects/SkGradientShader.h"
 #include "include/private/GrTypesPriv.h"
 #include "include/private/SkTArray.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrProxyProvider.h"
 #include "src/gpu/GrRenderTargetContext.h"
@@ -87,12 +88,11 @@
 
     DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
                       SkCanvas* canvas, SkString* errorMsg) override {
-        GrProxyProvider* proxyProvider = context->priv().proxyProvider();
-        sk_sp<GrTextureProxy> proxy;
         GrMipMapped mipMapped = fFilter == GrSamplerState::Filter::kMipMap &&
                                 context->priv().caps()->mipMapSupport()
                 ? GrMipMapped::kYes : GrMipMapped::kNo;
-        proxy = proxyProvider->createProxyFromBitmap(fBitmap, mipMapped);
+        GrBitmapTextureMaker maker(context, fBitmap);
+        auto [proxy, grCT] = maker.refTextureProxy(mipMapped);
         if (!proxy) {
             *errorMsg = "Failed to create proxy.";
             return DrawResult::kFail;
@@ -114,7 +114,9 @@
         for (size_t d = 0; d < SK_ARRAY_COUNT(texelDomains); ++d) {
             SkBitmap subset;
             fBitmap.extractSubset(&subset, texelDomains[d]);
-            subsetProxies[d] = proxyProvider->createProxyFromBitmap(subset, mipMapped);
+            subset.setImmutable();
+            GrBitmapTextureMaker maker(context, subset);
+            std::tie(subsetProxies[d], std::ignore) = maker.refTextureProxy(mipMapped);
         }
 
         SkRect localRect = SkRect::Make(fBitmap.bounds()).makeOutset(kDrawPad, kDrawPad);
diff --git a/gm/yuvtorgbeffect.cpp b/gm/yuvtorgbeffect.cpp
index deb5cb7..f2a471f1 100644
--- a/gm/yuvtorgbeffect.cpp
+++ b/gm/yuvtorgbeffect.cpp
@@ -23,11 +23,11 @@
 #include "include/core/SkYUVAIndex.h"
 #include "include/gpu/GrContext.h"
 #include "include/private/GrTypesPriv.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrClip.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrFragmentProcessor.h"
 #include "src/gpu/GrPaint.h"
-#include "src/gpu/GrProxyProvider.h"
 #include "src/gpu/GrRenderTargetContext.h"
 #include "src/gpu/GrRenderTargetContextPriv.h"
 #include "src/gpu/GrSamplerState.h"
@@ -67,41 +67,39 @@
     }
 
     void onOnceBeforeDraw() override {
-        SkBitmap bmp[3];
         SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
-        bmp[0].allocPixels(yinfo);
+        fBitmaps[0].allocPixels(yinfo);
         SkImageInfo uinfo = SkImageInfo::MakeA8(USIZE, USIZE);
-        bmp[1].allocPixels(uinfo);
+        fBitmaps[1].allocPixels(uinfo);
         SkImageInfo vinfo = SkImageInfo::MakeA8(VSIZE, VSIZE);
-        bmp[2].allocPixels(vinfo);
+        fBitmaps[2].allocPixels(vinfo);
         unsigned char* pixels[3];
         for (int i = 0; i < 3; ++i) {
-            pixels[i] = (unsigned char*)bmp[i].getPixels();
+            pixels[i] = (unsigned char*)fBitmaps[i].getPixels();
         }
         int color[] = {0, 85, 170};
         const int limit[] = {255, 0, 255};
         const int invl[]  = {0, 255, 0};
         const int inc[]   = {1, -1, 1};
         for (int i = 0; i < 3; ++i) {
-            const size_t nbBytes = bmp[i].rowBytes() * bmp[i].height();
+            const size_t nbBytes = fBitmaps[i].rowBytes() * fBitmaps[i].height();
             for (size_t j = 0; j < nbBytes; ++j) {
                 pixels[i][j] = (unsigned char)color[i];
                 color[i] = (color[i] == limit[i]) ? invl[i] : color[i] + inc[i];
             }
         }
         for (int i = 0; i < 3; ++i) {
-            fImage[i] = SkImage::MakeFromBitmap(bmp[i]);
+            fBitmaps[i].setImmutable();
         }
     }
 
     DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
                       SkCanvas* canvas, SkString* errorMsg) override {
-        GrProxyProvider* proxyProvider = context->priv().proxyProvider();
         sk_sp<GrTextureProxy> proxies[3];
 
         for (int i = 0; i < 3; ++i) {
-            proxies[i] = proxyProvider->createTextureProxy(fImage[i], 1, SkBudgeted::kYes,
-                                                           SkBackingFit::kExact);
+            GrBitmapTextureMaker maker(context, fBitmaps[i]);
+            std::tie(proxies[i], std::ignore) = maker.refTextureProxy(GrMipMapped::kNo);
             if (!proxies[i]) {
                 *errorMsg = "Failed to create proxy";
                 return DrawResult::kFail;
@@ -109,8 +107,8 @@
         }
 
         for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
-            SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fImage[0]->width()),
-                                               SkIntToScalar(fImage[0]->height()));
+            SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fBitmaps[0].width()),
+                                               SkIntToScalar(fBitmaps[0].height()));
             renderRect.outset(kDrawPad, kDrawPad);
 
             SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset;
@@ -147,7 +145,7 @@
      }
 
 private:
-    sk_sp<SkImage> fImage[3];
+    SkBitmap fBitmaps[3];
 
     static constexpr SkScalar kDrawPad = 10.f;
     static constexpr SkScalar kTestPad = 10.f;
@@ -177,19 +175,18 @@
     }
 
     void onOnceBeforeDraw() override {
-        SkBitmap bmp[2];
         SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
-        bmp[0].allocPixels(yinfo);
+        fBitmaps[0].allocPixels(yinfo);
         SkImageInfo uvinfo = SkImageInfo::MakeN32Premul(USIZE, USIZE);
-        bmp[1].allocPixels(uvinfo);
+        fBitmaps[1].allocPixels(uvinfo);
         int color[] = {0, 85, 170};
         const int limit[] = {255, 0, 255};
         const int invl[] = {0, 255, 0};
         const int inc[] = {1, -1, 1};
 
         {
-            unsigned char* pixels = (unsigned char*)bmp[0].getPixels();
-            const size_t nbBytes = bmp[0].rowBytes() * bmp[0].height();
+            unsigned char* pixels = (unsigned char*)fBitmaps[0].getPixels();
+            const size_t nbBytes = fBitmaps[0].rowBytes() * fBitmaps[0].height();
             for (size_t j = 0; j < nbBytes; ++j) {
                 pixels[j] = (unsigned char)color[0];
                 color[0] = (color[0] == limit[0]) ? invl[0] : color[0] + inc[0];
@@ -197,9 +194,9 @@
         }
 
         {
-            for (int y = 0; y < bmp[1].height(); ++y) {
-                uint32_t* pixels = bmp[1].getAddr32(0, y);
-                for (int j = 0; j < bmp[1].width(); ++j) {
+            for (int y = 0; y < fBitmaps[1].height(); ++y) {
+                uint32_t* pixels = fBitmaps[1].getAddr32(0, y);
+                for (int j = 0; j < fBitmaps[1].width(); ++j) {
                     pixels[j] = SkColorSetARGB(0, color[1], color[2], 0);
                     color[1] = (color[1] == limit[1]) ? invl[1] : color[1] + inc[1];
                     color[2] = (color[2] == limit[2]) ? invl[2] : color[2] + inc[2];
@@ -208,18 +205,17 @@
         }
 
         for (int i = 0; i < 2; ++i) {
-            fImage[i] = SkImage::MakeFromBitmap(bmp[i]);
+            fBitmaps[i].setImmutable();
         }
     }
 
     DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
                       SkCanvas* canvas, SkString* errorMsg) override {
-        GrProxyProvider* proxyProvider = context->priv().proxyProvider();
         sk_sp<GrTextureProxy> proxies[2];
 
         for (int i = 0; i < 2; ++i) {
-            proxies[i] = proxyProvider->createTextureProxy(fImage[i], 1, SkBudgeted::kYes,
-                                                           SkBackingFit::kExact);
+            GrBitmapTextureMaker maker(context, fBitmaps[i]);
+            std::tie(proxies[i], std::ignore) = maker.refTextureProxy(GrMipMapped::kNo);
             if (!proxies[i]) {
                 *errorMsg = "Failed to create proxy";
                 return DrawResult::kFail;
@@ -234,8 +230,8 @@
         };
 
         for (int space = kJPEG_SkYUVColorSpace; space <= kLastEnum_SkYUVColorSpace; ++space) {
-            SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fImage[0]->width()),
-                                               SkIntToScalar(fImage[0]->height()));
+            SkRect renderRect = SkRect::MakeWH(SkIntToScalar(fBitmaps[0].width()),
+                                               SkIntToScalar(fBitmaps[0].height()));
             renderRect.outset(kDrawPad, kDrawPad);
 
             SkScalar y = kDrawPad + kTestPad + space * kColorSpaceOffset;
@@ -260,7 +256,7 @@
     }
 
 private:
-    sk_sp<SkImage> fImage[2];
+    SkBitmap fBitmaps[2];
 
     static constexpr SkScalar kDrawPad = 10.f;
     static constexpr SkScalar kTestPad = 10.f;
@@ -292,32 +288,30 @@
     }
 
     void onOnceBeforeDraw() override {
-        SkBitmap bmp[3];
         SkImageInfo yinfo = SkImageInfo::MakeA8(YSIZE, YSIZE);
-        bmp[0].allocPixels(yinfo);
+        fBitmaps[0].allocPixels(yinfo);
         SkImageInfo uinfo = SkImageInfo::MakeA8(USIZE, USIZE);
-        bmp[1].allocPixels(uinfo);
+        fBitmaps[1].allocPixels(uinfo);
         SkImageInfo vinfo = SkImageInfo::MakeA8(VSIZE, VSIZE);
-        bmp[2].allocPixels(vinfo);
+        fBitmaps[2].allocPixels(vinfo);
 
         int innerColor[] = {149, 43, 21};
         int outerColor[] = {128, 128, 128};
         for (int i = 0; i < 3; ++i) {
-            bmp[i].eraseColor(SkColorSetARGB(outerColor[i], 0, 0, 0));
+            fBitmaps[i].eraseColor(SkColorSetARGB(outerColor[i], 0, 0, 0));
             SkIRect innerRect = i == 0 ? SkIRect::MakeLTRB(2, 2, 6, 6) : SkIRect::MakeLTRB(1, 1, 3, 3);
-            bmp[i].erase(SkColorSetARGB(innerColor[i], 0, 0, 0), innerRect);
-            fImage[i] = SkImage::MakeFromBitmap(bmp[i]);
+            fBitmaps[i].erase(SkColorSetARGB(innerColor[i], 0, 0, 0), innerRect);
+            fBitmaps[i].setImmutable();
         }
     }
 
     DrawResult onDraw(GrContext* context, GrRenderTargetContext* renderTargetContext,
                       SkCanvas* canvas, SkString* errorMsg) override {
-        GrProxyProvider* proxyProvider = context->priv().proxyProvider();
         sk_sp<GrTextureProxy> proxies[3];
 
         for (int i = 0; i < 3; ++i) {
-            proxies[i] = proxyProvider->createTextureProxy(fImage[i], 1, SkBudgeted::kYes,
-                                                           SkBackingFit::kExact);
+            GrBitmapTextureMaker maker(context, fBitmaps[i]);
+            std::tie(proxies[i], std::ignore) = maker.refTextureProxy(GrMipMapped::kNo);
             if (!proxies[i]) {
                 *errorMsg = "Failed to create proxy";
                 return DrawResult::kFail;
@@ -374,7 +368,7 @@
      }
 
 private:
-    sk_sp<SkImage> fImage[3];
+    SkBitmap fBitmaps[3];
 
     static constexpr SkScalar kDrawPad = 10.f;
     static constexpr SkScalar kTestPad = 10.f;
diff --git a/src/core/SkAutoPixmapStorage.cpp b/src/core/SkAutoPixmapStorage.cpp
index 3e20f0c..9b7a886 100644
--- a/src/core/SkAutoPixmapStorage.cpp
+++ b/src/core/SkAutoPixmapStorage.cpp
@@ -57,6 +57,18 @@
     SkASSERT_RELEASE(this->tryAlloc(info));
 }
 
+void* SkAutoPixmapStorage::detachPixels() {
+    if (!fStorage) {
+        return nullptr;
+    }
+
+    void* data = fStorage;
+    fStorage = nullptr;
+    this->INHERITED::reset();
+
+    return data;
+}
+
 sk_sp<SkData> SkAutoPixmapStorage::detachPixelsAsData() {
     if (!fStorage) {
         return nullptr;
diff --git a/src/core/SkAutoPixmapStorage.h b/src/core/SkAutoPixmapStorage.h
index ae869c0..385fc7c 100644
--- a/src/core/SkAutoPixmapStorage.h
+++ b/src/core/SkAutoPixmapStorage.h
@@ -48,6 +48,13 @@
     static size_t AllocSize(const SkImageInfo& info, size_t* rowBytes);
 
     /**
+    * Returns a void* of the allocated pixel memory and resets the pixmap. If the storage hasn't
+    * been allocated, the result is NULL. The caller is responsible for calling sk_free to free
+    * the returned memory.
+    */
+    void* SK_WARN_UNUSED_RESULT detachPixels();
+
+    /**
     *  Returns an SkData object wrapping the allocated pixels memory, and resets the pixmap.
     *  If the storage hasn't been allocated, the result is NULL.
     */
diff --git a/src/core/SkBlurMF.cpp b/src/core/SkBlurMF.cpp
index 5102f9e..84486c9 100644
--- a/src/core/SkBlurMF.cpp
+++ b/src/core/SkBlurMF.cpp
@@ -759,15 +759,14 @@
         return false;
     }
 
-    GrProxyProvider* proxyProvider = context->priv().proxyProvider();
     std::unique_ptr<GrFragmentProcessor> fp;
 
     if (devRRect.isRect() || SkRRectPriv::IsCircle(devRRect)) {
         if (devRRect.isRect()) {
-            fp = GrRectBlurEffect::Make(proxyProvider, *context->priv().caps()->shaderCaps(),
+            fp = GrRectBlurEffect::Make(context, *context->priv().caps()->shaderCaps(),
                                         devRRect.rect(), xformedSigma);
         } else {
-            fp = GrCircleBlurFragmentProcessor::Make(proxyProvider, devRRect.rect(), xformedSigma);
+            fp = GrCircleBlurFragmentProcessor::Make(context, devRRect.rect(), xformedSigma);
         }
 
         if (!fp) {
diff --git a/src/core/SkSpecialImage.cpp b/src/core/SkSpecialImage.cpp
index 46fa72d..9ab9b14 100644
--- a/src/core/SkSpecialImage.cpp
+++ b/src/core/SkSpecialImage.cpp
@@ -97,7 +97,6 @@
         return curContext->priv().matches(context) ? sk_ref_sp(this) : nullptr;
     }
 
-    auto proxyProvider = context->priv().proxyProvider();
     SkBitmap bmp;
     // At this point, we are definitely not texture-backed, so we must be raster or generator
     // backed. If we remove the special-wrapping-an-image subclass, we may be able to assert that
@@ -113,7 +112,7 @@
 
     // TODO: this is a tight copy of 'bmp' but it doesn't have to be (given SkSpecialImage's
     // semantics). Since this is cached though we would have to bake the fit into the cache key.
-    sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(proxyProvider, bmp);
+    sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(context, bmp);
     if (!proxy) {
         return nullptr;
     }
@@ -264,7 +263,7 @@
 #if SK_SUPPORT_GPU
     sk_sp<GrTextureProxy> onAsTextureProxyRef(GrRecordingContext* context) const override {
         if (context) {
-            return GrMakeCachedBitmapProxy(context->priv().proxyProvider(), fBitmap);
+            return GrMakeCachedBitmapProxy(context, fBitmap);
         }
 
         return nullptr;
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index 50bf3d4..451feba 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -341,13 +341,7 @@
     SkASSERT(kPremul_SkAlphaType == bitmap.alphaType());
     SkASSERT(bitmap.isImmutable());
 
-    sk_sp<SkImage> srcImage = SkImage::MakeFromBitmap(bitmap);
-    if (!srcImage) {
-        return nullptr;
-    }
-
-    sk_sp<GrTextureProxy> proxy = GrMakeCachedImageProxy(context->priv().proxyProvider(),
-                                                         std::move(srcImage));
+    sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(context, bitmap);
     if (!proxy) {
         return nullptr;
     }
diff --git a/src/gpu/GrBitmapTextureMaker.cpp b/src/gpu/GrBitmapTextureMaker.cpp
index 129f06e..a896ff6 100644
--- a/src/gpu/GrBitmapTextureMaker.cpp
+++ b/src/gpu/GrBitmapTextureMaker.cpp
@@ -27,10 +27,11 @@
 }
 
 GrBitmapTextureMaker::GrBitmapTextureMaker(GrRecordingContext* context, const SkBitmap& bitmap,
-                                           bool useDecal, bool shouldUseUniqueKey)
+                                           Cached cached, SkBackingFit fit, bool useDecal)
         : INHERITED(context, get_image_info(context, bitmap), useDecal)
-        , fBitmap(bitmap) {
-    if (!bitmap.isVolatile() && shouldUseUniqueKey) {
+        , fBitmap(bitmap)
+        , fFit(fit) {
+    if (!bitmap.isVolatile() && cached == Cached::kYes) {
         SkIPoint origin = bitmap.pixelRefOrigin();
         SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, bitmap.width(),
                                            bitmap.height());
@@ -65,11 +66,11 @@
                 return nullptr;
             }
             copy8888.setImmutable();
-            proxy = proxyProvider->createProxyFromBitmap(copy8888, willBeMipped ? GrMipMapped::kYes
-                                                                                : GrMipMapped::kNo);
+            proxy = proxyProvider->createProxyFromBitmap(
+                    copy8888, willBeMipped ? GrMipMapped::kYes : GrMipMapped::kNo, fFit);
         } else {
-            proxy = proxyProvider->createProxyFromBitmap(fBitmap, willBeMipped ? GrMipMapped::kYes
-                                                                               : GrMipMapped::kNo);
+            proxy = proxyProvider->createProxyFromBitmap(
+                    fBitmap, willBeMipped ? GrMipMapped::kYes : GrMipMapped::kNo, fFit);
         }
         if (proxy) {
             if (fOriginalKey.isValid()) {
diff --git a/src/gpu/GrBitmapTextureMaker.h b/src/gpu/GrBitmapTextureMaker.h
index 984cd40..717a41c 100644
--- a/src/gpu/GrBitmapTextureMaker.h
+++ b/src/gpu/GrBitmapTextureMaker.h
@@ -16,8 +16,11 @@
     subset of the pixelref specified by the bitmap. */
 class GrBitmapTextureMaker : public GrTextureMaker {
 public:
+    enum class Cached { kNo, kYes };
+
     GrBitmapTextureMaker(GrRecordingContext* context, const SkBitmap& bitmap,
-                         bool useDecal = false, bool shouldUseUniqueKey = true);
+                         Cached cached = Cached::kNo, SkBackingFit = SkBackingFit::kExact,
+                         bool useDecal = false);
 
 private:
     sk_sp<GrTextureProxy> refOriginalTextureProxy(bool willBeMipped,
@@ -26,8 +29,9 @@
     void makeCopyKey(const CopyParams& copyParams, GrUniqueKey* copyKey) override;
     void didCacheCopy(const GrUniqueKey& copyKey, uint32_t contextUniqueID) override;
 
-    const SkBitmap  fBitmap;
-    GrUniqueKey     fOriginalKey;
+    const SkBitmap     fBitmap;
+    const SkBackingFit fFit;
+    GrUniqueKey        fOriginalKey;
 
     typedef GrTextureMaker INHERITED;
 };
diff --git a/src/gpu/GrBlurUtils.cpp b/src/gpu/GrBlurUtils.cpp
index b2a7f65..c28cccf 100644
--- a/src/gpu/GrBlurUtils.cpp
+++ b/src/gpu/GrBlurUtils.cpp
@@ -8,6 +8,7 @@
 #include "src/gpu/GrBlurUtils.h"
 
 #include "include/private/GrRecordingContext.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrFixedClip.h"
 #include "src/gpu/GrProxyProvider.h"
@@ -145,13 +146,9 @@
         }
         bm.setImmutable();
 
-        sk_sp<SkImage> image = SkImage::MakeFromBitmap(bm);
-        if (!image) {
-            return false;
-        }
-
-        filteredMask = proxyProvider->createTextureProxy(std::move(image), 1, SkBudgeted::kYes,
-                                                         SkBackingFit::kApprox);
+        GrBitmapTextureMaker maker(context, bm, GrBitmapTextureMaker::Cached::kNo,
+                                   SkBackingFit::kApprox);
+        std::tie(filteredMask, std::ignore) = maker.refTextureProxy(GrMipMapped::kNo);
         if (!filteredMask) {
             return false;
         }
diff --git a/src/gpu/GrProxyProvider.cpp b/src/gpu/GrProxyProvider.cpp
index 7bbf53d..24057b0 100644
--- a/src/gpu/GrProxyProvider.cpp
+++ b/src/gpu/GrProxyProvider.cpp
@@ -250,83 +250,11 @@
     return result;
 }
 
-sk_sp<GrTextureProxy> GrProxyProvider::createTextureProxy(sk_sp<SkImage> srcImage,
-                                                          int sampleCnt,
-                                                          SkBudgeted budgeted,
-                                                          SkBackingFit fit,
-                                                          GrInternalSurfaceFlags surfaceFlags) {
-    ASSERT_SINGLE_OWNER
-    SkASSERT(srcImage);
-
-    if (this->isAbandoned()) {
-        return nullptr;
-    }
-
-    const SkImageInfo& info = srcImage->imageInfo();
-    GrColorType ct = SkColorTypeToGrColorType(info.colorType());
-
-    GrBackendFormat format = this->caps()->getDefaultBackendFormat(ct, GrRenderable::kNo);
-
-    if (!format.isValid()) {
-        SkBitmap copy8888;
-        if (!copy8888.tryAllocPixels(info.makeColorType(kRGBA_8888_SkColorType)) ||
-            !srcImage->readPixels(copy8888.pixmap(), 0, 0)) {
-            return nullptr;
-        }
-        copy8888.setImmutable();
-        srcImage = SkMakeImageFromRasterBitmap(copy8888, kNever_SkCopyPixelsMode);
-        ct = GrColorType::kRGBA_8888;
-        format = this->caps()->getDefaultBackendFormat(ct, GrRenderable::kNo);
-        if (!format.isValid()) {
-            return nullptr;
-        }
-    }
-
-    GrSurfaceDesc desc;
-    desc.fWidth = srcImage->width();
-    desc.fHeight = srcImage->height();
-
-    GrSwizzle swizzle = this->caps()->getReadSwizzle(format, ct);
-
-    sk_sp<GrTextureProxy> proxy = this->createLazyProxy(
-            [desc, format, sampleCnt, budgeted, srcImage, fit,
-             ct](GrResourceProvider* resourceProvider) {
-                SkPixmap pixMap;
-                SkAssertResult(srcImage->peekPixels(&pixMap));
-                GrMipLevel mipLevel = { pixMap.addr(), pixMap.rowBytes() };
-
-                return LazyCallbackResult(resourceProvider->createTexture(
-                        desc, format, ct, GrRenderable::kNo, sampleCnt, budgeted, fit,
-                        GrProtected::kNo, mipLevel));
-            },
-            format, desc, swizzle, GrRenderable::kNo, sampleCnt, kTopLeft_GrSurfaceOrigin,
-            GrMipMapped::kNo, GrMipMapsStatus::kNotAllocated, surfaceFlags, fit, budgeted,
-            GrProtected::kNo, UseAllocator::kYes);
-
-    if (!proxy) {
-        return nullptr;
-    }
-
-    GrContext* direct = fImageContext->priv().asDirectContext();
-    if (direct) {
-        GrResourceProvider* resourceProvider = direct->priv().resourceProvider();
-
-        // In order to reuse code we always create a lazy proxy. When we aren't in DDL mode however
-        // we're better off instantiating the proxy immediately here.
-        if (!proxy->priv().doLazyInstantiation(resourceProvider)) {
-            return nullptr;
-        }
-    }
-
-    SkASSERT(proxy->width() == desc.fWidth);
-    SkASSERT(proxy->height() == desc.fHeight);
-    return proxy;
-}
-
 sk_sp<GrTextureProxy> GrProxyProvider::createProxyFromBitmap(const SkBitmap& bitmap,
-                                                             GrMipMapped mipMapped) {
+                                                             GrMipMapped mipMapped,
+                                                             SkBackingFit fit) {
     ASSERT_SINGLE_OWNER
-    SkASSERT(GrMipMapped::kNo == mipMapped || this->caps()->mipMapSupport());
+    SkASSERT(fit == SkBackingFit::kExact || mipMapped == GrMipMapped::kNo);
 
     if (this->isAbandoned()) {
         return nullptr;
@@ -343,56 +271,107 @@
     // In non-ddl we will always instantiate right away. Thus we never want to copy the SkBitmap
     // even if its mutable. In ddl, if the bitmap is mutable then we must make a copy since the
     // upload of the data to the gpu can happen at anytime and the bitmap may change by then.
-    SkCopyPixelsMode copyMode = this->renderingDirectly() ? kNever_SkCopyPixelsMode
-                                                          : kIfMutable_SkCopyPixelsMode;
-    sk_sp<SkImage> baseLevel = SkMakeImageFromRasterBitmap(bitmap, copyMode);
-    if (!baseLevel) {
-        return nullptr;
+    SkBitmap copyBitmap = bitmap;
+    if (!this->renderingDirectly() && !bitmap.isImmutable()) {
+        copyBitmap.allocPixels();
+        if (!bitmap.readPixels(copyBitmap.pixmap())) {
+            return nullptr;
+        }
+        copyBitmap.setImmutable();
     }
 
-    // If mips weren't requested (or this was too small to have any), then take the fast path
-    if (GrMipMapped::kNo == mipMapped ||
-        0 == SkMipMap::ComputeLevelCount(baseLevel->width(), baseLevel->height())) {
-        return this->createTextureProxy(std::move(baseLevel), 1, SkBudgeted::kYes,
-                                        SkBackingFit::kExact);
-    }
-
-    GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bitmap.info());
-
-    GrColorType grCT = SkColorTypeToGrColorType(bitmap.info().colorType());
+    GrColorType grCT = SkColorTypeToGrColorType(copyBitmap.info().colorType());
     GrBackendFormat format = this->caps()->getDefaultBackendFormat(grCT, GrRenderable::kNo);
     if (!format.isValid()) {
         return nullptr;
     }
 
-    SkPixmap pixmap;
-    SkAssertResult(baseLevel->peekPixels(&pixmap));
-    sk_sp<SkMipMap> mipmaps(SkMipMap::Build(pixmap, nullptr));
+    sk_sp<GrTextureProxy> proxy;
+    if (mipMapped == GrMipMapped::kNo ||
+        0 == SkMipMap::ComputeLevelCount(copyBitmap.width(), copyBitmap.height())) {
+        proxy = this->createNonMippedProxyFromBitmap(copyBitmap, fit, format, grCT);
+    } else {
+        proxy = this->createMippedProxyFromBitmap(copyBitmap, format, grCT);
+    }
+
+    if (!proxy) {
+        return nullptr;
+    }
+
+    GrContext* direct = fImageContext->priv().asDirectContext();
+    if (direct) {
+        GrResourceProvider* resourceProvider = direct->priv().resourceProvider();
+
+        // In order to reuse code we always create a lazy proxy. When we aren't in DDL mode however
+        // we're better off instantiating the proxy immediately here.
+        if (!proxy->priv().doLazyInstantiation(resourceProvider)) {
+            return nullptr;
+        }
+    }
+    return proxy;
+}
+
+sk_sp<GrTextureProxy> GrProxyProvider::createNonMippedProxyFromBitmap(const SkBitmap& bitmap,
+                                                                      SkBackingFit fit,
+                                                                      const GrBackendFormat& format,
+                                                                      GrColorType colorType) {
+    GrSurfaceDesc desc;
+    desc.fWidth = bitmap.width();
+    desc.fHeight = bitmap.height();
+
+    GrSwizzle swizzle = this->caps()->getReadSwizzle(format, colorType);
+
+    sk_sp<GrTextureProxy> proxy = this->createLazyProxy(
+            [desc, format, bitmap, fit, colorType]
+            (GrResourceProvider* resourceProvider) {
+                GrMipLevel mipLevel = { bitmap.getPixels(), bitmap.rowBytes() };
+
+                return LazyCallbackResult(resourceProvider->createTexture(
+                        desc, format, colorType, GrRenderable::kNo, 1, SkBudgeted::kYes, fit,
+                        GrProtected::kNo, mipLevel));
+            },
+            format, desc, swizzle, GrRenderable::kNo, 1, kTopLeft_GrSurfaceOrigin,
+            GrMipMapped::kNo, GrMipMapsStatus::kNotAllocated, GrInternalSurfaceFlags::kNone, fit,
+            SkBudgeted::kYes, GrProtected::kNo, UseAllocator::kYes);
+
+    if (!proxy) {
+        return nullptr;
+    }
+    SkASSERT(proxy->width() == desc.fWidth);
+    SkASSERT(proxy->height() == desc.fHeight);
+    return proxy;
+}
+
+sk_sp<GrTextureProxy> GrProxyProvider::createMippedProxyFromBitmap(const SkBitmap& bitmap,
+                                                                   const GrBackendFormat& format,
+                                                                   GrColorType colorType) {
+    SkASSERT(this->caps()->mipMapSupport());
+
+    GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bitmap.info());
+
+    sk_sp<SkMipMap> mipmaps(SkMipMap::Build(bitmap.pixmap(), nullptr));
     if (!mipmaps) {
         return nullptr;
     }
 
-    GrSwizzle readSwizzle = this->caps()->getReadSwizzle(format, grCT);
+    GrSwizzle readSwizzle = this->caps()->getReadSwizzle(format, colorType);
 
     sk_sp<GrTextureProxy> proxy = this->createLazyProxy(
-            [desc, format, baseLevel, mipmaps](GrResourceProvider* resourceProvider) {
+            [desc, format, bitmap, mipmaps](GrResourceProvider* resourceProvider) {
                 const int mipLevelCount = mipmaps->countLevels() + 1;
                 std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipLevelCount]);
 
-                SkPixmap pixmap;
-                SkAssertResult(baseLevel->peekPixels(&pixmap));
+                texels[0].fPixels = bitmap.getPixels();
+                texels[0].fRowBytes = bitmap.rowBytes();
 
-                texels[0].fPixels = pixmap.addr();
-                texels[0].fRowBytes = pixmap.rowBytes();
-
-                auto colorType = SkColorTypeToGrColorType(pixmap.colorType());
+                auto colorType = SkColorTypeToGrColorType(bitmap.colorType());
                 for (int i = 1; i < mipLevelCount; ++i) {
                     SkMipMap::Level generatedMipLevel;
                     mipmaps->getLevel(i - 1, &generatedMipLevel);
                     texels[i].fPixels = generatedMipLevel.fPixmap.addr();
                     texels[i].fRowBytes = generatedMipLevel.fPixmap.rowBytes();
                     SkASSERT(texels[i].fPixels);
-                    SkASSERT(generatedMipLevel.fPixmap.colorType() == pixmap.colorType());
+                    SkASSERT(generatedMipLevel.fPixmap.colorType() == bitmap.colorType());
                 }
                 return LazyCallbackResult(resourceProvider->createTexture(
                         desc, format, colorType, GrRenderable::kNo, 1, SkBudgeted::kYes,
@@ -406,15 +385,9 @@
         return nullptr;
     }
 
-    GrContext* direct = fImageContext->priv().asDirectContext();
-    if (direct) {
-        GrResourceProvider* resourceProvider = direct->priv().resourceProvider();
-        // In order to reuse code we always create a lazy proxy. When we aren't in DDL mode however
-        // we're better off instantiating the proxy immediately here.
-        if (!proxy->priv().doLazyInstantiation(resourceProvider)) {
-            return nullptr;
-        }
-    }
+    SkASSERT(proxy->width() == desc.fWidth);
+    SkASSERT(proxy->height() == desc.fHeight);
+
     return proxy;
 }
 
diff --git a/src/gpu/GrProxyProvider.h b/src/gpu/GrProxyProvider.h
index 120f3f2..6b13d42 100644
--- a/src/gpu/GrProxyProvider.h
+++ b/src/gpu/GrProxyProvider.h
@@ -64,18 +64,10 @@
                                                        UseAllocator = UseAllocator::kYes);
 
     /*
-     * Create an un-mipmapped texture proxy with data. The SkImage must be a raster backend image.
-     * Since the SkImage is ref counted, we simply take a ref on it to keep the data alive until we
-     * actually upload the data to the gpu.
-     */
-    sk_sp<GrTextureProxy> createTextureProxy(
-            sk_sp<SkImage> srcImage, int sampleCnt, SkBudgeted, SkBackingFit,
-            GrInternalSurfaceFlags = GrInternalSurfaceFlags::kNone);
-
-    /*
      * Creates a new texture proxy for the bitmap, optionally with mip levels generated by the cpu.
      */
-    sk_sp<GrTextureProxy> createProxyFromBitmap(const SkBitmap& bitmap, GrMipMapped);
+    sk_sp<GrTextureProxy> createProxyFromBitmap(const SkBitmap& bitmap, GrMipMapped,
+                                                SkBackingFit fit);
 
     /*
      * Create a GrSurfaceProxy without any data.
@@ -282,6 +274,20 @@
 
     bool isAbandoned() const;
 
+    /*
+     * Create an un-mipmapped texture proxy for the bitmap.
+     */
+    sk_sp<GrTextureProxy> createNonMippedProxyFromBitmap(const SkBitmap& bitmap,
+                                                         SkBackingFit fit,
+                                                         const GrBackendFormat& format,
+                                                         GrColorType colorType);
+    /*
+     * Create an mipmapped texture proxy for the bitmap.
+     */
+    sk_sp<GrTextureProxy> createMippedProxyFromBitmap(const SkBitmap& bitmap,
+                                                      const GrBackendFormat& format,
+                                                      GrColorType colorType);
+
     // GrColorType is used to determine the proxy's texture swizzle.
     sk_sp<GrTextureProxy> createWrapped(sk_sp<GrTexture> tex, GrColorType, GrSurfaceOrigin origin,
                                         UseAllocator useAllocator);
diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp
index 879a028..65db58d 100644
--- a/src/gpu/GrSWMaskHelper.cpp
+++ b/src/gpu/GrSWMaskHelper.cpp
@@ -8,6 +8,7 @@
 #include "src/gpu/GrSWMaskHelper.h"
 
 #include "include/private/GrRecordingContext.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrProxyProvider.h"
 #include "src/gpu/GrRecordingContextPriv.h"
@@ -97,16 +98,13 @@
     SkImageInfo ii = SkImageInfo::MakeA8(fPixels->width(), fPixels->height());
     size_t rowBytes = fPixels->rowBytes();
 
-    sk_sp<SkData> data = fPixels->detachPixelsAsData();
-    if (!data) {
-        return nullptr;
-    }
+    SkBitmap bitmap;
+    SkAssertResult(bitmap.installPixels(ii, fPixels->detachPixels(), rowBytes,
+                                        [](void* addr, void* context) { sk_free(addr); },
+                                        nullptr));
+    bitmap.setImmutable();
 
-    sk_sp<SkImage> img = SkImage::MakeRasterData(ii, std::move(data), rowBytes);
-    if (!img) {
-        return nullptr;
-    }
-
-    return context->priv().proxyProvider()->createTextureProxy(std::move(img), 1, SkBudgeted::kYes,
-                                                               fit);
+    GrBitmapTextureMaker maker(context, bitmap, GrBitmapTextureMaker::Cached::kNo, fit);
+    auto [texture, ct] = maker.refTextureProxy(GrMipMapped::kNo);
+    return texture;
 }
diff --git a/src/gpu/GrYUVProvider.cpp b/src/gpu/GrYUVProvider.cpp
index 178554a..4cb2fe9 100644
--- a/src/gpu/GrYUVProvider.cpp
+++ b/src/gpu/GrYUVProvider.cpp
@@ -14,6 +14,7 @@
 #include "src/core/SkCachedData.h"
 #include "src/core/SkResourceCache.h"
 #include "src/core/SkYUVPlanesCache.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrCaps.h"
 #include "src/gpu/GrClip.h"
 #include "src/gpu/GrColorSpaceXform.h"
@@ -97,7 +98,7 @@
     return data;
 }
 
-void GrYUVProvider::YUVGen_DataReleaseProc(const void*, void* data) {
+void GrYUVProvider::YUVGen_DataReleaseProc(void*, void* data) {
     SkCachedData* cachedData = static_cast<SkCachedData*>(data);
     SkASSERT(cachedData);
     cachedData->unref();
@@ -137,21 +138,23 @@
                     ? SkBackingFit::kExact : SkBackingFit::kApprox;
 
         SkImageInfo imageInfo = SkImageInfo::MakeA8(componentWidth, componentHeight);
-        SkPixmap pixmap(imageInfo, planes[i], yuvSizeInfo.fWidthBytes[i]);
         SkCachedData* dataStoragePtr = dataStorage.get();
-        // We grab a ref to cached yuv data. When the SkImage we create below goes away it will call
-        // the YUVGen_DataReleaseProc which will release this ref.
+        // We grab a ref to cached yuv data. When the SkBitmap we create below goes away it will
+        // call the YUVGen_DataReleaseProc which will release this ref.
         // DDL TODO: Currently we end up creating a lazy proxy that will hold onto a ref to the
         // SkImage in its lambda. This means that we'll keep the ref on the YUV data around for the
         // life time of the proxy and not just upload. For non-DDL draws we should look into
         // releasing this SkImage after uploads (by deleting the lambda after instantiation).
         dataStoragePtr->ref();
-        sk_sp<SkImage> yuvImage = SkImage::MakeFromRaster(pixmap, YUVGen_DataReleaseProc,
-                                                          dataStoragePtr);
+        SkBitmap bitmap;
+        SkAssertResult(bitmap.installPixels(imageInfo, const_cast<void*>(planes[i]),
+                                            yuvSizeInfo.fWidthBytes[i],
+                                            YUVGen_DataReleaseProc, dataStoragePtr));
+        bitmap.setImmutable();
 
-        auto proxyProvider = ctx->priv().proxyProvider();
-        yuvTextureProxies[i] =
-                proxyProvider->createTextureProxy(yuvImage, 1, SkBudgeted::kYes, fit);
+        GrBitmapTextureMaker maker(ctx, bitmap, GrBitmapTextureMaker::Cached::kNo, fit);
+        std::tie(yuvTextureProxies[i], std::ignore) = maker.refTextureProxy(GrMipMapped::kNo);
+
         if (!yuvTextureProxies[i]) {
             return nullptr;
         }
diff --git a/src/gpu/GrYUVProvider.h b/src/gpu/GrYUVProvider.h
index 330c7fb..bfd1ddc 100644
--- a/src/gpu/GrYUVProvider.h
+++ b/src/gpu/GrYUVProvider.h
@@ -87,7 +87,7 @@
     // This is used as release callback for the YUV data that we capture in an SkImage when
     // uploading to a gpu. When the upload is complete and we release the SkImage this callback will
     // release the underlying data.
-    static void YUVGen_DataReleaseProc(const void*, void* data);
+    static void YUVGen_DataReleaseProc(void*, void* data);
 };
 
 #endif
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index ba29ad7..2710b03 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1183,15 +1183,14 @@
             return;
         }
     }
-    GrBitmapTextureMaker maker(fContext.get(), bitmap);
+    GrBitmapTextureMaker maker(fContext.get(), bitmap, GrBitmapTextureMaker::Cached::kYes);
     this->drawTextureProducer(&maker, src, dst, constraint, this->localToDevice(), paint, true);
 }
 
 sk_sp<SkSpecialImage> SkGpuDevice::makeSpecial(const SkBitmap& bitmap) {
     // TODO: this makes a tight copy of 'bitmap' but it doesn't have to be (given SkSpecialImage's
     // semantics). Since this is cached we would have to bake the fit into the cache key though.
-    sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(fContext->priv().proxyProvider(),
-                                                          bitmap);
+    sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(fContext.get(), bitmap);
     if (!proxy) {
         return nullptr;
     }
@@ -1327,7 +1326,7 @@
             GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint);
             this->drawProducerLattice(&maker, std::move(iter), dst, paint);
         } else if (as_IB(image)->getROPixels(&bm)) {
-            GrBitmapTextureMaker maker(fContext.get(), bm);
+            GrBitmapTextureMaker maker(fContext.get(), bm, GrBitmapTextureMaker::Cached::kYes);
             this->drawProducerLattice(&maker, std::move(iter), dst, paint);
         }
     }
@@ -1337,7 +1336,7 @@
                                  const SkRect& dst, const SkPaint& paint) {
     ASSERT_SINGLE_OWNER
     auto iter = std::make_unique<SkLatticeIter>(bitmap.width(), bitmap.height(), center, dst);
-    GrBitmapTextureMaker maker(fContext.get(), bitmap);
+    GrBitmapTextureMaker maker(fContext.get(), bitmap, GrBitmapTextureMaker::Cached::kYes);
     this->drawProducerLattice(&maker, std::move(iter), dst, paint);
 }
 
@@ -1391,7 +1390,7 @@
             GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint);
             this->drawProducerLattice(&maker, std::move(iter), dst, paint);
         } else if (as_IB(image)->getROPixels(&bm)) {
-            GrBitmapTextureMaker maker(fContext.get(), bm);
+            GrBitmapTextureMaker maker(fContext.get(), bm, GrBitmapTextureMaker::Cached::kYes);
             this->drawProducerLattice(&maker, std::move(iter), dst, paint);
         }
     }
@@ -1402,7 +1401,7 @@
                                     const SkPaint& paint) {
     ASSERT_SINGLE_OWNER
     auto iter = std::make_unique<SkLatticeIter>(lattice, dst);
-    GrBitmapTextureMaker maker(fContext.get(), bitmap);
+    GrBitmapTextureMaker maker(fContext.get(), bitmap, GrBitmapTextureMaker::Cached::kYes);
     this->drawProducerLattice(&maker, std::move(iter), dst, paint);
 }
 
diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp
index 6d5d0f0..26c988a 100644
--- a/src/gpu/SkGpuDevice_drawTexture.cpp
+++ b/src/gpu/SkGpuDevice_drawTexture.cpp
@@ -452,7 +452,8 @@
         return;
     }
     if (as_IB(image)->getROPixels(&bm)) {
-        GrBitmapTextureMaker maker(fContext.get(), bm, useDecal);
+        GrBitmapTextureMaker maker(fContext.get(), bm, GrBitmapTextureMaker::Cached::kYes,
+                                   SkBackingFit::kExact, useDecal);
         draw_texture_producer(fContext.get(), fRenderTargetContext.get(), this->clip(), ctm,
                               paint, &maker, src, dst, dstClip, srcToDst, aa, aaFlags, constraint,
                               attemptDrawTexture);
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index fa11800..74e43fe 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -140,75 +140,19 @@
                                                     const SkBitmap& bitmap,
                                                     GrSamplerState params,
                                                     SkScalar scaleAdjust[2]) {
-    return GrBitmapTextureMaker(ctx, bitmap).refTextureProxyForParams(params, scaleAdjust);
+    GrBitmapTextureMaker maker(ctx, bitmap, GrBitmapTextureMaker::Cached::kYes);
+    return maker.refTextureProxyForParams(params, scaleAdjust);
 }
 
-sk_sp<GrTextureProxy> GrMakeCachedBitmapProxy(GrProxyProvider* proxyProvider,
+sk_sp<GrTextureProxy> GrMakeCachedBitmapProxy(GrRecordingContext* context,
                                               const SkBitmap& bitmap,
                                               SkBackingFit fit) {
     if (!bitmap.peekPixels(nullptr)) {
         return nullptr;
     }
 
-    // In non-ddl we will always instantiate right away. Thus we never want to copy the SkBitmap
-    // even if its mutable. In ddl, if the bitmap is mutable then we must make a copy since the
-    // upload of the data to the gpu can happen at anytime and the bitmap may change by then.
-    SkCopyPixelsMode cpyMode = proxyProvider->renderingDirectly() ? kNever_SkCopyPixelsMode
-                                                                  : kIfMutable_SkCopyPixelsMode;
-    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, cpyMode);
-
-    if (!image) {
-        return nullptr;
-    }
-
-    return GrMakeCachedImageProxy(proxyProvider, std::move(image), fit);
-}
-
-static void create_unique_key_for_image(const SkImage* image, GrUniqueKey* result) {
-    if (!image) {
-        result->reset(); // will be invalid
-        return;
-    }
-
-    if (const SkBitmap* bm = as_IB(image)->onPeekBitmap()) {
-        if (!bm->isVolatile()) {
-            SkIPoint origin = bm->pixelRefOrigin();
-            SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, bm->width(), bm->height());
-            GrMakeKeyFromImageID(result, bm->getGenerationID(), subset);
-        }
-        return;
-    }
-
-    GrMakeKeyFromImageID(result, image->uniqueID(), image->bounds());
-}
-
-sk_sp<GrTextureProxy> GrMakeCachedImageProxy(GrProxyProvider* proxyProvider,
-                                             sk_sp<SkImage> srcImage,
-                                             SkBackingFit fit) {
-    sk_sp<GrTextureProxy> proxy;
-    GrUniqueKey originalKey;
-
-    create_unique_key_for_image(srcImage.get(), &originalKey);
-
-    if (originalKey.isValid()) {
-        proxy = proxyProvider->findOrCreateProxyByUniqueKey(
-                originalKey, SkColorTypeToGrColorType(srcImage->colorType()),
-                kTopLeft_GrSurfaceOrigin);
-    }
-    if (!proxy) {
-        proxy = proxyProvider->createTextureProxy(srcImage, 1, SkBudgeted::kYes, fit);
-        if (proxy && originalKey.isValid()) {
-            proxyProvider->assignUniqueKeyToProxy(originalKey, proxy.get());
-            const SkBitmap* bm = as_IB(srcImage.get())->onPeekBitmap();
-            // When recording DDLs we do not want to install change listeners because doing
-            // so isn't threadsafe.
-            if (bm && proxyProvider->renderingDirectly()) {
-                GrInstallBitmapUniqueKeyInvalidator(originalKey, proxyProvider->contextID(),
-                                                    bm->pixelRef());
-            }
-        }
-    }
-
+    GrBitmapTextureMaker maker(context, bitmap, GrBitmapTextureMaker::Cached::kYes, fit);
+    auto [proxy, ct] = maker.refTextureProxy(GrMipMapped::kNo);
     return proxy;
 }
 
diff --git a/src/gpu/SkGr.h b/src/gpu/SkGr.h
index 19a3f49..33c5146 100644
--- a/src/gpu/SkGr.h
+++ b/src/gpu/SkGr.h
@@ -185,19 +185,12 @@
                                                      GrColorType srcColorType);
 
 /*
- * Create a texture proxy from the provided bitmap by wrapping it in an image and calling
- * GrMakeCachedImageProxy.
+ * Create a texture proxy from the provided bitmap and add it to the texture cache
+ * using the key also extracted from 'bitmp'.
  */
-sk_sp<GrTextureProxy> GrMakeCachedBitmapProxy(GrProxyProvider*, const SkBitmap& bitmap,
+sk_sp<GrTextureProxy> GrMakeCachedBitmapProxy(GrRecordingContext*, const SkBitmap& bitmap,
                                               SkBackingFit fit = SkBackingFit::kExact);
 
-/*
- * Create a texture proxy from the provided 'srcImage' and add it to the texture cache
- * using the key also extracted from 'srcImage'.
- */
-sk_sp<GrTextureProxy> GrMakeCachedImageProxy(GrProxyProvider*, sk_sp<SkImage> srcImage,
-                                             SkBackingFit fit = SkBackingFit::kExact);
-
 /**
  *  Our key includes the offset, width, and height so that bitmaps created by extractSubset()
  *  are unique.
diff --git a/src/gpu/effects/GrCircleBlurFragmentProcessor.fp b/src/gpu/effects/GrCircleBlurFragmentProcessor.fp
index a710b2d..d71e9d6 100644
--- a/src/gpu/effects/GrCircleBlurFragmentProcessor.fp
+++ b/src/gpu/effects/GrCircleBlurFragmentProcessor.fp
@@ -21,7 +21,7 @@
 }
 
 @make {
-    static std::unique_ptr<GrFragmentProcessor> Make(GrProxyProvider*,
+    static std::unique_ptr<GrFragmentProcessor> Make(GrRecordingContext*,
                                                      const SkRect& circle, float sigma);
 }
 
@@ -31,7 +31,11 @@
 }
 
 @cpp {
+    #include "include/gpu/GrContext.h"
+    #include "include/private/GrRecordingContext.h"
+    #include "src/gpu/GrBitmapTextureMaker.h"
     #include "src/gpu/GrProxyProvider.h"
+    #include "src/gpu/GrRecordingContextPriv.h"
 
     // Computes an unnormalized half kernel (right side). Returns the summation of all the half
     // kernel values.
@@ -184,7 +188,7 @@
         profile[profileWidth - 1] = 0;
     }
 
-    static sk_sp<GrTextureProxy> create_profile_texture(GrProxyProvider* proxyProvider,
+    static sk_sp<GrTextureProxy> create_profile_texture(GrRecordingContext* context,
                                                         const SkRect& circle,
                                                         float sigma,
                                                         float* solidRadius, float* textureRadius) {
@@ -226,6 +230,7 @@
         builder[0] = sigmaToCircleRRatioFixed;
         builder.finish();
 
+        GrProxyProvider* proxyProvider = context->priv().proxyProvider();
         sk_sp<GrTextureProxy> blurProfile = proxyProvider->findOrCreateProxyByUniqueKey(
                 key, GrColorType::kAlpha_8, kTopLeft_GrSurfaceOrigin);
         if (!blurProfile) {
@@ -246,10 +251,9 @@
             }
 
             bm.setImmutable();
-            sk_sp<SkImage> image = SkImage::MakeFromBitmap(bm);
 
-            blurProfile = proxyProvider->createTextureProxy(std::move(image), 1,
-                                                            SkBudgeted::kYes, SkBackingFit::kExact);
+            GrBitmapTextureMaker maker(context, bm);
+            std::tie(blurProfile, std::ignore) = maker.refTextureProxy(GrMipMapped::kNo);
             if (!blurProfile) {
                 return nullptr;
             }
@@ -260,10 +264,10 @@
     }
 
     std::unique_ptr<GrFragmentProcessor> GrCircleBlurFragmentProcessor::Make(
-            GrProxyProvider* proxyProvider, const SkRect& circle, float sigma) {
+            GrRecordingContext* context, const SkRect& circle, float sigma) {
         float solidRadius;
         float textureRadius;
-        sk_sp<GrTextureProxy> profile(create_profile_texture(proxyProvider, circle, sigma,
+        sk_sp<GrTextureProxy> profile(create_profile_texture(context, circle, sigma,
                                                              &solidRadius, &textureRadius));
         if (!profile) {
             return nullptr;
@@ -286,5 +290,5 @@
     SkScalar wh = testData->fRandom->nextRangeScalar(100.f, 1000.f);
     SkScalar sigma = testData->fRandom->nextRangeF(1.f,10.f);
     SkRect circle = SkRect::MakeWH(wh, wh);
-    return GrCircleBlurFragmentProcessor::Make(testData->proxyProvider(), circle, sigma);
+    return GrCircleBlurFragmentProcessor::Make(testData->context(), circle, sigma);
 }
diff --git a/src/gpu/effects/GrConfigConversionEffect.fp b/src/gpu/effects/GrConfigConversionEffect.fp
index 0ff5b2d..ae4c26c 100644
--- a/src/gpu/effects/GrConfigConversionEffect.fp
+++ b/src/gpu/effects/GrConfigConversionEffect.fp
@@ -7,10 +7,10 @@
 
 @header {
     #include "include/gpu/GrContext.h"
+    #include "src/gpu/GrBitmapTextureMaker.h"
     #include "src/gpu/GrClip.h"
     #include "src/gpu/GrContextPriv.h"
     #include "src/gpu/GrImageInfo.h"
-    #include "src/gpu/GrProxyProvider.h"
     #include "src/gpu/GrRenderTargetContext.h"
 }
 
@@ -52,18 +52,16 @@
         // draw
         readRTC->discard();
 
-        GrProxyProvider* proxyProvider = context->priv().proxyProvider();
-
-        SkPixmap pixmap(ii, srcData, 4 * kSize);
-
         // This function is only ever called if we are in a GrContext that has a GrGpu since we are
         // calling read pixels here. Thus the pixel data will be uploaded immediately and we don't
         // need to keep the pixel data alive in the proxy. Therefore the ReleaseProc is nullptr.
-        sk_sp<SkImage> image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr);
-        sk_sp<GrTextureProxy> dataProxy = proxyProvider->createTextureProxy(std::move(image),
-                                                                            1,
-                                                                            SkBudgeted::kYes,
-                                                                            SkBackingFit::kExact);
+        SkBitmap bitmap;
+        bitmap.installPixels(ii, srcData, 4 * kSize);
+        bitmap.setImmutable();
+
+        GrBitmapTextureMaker maker(context, bitmap);
+        auto [dataProxy, ct] = maker.refTextureProxy(GrMipMapped::kNo);
+
         if (!dataProxy) {
             return false;
         }
diff --git a/src/gpu/effects/GrRectBlurEffect.fp b/src/gpu/effects/GrRectBlurEffect.fp
index 8cf364b..34f7112 100644
--- a/src/gpu/effects/GrRectBlurEffect.fp
+++ b/src/gpu/effects/GrRectBlurEffect.fp
@@ -9,9 +9,13 @@
 #include <cmath>
 #include "include/core/SkRect.h"
 #include "include/core/SkScalar.h"
+#include "include/gpu/GrContext.h"
+#include "include/private/GrRecordingContext.h"
 #include "src/core/SkBlurMask.h"
 #include "src/core/SkMathPriv.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrProxyProvider.h"
+#include "src/gpu/GrRecordingContextPriv.h"
 #include "src/gpu/GrShaderCaps.h"
 }
 
@@ -42,7 +46,7 @@
     samplerParams
 }
 @class {
-static sk_sp<GrTextureProxy> CreateIntegralTexture(GrProxyProvider* proxyProvider,
+static sk_sp<GrTextureProxy> CreateIntegralTexture(GrRecordingContext* context,
                                                    float sixSigma) {
     // The texture we're producing represents the integral of a normal distribution over a six-sigma
     // range centered at zero. We want enough resolution so that the linear interpolation done in
@@ -58,6 +62,7 @@
     builder[0] = width;
     builder.finish();
 
+    GrProxyProvider* proxyProvider = context->priv().proxyProvider();
     sk_sp<GrTextureProxy> proxy(proxyProvider->findOrCreateProxyByUniqueKey(
             key, GrColorType::kAlpha_8, kTopLeft_GrSurfaceOrigin));
     if (!proxy) {
@@ -75,10 +80,9 @@
         }
         *bitmap.getAddr8(width - 1, 0) = 0;
         bitmap.setImmutable();
-        // We directly call the proxyProvider instead of going through GrBitmapTextureMaker. This
-        // means we won't fall back to RGBA_8888. But we should have support for a single channel
-        // unorm format so we shouldn't need the fallback.
-        proxy = proxyProvider->createProxyFromBitmap(bitmap, GrMipMapped::kNo);
+
+        GrBitmapTextureMaker maker(context, bitmap);
+        std::tie(proxy, std::ignore) = maker.refTextureProxy(GrMipMapped::kNo);
         if (!proxy) {
             return nullptr;
         }
@@ -90,7 +94,7 @@
 }
 
 @make {
-     static std::unique_ptr<GrFragmentProcessor> Make(GrProxyProvider* proxyProvider,
+     static std::unique_ptr<GrFragmentProcessor> Make(GrRecordingContext* context,
                                                       const GrShaderCaps& caps,
                                                       const SkRect& rect, float sigma) {
          SkASSERT(rect.isSorted());
@@ -105,7 +109,7 @@
          }
 
          const float sixSigma = 6 * sigma;
-         auto integral = CreateIntegralTexture(proxyProvider, sixSigma);
+         auto integral = CreateIntegralTexture(context, sixSigma);
          if (!integral) {
              return nullptr;
          }
@@ -203,6 +207,6 @@
     float sigma = data->fRandom->nextRangeF(3,8);
     float width = data->fRandom->nextRangeF(200,300);
     float height = data->fRandom->nextRangeF(200,300);
-    return GrRectBlurEffect::Make(data->proxyProvider(), *data->caps()->shaderCaps(),
+    return GrRectBlurEffect::Make(data->context(), *data->caps()->shaderCaps(),
                                   SkRect::MakeWH(width, height), sigma);
 }
diff --git a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp b/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp
index 863e88f..a9e477c 100644
--- a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp
+++ b/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.cpp
@@ -10,7 +10,11 @@
  **************************************************************************************************/
 #include "GrCircleBlurFragmentProcessor.h"
 
+#include "include/gpu/GrContext.h"
+#include "include/private/GrRecordingContext.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrProxyProvider.h"
+#include "src/gpu/GrRecordingContextPriv.h"
 
 // Computes an unnormalized half kernel (right side). Returns the summation of all the half
 // kernel values.
@@ -162,7 +166,7 @@
     profile[profileWidth - 1] = 0;
 }
 
-static sk_sp<GrTextureProxy> create_profile_texture(GrProxyProvider* proxyProvider,
+static sk_sp<GrTextureProxy> create_profile_texture(GrRecordingContext* context,
                                                     const SkRect& circle, float sigma,
                                                     float* solidRadius, float* textureRadius) {
     float circleR = circle.width() / 2.0f;
@@ -203,6 +207,7 @@
     builder[0] = sigmaToCircleRRatioFixed;
     builder.finish();
 
+    GrProxyProvider* proxyProvider = context->priv().proxyProvider();
     sk_sp<GrTextureProxy> blurProfile = proxyProvider->findOrCreateProxyByUniqueKey(
             key, GrColorType::kAlpha_8, kTopLeft_GrSurfaceOrigin);
     if (!blurProfile) {
@@ -223,10 +228,9 @@
         }
 
         bm.setImmutable();
-        sk_sp<SkImage> image = SkImage::MakeFromBitmap(bm);
 
-        blurProfile = proxyProvider->createTextureProxy(std::move(image), 1, SkBudgeted::kYes,
-                                                        SkBackingFit::kExact);
+        GrBitmapTextureMaker maker(context, bm);
+        std::tie(blurProfile, std::ignore) = maker.refTextureProxy(GrMipMapped::kNo);
         if (!blurProfile) {
             return nullptr;
         }
@@ -237,11 +241,11 @@
 }
 
 std::unique_ptr<GrFragmentProcessor> GrCircleBlurFragmentProcessor::Make(
-        GrProxyProvider* proxyProvider, const SkRect& circle, float sigma) {
+        GrRecordingContext* context, const SkRect& circle, float sigma) {
     float solidRadius;
     float textureRadius;
     sk_sp<GrTextureProxy> profile(
-            create_profile_texture(proxyProvider, circle, sigma, &solidRadius, &textureRadius));
+            create_profile_texture(context, circle, sigma, &solidRadius, &textureRadius));
     if (!profile) {
         return nullptr;
     }
@@ -346,6 +350,6 @@
     SkScalar wh = testData->fRandom->nextRangeScalar(100.f, 1000.f);
     SkScalar sigma = testData->fRandom->nextRangeF(1.f, 10.f);
     SkRect circle = SkRect::MakeWH(wh, wh);
-    return GrCircleBlurFragmentProcessor::Make(testData->proxyProvider(), circle, sigma);
+    return GrCircleBlurFragmentProcessor::Make(testData->context(), circle, sigma);
 }
 #endif
diff --git a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h b/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h
index a18e756..a0e0396 100644
--- a/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h
+++ b/src/gpu/effects/generated/GrCircleBlurFragmentProcessor.h
@@ -17,7 +17,7 @@
 #include "src/gpu/GrFragmentProcessor.h"
 class GrCircleBlurFragmentProcessor : public GrFragmentProcessor {
 public:
-    static std::unique_ptr<GrFragmentProcessor> Make(GrProxyProvider*, const SkRect& circle,
+    static std::unique_ptr<GrFragmentProcessor> Make(GrRecordingContext*, const SkRect& circle,
                                                      float sigma);
     GrCircleBlurFragmentProcessor(const GrCircleBlurFragmentProcessor& src);
     std::unique_ptr<GrFragmentProcessor> clone() const override;
diff --git a/src/gpu/effects/generated/GrConfigConversionEffect.h b/src/gpu/effects/generated/GrConfigConversionEffect.h
index e892712..51045bc 100644
--- a/src/gpu/effects/generated/GrConfigConversionEffect.h
+++ b/src/gpu/effects/generated/GrConfigConversionEffect.h
@@ -14,10 +14,10 @@
 #include "include/private/SkM44.h"
 
 #include "include/gpu/GrContext.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrClip.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrImageInfo.h"
-#include "src/gpu/GrProxyProvider.h"
 #include "src/gpu/GrRenderTargetContext.h"
 
 #include "src/gpu/GrCoordTransform.h"
@@ -61,16 +61,16 @@
         // draw
         readRTC->discard();
 
-        GrProxyProvider* proxyProvider = context->priv().proxyProvider();
-
-        SkPixmap pixmap(ii, srcData, 4 * kSize);
-
         // This function is only ever called if we are in a GrContext that has a GrGpu since we are
         // calling read pixels here. Thus the pixel data will be uploaded immediately and we don't
         // need to keep the pixel data alive in the proxy. Therefore the ReleaseProc is nullptr.
-        sk_sp<SkImage> image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr);
-        sk_sp<GrTextureProxy> dataProxy = proxyProvider->createTextureProxy(
-                std::move(image), 1, SkBudgeted::kYes, SkBackingFit::kExact);
+        SkBitmap bitmap;
+        bitmap.installPixels(ii, srcData, 4 * kSize);
+        bitmap.setImmutable();
+
+        GrBitmapTextureMaker maker(context, bitmap);
+        auto[dataProxy, ct] = maker.refTextureProxy(GrMipMapped::kNo);
+
         if (!dataProxy) {
             return false;
         }
diff --git a/src/gpu/effects/generated/GrRectBlurEffect.cpp b/src/gpu/effects/generated/GrRectBlurEffect.cpp
index 8ba9506..9cc89b7 100644
--- a/src/gpu/effects/generated/GrRectBlurEffect.cpp
+++ b/src/gpu/effects/generated/GrRectBlurEffect.cpp
@@ -188,7 +188,7 @@
     float sigma = data->fRandom->nextRangeF(3, 8);
     float width = data->fRandom->nextRangeF(200, 300);
     float height = data->fRandom->nextRangeF(200, 300);
-    return GrRectBlurEffect::Make(data->proxyProvider(), *data->caps()->shaderCaps(),
+    return GrRectBlurEffect::Make(data->context(), *data->caps()->shaderCaps(),
                                   SkRect::MakeWH(width, height), sigma);
 }
 #endif
diff --git a/src/gpu/effects/generated/GrRectBlurEffect.h b/src/gpu/effects/generated/GrRectBlurEffect.h
index 9b680a0..4ed6526 100644
--- a/src/gpu/effects/generated/GrRectBlurEffect.h
+++ b/src/gpu/effects/generated/GrRectBlurEffect.h
@@ -16,16 +16,20 @@
 #include <cmath>
 #include "include/core/SkRect.h"
 #include "include/core/SkScalar.h"
+#include "include/gpu/GrContext.h"
+#include "include/private/GrRecordingContext.h"
 #include "src/core/SkBlurMask.h"
 #include "src/core/SkMathPriv.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrProxyProvider.h"
+#include "src/gpu/GrRecordingContextPriv.h"
 #include "src/gpu/GrShaderCaps.h"
 
 #include "src/gpu/GrCoordTransform.h"
 #include "src/gpu/GrFragmentProcessor.h"
 class GrRectBlurEffect : public GrFragmentProcessor {
 public:
-    static sk_sp<GrTextureProxy> CreateIntegralTexture(GrProxyProvider* proxyProvider,
+    static sk_sp<GrTextureProxy> CreateIntegralTexture(GrRecordingContext* context,
                                                        float sixSigma) {
         // The texture we're producing represents the integral of a normal distribution over a
         // six-sigma range centered at zero. We want enough resolution so that the linear
@@ -41,6 +45,7 @@
         builder[0] = width;
         builder.finish();
 
+        GrProxyProvider* proxyProvider = context->priv().proxyProvider();
         sk_sp<GrTextureProxy> proxy(proxyProvider->findOrCreateProxyByUniqueKey(
                 key, GrColorType::kAlpha_8, kTopLeft_GrSurfaceOrigin));
         if (!proxy) {
@@ -58,10 +63,9 @@
             }
             *bitmap.getAddr8(width - 1, 0) = 0;
             bitmap.setImmutable();
-            // We directly call the proxyProvider instead of going through GrBitmapTextureMaker.
-            // This means we won't fall back to RGBA_8888. But we should have support for a single
-            // channel unorm format so we shouldn't need the fallback.
-            proxy = proxyProvider->createProxyFromBitmap(bitmap, GrMipMapped::kNo);
+
+            GrBitmapTextureMaker maker(context, bitmap);
+            std::tie(proxy, std::ignore) = maker.refTextureProxy(GrMipMapped::kNo);
             if (!proxy) {
                 return nullptr;
             }
@@ -71,7 +75,7 @@
         return proxy;
     }
 
-    static std::unique_ptr<GrFragmentProcessor> Make(GrProxyProvider* proxyProvider,
+    static std::unique_ptr<GrFragmentProcessor> Make(GrRecordingContext* context,
                                                      const GrShaderCaps& caps, const SkRect& rect,
                                                      float sigma) {
         SkASSERT(rect.isSorted());
@@ -86,7 +90,7 @@
         }
 
         const float sixSigma = 6 * sigma;
-        auto integral = CreateIntegralTexture(proxyProvider, sixSigma);
+        auto integral = CreateIntegralTexture(context, sixSigma);
         if (!integral) {
             return nullptr;
         }
diff --git a/src/gpu/gradients/GrGradientShader.cpp b/src/gpu/gradients/GrGradientShader.cpp
index a794652..cb0514e 100644
--- a/src/gpu/gradients/GrGradientShader.cpp
+++ b/src/gpu/gradients/GrGradientShader.cpp
@@ -59,8 +59,7 @@
     SkASSERT(1 == bitmap.height() && SkIsPow2(bitmap.width()));
     SkASSERT(bitmap.isImmutable());
 
-    sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(
-            args.fContext->priv().proxyProvider(), bitmap);
+    sk_sp<GrTextureProxy> proxy = GrMakeCachedBitmapProxy(args.fContext, bitmap);
     if (proxy == nullptr) {
         SkDebugf("Gradient won't draw. Could not create texture.");
         return nullptr;
diff --git a/src/gpu/ops/GrShadowRRectOp.cpp b/src/gpu/ops/GrShadowRRectOp.cpp
index 1ae8b44..e9fab08 100644
--- a/src/gpu/ops/GrShadowRRectOp.cpp
+++ b/src/gpu/ops/GrShadowRRectOp.cpp
@@ -9,6 +9,7 @@
 
 #include "include/private/GrRecordingContext.h"
 #include "src/core/SkRRectPriv.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrDrawOpTest.h"
 #include "src/gpu/GrMemoryPool.h"
 #include "src/gpu/GrOpFlushState.h"
@@ -637,12 +638,14 @@
 
 namespace GrShadowRRectOp {
 
-static sk_sp<GrTextureProxy> create_falloff_texture(GrProxyProvider* proxyProvider) {
+static sk_sp<GrTextureProxy> create_falloff_texture(GrRecordingContext* context) {
     static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
     GrUniqueKey key;
     GrUniqueKey::Builder builder(&key, kDomain, 0, "Shadow Gaussian Falloff");
     builder.finish();
 
+    GrProxyProvider* proxyProvider = context->priv().proxyProvider();
+
     sk_sp<GrTextureProxy> falloffTexture = proxyProvider->findOrCreateProxyByUniqueKey(
             key, GrColorType::kAlpha_8, kTopLeft_GrSurfaceOrigin);
     if (!falloffTexture) {
@@ -650,23 +653,19 @@
         static const size_t kRowBytes = kWidth*GrColorTypeBytesPerPixel(GrColorType::kAlpha_8);
         SkImageInfo ii = SkImageInfo::MakeA8(kWidth, 1);
 
-        sk_sp<SkData> data = SkData::MakeUninitialized(kRowBytes);
-        if (!data) {
-            return nullptr;
-        }
-        unsigned char* values = (unsigned char*) data->writable_data();
+        SkBitmap bitmap;
+        bitmap.allocPixels(ii, kRowBytes);
+
+        unsigned char* values = (unsigned char*) bitmap.getPixels();
         for (int i = 0; i < 128; ++i) {
             SkScalar d = SK_Scalar1 - i/SkIntToScalar(127);
             values[i] = SkScalarRoundToInt((SkScalarExp(-4*d*d) - 0.018f)*255);
         }
+        bitmap.setImmutable();
 
-        sk_sp<SkImage> img = SkImage::MakeRasterData(ii, std::move(data), kRowBytes);
-        if (!img) {
-            return nullptr;
-        }
+        GrBitmapTextureMaker maker(context, bitmap);
+        std::tie(falloffTexture, std::ignore) = maker.refTextureProxy(GrMipMapped::kNo);
 
-        falloffTexture = proxyProvider->createTextureProxy(std::move(img), 1, SkBudgeted::kYes,
-                                                           SkBackingFit::kExact);
         if (!falloffTexture) {
             return nullptr;
         }
@@ -688,7 +687,7 @@
     // Shadow rrect ops only handle simple circular rrects.
     SkASSERT(viewMatrix.isSimilarity() && SkRRectPriv::EqualRadii(rrect));
 
-    sk_sp<GrTextureProxy> falloffTexture = create_falloff_texture(context->priv().proxyProvider());
+    sk_sp<GrTextureProxy> falloffTexture = create_falloff_texture(context);
     if (!falloffTexture) {
         return nullptr;
     }
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index bd1e4c5..13043ce 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -453,7 +453,7 @@
     }
 
     if (const SkBitmap* bmp = as_IB(this)->onPeekBitmap()) {
-        GrBitmapTextureMaker maker(context, *bmp);
+        GrBitmapTextureMaker maker(context, *bmp, GrBitmapTextureMaker::Cached::kYes);
         return create_image_from_producer(context, &maker, this->uniqueID(), mipMapped);
     }
     return nullptr;
@@ -556,7 +556,7 @@
     // Turn the pixmap into a GrTextureProxy
     SkBitmap bmp;
     bmp.installPixels(*pixmap);
-    GrBitmapTextureMaker bitmapMaker(context, bmp, false, false);
+    GrBitmapTextureMaker bitmapMaker(context, bmp);
     GrMipMapped mipMapped = buildMips ? GrMipMapped::kYes : GrMipMapped::kNo;
     auto [proxy, grCT] = bitmapMaker.refTextureProxy(mipMapped);
     if (!proxy) {
diff --git a/src/image/SkImage_GpuYUVA.cpp b/src/image/SkImage_GpuYUVA.cpp
index 2c22053..6f4524b 100644
--- a/src/image/SkImage_GpuYUVA.cpp
+++ b/src/image/SkImage_GpuYUVA.cpp
@@ -296,7 +296,7 @@
         // Turn the pixmap into a GrTextureProxy
         SkBitmap bmp;
         bmp.installPixels(*pixmap);
-        GrBitmapTextureMaker bitmapMaker(context, bmp, false, false);
+        GrBitmapTextureMaker bitmapMaker(context, bmp);
         GrMipMapped mipMapped = buildMips ? GrMipMapped::kYes : GrMipMapped::kNo;
         std::tie(tempTextureProxies[i], proxyColorTypes[i]) =
                 bitmapMaker.refTextureProxy(mipMapped);
diff --git a/src/image/SkImage_Lazy.cpp b/src/image/SkImage_Lazy.cpp
index c47a9a5..5ef1d91 100644
--- a/src/image/SkImage_Lazy.cpp
+++ b/src/image/SkImage_Lazy.cpp
@@ -519,10 +519,9 @@
     // 4. Ask the generator to return RGB(A) data, which the GPU can convert
     SkBitmap bitmap;
     if (!proxy && this->getROPixels(&bitmap, chint)) {
-        GrBitmapTextureMaker bitmapMaker(ctx, bitmap, false, false);
-        GrColorType grCT;
-        std::tie(proxy, grCT) = bitmapMaker.refTextureProxy(willBeMipped ? GrMipMapped::kYes
-                                                                         : GrMipMapped::kNo);
+        GrBitmapTextureMaker bitmapMaker(ctx, bitmap);
+        std::tie(proxy, std::ignore) = bitmapMaker.refTextureProxy(willBeMipped ? GrMipMapped::kYes
+                                                                                : GrMipMapped::kNo);
         if (proxy && (!willBeMipped || GrMipMapped::kYes == proxy->mipMapped())) {
             SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath,
                                      kLockTexturePathCount);
diff --git a/src/shaders/SkPerlinNoiseShader.cpp b/src/shaders/SkPerlinNoiseShader.cpp
index 8555f37..acf82f8 100644
--- a/src/shaders/SkPerlinNoiseShader.cpp
+++ b/src/shaders/SkPerlinNoiseShader.cpp
@@ -115,17 +115,17 @@
 
     #if SK_SUPPORT_GPU
             SkImageInfo info = SkImageInfo::MakeA8(kBlockSize, 1);
-            SkPixmap permutationsPixmap(info, fLatticeSelector, info.minRowBytes());
-            fPermutationsImage = SkImage::MakeFromRaster(permutationsPixmap, nullptr, nullptr);
+            fPermutationsBitmap.installPixels(info, fLatticeSelector, info.minRowBytes());
+            fPermutationsBitmap.setImmutable();
 
             info = SkImageInfo::MakeN32Premul(kBlockSize, 4);
-            SkPixmap noisePixmap(info, fNoise[0][0], info.minRowBytes());
-            fNoiseImage = SkImage::MakeFromRaster(noisePixmap, nullptr, nullptr);
+            fNoiseBitmap.installPixels(info, fNoise[0][0], info.minRowBytes());
+            fNoiseBitmap.setImmutable();
 
             info = SkImageInfo::MakeA8(256, 1);
-            SkPixmap impPermutationsPixmap(info, improved_noise_permutations, info.minRowBytes());
-            fImprovedPermutationsImage = SkImage::MakeFromRaster(impPermutationsPixmap, nullptr,
-                                                                 nullptr);
+            fImprovedPermutationsBitmap.installPixels(info, improved_noise_permutations,
+                                                      info.minRowBytes());
+            fImprovedPermutationsBitmap.setImmutable();
 
             static uint8_t gradients[] = { 2, 2, 1, 0,
                                            0, 2, 1, 0,
@@ -144,8 +144,8 @@
                                            0, 2, 1, 0,
                                            1, 0, 0, 0 };
             info = SkImageInfo::MakeN32Premul(16, 1);
-            SkPixmap gradPixmap(info, gradients, info.minRowBytes());
-            fGradientImage = SkImage::MakeFromRaster(gradPixmap, nullptr, nullptr);
+            fGradientBitmap.installPixels(info, gradients, info.minRowBytes());
+            fGradientBitmap.setImmutable();
     #endif
         }
 
@@ -155,10 +155,10 @@
                 , fTileSize(that.fTileSize)
                 , fBaseFrequency(that.fBaseFrequency)
                 , fStitchDataInit(that.fStitchDataInit)
-                , fPermutationsImage(that.fPermutationsImage)
-                , fNoiseImage(that.fNoiseImage)
-                , fImprovedPermutationsImage(that.fImprovedPermutationsImage)
-                , fGradientImage(that.fGradientImage) {
+                , fPermutationsBitmap(that.fPermutationsBitmap)
+                , fNoiseBitmap(that.fNoiseBitmap)
+                , fImprovedPermutationsBitmap(that.fImprovedPermutationsBitmap)
+                , fGradientBitmap(that.fGradientBitmap) {
             memcpy(fLatticeSelector, that.fLatticeSelector, sizeof(fLatticeSelector));
             memcpy(fNoise, that.fNoise, sizeof(fNoise));
             memcpy(fGradient, that.fGradient, sizeof(fGradient));
@@ -176,10 +176,10 @@
     private:
 
     #if SK_SUPPORT_GPU
-        sk_sp<SkImage> fPermutationsImage;
-        sk_sp<SkImage> fNoiseImage;
-        sk_sp<SkImage> fImprovedPermutationsImage;
-        sk_sp<SkImage> fGradientImage;
+        SkBitmap fPermutationsBitmap;
+        SkBitmap fNoiseBitmap;
+        SkBitmap fImprovedPermutationsBitmap;
+        SkBitmap fGradientBitmap;
     #endif
 
         inline int random()  {
@@ -304,15 +304,15 @@
     public:
 
 #if SK_SUPPORT_GPU
-        const sk_sp<SkImage> getPermutationsImage() const { return fPermutationsImage; }
+        const SkBitmap& getPermutationsBitmap() const { return fPermutationsBitmap; }
 
-        const sk_sp<SkImage> getNoiseImage() const { return fNoiseImage; }
+        const SkBitmap& getNoiseBitmap() const { return fNoiseBitmap; }
 
-        const sk_sp<SkImage> getImprovedPermutationsImage() const {
-            return fImprovedPermutationsImage;
+        const SkBitmap& getImprovedPermutationsBitmap() const {
+            return fImprovedPermutationsBitmap;
         }
 
-        const sk_sp<SkImage> getGradientImage() const { return fGradientImage; }
+        const SkBitmap& getGradientBitmap() const { return fGradientBitmap; }
 #endif
     };
 
@@ -1417,20 +1417,19 @@
     m.setTranslateX(-localMatrix->getTranslateX() + SK_Scalar1);
     m.setTranslateY(-localMatrix->getTranslateY() + SK_Scalar1);
 
-    auto proxyProvider = args.fContext->priv().proxyProvider();
+    auto context = args.fContext;
     if (fType == kImprovedNoise_Type) {
         // Need to assert that the textures we'll create are power of 2 so a copy isn't needed.
         // We also know that we will not be using mipmaps. If things things weren't true we should
         // go through GrBitmapTextureMaker to handle needed copies.
-        const sk_sp<SkImage> permutationsImage = paintingData->getImprovedPermutationsImage();
-        SkASSERT(SkIsPow2(permutationsImage->width()) && SkIsPow2(permutationsImage->height()));
+        const SkBitmap& permutationsBitmap = paintingData->getImprovedPermutationsBitmap();
+        SkASSERT(SkIsPow2(permutationsBitmap.width()) && SkIsPow2(permutationsBitmap.height()));
         sk_sp<GrTextureProxy> permutationsTexture(
-                GrMakeCachedImageProxy(proxyProvider, std::move(permutationsImage)));
+                GrMakeCachedBitmapProxy(context, permutationsBitmap));
 
-        const sk_sp<SkImage> gradientImage = paintingData->getGradientImage();
-        SkASSERT(SkIsPow2(gradientImage->width()) && SkIsPow2(gradientImage->height()));
-        sk_sp<GrTextureProxy> gradientTexture(
-                GrMakeCachedImageProxy(proxyProvider, std::move(gradientImage)));
+        const SkBitmap& gradientBitmap = paintingData->getGradientBitmap();
+        SkASSERT(SkIsPow2(gradientBitmap.width()) && SkIsPow2(gradientBitmap.height()));
+        sk_sp<GrTextureProxy> gradientTexture(GrMakeCachedBitmapProxy(context, gradientBitmap));
         return GrImprovedPerlinNoiseEffect::Make(fNumOctaves, fSeed, std::move(paintingData),
                                                  std::move(permutationsTexture),
                                                  std::move(gradientTexture), m);
@@ -1455,15 +1454,13 @@
     // Need to assert that the textures we'll create are power of 2 so that now copy is needed. We
     // also know that we will not be using mipmaps. If things things weren't true we should go
     // through GrBitmapTextureMaker to handle needed copies.
-    const sk_sp<SkImage> permutationsImage = paintingData->getPermutationsImage();
-    SkASSERT(SkIsPow2(permutationsImage->width()) && SkIsPow2(permutationsImage->height()));
-    sk_sp<GrTextureProxy> permutationsProxy = GrMakeCachedImageProxy(proxyProvider,
-                                                                     std::move(permutationsImage));
+    const SkBitmap& permutationsBitmap = paintingData->getPermutationsBitmap();
+    SkASSERT(SkIsPow2(permutationsBitmap.width()) && SkIsPow2(permutationsBitmap.height()));
+    sk_sp<GrTextureProxy> permutationsProxy = GrMakeCachedBitmapProxy(context, permutationsBitmap);
 
-    const sk_sp<SkImage> noiseImage = paintingData->getNoiseImage();
-    SkASSERT(SkIsPow2(noiseImage->width()) && SkIsPow2(noiseImage->height()));
-    sk_sp<GrTextureProxy> noiseProxy = GrMakeCachedImageProxy(proxyProvider,
-                                                              std::move(noiseImage));
+    const SkBitmap& noiseBitmap = paintingData->getNoiseBitmap();
+    SkASSERT(SkIsPow2(noiseBitmap.width()) && SkIsPow2(noiseBitmap.height()));
+    sk_sp<GrTextureProxy> noiseProxy = GrMakeCachedBitmapProxy(context, noiseBitmap);
 
     if (permutationsProxy && noiseProxy) {
         auto inner = GrPerlinNoise2Effect::Make(fType,
diff --git a/tests/GrSurfaceTest.cpp b/tests/GrSurfaceTest.cpp
index c5ea2a5..bd7a669 100644
--- a/tests/GrSurfaceTest.cpp
+++ b/tests/GrSurfaceTest.cpp
@@ -11,6 +11,7 @@
 #include "include/gpu/GrTexture.h"
 #include "src/core/SkAutoPixmapStorage.h"
 #include "src/core/SkCompressedDataUtils.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrClip.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrGpu.h"
@@ -420,10 +421,13 @@
                 kSize * GrColorTypeBytesPerPixel(GrColorType::kRGBA_8888));
         REPORTER_ASSERT(reporter, gpuWriteResult == (ioType == kRW_GrIOType));
 
-        // Copies should not work with a read-only texture
-        auto copySrc =
-                proxyProvider->createTextureProxy(SkImage::MakeFromRaster(write, nullptr, nullptr),
-                                                  1, SkBudgeted::kYes, SkBackingFit::kExact);
+        SkBitmap copySrcBitmap;
+        copySrcBitmap.installPixels(write);
+        copySrcBitmap.setImmutable();
+
+        GrBitmapTextureMaker maker(context, copySrcBitmap);
+        auto [copySrc, grCT] = maker.refTextureProxy(GrMipMapped::kNo);
+
         REPORTER_ASSERT(reporter, copySrc);
         auto copyResult = surfContext->testCopy(copySrc.get());
         REPORTER_ASSERT(reporter, copyResult == (ioType == kRW_GrIOType));
diff --git a/tests/ImageFilterCacheTest.cpp b/tests/ImageFilterCacheTest.cpp
index 8703fe2..6ef92b8 100644
--- a/tests/ImageFilterCacheTest.cpp
+++ b/tests/ImageFilterCacheTest.cpp
@@ -29,6 +29,7 @@
     SkBitmap bm;
     bm.allocPixels(ii);
     bm.eraseColor(SK_ColorTRANSPARENT);
+    bm.setImmutable();
     return bm;
 }
 
@@ -199,22 +200,24 @@
 
 #include "include/gpu/GrContext.h"
 #include "include/gpu/GrTexture.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrProxyProvider.h"
 #include "src/gpu/GrResourceProvider.h"
 #include "src/gpu/GrSurfaceProxyPriv.h"
 #include "src/gpu/GrTextureProxy.h"
 
-static sk_sp<GrTextureProxy> create_proxy(GrProxyProvider* proxyProvider) {
+static sk_sp<GrTextureProxy> create_proxy(GrRecordingContext* context) {
     SkBitmap srcBM = create_bm();
-    sk_sp<SkImage> srcImage(SkImage::MakeFromBitmap(srcBM));
-    return proxyProvider->createTextureProxy(srcImage, 1, SkBudgeted::kYes, SkBackingFit::kExact);
+    GrBitmapTextureMaker maker(context, srcBM);
+    auto [proxy, grCT] = maker.refTextureProxy(GrMipMapped::kNo);
+    return proxy;
 }
 
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_ImageBackedGPU, reporter, ctxInfo) {
     GrContext* context = ctxInfo.grContext();
 
-    sk_sp<GrTextureProxy> srcProxy(create_proxy(context->priv().proxyProvider()));
+    sk_sp<GrTextureProxy> srcProxy(create_proxy(context));
     if (!srcProxy) {
         return;
     }
@@ -255,7 +258,7 @@
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ImageFilterCache_GPUBacked, reporter, ctxInfo) {
     GrContext* context = ctxInfo.grContext();
 
-    sk_sp<GrTextureProxy> srcProxy(create_proxy(context->priv().proxyProvider()));
+    sk_sp<GrTextureProxy> srcProxy(create_proxy(context));
     if (!srcProxy) {
         return;
     }
diff --git a/tests/ProcessorTest.cpp b/tests/ProcessorTest.cpp
index 5db99ac..96e076d 100644
--- a/tests/ProcessorTest.cpp
+++ b/tests/ProcessorTest.cpp
@@ -9,6 +9,7 @@
 
 #include "include/gpu/GrContext.h"
 #include "include/gpu/GrGpuResource.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrClip.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrImageInfo.h"
@@ -259,14 +260,14 @@
 
 /** Initializes the two test texture proxies that are available to the FP test factories. */
 bool init_test_textures(GrResourceProvider* resourceProvider,
-                        GrProxyProvider* proxyProvider,
+                        GrRecordingContext* context,
                         SkRandom* random,
                         GrProcessorTestData::ProxyInfo proxies[2]) {
     static const int kTestTextureSize = 256;
 
     {
         // Put premul data into the RGBA texture that the test FPs can optionally use.
-        std::unique_ptr<GrColor[]> rgbaData(new GrColor[kTestTextureSize * kTestTextureSize]);
+        GrColor* rgbaData = new GrColor[kTestTextureSize * kTestTextureSize];
         for (int y = 0; y < kTestTextureSize; ++y) {
             for (int x = 0; x < kTestTextureSize; ++x) {
                 rgbaData[kTestTextureSize * y + x] = input_texel_color(
@@ -276,10 +277,12 @@
 
         SkImageInfo ii = SkImageInfo::Make(kTestTextureSize, kTestTextureSize,
                                            kRGBA_8888_SkColorType, kPremul_SkAlphaType);
-        SkPixmap pixmap(ii, rgbaData.get(), ii.minRowBytes());
-        sk_sp<SkImage> img = SkImage::MakeRasterCopy(pixmap);
-        auto proxy =
-                proxyProvider->createTextureProxy(img, 1, SkBudgeted::kYes, SkBackingFit::kExact);
+        SkBitmap bitmap;
+        bitmap.installPixels(ii, rgbaData, ii.minRowBytes(),
+                             [](void* addr, void* context) { delete[] (GrColor*)addr; }, nullptr);
+        bitmap.setImmutable();
+        GrBitmapTextureMaker maker(context, bitmap);
+        auto [proxy, grCT] = maker.refTextureProxy(GrMipMapped::kNo);
         if (!proxy || !proxy->instantiate(resourceProvider)) {
             return false;
         }
@@ -288,7 +291,7 @@
 
     {
         // Put random values into the alpha texture that the test FPs can optionally use.
-        std::unique_ptr<uint8_t[]> alphaData(new uint8_t[kTestTextureSize * kTestTextureSize]);
+        uint8_t* alphaData = new uint8_t[kTestTextureSize * kTestTextureSize];
         for (int y = 0; y < kTestTextureSize; ++y) {
             for (int x = 0; x < kTestTextureSize; ++x) {
                 alphaData[kTestTextureSize * y + x] = random->nextULessThan(256);
@@ -297,10 +300,12 @@
 
         SkImageInfo ii = SkImageInfo::Make(kTestTextureSize, kTestTextureSize,
                                            kAlpha_8_SkColorType, kPremul_SkAlphaType);
-        SkPixmap pixmap(ii, alphaData.get(), ii.minRowBytes());
-        sk_sp<SkImage> img = SkImage::MakeRasterCopy(pixmap);
-        auto proxy =
-                proxyProvider->createTextureProxy(img, 1, SkBudgeted::kYes, SkBackingFit::kExact);
+        SkBitmap bitmap;
+        bitmap.installPixels(ii, alphaData, ii.minRowBytes(),
+                             [](void* addr, void* context) { delete[] (uint8_t*)addr; }, nullptr);
+        bitmap.setImmutable();
+        GrBitmapTextureMaker maker(context, bitmap);
+        auto [proxy, grCT] = maker.refTextureProxy(GrMipMapped::kNo);
         if (!proxy || !proxy->instantiate(resourceProvider)) {
             return false;
         }
@@ -312,19 +317,23 @@
 
 // Creates a texture of premul colors used as the output of the fragment processor that precedes
 // the fragment processor under test. Color values are those provided by input_texel_color().
-sk_sp<GrTextureProxy> make_input_texture(GrProxyProvider* proxyProvider, int width, int height,
+sk_sp<GrTextureProxy> make_input_texture(GrRecordingContext* context, int width, int height,
                                          SkScalar delta) {
-    std::unique_ptr<GrColor[]> data(new GrColor[width * height]);
+    GrColor* data = new GrColor[width * height];
     for (int y = 0; y < width; ++y) {
         for (int x = 0; x < height; ++x) {
-            data.get()[width * y + x] = input_texel_color(x, y, delta);
+            data[width * y + x] = input_texel_color(x, y, delta);
         }
     }
 
     SkImageInfo ii = SkImageInfo::Make(width, height, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
-    SkPixmap pixmap(ii, data.get(), ii.minRowBytes());
-    sk_sp<SkImage> img = SkImage::MakeRasterCopy(pixmap);
-    return proxyProvider->createTextureProxy(img, 1, SkBudgeted::kYes, SkBackingFit::kExact);
+    SkBitmap bitmap;
+    bitmap.installPixels(ii, data, ii.minRowBytes(),
+                         [](void* addr, void* context) { delete[] (GrColor*)addr; }, nullptr);
+    bitmap.setImmutable();
+    GrBitmapTextureMaker maker(context, bitmap);
+    auto [proxy, grCT] = maker.refTextureProxy(GrMipMapped::kNo);
+    return proxy;
 }
 
 // We tag logged  data as unpremul to avoid conversion when encoding as  PNG. The input texture
@@ -473,7 +482,6 @@
 
 DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, reporter, ctxInfo) {
     GrContext* context = ctxInfo.grContext();
-    GrProxyProvider* proxyProvider = context->priv().proxyProvider();
     auto resourceProvider = context->priv().resourceProvider();
     using FPFactory = GrFragmentProcessorTestFactory;
 
@@ -493,7 +501,7 @@
             {kRenderSize, kRenderSize});
 
     GrProcessorTestData::ProxyInfo proxies[2];
-    if (!init_test_textures(resourceProvider, proxyProvider, &random, proxies)) {
+    if (!init_test_textures(resourceProvider, context, &random, proxies)) {
         ERRORF(reporter, "Could not create test textures");
         return;
     }
@@ -504,9 +512,9 @@
     // difference between the frame outputs if the FP is properly following the modulation
     // requirements of the coverage optimization.
     static constexpr SkScalar kInputDelta = 0.2f;
-    auto inputTexture1 = make_input_texture(proxyProvider, kRenderSize, kRenderSize, 0.0f);
-    auto inputTexture2 = make_input_texture(proxyProvider, kRenderSize, kRenderSize, kInputDelta);
-    auto inputTexture3 = make_input_texture(proxyProvider, kRenderSize, kRenderSize, 2*kInputDelta);
+    auto inputTexture1 = make_input_texture(context, kRenderSize, kRenderSize, 0.0f);
+    auto inputTexture2 = make_input_texture(context, kRenderSize, kRenderSize, kInputDelta);
+    auto inputTexture3 = make_input_texture(context, kRenderSize, kRenderSize, 2*kInputDelta);
 
     // Encoded images are very verbose and this tests many potential images, so only export the
     // first failure (subsequent failures have a reasonable chance of being related).
@@ -716,7 +724,6 @@
 // progenitors.
 DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorCloneTest, reporter, ctxInfo) {
     GrContext* context = ctxInfo.grContext();
-    GrProxyProvider* proxyProvider = context->priv().proxyProvider();
     auto resourceProvider = context->priv().resourceProvider();
 
     SkRandom random;
@@ -728,13 +735,13 @@
             {kRenderSize, kRenderSize});
 
     GrProcessorTestData::ProxyInfo proxies[2];
-    if (!init_test_textures(resourceProvider, proxyProvider, &random, proxies)) {
+    if (!init_test_textures(resourceProvider, context, &random, proxies)) {
         ERRORF(reporter, "Could not create test textures");
         return;
     }
     GrProcessorTestData testData(&random, context, 2, proxies);
 
-    auto inputTexture = make_input_texture(proxyProvider, kRenderSize, kRenderSize, 0.0f);
+    auto inputTexture = make_input_texture(context, kRenderSize, kRenderSize, 0.0f);
     std::unique_ptr<GrColor[]> readData1(new GrColor[kRenderSize * kRenderSize]);
     std::unique_ptr<GrColor[]> readData2(new GrColor[kRenderSize * kRenderSize]);
     // On failure we write out images, but just write the first failing set as the print is very
diff --git a/tests/ReadWriteAlphaTest.cpp b/tests/ReadWriteAlphaTest.cpp
index dd758f1..8e3904b 100644
--- a/tests/ReadWriteAlphaTest.cpp
+++ b/tests/ReadWriteAlphaTest.cpp
@@ -11,6 +11,7 @@
 #include "include/core/SkSurface.h"
 #include "include/gpu/GrContext.h"
 #include "include/private/SkTo.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrImageInfo.h"
 #include "src/gpu/GrProxyProvider.h"
@@ -47,7 +48,6 @@
 
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ReadWriteAlpha, reporter, ctxInfo) {
     GrContext* context = ctxInfo.grContext();
-    GrProxyProvider* proxyProvider = context->priv().proxyProvider();
 
     unsigned char alphaData[X_SIZE * Y_SIZE];
 
@@ -62,13 +62,16 @@
 
         // We are initializing the texture with zeros here
         memset(alphaData, 0, X_SIZE * Y_SIZE);
+        unsigned char alphaDataCopy[X_SIZE * Y_SIZE];
+        memcpy(alphaDataCopy, alphaData, X_SIZE * Y_SIZE);
 
         const SkImageInfo ii = SkImageInfo::MakeA8(X_SIZE, Y_SIZE);
 
-        SkPixmap pixmap(ii, alphaData, ii.minRowBytes());
-        sk_sp<SkImage> alphaImg = SkImage::MakeRasterCopy(pixmap);
-        sk_sp<GrTextureProxy> proxy = proxyProvider->createTextureProxy(
-                alphaImg, 1, SkBudgeted::kNo, SkBackingFit::kExact);
+        SkBitmap bitmap;
+        bitmap.installPixels(ii, alphaDataCopy, ii.minRowBytes());
+        bitmap.setImmutable();
+        GrBitmapTextureMaker maker(context, bitmap);
+        auto [proxy, grCT] = maker.refTextureProxy(GrMipMapped::kNo);
         if (!proxy) {
             ERRORF(reporter, "Could not create alpha texture.");
             return;
@@ -76,10 +79,10 @@
 
         SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin);
         GrSwizzle swizzle = context->priv().caps()->getReadSwizzle(proxy->backendFormat(),
-                                                                   GrColorType::kAlpha_8);
+                                                                   grCT);
         GrSurfaceProxyView view(std::move(proxy), kTopLeft_GrSurfaceOrigin, swizzle);
-        auto sContext = GrSurfaceContext::Make(context, std::move(view), GrColorType::kAlpha_8,
-                                               kPremul_SkAlphaType, nullptr);
+        auto sContext = GrSurfaceContext::Make(context, std::move(view), grCT, kPremul_SkAlphaType,
+                                               nullptr);
 
         sk_sp<SkSurface> surf(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii));
 
diff --git a/tests/SpecialImageTest.cpp b/tests/SpecialImageTest.cpp
index f335411..9bd5a54 100644
--- a/tests/SpecialImageTest.cpp
+++ b/tests/SpecialImageTest.cpp
@@ -17,6 +17,7 @@
 
 #include "include/gpu/GrBackendSurface.h"
 #include "include/gpu/GrContext.h"
+#include "src/gpu/GrBitmapTextureMaker.h"
 #include "src/gpu/GrContextPriv.h"
 #include "src/gpu/GrProxyProvider.h"
 #include "src/gpu/GrSurfaceProxy.h"
@@ -52,6 +53,7 @@
                                    SkIntToScalar(kSmallerSize), SkIntToScalar(kSmallerSize)),
                   p);
 
+    bm.setImmutable();
     return bm;
 }
 
@@ -195,7 +197,6 @@
 // Test out the SkSpecialImage::makeTextureImage entry point
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_MakeTexture, reporter, ctxInfo) {
     GrContext* context = ctxInfo.grContext();
-    GrProxyProvider* proxyProvider = context->priv().proxyProvider();
     SkBitmap bm = create_bm();
 
     const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
@@ -222,9 +223,8 @@
 
     {
         // gpu
-        sk_sp<SkImage> rasterImage = SkImage::MakeFromBitmap(bm);
-        sk_sp<GrTextureProxy> proxy = proxyProvider->createTextureProxy(
-                rasterImage, 1, SkBudgeted::kNo, SkBackingFit::kExact);
+        GrBitmapTextureMaker maker(context, bm);
+        auto [proxy, grCT] = maker.refTextureProxy(GrMipMapped::kNo);
         if (!proxy) {
             return;
         }
@@ -234,7 +234,7 @@
                                                             SkIRect::MakeWH(kFullSize, kFullSize),
                                                             kNeedNewImageUniqueID_SpecialImage,
                                                             std::move(proxy),
-                                                            GrColorType::kRGBA_8888,
+                                                            grCT,
                                                             nullptr));
 
         {
@@ -253,12 +253,9 @@
 
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_Gpu, reporter, ctxInfo) {
     GrContext* context = ctxInfo.grContext();
-    GrProxyProvider* proxyProvider = context->priv().proxyProvider();
     SkBitmap bm = create_bm();
-    sk_sp<SkImage> rasterImage = SkImage::MakeFromBitmap(bm);
-
-    sk_sp<GrTextureProxy> proxy = proxyProvider->createTextureProxy(rasterImage, 1, SkBudgeted::kNo,
-                                                                    SkBackingFit::kExact);
+    GrBitmapTextureMaker maker(context, bm);
+    auto [proxy, grCT] = maker.refTextureProxy(GrMipMapped::kNo);
     if (!proxy) {
         return;
     }
@@ -268,7 +265,7 @@
                                                             SkIRect::MakeWH(kFullSize, kFullSize),
                                                             kNeedNewImageUniqueID_SpecialImage,
                                                             proxy,
-                                                            GrColorType::kRGBA_8888,
+                                                            grCT,
                                                             nullptr));
 
     const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
@@ -278,7 +275,7 @@
                                                                context, subset,
                                                                kNeedNewImageUniqueID_SpecialImage,
                                                                std::move(proxy),
-                                                               GrColorType::kRGBA_8888,
+                                                               grCT,
                                                                nullptr));
         test_image(subSImg1, reporter, context, true);
     }