Make SkImage_Lazy::onMakeColorSpace return a SkImage_Lazy

Make SkImage_Lazy::onMakeColorSpace return a new SkImage_Lazy
with the color space of SkImage_Lazy::fInfo changed.

Update the call to SkImageGenerator::getPixels to specify
image info with this new color space.

Update the call to SkImageGenerator::generateTexture to specify
this new color space. Update SkPictureImageGenerator to respect
the color space argument. Add a SkTransferFunctionBehavior
argument to SkImageGenerator::generateTexture to indicate if
color conversion is to be doing using an xform canvas.

Update Generator_GrYUVProvider::refAsTextureProxy to include a
color conversion step to respect this new color space.

TBR=reed@google.com
Bug:739559
Change-Id: I156a858884659e9dfae739a653bab2ef89274959
Reviewed-on: https://skia-review.googlesource.com/21605
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Christopher Cameron <ccameron@chromium.org>
Reviewed-by: Christopher Cameron <ccameron@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/gm/image_pict.cpp b/gm/image_pict.cpp
index 2a902cc..4a77797 100644
--- a/gm/image_pict.cpp
+++ b/gm/image_pict.cpp
@@ -163,7 +163,8 @@
     }
 protected:
     sk_sp<GrTextureProxy> onGenerateTexture(GrContext* ctx, const SkImageInfo& info,
-                                            const SkIPoint& origin) override {
+                                            const SkIPoint& origin,
+                                            SkTransferFunctionBehavior) override {
         SkASSERT(ctx);
         SkASSERT(ctx == fCtx.get());
 
diff --git a/include/core/SkImageGenerator.h b/include/core/SkImageGenerator.h
index 776df56..2a536d8 100644
--- a/include/core/SkImageGenerator.h
+++ b/include/core/SkImageGenerator.h
@@ -142,7 +142,8 @@
      *  - it can somehow convert its texture into one that is valid for the provided context.
      */
     sk_sp<GrTextureProxy> generateTexture(GrContext*, const SkImageInfo& info,
-                                          const SkIPoint& origin);
+                                          const SkIPoint& origin,
+                                          SkTransferFunctionBehavior behavior);
 #endif
 
     /**
@@ -183,8 +184,8 @@
     };
 
     virtual TexGenType onCanGenerateTexture() const { return TexGenType::kNone; }
-    virtual sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
-                                                    const SkIPoint&);   // returns nullptr
+    virtual sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&, const SkIPoint&,
+                                                    SkTransferFunctionBehavior);  // returns nullptr
 #endif
 
 private:
diff --git a/src/core/SkColorSpaceXformImageGenerator.cpp b/src/core/SkColorSpaceXformImageGenerator.cpp
index e98571d..707de08 100644
--- a/src/core/SkColorSpaceXformImageGenerator.cpp
+++ b/src/core/SkColorSpaceXformImageGenerator.cpp
@@ -62,9 +62,9 @@
 #include "SkGr.h"
 #include "effects/GrNonlinearColorSpaceXformEffect.h"
 
-sk_sp<GrTextureProxy> SkColorSpaceXformImageGenerator::onGenerateTexture(GrContext* ctx,
-                                                                         const SkImageInfo& info,
-                                                                         const SkIPoint& origin) {
+sk_sp<GrTextureProxy> SkColorSpaceXformImageGenerator::onGenerateTexture(
+        GrContext* ctx, const SkImageInfo& info, const SkIPoint& origin,
+        SkTransferFunctionBehavior) {
     // FIXME:
     // This always operates as if SkTranferFunctionBehavior is kIgnore.  Should we add
     // options so that caller can also request kRespect?
diff --git a/src/core/SkColorSpaceXformImageGenerator.h b/src/core/SkColorSpaceXformImageGenerator.h
index c548fa9..ab72eba 100644
--- a/src/core/SkColorSpaceXformImageGenerator.h
+++ b/src/core/SkColorSpaceXformImageGenerator.h
@@ -22,8 +22,8 @@
                      const Options& opts) override;
 
 #if SK_SUPPORT_GPU
-    sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
-                                            const SkIPoint&) override;
+    sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&, const SkIPoint&,
+                                            SkTransferFunctionBehavior) override;
     TexGenType onCanGenerateTexture() const override {
         return TexGenType::kExpensive;
     }
diff --git a/src/core/SkImageGenerator.cpp b/src/core/SkImageGenerator.cpp
index 9e5f78e..ab76674 100644
--- a/src/core/SkImageGenerator.cpp
+++ b/src/core/SkImageGenerator.cpp
@@ -65,16 +65,18 @@
 #include "GrTextureProxy.h"
 
 sk_sp<GrTextureProxy> SkImageGenerator::generateTexture(GrContext* ctx, const SkImageInfo& info,
-                                                        const SkIPoint& origin) {
+                                                        const SkIPoint& origin,
+                                                        SkTransferFunctionBehavior behavior) {
     SkIRect srcRect = SkIRect::MakeXYWH(origin.x(), origin.y(), info.width(), info.height());
     if (!SkIRect::MakeWH(fInfo.width(), fInfo.height()).contains(srcRect)) {
         return nullptr;
     }
-    return this->onGenerateTexture(ctx, info, origin);
+    return this->onGenerateTexture(ctx, info, origin, behavior);
 }
 
 sk_sp<GrTextureProxy> SkImageGenerator::onGenerateTexture(GrContext*, const SkImageInfo&,
-                                                          const SkIPoint&) {
+                                                          const SkIPoint&,
+                                                          SkTransferFunctionBehavior) {
     return nullptr;
 }
 #endif
diff --git a/src/core/SkPictureImageGenerator.cpp b/src/core/SkPictureImageGenerator.cpp
index 86e98d8..1f60380 100644
--- a/src/core/SkPictureImageGenerator.cpp
+++ b/src/core/SkPictureImageGenerator.cpp
@@ -100,23 +100,32 @@
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #if SK_SUPPORT_GPU
-sk_sp<GrTextureProxy> SkPictureImageGenerator::onGenerateTexture(GrContext* ctx,
-                                                                 const SkImageInfo& info,
-                                                                 const SkIPoint& origin) {
+sk_sp<GrTextureProxy> SkPictureImageGenerator::onGenerateTexture(
+        GrContext* ctx, const SkImageInfo& info, const SkIPoint& origin,
+        SkTransferFunctionBehavior behavior) {
     SkASSERT(ctx);
+    bool useXformCanvas = SkTransferFunctionBehavior::kIgnore == behavior && info.colorSpace();
 
     //
     // TODO: respect the usage, by possibly creating a different (pow2) surface
     //
-    sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kYes, info));
+    SkImageInfo surfaceInfo = useXformCanvas ? info.makeColorSpace(nullptr) : info;
+    sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kYes, surfaceInfo));
     if (!surface) {
         return nullptr;
     }
 
+    SkCanvas* canvas = surface->getCanvas();
+    std::unique_ptr<SkCanvas> xformCanvas;
+    if (useXformCanvas) {
+        xformCanvas = SkCreateColorSpaceXformCanvas(canvas, info.refColorSpace());
+        canvas = xformCanvas.get();
+    }
+
     SkMatrix matrix = fMatrix;
     matrix.postTranslate(-origin.x(), -origin.y());
-    surface->getCanvas()->clear(0); // does NewRenderTarget promise to do this for us?
-    surface->getCanvas()->drawPicture(fPicture.get(), &matrix, fPaint.getMaybeNull());
+    canvas->clear(0);  // does NewRenderTarget promise to do this for us?
+    canvas->drawPicture(fPicture.get(), &matrix, fPaint.getMaybeNull());
     sk_sp<SkImage> image(surface->makeImageSnapshot());
     if (!image) {
         return nullptr;
diff --git a/src/core/SkPictureImageGenerator.h b/src/core/SkPictureImageGenerator.h
index f328e4f..f2ebc80 100644
--- a/src/core/SkPictureImageGenerator.h
+++ b/src/core/SkPictureImageGenerator.h
@@ -22,8 +22,8 @@
 
 #if SK_SUPPORT_GPU
     TexGenType onCanGenerateTexture() const override { return TexGenType::kExpensive; }
-    sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
-                                            const SkIPoint&) override;
+    sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&, const SkIPoint&,
+                                            SkTransferFunctionBehavior) override;
 #endif
 
 private:
diff --git a/src/gpu/GrAHardwareBufferImageGenerator.cpp b/src/gpu/GrAHardwareBufferImageGenerator.cpp
index b05605c..9edc25c 100644
--- a/src/gpu/GrAHardwareBufferImageGenerator.cpp
+++ b/src/gpu/GrAHardwareBufferImageGenerator.cpp
@@ -96,9 +96,9 @@
 
 #if SK_SUPPORT_GPU
 
-
 sk_sp<GrTextureProxy> GrAHardwareBufferImageGenerator::onGenerateTexture(
-        GrContext* context, const SkImageInfo& info, const SkIPoint& origin) {
+        GrContext* context, const SkImageInfo& info, const SkIPoint& origin,
+        SkTransferFunctionBehavior) {
     auto proxy = this->makeProxy(context);
     if (!proxy) {
         return nullptr;
diff --git a/src/gpu/GrAHardwareBufferImageGenerator.h b/src/gpu/GrAHardwareBufferImageGenerator.h
index f23738b..f270d6a 100644
--- a/src/gpu/GrAHardwareBufferImageGenerator.h
+++ b/src/gpu/GrAHardwareBufferImageGenerator.h
@@ -35,8 +35,8 @@
 
 #if SK_SUPPORT_GPU
     TexGenType onCanGenerateTexture() const override { return TexGenType::kCheap; }
-    sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
-                                            const SkIPoint&) override;
+    sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&, const SkIPoint&,
+                                            SkTransferFunctionBehavior) override;
 #endif
 
 private:
diff --git a/src/gpu/GrBackendTextureImageGenerator.cpp b/src/gpu/GrBackendTextureImageGenerator.cpp
index 7ab7c7f..52ba4e7 100644
--- a/src/gpu/GrBackendTextureImageGenerator.cpp
+++ b/src/gpu/GrBackendTextureImageGenerator.cpp
@@ -115,7 +115,8 @@
 }
 
 sk_sp<GrTextureProxy> GrBackendTextureImageGenerator::onGenerateTexture(
-        GrContext* context, const SkImageInfo& info, const SkIPoint& origin) {
+        GrContext* context, const SkImageInfo& info, const SkIPoint& origin,
+        SkTransferFunctionBehavior) {
     SkASSERT(context);
 
     if (context->contextPriv().getBackend() != fBackendTexture.backend()) {
diff --git a/src/gpu/GrBackendTextureImageGenerator.h b/src/gpu/GrBackendTextureImageGenerator.h
index 675c553..1124b96 100644
--- a/src/gpu/GrBackendTextureImageGenerator.h
+++ b/src/gpu/GrBackendTextureImageGenerator.h
@@ -28,8 +28,8 @@
 
 #if SK_SUPPORT_GPU
     TexGenType onCanGenerateTexture() const override { return TexGenType::kCheap; }
-    sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&,
-                                            const SkIPoint&) override;
+    sk_sp<GrTextureProxy> onGenerateTexture(GrContext*, const SkImageInfo&, const SkIPoint&,
+                                            SkTransferFunctionBehavior) override;
 #endif
 
 private:
diff --git a/src/gpu/GrYUVProvider.cpp b/src/gpu/GrYUVProvider.cpp
index df71139..341b711 100644
--- a/src/gpu/GrYUVProvider.cpp
+++ b/src/gpu/GrYUVProvider.cpp
@@ -16,6 +16,7 @@
 #include "SkRefCnt.h"
 #include "SkResourceCache.h"
 #include "SkYUVPlanesCache.h"
+#include "effects/GrNonlinearColorSpaceXformEffect.h"
 #include "effects/GrSRGBEffect.h"
 #include "effects/GrYUVEffect.h"
 
@@ -84,9 +85,10 @@
     return true;
 }
 
-sk_sp<GrTextureProxy> GrYUVProvider::refAsTextureProxy(GrContext* ctx,
-                                                       const GrSurfaceDesc& desc,
-                                                       bool useCache) {
+sk_sp<GrTextureProxy> GrYUVProvider::refAsTextureProxy(GrContext* ctx, const GrSurfaceDesc& desc,
+                                                       bool useCache,
+                                                       const SkColorSpace* srcColorSpace,
+                                                       const SkColorSpace* dstColorSpace) {
     SkYUVPlanesCache::Info yuvInfo;
     void* planes[3];
     YUVScoper scoper;
@@ -153,6 +155,14 @@
         }
     }
 
+    // If the caller expects the pixels in a different color space than the one from the image,
+    // apply a color conversion to do this.
+    sk_sp<GrFragmentProcessor> colorConversionProcessor =
+            GrNonlinearColorSpaceXformEffect::Make(srcColorSpace, dstColorSpace);
+    if (colorConversionProcessor) {
+        paint.addColorFragmentProcessor(colorConversionProcessor);
+    }
+
     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
     const SkRect r = SkRect::MakeIWH(yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth,
                                      yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
diff --git a/src/gpu/GrYUVProvider.h b/src/gpu/GrYUVProvider.h
index b5be426..372a26a 100644
--- a/src/gpu/GrYUVProvider.h
+++ b/src/gpu/GrYUVProvider.h
@@ -32,11 +32,15 @@
      *  On success, this returns a texture proxy that has converted the YUV data from the provider
      *  into a form that is supported by the GPU (typically transformed into RGB). If useCache
      *  is true, then the texture will automatically have a key added, so it can be retrieved
-     *  from the cache (assuming it is requested by a provider w/ the same genID).
+     *  from the cache (assuming it is requested by a provider w/ the same genID). If srcColorSpace
+     *  and dstColorSpace are specified, then a color conversion from src to dst will be applied to
+     *  the pixels.
      *
      *  On failure (e.g. the provider had no data), this returns NULL.
      */
-    sk_sp<GrTextureProxy> refAsTextureProxy(GrContext*, const GrSurfaceDesc&, bool useCache);
+    sk_sp<GrTextureProxy> refAsTextureProxy(GrContext*, const GrSurfaceDesc&, bool useCache,
+                                            const SkColorSpace* srcColorSpace,
+                                            const SkColorSpace* dstColorSpace);
 
     virtual uint32_t onGetID() = 0;
 
diff --git a/src/image/SkImage_Lazy.cpp b/src/image/SkImage_Lazy.cpp
index 7fe3236..09130a6 100644
--- a/src/image/SkImage_Lazy.cpp
+++ b/src/image/SkImage_Lazy.cpp
@@ -55,13 +55,14 @@
 class SkImage_Lazy : public SkImage_Base, public SkImageCacherator {
 public:
     struct Validator {
-        Validator(sk_sp<SharedGenerator>, const SkIRect* subset);
+        Validator(sk_sp<SharedGenerator>, const SkIRect* subset, sk_sp<SkColorSpace> colorSpace);
 
         operator bool() const { return fSharedGenerator.get(); }
 
         sk_sp<SharedGenerator> fSharedGenerator;
         SkImageInfo            fInfo;
         SkIPoint               fOrigin;
+        sk_sp<SkColorSpace>    fColorSpace;
         uint32_t               fUniqueID;
     };
 
@@ -131,9 +132,22 @@
      *  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;
+    bool lockAsBitmap(SkBitmap*, SkImage::CachingHint, CachedFormat, const SkImageInfo&,
+                      SkTransferFunctionBehavior) const;
+
+    /**
+     * Populates parameters to pass to the generator for reading pixels or generating a texture.
+     * For image generators, legacy versus true color blending is indicated using a
+     * SkTransferFunctionBehavior, and the target color space is specified on the SkImageInfo.
+     * If generatorImageInfo has no color space set, set its color space to this SkImage's color
+     * space, and return "ignore" behavior, indicating legacy mode. If generatorImageInfo has a
+     * color space set, return "respect" behavior, indicating linear blending mode.
+     */
+    SkTransferFunctionBehavior getGeneratorBehaviorAndInfo(SkImageInfo* generatorImageInfo) const;
 
     sk_sp<SharedGenerator> fSharedGenerator;
+    // Note that fInfo is not necessarily the info from the generator. It may be cropped by
+    // onMakeSubset and its color space may be changed by onMakeColorSpace.
     const SkImageInfo      fInfo;
     const SkIPoint         fOrigin;
 
@@ -150,9 +164,9 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkImage_Lazy::Validator::Validator(sk_sp<SharedGenerator> gen, const SkIRect* subset)
+SkImage_Lazy::Validator::Validator(sk_sp<SharedGenerator> gen, const SkIRect* subset,
+                                   sk_sp<SkColorSpace> colorSpace)
         : fSharedGenerator(std::move(gen)) {
-
     if (!fSharedGenerator) {
         return;
     }
@@ -182,6 +196,10 @@
 
     fInfo   = info.makeWH(subset->width(), subset->height());
     fOrigin = SkIPoint::Make(subset->x(), subset->y());
+    if (colorSpace) {
+        fInfo = fInfo.makeColorSpace(colorSpace);
+        fUniqueID = SkNextID::ImageID();
+    }
 
     // colortables are poorly to not-at-all supported in our resourcecache, so we
     // bully them into N32 (the generator will perform the up-sample)
@@ -421,7 +439,8 @@
     }
 
     SkImageGenerator::Options opts;
-    opts.fBehavior = behavior;
+    // TODO: This should respect the behavior argument.
+    opts.fBehavior = SkTransferFunctionBehavior::kIgnore;
     return generator->getPixels(info, pixels, rb, &opts);
 }
 
@@ -434,7 +453,8 @@
            check_output_bitmap(*bitmap, uniqueID);
 }
 
-static bool generate_pixels(SkImageGenerator* gen, const SkPixmap& pmap, int originX, int originY) {
+static bool generate_pixels(SkImageGenerator* gen, const SkPixmap& pmap, int originX, int originY,
+                            SkTransferFunctionBehavior behavior) {
     const int genW = gen->getInfo().width();
     const int genH = gen->getInfo().height();
     const SkIRect srcR = SkIRect::MakeWH(genW, genH);
@@ -458,7 +478,9 @@
         dstPM = &fullPM;
     }
 
-    if (!gen->getPixels(dstPM->info(), dstPM->writable_addr(), dstPM->rowBytes())) {
+    SkImageGenerator::Options opts;
+    opts.fBehavior = behavior;
+    if (!gen->getPixels(dstPM->info(), dstPM->writable_addr(), dstPM->rowBytes(), &opts)) {
         return false;
     }
 
@@ -470,8 +492,9 @@
     return true;
 }
 
-bool SkImage_Lazy::lockAsBitmap(SkBitmap* bitmap, SkImage::CachingHint chint,
-                                CachedFormat format, const SkImageInfo& info) const {
+bool SkImage_Lazy::lockAsBitmap(SkBitmap* bitmap, SkImage::CachingHint chint, CachedFormat format,
+                                const SkImageInfo& info,
+                                SkTransferFunctionBehavior behavior) const {
     if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap, format)) {
         return true;
     }
@@ -497,7 +520,7 @@
     }
 
     ScopedGenerator generator(fSharedGenerator);
-    if (!generate_pixels(generator, pmap, fOrigin.x(), fOrigin.y())) {
+    if (!generate_pixels(generator, pmap, fOrigin.x(), fOrigin.y(), behavior)) {
         return false;
     }
 
@@ -524,14 +547,15 @@
     SkBitmap bm;
     if (kDisallow_CachingHint == chint) {
         CachedFormat cacheFormat = this->chooseCacheFormat(dstColorSpace);
+        SkImageInfo genPixelsInfo = dstInfo;
+        SkTransferFunctionBehavior behavior = getGeneratorBehaviorAndInfo(&genPixelsInfo);
         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 (this->directGeneratePixels(dstInfo, dstPixels, dstRB, srcX, srcY,
-                                           SkTransferFunctionBehavior::kRespect)) {
+            if (this->directGeneratePixels(genPixelsInfo, dstPixels, dstRB, srcX, srcY, behavior)) {
                 return true;
             }
             // else fall through
@@ -552,8 +576,10 @@
 bool SkImage_Lazy::getROPixels(SkBitmap* bitmap, SkColorSpace* dstColorSpace,
                                CachingHint chint) const {
     CachedFormat cacheFormat = this->chooseCacheFormat(dstColorSpace);
-    SkImageInfo cacheInfo = this->buildCacheInfo(cacheFormat);
-    return this->lockAsBitmap(bitmap, chint, cacheFormat, cacheInfo);
+    const SkImageInfo cacheInfo = this->buildCacheInfo(cacheFormat);
+    SkImageInfo genPixelsInfo = cacheInfo;
+    SkTransferFunctionBehavior behavior = getGeneratorBehaviorAndInfo(&genPixelsInfo);
+    return this->lockAsBitmap(bitmap, chint, cacheFormat, genPixelsInfo, behavior);
 }
 
 bool SkImage_Lazy::onIsValid(GrContext* context) const {
@@ -570,6 +596,24 @@
 #endif
 }
 
+SkTransferFunctionBehavior SkImage_Lazy::getGeneratorBehaviorAndInfo(SkImageInfo* generatorImageInfo) const {
+    if (generatorImageInfo->colorSpace()) {
+        return SkTransferFunctionBehavior::kRespect;
+    }
+    // Only specify an output color space if color conversion can be done on the color type.
+    switch (generatorImageInfo->colorType()) {
+        case kRGBA_8888_SkColorType:
+        case kBGRA_8888_SkColorType:
+        case kRGBA_F16_SkColorType:
+        case kRGB_565_SkColorType:
+            *generatorImageInfo = generatorImageInfo->makeColorSpace(fInfo.refColorSpace());
+            break;
+        default:
+            break;
+    }
+    return SkTransferFunctionBehavior::kIgnore;
+}
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #if SK_SUPPORT_GPU
@@ -592,37 +636,22 @@
     SkASSERT(fInfo.bounds() != subset);
 
     const SkIRect generatorSubset = subset.makeOffset(fOrigin.x(), fOrigin.y());
-    Validator validator(fSharedGenerator, &generatorSubset);
+    Validator validator(fSharedGenerator, &generatorSubset, fInfo.refColorSpace());
     return validator ? sk_sp<SkImage>(new SkImage_Lazy(&validator)) : nullptr;
 }
 
 sk_sp<SkImage> SkImage_Lazy::onMakeColorSpace(sk_sp<SkColorSpace> target,
                                               SkColorType targetColorType,
                                               SkTransferFunctionBehavior premulBehavior) const {
-    SkBitmap dst;
-    const SkImageInfo& genInfo = fSharedGenerator->getInfo();
-    SkImageInfo dstInfo = genInfo.makeColorType(targetColorType).makeColorSpace(target);
-    dst.allocPixels(dstInfo);
-    if (!this->directGeneratePixels(dstInfo, dst.getPixels(), dst.rowBytes(), 0, 0,
-                                    premulBehavior)) {
-        return nullptr;
-    }
-
-    dst.setImmutable();
-    sk_sp<SkImage> image = SkImage::MakeFromBitmap(dst);
-
-    if (genInfo.dimensions() != fInfo.dimensions()) {
-        // This image must be a subset.
-        image = image->makeSubset(SkIRect::MakeXYWH(fOrigin.fX, fOrigin.fY,
-                                                    fInfo.width(), fInfo.height()));
-    }
-
-    return image;
+    const SkIRect generatorSubset =
+            SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), fInfo.width(), fInfo.height());
+    Validator validator(fSharedGenerator, &generatorSubset, target);
+    return validator ? sk_sp<SkImage>(new SkImage_Lazy(&validator)) : nullptr;
 }
 
 sk_sp<SkImage> SkImage::MakeFromGenerator(std::unique_ptr<SkImageGenerator> generator,
                                           const SkIRect* subset) {
-    SkImage_Lazy::Validator validator(SharedGenerator::Make(std::move(generator)), subset);
+    SkImage_Lazy::Validator validator(SharedGenerator::Make(std::move(generator)), subset, nullptr);
 
     return validator ? sk_make_sp<SkImage_Lazy>(&validator) : nullptr;
 }
@@ -723,7 +752,9 @@
     // 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);
+    const SkImageInfo cacheInfo = this->buildCacheInfo(format);
+    SkImageInfo genPixelsInfo = cacheInfo;
+    SkTransferFunctionBehavior behavior = getGeneratorBehaviorAndInfo(&genPixelsInfo);
 
     // 2. Ask the generator to natively create one
     {
@@ -732,7 +763,8 @@
                 SkImageGenerator::TexGenType::kCheap != generator->onCanGenerateTexture()) {
             return nullptr;
         }
-        if (sk_sp<GrTextureProxy> proxy = generator->generateTexture(ctx, cacheInfo, fOrigin)) {
+        if (sk_sp<GrTextureProxy> proxy =
+                    generator->generateTexture(ctx, genPixelsInfo, fOrigin, behavior)) {
             SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath,
                                      kLockTexturePathCount);
             set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
@@ -745,7 +777,17 @@
         const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->caps());
         ScopedGenerator generator(fSharedGenerator);
         Generator_GrYUVProvider provider(generator);
-        if (sk_sp<GrTextureProxy> proxy = provider.refAsTextureProxy(ctx, desc, true)) {
+
+        // The pixels in the texture will be in the generator's color space. If onMakeColorSpace
+        // has been called then this will not match this image's color space. To correct this, apply
+        // a color space conversion from the generator's color space to this image's color space.
+        const SkColorSpace* generatorColorSpace =
+                fSharedGenerator->fGenerator->getInfo().colorSpace();
+        const SkColorSpace* thisColorSpace = fInfo.colorSpace();
+
+        sk_sp<GrTextureProxy> proxy =
+                provider.refAsTextureProxy(ctx, desc, true, generatorColorSpace, thisColorSpace);
+        if (proxy) {
             SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath,
                                      kLockTexturePathCount);
             set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
@@ -755,7 +797,7 @@
 
     // 4. Ask the generator to return RGB(A) data, which the GPU can convert
     SkBitmap bitmap;
-    if (this->lockAsBitmap(&bitmap, chint, format, cacheInfo)) {
+    if (this->lockAsBitmap(&bitmap, chint, format, genPixelsInfo, behavior)) {
         sk_sp<GrTextureProxy> proxy;
         if (willBeMipped) {
             proxy = GrGenerateMipMapsAndUploadToTextureProxy(ctx, bitmap, dstColorSpace);