Fold SkImageCacherator into SkImage_Lazy

SkImageCacherator still exists, but only as an interface implemented
(solely) by SkImage_Lazy. The only external clients are
GrImageTextureMaker and SkImage_Gpu::getDeferredTextureImageData.

This is probably an improvement, but doesn't go as far as I'd hoped.

Bug: skia:
Change-Id: I6812badfabb6924b025621b21af00cbde9c16cac
Reviewed-on: https://skia-review.googlesource.com/14371
Reviewed-by: Matt Sarett <msarett@google.com>
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/gm/etc1.cpp b/gm/etc1.cpp
index 369a62c..0c99e50 100644
--- a/gm/etc1.cpp
+++ b/gm/etc1.cpp
@@ -35,8 +35,6 @@
         return SkISize::Make(kTexWidth + 2*kPad, kTexHeight + 2*kPad);
     }
 
-    // TODO: we should be creating an ETC1 SkData blob here and going through SkImageCacherator.
-    // That will require an ETC1 Codec though - so for later.
     void onOnceBeforeDraw() override {
         SkBitmap bm;
         SkImageInfo ii = SkImageInfo::Make(kTexWidth, kTexHeight, kRGB_565_SkColorType,
diff --git a/gm/image_pict.cpp b/gm/image_pict.cpp
index c5e9fcf..16ee896 100644
--- a/gm/image_pict.cpp
+++ b/gm/image_pict.cpp
@@ -8,7 +8,7 @@
 #include "gm.h"
 #include "SkCanvas.h"
 #include "SkImage.h"
-#include "SkImageCacherator.h"
+#include "SkImageGenerator.h"
 #include "SkImage_Base.h"
 #include "SkMakeUnique.h"
 #include "SkPictureRecorder.h"
@@ -316,26 +316,15 @@
 
     void makeCaches(GrContext* ctx) {
         auto gen = fFactory(ctx, fPicture);
-        SkDEBUGCODE(const uint32_t genID = gen->uniqueID();)
         fImage = SkImage::MakeFromGenerator(std::move(gen));
 
         const SkIRect subset = SkIRect::MakeLTRB(50, 50, 100, 100);
 
         gen = fFactory(ctx, fPicture);
-        SkDEBUGCODE(const uint32_t genSubsetID = gen->uniqueID();)
         fImageSubset = SkImage::MakeFromGenerator(std::move(gen), &subset);
 
-        // whole caches should have the same ID as the generator. Subsets should be diff
-        SkDEBUGCODE(SkImageCacherator* cache = as_IB(fImage)->peekCacherator();)
-        SkDEBUGCODE(SkImageCacherator* cacheSubset = as_IB(fImageSubset)->peekCacherator();)
-        SkASSERT(cache);
-        SkASSERT(cacheSubset);
-        SkASSERT(cache->uniqueID() == genID);
-        SkASSERT(cacheSubset->uniqueID() != genID);
-        SkASSERT(cacheSubset->uniqueID() != genSubsetID);
-
-        SkASSERT(cache->info().dimensions() == SkISize::Make(100, 100));
-        SkASSERT(cacheSubset->info().dimensions() == SkISize::Make(50, 50));
+        SkASSERT(fImage->dimensions() == SkISize::Make(100, 100));
+        SkASSERT(fImageSubset->dimensions() == SkISize::Make(50, 50));
     }
 
     static void draw_as_bitmap(SkCanvas* canvas, SkImage* image, SkScalar x, SkScalar y) {
diff --git a/gn/core.gni b/gn/core.gni
index 21f7ee8..8f2491d 100644
--- a/gn/core.gni
+++ b/gn/core.gni
@@ -171,7 +171,6 @@
   "$_src/core/SkImageFilterCache.h",
   "$_src/core/SkImageInfo.cpp",
   "$_src/core/SkImageCacherator.h",
-  "$_src/core/SkImageCacherator.cpp",
   "$_src/core/SkImageGenerator.cpp",
   "$_src/core/SkLightingShader.h",
   "$_src/core/SkLightingShader.cpp",
diff --git a/include/core/SkImageGenerator.h b/include/core/SkImageGenerator.h
index 8dfc94f..a899c44 100644
--- a/include/core/SkImageGenerator.h
+++ b/include/core/SkImageGenerator.h
@@ -199,7 +199,7 @@
     const SkImageInfo fInfo;
     const uint32_t fUniqueID;
 
-    friend class SkImageCacherator;
+    friend class SkImage_Lazy;
 
     // This is our default impl, which may be different on different platforms.
     // It is called from NewFromEncoded() after it has checked for any runtime factory.
diff --git a/include/core/SkPixelRef.h b/include/core/SkPixelRef.h
index 9a1c240..6a86e59 100644
--- a/include/core/SkPixelRef.h
+++ b/include/core/SkPixelRef.h
@@ -167,7 +167,7 @@
 
     void setImmutableWithID(uint32_t genID);
     friend class SkImage_Gpu;
-    friend class SkImageCacherator;
+    friend class SkImage_Lazy;
     friend class SkSpecialImage_Gpu;
     friend void SkBitmapCache_setImmutableWithID(SkPixelRef*, uint32_t);
 
diff --git a/src/core/SkBitmapProvider.cpp b/src/core/SkBitmapProvider.cpp
index 928214c..90c4e62 100644
--- a/src/core/SkBitmapProvider.cpp
+++ b/src/core/SkBitmapProvider.cpp
@@ -7,7 +7,6 @@
 
 #include "SkBitmapProvider.h"
 #include "SkImage_Base.h"
-#include "SkImageCacherator.h"
 #include "SkPixelRef.h"
 
 int SkBitmapProvider::width() const {
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index f2732e2..99a7a0c 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -9,7 +9,6 @@
 #include "SkColorFilter.h"
 #include "SkDraw.h"
 #include "SkDrawFilter.h"
-#include "SkImageCacherator.h"
 #include "SkImageFilter.h"
 #include "SkImageFilterCache.h"
 #include "SkImagePriv.h"
diff --git a/src/core/SkImageCacherator.cpp b/src/core/SkImageCacherator.cpp
deleted file mode 100644
index 9ef9f79..0000000
--- a/src/core/SkImageCacherator.cpp
+++ /dev/null
@@ -1,573 +0,0 @@
-/*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkBitmap.h"
-#include "SkBitmapCache.h"
-#include "SkColorSpace_Base.h"
-#include "SkImage_Base.h"
-#include "SkImageCacherator.h"
-#include "SkMallocPixelRef.h"
-#include "SkNextID.h"
-#include "SkPixelRef.h"
-#include "SkResourceCache.h"
-
-#if SK_SUPPORT_GPU
-#include "GrContext.h"
-#include "GrContextPriv.h"
-#include "GrGpuResourcePriv.h"
-#include "GrImageTextureMaker.h"
-#include "GrResourceKey.h"
-#include "GrResourceProvider.h"
-#include "GrSamplerParams.h"
-#include "GrYUVProvider.h"
-#include "SkGr.h"
-#endif
-
-// Until we actually have codecs/etc. that can contain/support a GPU texture format
-// skip this step, since for some generators, returning their encoded data as a SkData
-// can be somewhat expensive, and this call doesn't indicate to the generator that we're
-// only interested in GPU datas...
-// see skbug.com/ 4971, 5128, ...
-//#define SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
-
-// Helper for exclusive access to a shared generator.
-class SkImageCacherator::ScopedGenerator {
-public:
-    ScopedGenerator(const sk_sp<SharedGenerator>& gen)
-      : fSharedGenerator(gen)
-      , fAutoAquire(gen->fMutex) {}
-
-    SkImageGenerator* operator->() const {
-        fSharedGenerator->fMutex.assertHeld();
-        return fSharedGenerator->fGenerator.get();
-    }
-
-    operator SkImageGenerator*() const {
-        fSharedGenerator->fMutex.assertHeld();
-        return fSharedGenerator->fGenerator.get();
-    }
-
-private:
-    const sk_sp<SharedGenerator>& fSharedGenerator;
-    SkAutoExclusive               fAutoAquire;
-};
-
-SkImageCacherator::Validator::Validator(sk_sp<SharedGenerator> gen, const SkIRect* subset)
-    : fSharedGenerator(std::move(gen)) {
-
-    if (!fSharedGenerator) {
-        return;
-    }
-
-    // The following generator accessors are safe without acquiring the mutex (const getters).
-    // TODO: refactor to use a ScopedGenerator instead, for clarity.
-    const SkImageInfo& info = fSharedGenerator->fGenerator->getInfo();
-    if (info.isEmpty()) {
-        fSharedGenerator.reset();
-        return;
-    }
-
-    fUniqueID = fSharedGenerator->fGenerator->uniqueID();
-    const SkIRect bounds = SkIRect::MakeWH(info.width(), info.height());
-    if (subset) {
-        if (!bounds.contains(*subset)) {
-            fSharedGenerator.reset();
-            return;
-        }
-        if (*subset != bounds) {
-            // we need a different uniqueID since we really are a subset of the raw generator
-            fUniqueID = SkNextID::ImageID();
-        }
-    } else {
-        subset = &bounds;
-    }
-
-    fInfo   = info.makeWH(subset->width(), subset->height());
-    fOrigin = SkIPoint::Make(subset->x(), subset->y());
-
-    // colortables are poorly to not-at-all supported in our resourcecache, so we
-    // bully them into N32 (the generator will perform the up-sample)
-    if (fInfo.colorType() == kIndex_8_SkColorType) {
-        fInfo = fInfo.makeColorType(kN32_SkColorType);
-    }
-}
-
-SkImageCacherator* SkImageCacherator::NewFromGenerator(std::unique_ptr<SkImageGenerator> gen,
-                                                       const SkIRect* subset) {
-    Validator validator(SharedGenerator::Make(std::move(gen)), subset);
-
-    return validator ? new SkImageCacherator(&validator) : nullptr;
-}
-
-SkImageCacherator::SkImageCacherator(Validator* validator)
-    : fSharedGenerator(std::move(validator->fSharedGenerator)) // we take ownership
-    , fInfo(validator->fInfo)
-    , fOrigin(validator->fOrigin)
-{
-    SkASSERT(fSharedGenerator);
-    SkASSERT(fInfo.colorType() != kIndex_8_SkColorType);
-    // We explicit set the legacy format slot, but leave the others uninitialized (via SkOnce)
-    // and only resolove them to IDs as needed (by calling getUniqueID()).
-    fIDRecs[kLegacy_CachedFormat].fOnce([this, validator] {
-        fIDRecs[kLegacy_CachedFormat].fUniqueID = validator->fUniqueID;
-    });
-}
-
-SkImageCacherator::~SkImageCacherator() {}
-
-uint32_t SkImageCacherator::getUniqueID(CachedFormat format) const {
-    IDRec* rec = &fIDRecs[format];
-    rec->fOnce([rec] {
-        rec->fUniqueID = SkNextID::ImageID();
-    });
-    return rec->fUniqueID;
-}
-
-SkData* SkImageCacherator::refEncoded() {
-    ScopedGenerator generator(fSharedGenerator);
-    return generator->refEncodedData();
-}
-
-static bool check_output_bitmap(const SkBitmap& bitmap, uint32_t expectedID) {
-    SkASSERT(bitmap.getGenerationID() == expectedID);
-    SkASSERT(bitmap.isImmutable());
-    SkASSERT(bitmap.getPixels());
-    return true;
-}
-
-bool SkImageCacherator::directGeneratePixels(const SkImageInfo& info, void* pixels, size_t rb,
-                                             int srcX, int srcY,
-                                             SkTransferFunctionBehavior behavior) {
-    ScopedGenerator generator(fSharedGenerator);
-    const SkImageInfo& genInfo = generator->getInfo();
-    // Currently generators do not natively handle subsets, so check that first.
-    if (srcX || srcY || genInfo.width() != info.width() || genInfo.height() != info.height()) {
-        return false;
-    }
-
-    SkImageGenerator::Options opts;
-    opts.fBehavior = behavior;
-    return generator->getPixels(info, pixels, rb, &opts);
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////
-
-bool SkImageCacherator::lockAsBitmapOnlyIfAlreadyCached(SkBitmap* bitmap, CachedFormat format) {
-    uint32_t uniqueID = this->getUniqueID(format);
-    return SkBitmapCache::Find(SkBitmapCacheDesc::Make(uniqueID,
-                                                       fInfo.width(), fInfo.height()), bitmap) &&
-           check_output_bitmap(*bitmap, uniqueID);
-}
-
-static bool generate_pixels(SkImageGenerator* gen, const SkPixmap& pmap, int originX, int originY) {
-    const int genW = gen->getInfo().width();
-    const int genH = gen->getInfo().height();
-    const SkIRect srcR = SkIRect::MakeWH(genW, genH);
-    const SkIRect dstR = SkIRect::MakeXYWH(originX, originY, pmap.width(), pmap.height());
-    if (!srcR.contains(dstR)) {
-        return false;
-    }
-
-    // If they are requesting a subset, we have to have a temp allocation for full image, and
-    // then copy the subset into their allocation
-    SkBitmap full;
-    SkPixmap fullPM;
-    const SkPixmap* dstPM = &pmap;
-    if (srcR != dstR) {
-        if (!full.tryAllocPixels(pmap.info().makeWH(genW, genH))) {
-            return false;
-        }
-        if (!full.peekPixels(&fullPM)) {
-            return false;
-        }
-        dstPM = &fullPM;
-    }
-
-    if (!gen->getPixels(dstPM->info(), dstPM->writable_addr(), dstPM->rowBytes())) {
-        return false;
-    }
-
-    if (srcR != dstR) {
-        if (!full.readPixels(pmap, originX, originY)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool SkImageCacherator::lockAsBitmap(SkBitmap* bitmap, const SkImage* client,
-                                     SkImage::CachingHint chint, CachedFormat format,
-                                     const SkImageInfo& info) {
-    if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap, format)) {
-        return true;
-    }
-
-    uint32_t uniqueID = this->getUniqueID(format);
-
-    SkBitmap tmpBitmap;
-    SkBitmapCache::RecPtr cacheRec;
-    SkPixmap pmap;
-    if (SkImage::kAllow_CachingHint == chint) {
-        auto desc = SkBitmapCacheDesc::Make(uniqueID, info.width(), info.height());
-        cacheRec = SkBitmapCache::Alloc(desc, info, &pmap);
-        if (!cacheRec) {
-            return false;
-        }
-    } else {
-        if (!tmpBitmap.tryAllocPixels(info)) {
-            return false;
-        }
-        if (!tmpBitmap.peekPixels(&pmap)) {
-            return false;
-        }
-    }
-
-    ScopedGenerator generator(fSharedGenerator);
-    if (!generate_pixels(generator, pmap, fOrigin.x(), fOrigin.y())) {
-        return false;
-    }
-
-    if (cacheRec) {
-        SkBitmapCache::Add(std::move(cacheRec), bitmap);
-        SkASSERT(bitmap->getPixels());  // we're locked
-        SkASSERT(bitmap->isImmutable());
-        SkASSERT(bitmap->getGenerationID() == uniqueID);
-        if (client) {
-            as_IB(client)->notifyAddedToCache();
-        }
-    } else {
-        *bitmap = tmpBitmap;
-        bitmap->pixelRef()->setImmutableWithID(uniqueID);
-    }
-
-    check_output_bitmap(*bitmap, uniqueID);
-    return true;
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Abstraction of GrCaps that handles the cases where we don't have a caps pointer (because
-// we're in raster mode), or where GPU support is entirely missing. In theory, we only need the
-// chosen format to be texturable, but that lets us choose F16 on GLES implemenations where we
-// won't be able to read the texture back. We'd like to ensure that SkImake::makeNonTextureImage
-// works, so we require that the formats we choose are renderable (as a proxy for being readable).
-struct CacheCaps {
-    CacheCaps(const GrCaps* caps) : fCaps(caps) {}
-
-#if SK_SUPPORT_GPU
-    bool supportsHalfFloat() const {
-        return !fCaps ||
-            (fCaps->isConfigTexturable(kRGBA_half_GrPixelConfig) &&
-             fCaps->isConfigRenderable(kRGBA_half_GrPixelConfig, false));
-    }
-
-    bool supportsSRGB() const {
-        return !fCaps ||
-            (fCaps->srgbSupport() && fCaps->isConfigTexturable(kSRGBA_8888_GrPixelConfig));
-    }
-
-    bool supportsSBGR() const {
-        return !fCaps || fCaps->srgbSupport();
-    }
-#else
-    bool supportsHalfFloat() const { return true; }
-    bool supportsSRGB() const { return true; }
-    bool supportsSBGR() const { return true; }
-#endif
-
-    const GrCaps* fCaps;
-};
-
-SkImageCacherator::CachedFormat SkImageCacherator::chooseCacheFormat(SkColorSpace* dstColorSpace,
-                                                                     const GrCaps* grCaps) {
-    SkColorSpace* cs = fInfo.colorSpace();
-    if (!cs || !dstColorSpace) {
-        return kLegacy_CachedFormat;
-    }
-
-    CacheCaps caps(grCaps);
-    switch (fInfo.colorType()) {
-        case kUnknown_SkColorType:
-        case kAlpha_8_SkColorType:
-        case kRGB_565_SkColorType:
-        case kARGB_4444_SkColorType:
-            // We don't support color space on these formats, so always decode in legacy mode:
-            // TODO: Ask the codec to decode these to something else (at least sRGB 8888)?
-            return kLegacy_CachedFormat;
-
-        case kIndex_8_SkColorType:
-            SkDEBUGFAIL("Index_8 should have been remapped at construction time.");
-            return kLegacy_CachedFormat;
-
-        case kGray_8_SkColorType:
-            // TODO: What do we do with grayscale sources that have strange color spaces attached?
-            // The codecs and color space xform don't handle this correctly (yet), so drop it on
-            // the floor. (Also, inflating by a factor of 8 is going to be unfortunate).
-            // As it is, we don't directly support sRGB grayscale, so ask the codec to convert
-            // it for us. This bypasses some really sketchy code GrUploadPixmapToTexture.
-            if (cs->gammaCloseToSRGB() && caps.supportsSRGB()) {
-                return kSRGB8888_CachedFormat;
-            } else {
-                return kLegacy_CachedFormat;
-            }
-
-        case kRGBA_8888_SkColorType:
-            if (cs->gammaCloseToSRGB()) {
-                if (caps.supportsSRGB()) {
-                    return kSRGB8888_CachedFormat;
-                } else if (caps.supportsHalfFloat()) {
-                    return kLinearF16_CachedFormat;
-                } else {
-                    return kLegacy_CachedFormat;
-                }
-            } else {
-                if (caps.supportsHalfFloat()) {
-                    return kLinearF16_CachedFormat;
-                } else if (caps.supportsSRGB()) {
-                    return kSRGB8888_CachedFormat;
-                } else {
-                    return kLegacy_CachedFormat;
-                }
-            }
-
-        case kBGRA_8888_SkColorType:
-            // Odd case. sBGRA isn't a real thing, so we may not have this texturable.
-            if (caps.supportsSBGR()) {
-                if (cs->gammaCloseToSRGB()) {
-                    return kSBGR8888_CachedFormat;
-                } else if (caps.supportsHalfFloat()) {
-                    return kLinearF16_CachedFormat;
-                } else if (caps.supportsSRGB()) {
-                    return kSRGB8888_CachedFormat;
-                } else {
-                    // sBGRA support without sRGBA is highly unlikely (impossible?) Nevertheless.
-                    return kLegacy_CachedFormat;
-                }
-            } else {
-                if (cs->gammaCloseToSRGB()) {
-                    if (caps.supportsSRGB()) {
-                        return kSRGB8888_CachedFormat;
-                    } else if (caps.supportsHalfFloat()) {
-                        return kLinearF16_CachedFormat;
-                    } else {
-                        return kLegacy_CachedFormat;
-                    }
-                } else {
-                    if (caps.supportsHalfFloat()) {
-                        return kLinearF16_CachedFormat;
-                    } else if (caps.supportsSRGB()) {
-                        return kSRGB8888_CachedFormat;
-                    } else {
-                        return kLegacy_CachedFormat;
-                    }
-                }
-            }
-
-        case kRGBA_F16_SkColorType:
-            if (caps.supportsHalfFloat()) {
-                return kLinearF16_CachedFormat;
-            } else if (caps.supportsSRGB()) {
-                return kSRGB8888_CachedFormat;
-            } else {
-                return kLegacy_CachedFormat;
-            }
-    }
-    SkDEBUGFAIL("Unreachable");
-    return kLegacy_CachedFormat;
-}
-
-SkImageInfo SkImageCacherator::buildCacheInfo(CachedFormat format) {
-    switch (format) {
-        case kLegacy_CachedFormat:
-            return fInfo.makeColorSpace(nullptr);
-        case kLinearF16_CachedFormat:
-            return fInfo.makeColorType(kRGBA_F16_SkColorType)
-                        .makeColorSpace(as_CSB(fInfo.colorSpace())->makeLinearGamma());
-        case kSRGB8888_CachedFormat:
-            // If the transfer function is nearly (but not exactly) sRGB, we don't want the codec
-            // to bother trans-coding. It would be slow, and do more harm than good visually,
-            // so we make sure to leave the colorspace as-is.
-            if (fInfo.colorSpace()->gammaCloseToSRGB()) {
-                return fInfo.makeColorType(kRGBA_8888_SkColorType);
-            } else {
-                return fInfo.makeColorType(kRGBA_8888_SkColorType)
-                            .makeColorSpace(as_CSB(fInfo.colorSpace())->makeSRGBGamma());
-            }
-        case kSBGR8888_CachedFormat:
-            // See note above about not-quite-sRGB transfer functions.
-            if (fInfo.colorSpace()->gammaCloseToSRGB()) {
-                return fInfo.makeColorType(kBGRA_8888_SkColorType);
-            } else {
-                return fInfo.makeColorType(kBGRA_8888_SkColorType)
-                            .makeColorSpace(as_CSB(fInfo.colorSpace())->makeSRGBGamma());
-            }
-        default:
-            SkDEBUGFAIL("Invalid cached format");
-            return fInfo;
-    }
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////
-
-#if SK_SUPPORT_GPU
-
-void SkImageCacherator::makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat format,
-                                                GrUniqueKey* cacheKey) {
-    SkASSERT(!cacheKey->isValid());
-    if (origKey.isValid()) {
-        static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
-        GrUniqueKey::Builder builder(cacheKey, origKey, kDomain, 1);
-        builder[0] = format;
-    }
-}
-
-class Generator_GrYUVProvider : public GrYUVProvider {
-    SkImageGenerator* fGen;
-
-public:
-    Generator_GrYUVProvider(SkImageGenerator* gen) : fGen(gen) {}
-
-    uint32_t onGetID() override { return fGen->uniqueID(); }
-    bool onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const override {
-        return fGen->queryYUV8(sizeInfo, colorSpace);
-    }
-    bool onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) override {
-        return fGen->getYUV8Planes(sizeInfo, planes);
-    }
-};
-
-static void set_key_on_proxy(GrResourceProvider* resourceProvider,
-                             GrTextureProxy* proxy, const GrUniqueKey& key) {
-    if (key.isValid()) {
-        resourceProvider->assignUniqueKeyToProxy(key, proxy);
-    }
-}
-
-sk_sp<SkColorSpace> SkImageCacherator::getColorSpace(GrContext* ctx, SkColorSpace* dstColorSpace) {
-    // TODO: This isn't always correct. Picture generator currently produces textures in N32,
-    // and will (soon) emit them in an arbitrary (destination) space. We will need to stash that
-    // information in/on the key so we can return the correct space in case #1 of lockTexture.
-    CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
-    SkImageInfo cacheInfo = this->buildCacheInfo(format);
-    return sk_ref_sp(cacheInfo.colorSpace());
-}
-
-/*
- *  We have 4 ways to try to return a texture (in sorted order)
- *
- *  1. Check the cache for a pre-existing one
- *  2. Ask the generator to natively create one
- *  3. Ask the generator to return YUV planes, which the GPU can convert
- *  4. Ask the generator to return RGB(A) data, which the GPU can convert
- */
-sk_sp<GrTextureProxy> SkImageCacherator::lockTextureProxy(GrContext* ctx,
-                                                          const GrUniqueKey& origKey,
-                                                          const SkImage* client,
-                                                          SkImage::CachingHint chint,
-                                                          bool willBeMipped,
-                                                          SkColorSpace* dstColorSpace) {
-    // Values representing the various texture lock paths we can take. Used for logging the path
-    // taken to a histogram.
-    enum LockTexturePath {
-        kFailure_LockTexturePath,
-        kPreExisting_LockTexturePath,
-        kNative_LockTexturePath,
-        kCompressed_LockTexturePath, // Deprecated
-        kYUV_LockTexturePath,
-        kRGBA_LockTexturePath,
-    };
-
-    enum { kLockTexturePathCount = kRGBA_LockTexturePath + 1 };
-
-    // Determine which cached format we're going to use (which may involve decoding to a different
-    // info than the generator provides).
-    CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
-
-    // Fold the cache format into our texture key
-    GrUniqueKey key;
-    this->makeCacheKeyFromOrigKey(origKey, format, &key);
-
-    // 1. Check the cache for a pre-existing one
-    if (key.isValid()) {
-        if (sk_sp<GrTextureProxy> proxy = ctx->resourceProvider()->findProxyByUniqueKey(key)) {
-            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kPreExisting_LockTexturePath,
-                                     kLockTexturePathCount);
-            return proxy;
-        }
-    }
-
-    // The CachedFormat is both an index for which cache "slot" we'll use to store this particular
-    // decoded variant of the encoded data, and also a recipe for how to transform the original
-    // info to get the one that we're going to decode to.
-    SkImageInfo cacheInfo = this->buildCacheInfo(format);
-
-    // 2. Ask the generator to natively create one
-    {
-        ScopedGenerator generator(fSharedGenerator);
-        if (sk_sp<GrTextureProxy> proxy = generator->generateTexture(ctx, cacheInfo, fOrigin)) {
-            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath,
-                                     kLockTexturePathCount);
-            set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
-            return proxy;
-        }
-    }
-
-    // 3. Ask the generator to return YUV planes, which the GPU can convert
-    if (!ctx->contextPriv().disableGpuYUVConversion()) {
-        const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->caps());
-        ScopedGenerator generator(fSharedGenerator);
-        Generator_GrYUVProvider provider(generator);
-        if (sk_sp<GrTextureProxy> proxy = provider.refAsTextureProxy(ctx, desc, true)) {
-            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath,
-                                     kLockTexturePathCount);
-            set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
-            return proxy;
-        }
-    }
-
-    // 4. Ask the generator to return RGB(A) data, which the GPU can convert
-    SkBitmap bitmap;
-    if (this->lockAsBitmap(&bitmap, client, chint, format, cacheInfo)) {
-        sk_sp<GrTextureProxy> proxy;
-        if (willBeMipped) {
-            proxy = GrGenerateMipMapsAndUploadToTextureProxy(ctx, bitmap, dstColorSpace);
-        }
-        if (!proxy) {
-            proxy = GrUploadBitmapToTextureProxy(ctx->resourceProvider(), bitmap);
-        }
-        if (proxy) {
-            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath,
-                                     kLockTexturePathCount);
-            set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
-            return proxy;
-        }
-    }
-    SK_HISTOGRAM_ENUMERATION("LockTexturePath", kFailure_LockTexturePath,
-                             kLockTexturePathCount);
-    return nullptr;
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-sk_sp<GrTextureProxy> SkImageCacherator::lockAsTextureProxy(GrContext* ctx,
-                                                            const GrSamplerParams& params,
-                                                            SkColorSpace* dstColorSpace,
-                                                            sk_sp<SkColorSpace>* texColorSpace,
-                                                            const SkImage* client,
-                                                            SkScalar scaleAdjust[2],
-                                                            SkImage::CachingHint chint) {
-    if (!ctx) {
-        return nullptr;
-    }
-
-    return GrImageTextureMaker(ctx, this, client, chint).refTextureProxyForParams(params,
-                                                                                  dstColorSpace,
-                                                                                  texColorSpace,
-                                                                                  scaleAdjust);
-}
-
-#endif
diff --git a/src/core/SkImageCacherator.h b/src/core/SkImageCacherator.h
index fb07b76..3587446 100644
--- a/src/core/SkImageCacherator.h
+++ b/src/core/SkImageCacherator.h
@@ -8,30 +8,22 @@
 #ifndef SkImageCacherator_DEFINED
 #define SkImageCacherator_DEFINED
 
-#include "SkImageGenerator.h"
-#include "SkMutex.h"
-#include "SkTemplates.h"
+#include "SkImage.h"
+#include "SkImageInfo.h"
 
 class GrCaps;
 class GrContext;
-class GrSamplerParams;
 class GrTextureProxy;
 class GrUniqueKey;
-class SkBitmap;
-class SkImage;
+class SkColorSpace;
 
 /*
- *  Internal class to manage caching the output of an ImageGenerator.
+ *  Interface used by GrImageTextureMaker to construct textures from instances of SkImage
+ *  (specifically, SkImage_Lazy).
  */
 class SkImageCacherator {
 public:
-    static SkImageCacherator* NewFromGenerator(std::unique_ptr<SkImageGenerator>,
-                                               const SkIRect* subset = nullptr);
-
-    ~SkImageCacherator();
-
-    const SkImageInfo& info() const { return fInfo; }
-    uint32_t uniqueID() const { return this->getUniqueID(kLegacy_CachedFormat); }
+    virtual ~SkImageCacherator() {}
 
     enum CachedFormat {
         kLegacy_CachedFormat,    // The format from the generator, with any color space stripped out
@@ -42,122 +34,26 @@
         kNumCachedFormats,
     };
 
-#if SK_SUPPORT_GPU
-    /**
-     *  Returns a ref() on the texture produced by this generator. The caller must call unref()
-     *  when it is done. Will return nullptr on failure.
-     *
-     *  If not NULL, the client will be notified (->notifyAddedToCache()) when resources are
-     *  added to the cache on its behalf.
-     *
-     *  The caller is responsible for calling proxy->unref() when they are done.
-     *
-     *  The scaleAdjust in/out parameter will return any scale adjustment that needs
-     *  to be applied to the absolute texture coordinates in the case where the image
-     *  was resized to meet the sampling requirements (e.g., resized out to the next power of 2).
-     *  It can be null if the caller knows resizing will not be required.
-     */
-    sk_sp<GrTextureProxy> lockAsTextureProxy(GrContext*, const GrSamplerParams&,
-                                             SkColorSpace* dstColorSpace,
-                                             sk_sp<SkColorSpace>* texColorSpace,
-                                             const SkImage* client,
-                                             SkScalar scaleAdjust[2],
-                                             SkImage::CachingHint = SkImage::kAllow_CachingHint);
-#endif
-
-    /**
-     *  If the underlying src naturally is represented by an encoded blob (in SkData), this returns
-     *  a ref to that data. If not, it returns null.
-     */
-    SkData* refEncoded();
-
-    // Only return true if the generate has already been cached.
-    bool lockAsBitmapOnlyIfAlreadyCached(SkBitmap*, CachedFormat);
-    // Call the underlying generator directly
-    bool directGeneratePixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
-                              int srcX, int srcY, SkTransferFunctionBehavior behavior);
-
-private:
-    // Ref-counted tuple(SkImageGenerator, SkMutex) which allows sharing of one generator
-    // among several cacherators.
-    class SharedGenerator final : public SkNVRefCnt<SharedGenerator> {
-    public:
-        static sk_sp<SharedGenerator> Make(std::unique_ptr<SkImageGenerator> gen) {
-            return gen ? sk_sp<SharedGenerator>(new SharedGenerator(std::move(gen))) : nullptr;
-        }
-
-    private:
-        explicit SharedGenerator(std::unique_ptr<SkImageGenerator> gen)
-            : fGenerator(std::move(gen))
-        {
-            SkASSERT(fGenerator);
-        }
-
-        friend class ScopedGenerator;
-        friend class SkImageCacherator;
-
-        std::unique_ptr<SkImageGenerator> fGenerator;
-        SkMutex                           fMutex;
-    };
-    class ScopedGenerator;
-
-    struct Validator {
-        Validator(sk_sp<SharedGenerator>, const SkIRect* subset);
-
-        operator bool() const { return fSharedGenerator.get(); }
-
-        sk_sp<SharedGenerator> fSharedGenerator;
-        SkImageInfo            fInfo;
-        SkIPoint               fOrigin;
-        uint32_t               fUniqueID;
-    };
-
-    SkImageCacherator(Validator*);
-
-    CachedFormat chooseCacheFormat(SkColorSpace* dstColorSpace, const GrCaps* = nullptr);
-    SkImageInfo buildCacheInfo(CachedFormat);
-
-    /**
-     *  On success (true), bitmap will point to the pixels for this generator. If this returns
-     *  false, the bitmap will be reset to empty.
-     *
-     *  If not NULL, the client will be notified (->notifyAddedToCache()) when resources are
-     *  added to the cache on its behalf.
-     */
-    bool lockAsBitmap(SkBitmap*, const SkImage* client, SkImage::CachingHint, CachedFormat,
-                      const SkImageInfo&);
+    virtual CachedFormat chooseCacheFormat(SkColorSpace* dstColorSpace,
+                                           const GrCaps* = nullptr) const = 0;
+    virtual SkImageInfo buildCacheInfo(CachedFormat) const = 0;
 
 #if SK_SUPPORT_GPU
     // Returns the texture proxy. If the cacherator is generating the texture and wants to cache it,
     // it should use the passed in key (if the key is valid).
-    sk_sp<GrTextureProxy> lockTextureProxy(GrContext*,
-                                           const GrUniqueKey& key,
-                                           const SkImage* client,
-                                           SkImage::CachingHint,
-                                           bool willBeMipped,
-                                           SkColorSpace* dstColorSpace);
+    virtual sk_sp<GrTextureProxy> lockTextureProxy(GrContext*,
+                                                   const GrUniqueKey& key,
+                                                   SkImage::CachingHint,
+                                                   bool willBeMipped,
+                                                   SkColorSpace* dstColorSpace) = 0;
+
     // Returns the color space of the texture that would be returned if you called lockTexture.
     // Separate code path to allow querying of the color space for textures that cached (even
     // externally).
-    sk_sp<SkColorSpace> getColorSpace(GrContext*, SkColorSpace* dstColorSpace);
-    void makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat, GrUniqueKey* cacheKey);
+    virtual sk_sp<SkColorSpace> getColorSpace(GrContext*, SkColorSpace* dstColorSpace) = 0;
+    virtual void makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat,
+                                         GrUniqueKey* cacheKey) = 0;
 #endif
-
-    sk_sp<SharedGenerator> fSharedGenerator;
-    const SkImageInfo      fInfo;
-    const SkIPoint         fOrigin;
-
-    struct IDRec {
-        SkOnce      fOnce;
-        uint32_t    fUniqueID;
-    };
-    mutable IDRec fIDRecs[kNumCachedFormats];
-
-    uint32_t getUniqueID(CachedFormat) const;
-
-    friend class GrImageTextureMaker;
-    friend class SkImage;
-    friend class SkImage_Lazy;
 };
 
 #endif
diff --git a/src/gpu/GrImageTextureMaker.cpp b/src/gpu/GrImageTextureMaker.cpp
index a1a6053..bbdc9c1 100644
--- a/src/gpu/GrImageTextureMaker.cpp
+++ b/src/gpu/GrImageTextureMaker.cpp
@@ -14,26 +14,20 @@
 #include "SkImageCacherator.h"
 #include "SkPixelRef.h"
 
-static bool cacher_is_alpha_only(const SkImageCacherator& cacher) {
-    return kAlpha_8_SkColorType == cacher.info().colorType();
-}
-
-GrImageTextureMaker::GrImageTextureMaker(GrContext* context, SkImageCacherator* cacher,
-                                         const SkImage* client, SkImage::CachingHint chint)
-    : INHERITED(context, cacher->info().width(), cacher->info().height(),
-                cacher_is_alpha_only(*cacher))
-    , fCacher(cacher)
-    , fClient(client)
-    , fCachingHint(chint) {
-    if (client) {
-        GrMakeKeyFromImageID(&fOriginalKey, client->uniqueID(),
-                             SkIRect::MakeWH(this->width(), this->height()));
-    }
+GrImageTextureMaker::GrImageTextureMaker(GrContext* context, const SkImage* client,
+                                         SkImage::CachingHint chint)
+        : INHERITED(context, client->width(), client->height(), client->isAlphaOnly())
+        , fCacher(as_IB(client)->peekCacherator())
+        , fClient(client)
+        , fCachingHint(chint) {
+    SkASSERT(fCacher);
+    GrMakeKeyFromImageID(&fOriginalKey, client->uniqueID(),
+                         SkIRect::MakeWH(this->width(), this->height()));
 }
 
 sk_sp<GrTextureProxy> GrImageTextureMaker::refOriginalTextureProxy(bool willBeMipped,
                                                                    SkColorSpace* dstColorSpace) {
-    return fCacher->lockTextureProxy(this->context(), fOriginalKey, fClient, fCachingHint,
+    return fCacher->lockTextureProxy(this->context(), fOriginalKey, fCachingHint,
                                      willBeMipped, dstColorSpace);
 }
 
@@ -49,13 +43,11 @@
 }
 
 void GrImageTextureMaker::didCacheCopy(const GrUniqueKey& copyKey) {
-    if (fClient) {
-        as_IB(fClient)->notifyAddedToCache();
-    }
+    as_IB(fClient)->notifyAddedToCache();
 }
 
 SkAlphaType GrImageTextureMaker::alphaType() const {
-    return fCacher->info().alphaType();
+    return fClient->alphaType();
 }
 sk_sp<SkColorSpace> GrImageTextureMaker::getColorSpace(SkColorSpace* dstColorSpace) {
     return fCacher->getColorSpace(this->context(), dstColorSpace);
diff --git a/src/gpu/GrImageTextureMaker.h b/src/gpu/GrImageTextureMaker.h
index 5145903..4f3e8bd 100644
--- a/src/gpu/GrImageTextureMaker.h
+++ b/src/gpu/GrImageTextureMaker.h
@@ -17,8 +17,7 @@
     is kAllow the image's ID is used for the cache key. */
 class GrImageTextureMaker : public GrTextureMaker {
 public:
-    GrImageTextureMaker(GrContext* context, SkImageCacherator* cacher, const SkImage* client,
-                        SkImage::CachingHint chint);
+    GrImageTextureMaker(GrContext* context, const SkImage* client, SkImage::CachingHint chint);
 
 protected:
     // TODO: consider overriding this, for the case where the underlying generator might be
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index 6ddae65..7a0585e 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -22,7 +22,6 @@
 #include "SkDraw.h"
 #include "SkGlyphCache.h"
 #include "SkGr.h"
-#include "SkImageCacherator.h"
 #include "SkImageFilter.h"
 #include "SkImageFilterCache.h"
 #include "SkImageInfoPriv.h"
@@ -1382,9 +1381,9 @@
                 return;
             }
             this->drawBitmap(bm, SkMatrix::MakeTrans(x, y), paint);
-        } else if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) {
+        } else if (image->isLazyGenerated()) {
             CHECK_SHOULD_DRAW();
-            GrImageTextureMaker maker(fContext.get(), cacher, image, SkImage::kAllow_CachingHint);
+            GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint);
             this->drawTextureProducer(&maker, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint,
                                       viewMatrix, this->clip(), paint);
         } else if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->getColorSpace())) {
@@ -1418,9 +1417,9 @@
             return;
         }
         this->drawBitmapRect(bm, src, dst, paint, constraint);
-    } else if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) {
+    } else if (image->isLazyGenerated()) {
         CHECK_SHOULD_DRAW();
-        GrImageTextureMaker maker(fContext.get(), cacher, image, SkImage::kAllow_CachingHint);
+        GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint);
         this->drawTextureProducer(&maker, src, &dst, constraint, this->ctm(), this->clip(), paint);
     } else if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->getColorSpace())) {
         this->drawBitmapRect(bm, src, dst, paint, constraint);
@@ -1482,8 +1481,8 @@
         this->drawProducerNine(&adjuster, center, dst, paint);
     } else {
         SkBitmap bm;
-        if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) {
-            GrImageTextureMaker maker(fContext.get(), cacher, image, SkImage::kAllow_CachingHint);
+        if (image->isLazyGenerated()) {
+            GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint);
             this->drawProducerNine(&maker, center, dst, paint);
         } else if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->getColorSpace())) {
             this->drawBitmapNine(bm, center, dst, paint);
@@ -1538,8 +1537,8 @@
         this->drawProducerLattice(&adjuster, lattice, dst, paint);
     } else {
         SkBitmap bm;
-        if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) {
-            GrImageTextureMaker maker(fContext.get(), cacher, image, SkImage::kAllow_CachingHint);
+        if (image->isLazyGenerated()) {
+            GrImageTextureMaker maker(fContext.get(), image, SkImage::kAllow_CachingHint);
             this->drawProducerLattice(&maker, lattice, dst, paint);
         } else if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->getColorSpace())) {
             this->drawBitmapLattice(bm, lattice, dst, paint);
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 7b7737d..d3b8a85 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -457,8 +457,8 @@
         return peek->getContext() == context ? sk_ref_sp(const_cast<SkImage*>(this)) : nullptr;
     }
 
-    if (SkImageCacherator* cacher = as_IB(this)->peekCacherator()) {
-        GrImageTextureMaker maker(context, cacher, this, kDisallow_CachingHint);
+    if (this->isLazyGenerated()) {
+        GrImageTextureMaker maker(context, this, kDisallow_CachingHint);
         return create_image_from_maker(context, &maker, this->alphaType(),
                                        this->uniqueID(), dstColorSpace);
     }
diff --git a/src/image/SkImage_Lazy.cpp b/src/image/SkImage_Lazy.cpp
index 99995d1..215f4c2 100644
--- a/src/image/SkImage_Lazy.cpp
+++ b/src/image/SkImage_Lazy.cpp
@@ -6,24 +6,69 @@
  */
 
 #include "SkImage_Base.h"
-#include "SkBitmap.h"
-#include "SkData.h"
 #include "SkImageCacherator.h"
+
+#include "SkBitmap.h"
+#include "SkBitmapCache.h"
+#include "SkColorSpace_Base.h"
+#include "SkData.h"
+#include "SkImageGenerator.h"
 #include "SkImagePriv.h"
+#include "SkNextID.h"
 #include "SkPixelRef.h"
 
-class SkImage_Lazy : public SkImage_Base {
-public:
-    SkImage_Lazy(SkImageCacherator::Validator* validator)
-        : INHERITED(validator->fInfo.width(), validator->fInfo.height(), validator->fUniqueID)
-        , fCache(validator)
-    {}
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrContextPriv.h"
+#include "GrGpuResourcePriv.h"
+#include "GrImageTextureMaker.h"
+#include "GrResourceKey.h"
+#include "GrResourceProvider.h"
+#include "GrSamplerParams.h"
+#include "GrYUVProvider.h"
+#include "SkGr.h"
+#endif
 
-    virtual SkImageInfo onImageInfo() const override {
-        return fCache.info();
+// Ref-counted tuple(SkImageGenerator, SkMutex) which allows sharing one generator among N images
+class SharedGenerator final : public SkNVRefCnt<SharedGenerator> {
+public:
+    static sk_sp<SharedGenerator> Make(std::unique_ptr<SkImageGenerator> gen) {
+        return gen ? sk_sp<SharedGenerator>(new SharedGenerator(std::move(gen))) : nullptr;
+    }
+
+private:
+    explicit SharedGenerator(std::unique_ptr<SkImageGenerator> gen)
+            : fGenerator(std::move(gen)) {
+        SkASSERT(fGenerator);
+    }
+
+    friend class ScopedGenerator;
+    friend class SkImage_Lazy;
+
+    std::unique_ptr<SkImageGenerator> fGenerator;
+    SkMutex                           fMutex;
+};
+
+class SkImage_Lazy : public SkImage_Base, public SkImageCacherator {
+public:
+    struct Validator {
+        Validator(sk_sp<SharedGenerator>, const SkIRect* subset);
+
+        operator bool() const { return fSharedGenerator.get(); }
+
+        sk_sp<SharedGenerator> fSharedGenerator;
+        SkImageInfo            fInfo;
+        SkIPoint               fOrigin;
+        uint32_t               fUniqueID;
+    };
+
+    SkImage_Lazy(Validator* validator);
+
+    SkImageInfo onImageInfo() const override {
+        return fInfo;
     }
     SkAlphaType onAlphaType() const override {
-        return fCache.info().alphaType();
+        return fInfo.alphaType();
     }
 
     bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY,
@@ -33,35 +78,452 @@
                                             SkColorSpace*, sk_sp<SkColorSpace>*,
                                             SkScalar scaleAdjust[2]) const override;
 #endif
-    SkImageCacherator* peekCacherator() const override { return &fCache; }
     SkData* onRefEncoded() const override;
     sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
     bool getROPixels(SkBitmap*, SkColorSpace* dstColorSpace, CachingHint) const override;
     bool onIsLazyGenerated() const override { return true; }
     sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>) const override;
 
+    SkImageCacherator* peekCacherator() const override {
+        return const_cast<SkImage_Lazy*>(this);
+    }
+
+    // Only return true if the generate has already been cached.
+    bool lockAsBitmapOnlyIfAlreadyCached(SkBitmap*, CachedFormat) const;
+    // Call the underlying generator directly
+    bool directGeneratePixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
+                              int srcX, int srcY, SkTransferFunctionBehavior behavior) const;
+
+    // SkImageCacherator interface
+#if SK_SUPPORT_GPU
+    // Returns the texture proxy. If the cacherator is generating the texture and wants to cache it,
+    // it should use the passed in key (if the key is valid).
+    sk_sp<GrTextureProxy> lockTextureProxy(GrContext*,
+                                           const GrUniqueKey& key,
+                                           SkImage::CachingHint,
+                                           bool willBeMipped,
+                                           SkColorSpace* dstColorSpace) override;
+
+    // Returns the color space of the texture that would be returned if you called lockTexture.
+    // Separate code path to allow querying of the color space for textures that cached (even
+    // externally).
+    sk_sp<SkColorSpace> getColorSpace(GrContext*, SkColorSpace* dstColorSpace) override;
+    void makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat,
+                                 GrUniqueKey* cacheKey) override;
+#endif
+
+    CachedFormat chooseCacheFormat(SkColorSpace* dstColorSpace,
+                                   const GrCaps* = nullptr) const override;
+    SkImageInfo buildCacheInfo(CachedFormat) const override;
+
 private:
-    mutable SkImageCacherator fCache;
+    class ScopedGenerator;
+
+    /**
+     *  On success (true), bitmap will point to the pixels for this generator. If this returns
+     *  false, the bitmap will be reset to empty.
+     */
+    bool lockAsBitmap(SkBitmap*, SkImage::CachingHint, CachedFormat, const SkImageInfo&) const;
+
+    sk_sp<SharedGenerator> fSharedGenerator;
+    const SkImageInfo      fInfo;
+    const SkIPoint         fOrigin;
+
+    struct IDRec {
+        SkOnce      fOnce;
+        uint32_t    fUniqueID;
+    };
+    mutable IDRec fIDRecs[kNumCachedFormats];
+
+    uint32_t getUniqueID(CachedFormat) const;
 
     typedef SkImage_Base INHERITED;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
+SkImage_Lazy::Validator::Validator(sk_sp<SharedGenerator> gen, const SkIRect* subset)
+        : fSharedGenerator(std::move(gen)) {
+
+    if (!fSharedGenerator) {
+        return;
+    }
+
+    // The following generator accessors are safe without acquiring the mutex (const getters).
+    // TODO: refactor to use a ScopedGenerator instead, for clarity.
+    const SkImageInfo& info = fSharedGenerator->fGenerator->getInfo();
+    if (info.isEmpty()) {
+        fSharedGenerator.reset();
+        return;
+    }
+
+    fUniqueID = fSharedGenerator->fGenerator->uniqueID();
+    const SkIRect bounds = SkIRect::MakeWH(info.width(), info.height());
+    if (subset) {
+        if (!bounds.contains(*subset)) {
+            fSharedGenerator.reset();
+            return;
+        }
+        if (*subset != bounds) {
+            // we need a different uniqueID since we really are a subset of the raw generator
+            fUniqueID = SkNextID::ImageID();
+        }
+    } else {
+        subset = &bounds;
+    }
+
+    fInfo   = info.makeWH(subset->width(), subset->height());
+    fOrigin = SkIPoint::Make(subset->x(), subset->y());
+
+    // colortables are poorly to not-at-all supported in our resourcecache, so we
+    // bully them into N32 (the generator will perform the up-sample)
+    if (fInfo.colorType() == kIndex_8_SkColorType) {
+        fInfo = fInfo.makeColorType(kN32_SkColorType);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Helper for exclusive access to a shared generator.
+class SkImage_Lazy::ScopedGenerator {
+public:
+    ScopedGenerator(const sk_sp<SharedGenerator>& gen)
+      : fSharedGenerator(gen)
+      , fAutoAquire(gen->fMutex) {}
+
+    SkImageGenerator* operator->() const {
+        fSharedGenerator->fMutex.assertHeld();
+        return fSharedGenerator->fGenerator.get();
+    }
+
+    operator SkImageGenerator*() const {
+        fSharedGenerator->fMutex.assertHeld();
+        return fSharedGenerator->fGenerator.get();
+    }
+
+private:
+    const sk_sp<SharedGenerator>& fSharedGenerator;
+    SkAutoExclusive               fAutoAquire;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImage_Lazy::SkImage_Lazy(Validator* validator)
+        : INHERITED(validator->fInfo.width(), validator->fInfo.height(), validator->fUniqueID)
+        , fSharedGenerator(std::move(validator->fSharedGenerator))
+        , fInfo(validator->fInfo)
+        , fOrigin(validator->fOrigin) {
+    SkASSERT(fSharedGenerator);
+    SkASSERT(kIndex_8_SkColorType != fInfo.colorType());
+    // We explicit set the legacy format slot, but leave the others uninitialized (via SkOnce)
+    // and only resolove them to IDs as needed (by calling getUniqueID()).
+    fIDRecs[kLegacy_CachedFormat].fOnce([this, validator] {
+        fIDRecs[kLegacy_CachedFormat].fUniqueID = validator->fUniqueID;
+    });
+}
+
+uint32_t SkImage_Lazy::getUniqueID(CachedFormat format) const {
+    IDRec* rec = &fIDRecs[format];
+    rec->fOnce([rec] {
+        rec->fUniqueID = SkNextID::ImageID();
+    });
+    return rec->fUniqueID;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Abstraction of GrCaps that handles the cases where we don't have a caps pointer (because
+// we're in raster mode), or where GPU support is entirely missing. In theory, we only need the
+// chosen format to be texturable, but that lets us choose F16 on GLES implemenations where we
+// won't be able to read the texture back. We'd like to ensure that SkImake::makeNonTextureImage
+// works, so we require that the formats we choose are renderable (as a proxy for being readable).
+struct CacheCaps {
+    CacheCaps(const GrCaps* caps) : fCaps(caps) {}
+
+#if SK_SUPPORT_GPU
+    bool supportsHalfFloat() const {
+        return !fCaps ||
+            (fCaps->isConfigTexturable(kRGBA_half_GrPixelConfig) &&
+             fCaps->isConfigRenderable(kRGBA_half_GrPixelConfig, false));
+    }
+
+    bool supportsSRGB() const {
+        return !fCaps ||
+            (fCaps->srgbSupport() && fCaps->isConfigTexturable(kSRGBA_8888_GrPixelConfig));
+    }
+
+    bool supportsSBGR() const {
+        return !fCaps || fCaps->srgbSupport();
+    }
+#else
+    bool supportsHalfFloat() const { return true; }
+    bool supportsSRGB() const { return true; }
+    bool supportsSBGR() const { return true; }
+#endif
+
+    const GrCaps* fCaps;
+};
+
+SkImageCacherator::CachedFormat SkImage_Lazy::chooseCacheFormat(SkColorSpace* dstColorSpace,
+                                                                const GrCaps* grCaps) const {
+    SkColorSpace* cs = fInfo.colorSpace();
+    if (!cs || !dstColorSpace) {
+        return kLegacy_CachedFormat;
+    }
+
+    CacheCaps caps(grCaps);
+    switch (fInfo.colorType()) {
+        case kUnknown_SkColorType:
+        case kAlpha_8_SkColorType:
+        case kRGB_565_SkColorType:
+        case kARGB_4444_SkColorType:
+            // We don't support color space on these formats, so always decode in legacy mode:
+            // TODO: Ask the codec to decode these to something else (at least sRGB 8888)?
+            return kLegacy_CachedFormat;
+
+        case kIndex_8_SkColorType:
+            SkDEBUGFAIL("Index_8 should have been remapped at construction time.");
+            return kLegacy_CachedFormat;
+
+        case kGray_8_SkColorType:
+            // TODO: What do we do with grayscale sources that have strange color spaces attached?
+            // The codecs and color space xform don't handle this correctly (yet), so drop it on
+            // the floor. (Also, inflating by a factor of 8 is going to be unfortunate).
+            // As it is, we don't directly support sRGB grayscale, so ask the codec to convert
+            // it for us. This bypasses some really sketchy code GrUploadPixmapToTexture.
+            if (cs->gammaCloseToSRGB() && caps.supportsSRGB()) {
+                return kSRGB8888_CachedFormat;
+            } else {
+                return kLegacy_CachedFormat;
+            }
+
+        case kRGBA_8888_SkColorType:
+            if (cs->gammaCloseToSRGB()) {
+                if (caps.supportsSRGB()) {
+                    return kSRGB8888_CachedFormat;
+                } else if (caps.supportsHalfFloat()) {
+                    return kLinearF16_CachedFormat;
+                } else {
+                    return kLegacy_CachedFormat;
+                }
+            } else {
+                if (caps.supportsHalfFloat()) {
+                    return kLinearF16_CachedFormat;
+                } else if (caps.supportsSRGB()) {
+                    return kSRGB8888_CachedFormat;
+                } else {
+                    return kLegacy_CachedFormat;
+                }
+            }
+
+        case kBGRA_8888_SkColorType:
+            // Odd case. sBGRA isn't a real thing, so we may not have this texturable.
+            if (caps.supportsSBGR()) {
+                if (cs->gammaCloseToSRGB()) {
+                    return kSBGR8888_CachedFormat;
+                } else if (caps.supportsHalfFloat()) {
+                    return kLinearF16_CachedFormat;
+                } else if (caps.supportsSRGB()) {
+                    return kSRGB8888_CachedFormat;
+                } else {
+                    // sBGRA support without sRGBA is highly unlikely (impossible?) Nevertheless.
+                    return kLegacy_CachedFormat;
+                }
+            } else {
+                if (cs->gammaCloseToSRGB()) {
+                    if (caps.supportsSRGB()) {
+                        return kSRGB8888_CachedFormat;
+                    } else if (caps.supportsHalfFloat()) {
+                        return kLinearF16_CachedFormat;
+                    } else {
+                        return kLegacy_CachedFormat;
+                    }
+                } else {
+                    if (caps.supportsHalfFloat()) {
+                        return kLinearF16_CachedFormat;
+                    } else if (caps.supportsSRGB()) {
+                        return kSRGB8888_CachedFormat;
+                    } else {
+                        return kLegacy_CachedFormat;
+                    }
+                }
+            }
+
+        case kRGBA_F16_SkColorType:
+            if (caps.supportsHalfFloat()) {
+                return kLinearF16_CachedFormat;
+            } else if (caps.supportsSRGB()) {
+                return kSRGB8888_CachedFormat;
+            } else {
+                return kLegacy_CachedFormat;
+            }
+    }
+    SkDEBUGFAIL("Unreachable");
+    return kLegacy_CachedFormat;
+}
+
+SkImageInfo SkImage_Lazy::buildCacheInfo(CachedFormat format) const {
+    switch (format) {
+        case kLegacy_CachedFormat:
+            return fInfo.makeColorSpace(nullptr);
+        case kLinearF16_CachedFormat:
+            return fInfo.makeColorType(kRGBA_F16_SkColorType)
+                        .makeColorSpace(as_CSB(fInfo.colorSpace())->makeLinearGamma());
+        case kSRGB8888_CachedFormat:
+            // If the transfer function is nearly (but not exactly) sRGB, we don't want the codec
+            // to bother trans-coding. It would be slow, and do more harm than good visually,
+            // so we make sure to leave the colorspace as-is.
+            if (fInfo.colorSpace()->gammaCloseToSRGB()) {
+                return fInfo.makeColorType(kRGBA_8888_SkColorType);
+            } else {
+                return fInfo.makeColorType(kRGBA_8888_SkColorType)
+                            .makeColorSpace(as_CSB(fInfo.colorSpace())->makeSRGBGamma());
+            }
+        case kSBGR8888_CachedFormat:
+            // See note above about not-quite-sRGB transfer functions.
+            if (fInfo.colorSpace()->gammaCloseToSRGB()) {
+                return fInfo.makeColorType(kBGRA_8888_SkColorType);
+            } else {
+                return fInfo.makeColorType(kBGRA_8888_SkColorType)
+                            .makeColorSpace(as_CSB(fInfo.colorSpace())->makeSRGBGamma());
+            }
+        default:
+            SkDEBUGFAIL("Invalid cached format");
+            return fInfo;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+static bool check_output_bitmap(const SkBitmap& bitmap, uint32_t expectedID) {
+    SkASSERT(bitmap.getGenerationID() == expectedID);
+    SkASSERT(bitmap.isImmutable());
+    SkASSERT(bitmap.getPixels());
+    return true;
+}
+
+bool SkImage_Lazy::directGeneratePixels(const SkImageInfo& info, void* pixels, size_t rb,
+                                        int srcX, int srcY,
+                                        SkTransferFunctionBehavior behavior) const {
+    ScopedGenerator generator(fSharedGenerator);
+    const SkImageInfo& genInfo = generator->getInfo();
+    // Currently generators do not natively handle subsets, so check that first.
+    if (srcX || srcY || genInfo.width() != info.width() || genInfo.height() != info.height()) {
+        return false;
+    }
+
+    SkImageGenerator::Options opts;
+    opts.fBehavior = behavior;
+    return generator->getPixels(info, pixels, rb, &opts);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+bool SkImage_Lazy::lockAsBitmapOnlyIfAlreadyCached(SkBitmap* bitmap, CachedFormat format) const {
+    uint32_t uniqueID = this->getUniqueID(format);
+    return SkBitmapCache::Find(SkBitmapCacheDesc::Make(uniqueID,
+                                                       fInfo.width(), fInfo.height()), bitmap) &&
+           check_output_bitmap(*bitmap, uniqueID);
+}
+
+static bool generate_pixels(SkImageGenerator* gen, const SkPixmap& pmap, int originX, int originY) {
+    const int genW = gen->getInfo().width();
+    const int genH = gen->getInfo().height();
+    const SkIRect srcR = SkIRect::MakeWH(genW, genH);
+    const SkIRect dstR = SkIRect::MakeXYWH(originX, originY, pmap.width(), pmap.height());
+    if (!srcR.contains(dstR)) {
+        return false;
+    }
+
+    // If they are requesting a subset, we have to have a temp allocation for full image, and
+    // then copy the subset into their allocation
+    SkBitmap full;
+    SkPixmap fullPM;
+    const SkPixmap* dstPM = &pmap;
+    if (srcR != dstR) {
+        if (!full.tryAllocPixels(pmap.info().makeWH(genW, genH))) {
+            return false;
+        }
+        if (!full.peekPixels(&fullPM)) {
+            return false;
+        }
+        dstPM = &fullPM;
+    }
+
+    if (!gen->getPixels(dstPM->info(), dstPM->writable_addr(), dstPM->rowBytes())) {
+        return false;
+    }
+
+    if (srcR != dstR) {
+        if (!full.readPixels(pmap, originX, originY)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool SkImage_Lazy::lockAsBitmap(SkBitmap* bitmap, SkImage::CachingHint chint,
+                                CachedFormat format, const SkImageInfo& info) const {
+    if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap, format)) {
+        return true;
+    }
+
+    uint32_t uniqueID = this->getUniqueID(format);
+
+    SkBitmap tmpBitmap;
+    SkBitmapCache::RecPtr cacheRec;
+    SkPixmap pmap;
+    if (SkImage::kAllow_CachingHint == chint) {
+        auto desc = SkBitmapCacheDesc::Make(uniqueID, info.width(), info.height());
+        cacheRec = SkBitmapCache::Alloc(desc, info, &pmap);
+        if (!cacheRec) {
+            return false;
+        }
+    } else {
+        if (!tmpBitmap.tryAllocPixels(info)) {
+            return false;
+        }
+        if (!tmpBitmap.peekPixels(&pmap)) {
+            return false;
+        }
+    }
+
+    ScopedGenerator generator(fSharedGenerator);
+    if (!generate_pixels(generator, pmap, fOrigin.x(), fOrigin.y())) {
+        return false;
+    }
+
+    if (cacheRec) {
+        SkBitmapCache::Add(std::move(cacheRec), bitmap);
+        SkASSERT(bitmap->getPixels());  // we're locked
+        SkASSERT(bitmap->isImmutable());
+        SkASSERT(bitmap->getGenerationID() == uniqueID);
+        this->notifyAddedToCache();
+    } else {
+        *bitmap = tmpBitmap;
+        bitmap->pixelRef()->setImmutableWithID(uniqueID);
+    }
+
+    check_output_bitmap(*bitmap, uniqueID);
+    return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
 bool SkImage_Lazy::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
                                 int srcX, int srcY, CachingHint chint) const {
     SkColorSpace* dstColorSpace = dstInfo.colorSpace();
     SkBitmap bm;
     if (kDisallow_CachingHint == chint) {
-        SkImageCacherator::CachedFormat cacheFormat = fCache.chooseCacheFormat(dstColorSpace);
-        if (fCache.lockAsBitmapOnlyIfAlreadyCached(&bm, cacheFormat)) {
+        CachedFormat cacheFormat = this->chooseCacheFormat(dstColorSpace);
+        if (this->lockAsBitmapOnlyIfAlreadyCached(&bm, cacheFormat)) {
             return bm.readPixels(dstInfo, dstPixels, dstRB, srcX, srcY);
         } else {
             // Try passing the caller's buffer directly down to the generator. If this fails we
             // may still succeed in the general case, as the generator may prefer some other
             // config, which we could then convert via SkBitmap::readPixels.
-            if (fCache.directGeneratePixels(dstInfo, dstPixels, dstRB, srcX, srcY,
-                                            SkTransferFunctionBehavior::kRespect)) {
+            if (this->directGeneratePixels(dstInfo, dstPixels, dstRB, srcX, srcY,
+                                           SkTransferFunctionBehavior::kRespect)) {
                 return true;
             }
             // else fall through
@@ -75,39 +537,46 @@
 }
 
 SkData* SkImage_Lazy::onRefEncoded() const {
-    return fCache.refEncoded();
+    ScopedGenerator generator(fSharedGenerator);
+    return generator->refEncodedData();
 }
 
 bool SkImage_Lazy::getROPixels(SkBitmap* bitmap, SkColorSpace* dstColorSpace,
                                CachingHint chint) const {
-    SkImageCacherator::CachedFormat cacheFormat = fCache.chooseCacheFormat(dstColorSpace);
-    SkImageInfo cacheInfo = fCache.buildCacheInfo(cacheFormat);
-    return fCache.lockAsBitmap(bitmap, this, chint, cacheFormat, cacheInfo);
+    CachedFormat cacheFormat = this->chooseCacheFormat(dstColorSpace);
+    SkImageInfo cacheInfo = this->buildCacheInfo(cacheFormat);
+    return this->lockAsBitmap(bitmap, chint, cacheFormat, cacheInfo);
 }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
 #if SK_SUPPORT_GPU
 sk_sp<GrTextureProxy> SkImage_Lazy::asTextureProxyRef(GrContext* context,
                                                       const GrSamplerParams& params,
                                                       SkColorSpace* dstColorSpace,
                                                       sk_sp<SkColorSpace>* texColorSpace,
                                                       SkScalar scaleAdjust[2]) const {
-    return fCache.lockAsTextureProxy(context, params, dstColorSpace,
-                                     texColorSpace, this, scaleAdjust);
+    if (!context) {
+        return nullptr;
+    }
+
+    GrImageTextureMaker textureMaker(context, this, kAllow_CachingHint);
+    return textureMaker.refTextureProxyForParams(params, dstColorSpace, texColorSpace, scaleAdjust);
 }
 #endif
 
 sk_sp<SkImage> SkImage_Lazy::onMakeSubset(const SkIRect& subset) const {
-    SkASSERT(fCache.info().bounds().contains(subset));
-    SkASSERT(fCache.info().bounds() != subset);
+    SkASSERT(fInfo.bounds().contains(subset));
+    SkASSERT(fInfo.bounds() != subset);
 
-    const SkIRect generatorSubset = subset.makeOffset(fCache.fOrigin.x(), fCache.fOrigin.y());
-    SkImageCacherator::Validator validator(fCache.fSharedGenerator, &generatorSubset);
+    const SkIRect generatorSubset = subset.makeOffset(fOrigin.x(), fOrigin.y());
+    Validator validator(fSharedGenerator, &generatorSubset);
     return validator ? sk_sp<SkImage>(new SkImage_Lazy(&validator)) : nullptr;
 }
 
 sk_sp<SkImage> SkImage_Lazy::onMakeColorSpace(sk_sp<SkColorSpace> target) const {
     SkBitmap dst;
-    SkImageInfo dstInfo = fCache.info().makeColorSpace(target);
+    SkImageInfo dstInfo = fInfo.makeColorSpace(target);
     if (kIndex_8_SkColorType == dstInfo.colorType() ||
         kGray_8_SkColorType == dstInfo.colorType() ||
         kRGB_565_SkColorType == dstInfo.colorType()) {
@@ -117,8 +586,8 @@
 
     // Use kIgnore for transfer function behavior.  This is used by the SkColorSpaceXformCanvas,
     // which wants to pre-xform the inputs but ignore the transfer function on blends.
-    if (!fCache.directGeneratePixels(dstInfo, dst.getPixels(), dst.rowBytes(), 0, 0,
-                                     SkTransferFunctionBehavior::kIgnore)) {
+    if (!this->directGeneratePixels(dstInfo, dst.getPixels(), dst.rowBytes(), 0, 0,
+                                    SkTransferFunctionBehavior::kIgnore)) {
         return nullptr;
     }
 
@@ -128,8 +597,154 @@
 
 sk_sp<SkImage> SkImage::MakeFromGenerator(std::unique_ptr<SkImageGenerator> generator,
                                           const SkIRect* subset) {
-    SkImageCacherator::Validator validator(
-                       SkImageCacherator::SharedGenerator::Make(std::move(generator)), subset);
+    SkImage_Lazy::Validator validator(SharedGenerator::Make(std::move(generator)), subset);
 
     return validator ? sk_make_sp<SkImage_Lazy>(&validator) : nullptr;
 }
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  Implementation of SkImageCacherator interface, as needed by GrImageTextureMaker
+ */
+
+#if SK_SUPPORT_GPU
+
+void SkImage_Lazy::makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat format,
+                                           GrUniqueKey* cacheKey) {
+    SkASSERT(!cacheKey->isValid());
+    if (origKey.isValid()) {
+        static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
+        GrUniqueKey::Builder builder(cacheKey, origKey, kDomain, 1);
+        builder[0] = format;
+    }
+}
+
+class Generator_GrYUVProvider : public GrYUVProvider {
+    SkImageGenerator* fGen;
+
+public:
+    Generator_GrYUVProvider(SkImageGenerator* gen) : fGen(gen) {}
+
+    uint32_t onGetID() override { return fGen->uniqueID(); }
+    bool onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const override {
+        return fGen->queryYUV8(sizeInfo, colorSpace);
+    }
+    bool onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) override {
+        return fGen->getYUV8Planes(sizeInfo, planes);
+    }
+};
+
+static void set_key_on_proxy(GrResourceProvider* resourceProvider,
+                             GrTextureProxy* proxy, const GrUniqueKey& key) {
+    if (key.isValid()) {
+        resourceProvider->assignUniqueKeyToProxy(key, proxy);
+    }
+}
+
+sk_sp<SkColorSpace> SkImage_Lazy::getColorSpace(GrContext* ctx, SkColorSpace* dstColorSpace) {
+    // TODO: This isn't always correct. Picture generator currently produces textures in N32,
+    // and will (soon) emit them in an arbitrary (destination) space. We will need to stash that
+    // information in/on the key so we can return the correct space in case #1 of lockTexture.
+    CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
+    SkImageInfo cacheInfo = this->buildCacheInfo(format);
+    return sk_ref_sp(cacheInfo.colorSpace());
+}
+
+/*
+ *  We have 4 ways to try to return a texture (in sorted order)
+ *
+ *  1. Check the cache for a pre-existing one
+ *  2. Ask the generator to natively create one
+ *  3. Ask the generator to return YUV planes, which the GPU can convert
+ *  4. Ask the generator to return RGB(A) data, which the GPU can convert
+ */
+sk_sp<GrTextureProxy> SkImage_Lazy::lockTextureProxy(GrContext* ctx,
+                                                     const GrUniqueKey& origKey,
+                                                     SkImage::CachingHint chint,
+                                                     bool willBeMipped,
+                                                     SkColorSpace* dstColorSpace) {
+    // Values representing the various texture lock paths we can take. Used for logging the path
+    // taken to a histogram.
+    enum LockTexturePath {
+        kFailure_LockTexturePath,
+        kPreExisting_LockTexturePath,
+        kNative_LockTexturePath,
+        kCompressed_LockTexturePath, // Deprecated
+        kYUV_LockTexturePath,
+        kRGBA_LockTexturePath,
+    };
+
+    enum { kLockTexturePathCount = kRGBA_LockTexturePath + 1 };
+
+    // Determine which cached format we're going to use (which may involve decoding to a different
+    // info than the generator provides).
+    CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
+
+    // Fold the cache format into our texture key
+    GrUniqueKey key;
+    this->makeCacheKeyFromOrigKey(origKey, format, &key);
+
+    // 1. Check the cache for a pre-existing one
+    if (key.isValid()) {
+        if (sk_sp<GrTextureProxy> proxy = ctx->resourceProvider()->findProxyByUniqueKey(key)) {
+            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kPreExisting_LockTexturePath,
+                                     kLockTexturePathCount);
+            return proxy;
+        }
+    }
+
+    // The CachedFormat is both an index for which cache "slot" we'll use to store this particular
+    // decoded variant of the encoded data, and also a recipe for how to transform the original
+    // info to get the one that we're going to decode to.
+    SkImageInfo cacheInfo = this->buildCacheInfo(format);
+
+    // 2. Ask the generator to natively create one
+    {
+        ScopedGenerator generator(fSharedGenerator);
+        if (sk_sp<GrTextureProxy> proxy = generator->generateTexture(ctx, cacheInfo, fOrigin)) {
+            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath,
+                                     kLockTexturePathCount);
+            set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
+            return proxy;
+        }
+    }
+
+    // 3. Ask the generator to return YUV planes, which the GPU can convert
+    if (!ctx->contextPriv().disableGpuYUVConversion()) {
+        const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->caps());
+        ScopedGenerator generator(fSharedGenerator);
+        Generator_GrYUVProvider provider(generator);
+        if (sk_sp<GrTextureProxy> proxy = provider.refAsTextureProxy(ctx, desc, true)) {
+            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath,
+                                     kLockTexturePathCount);
+            set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
+            return proxy;
+        }
+    }
+
+    // 4. Ask the generator to return RGB(A) data, which the GPU can convert
+    SkBitmap bitmap;
+    if (this->lockAsBitmap(&bitmap, chint, format, cacheInfo)) {
+        sk_sp<GrTextureProxy> proxy;
+        if (willBeMipped) {
+            proxy = GrGenerateMipMapsAndUploadToTextureProxy(ctx, bitmap, dstColorSpace);
+        }
+        if (!proxy) {
+            proxy = GrUploadBitmapToTextureProxy(ctx->resourceProvider(), bitmap);
+        }
+        if (proxy) {
+            SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath,
+                                     kLockTexturePathCount);
+            set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
+            return proxy;
+        }
+    }
+    SK_HISTOGRAM_ENUMERATION("LockTexturePath", kFailure_LockTexturePath,
+                             kLockTexturePathCount);
+    return nullptr;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#endif