Support decoding images to multiple formats, depending on usage
authorBrian Osman <brianosman@google.com>
Fri, 18 Nov 2016 16:28:24 +0000 (11:28 -0500)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Mon, 21 Nov 2016 14:58:32 +0000 (14:58 +0000)
Our codec generator will now preserve any asked-for color space, and
convert the encoded data to that representation. Cacherator now
allows decoding an image to both legacy (nullptr color space), and
color-correct formats. In color-correct mode, we choose the best
decoded format, based on the original properties, and our backend's
capabilities. Preference is given to the native format, when it's
already texturable (sRGB 8888 or F16 linear). Otherwise, we prefer
linear F16, and fall back to sRGB when that's not an option.

Re-land (and fix) of:
https://skia-review.googlesource.com/c/4438/
https://skia-review.googlesource.com/c/4796/

BUG=skia:5907

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=4838

Change-Id: I20ff972ffe1c7e6535ddc501e2a8ab8c246e4061
Reviewed-on: https://skia-review.googlesource.com/4838
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Matt Sarett <msarett@google.com>
33 files changed:
bench/SkBlend_optsBench.cpp
gm/image_pict.cpp
src/codec/SkCodecImageGenerator.cpp
src/core/SkBitmapDevice.cpp
src/core/SkBitmapProvider.cpp
src/core/SkBitmapProvider.h
src/core/SkDevice.cpp
src/core/SkImageCacherator.cpp
src/core/SkImageCacherator.h
src/core/SkShadowShader.cpp
src/core/SkSpecialImage.cpp
src/core/SkSpecialImage.h
src/effects/SkImageSource.cpp
src/gpu/GrImageIDTextureAdjuster.cpp
src/gpu/GrImageIDTextureAdjuster.h
src/gpu/GrTextureParamsAdjuster.cpp
src/gpu/GrTextureParamsAdjuster.h
src/gpu/SkGpuDevice.cpp
src/gpu/SkGr.cpp
src/image/SkImage.cpp
src/image/SkImageShader.cpp
src/image/SkImage_Base.h
src/image/SkImage_Generator.cpp
src/image/SkImage_Gpu.cpp
src/image/SkImage_Gpu.h
src/image/SkImage_Raster.cpp
src/pdf/SkPDFBitmap.cpp
src/pdf/SkPDFDevice.cpp
tests/ImageFilterCacheTest.cpp
tests/ImageTest.cpp
tests/SkBlend_optsTest.cpp
tests/SpecialImageTest.cpp
tools/Resources.cpp

index 6a4593e664bfb781e77c567f1b03547f1a0e3e76..b5827ef03bc9c5f7ff2bffc39632ab0b3d1b28ef 100644 (file)
@@ -146,7 +146,7 @@ protected:
         if (!fPixmap.addr()) {
             sk_sp<SkImage> image = GetResourceAsImage(fFileName.c_str());
             SkBitmap bm;
-            if (!as_IB(image)->getROPixels(&bm)) {
+            if (!as_IB(image)->getROPixels(&bm, SkDestinationSurfaceColorMode::kLegacy)) {
                 SkFAIL("Could not read resource");
             }
             bm.peekPixels(&fPixmap);
index 148426da5a3535df7452e2cad3aade4f00428023..11a56da9fcb72719bb4eca3ac7d8a6a42d498b4a 100644 (file)
@@ -308,15 +308,18 @@ protected:
 
     static void draw_as_bitmap(SkCanvas* canvas, SkImageCacherator* cache, SkScalar x, SkScalar y) {
         SkBitmap bitmap;
-        cache->lockAsBitmap(&bitmap, nullptr);
+        cache->lockAsBitmap(&bitmap, nullptr,
+                            SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware);
         canvas->drawBitmap(bitmap, x, y);
     }
 
     static void draw_as_tex(SkCanvas* canvas, SkImageCacherator* cache, SkScalar x, SkScalar y) {
 #if SK_SUPPORT_GPU
+        sk_sp<SkColorSpace> texColorSpace;
         sk_sp<GrTexture> texture(
             cache->lockAsTexture(canvas->getGrContext(), GrSamplerParams::ClampBilerp(),
-                                 SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware, nullptr));
+                                 SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware,
+                                 &texColorSpace, nullptr));
         if (!texture) {
             // show placeholder if we have no texture
             SkPaint paint;
@@ -331,8 +334,7 @@ protected:
         // No API to draw a GrTexture directly, so we cheat and create a private image subclass
         sk_sp<SkImage> image(new SkImage_Gpu(cache->info().width(), cache->info().height(),
                                              cache->uniqueID(), kPremul_SkAlphaType,
-                                             std::move(texture),
-                                             sk_ref_sp(cache->info().colorSpace()),
+                                             std::move(texture), std::move(texColorSpace),
                                              SkBudgeted::kNo));
         canvas->drawImage(image.get(), x, y);
 #endif
index 8108f0de441c64744faf5f6ffe9626798c027356..9f85971f898025943e1afdc3938f33c4bb4b51d7 100644 (file)
@@ -36,13 +36,7 @@ SkData* SkCodecImageGenerator::onRefEncodedData(SK_REFENCODEDDATA_CTXPARAM) {
 
 bool SkCodecImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
         SkPMColor ctable[], int* ctableCount) {
-
-    // FIXME (msarett):
-    // We don't give the client the chance to request an SkColorSpace.  Until we improve
-    // the API, let's assume that they want legacy mode.
-    SkImageInfo decodeInfo = info.makeColorSpace(nullptr);
-
-    SkCodec::Result result = fCodec->getPixels(decodeInfo, pixels, rowBytes, nullptr, ctable,
+    SkCodec::Result result = fCodec->getPixels(info, pixels, rowBytes, nullptr, ctable,
             ctableCount);
     switch (result) {
         case SkCodec::kSuccess:
index e44b76926ad07a2fdb39f2776b92051b1421c225..1e618accb0e0d03afddb61acbb3275af0de73869 100644 (file)
@@ -418,8 +418,13 @@ sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkBitmap& bitmap) {
 }
 
 sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkImage* image) {
+    // This is called when we're about to draw the special-ized version of image to *this* device,
+    // so we can use our own presense/absence of a color space to decide how to decode the image.
+    SkDestinationSurfaceColorMode decodeColorMode = fBitmap.colorSpace()
+        ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
+        : SkDestinationSurfaceColorMode::kLegacy;
     return SkSpecialImage::MakeFromImage(SkIRect::MakeWH(image->width(), image->height()),
-                                         image->makeNonTextureImage());
+                                         image->makeNonTextureImage(), decodeColorMode);
 }
 
 sk_sp<SkSpecialImage> SkBitmapDevice::snapSpecial() {
index 11b1ac98d1549b9256640fab5cf377a8ca2801ee..3ec4748c90feb431222e0308f7546c7f45ef4238 100644 (file)
@@ -45,7 +45,7 @@ void SkBitmapProvider::notifyAddedToCache() const {
 }
 
 bool SkBitmapProvider::asBitmap(SkBitmap* bm) const {
-    return as_IB(fImage)->getROPixels(bm, SkImage::kAllow_CachingHint);
+    return as_IB(fImage)->getROPixels(bm, fColorMode, SkImage::kAllow_CachingHint);
 }
 
 bool SkBitmapProvider::accessScaledImage(const SkRect& srcRect,
index fd5b66219e9f25b22597fd52a350fee3a77f6d9e..4437889fb0c6311e4801f3a00b0feedc143b12df 100644 (file)
 
 class SkBitmapProvider {
 public:
-    explicit SkBitmapProvider(const SkImage* img) : fImage(img) { SkASSERT(img); }
+    explicit SkBitmapProvider(const SkImage* img, SkDestinationSurfaceColorMode colorMode)
+        : fImage(img)
+        , fColorMode(colorMode) {
+        SkASSERT(img);
+    }
     SkBitmapProvider(const SkBitmapProvider& other)
         : fImage(other.fImage)
+        , fColorMode(other.fColorMode)
     {}
 
     int width() const;
@@ -44,7 +49,8 @@ private:
 
     // SkBitmapProvider is always short-lived/stack allocated, and the source image is guaranteed
     // to outlive its scope => we can store a raw ptr to avoid ref churn.
-    const SkImage* fImage;
+    const SkImage*                fImage;
+    SkDestinationSurfaceColorMode fColorMode;
 };
 
 #endif
index 4caa8756cb02b596dd12fe86d86d34d7d7356a7e..73f412d1d5b4f135291cbc3dd0bee2f9c0f669f3 100644 (file)
@@ -209,8 +209,11 @@ bool SkBaseDevice::drawExternallyScaledImage(const SkDraw& draw,
         return false;
     }
 
+    SkDestinationSurfaceColorMode colorMode = this->imageInfo().colorSpace()
+        ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
+        : SkDestinationSurfaceColorMode::kLegacy;
     SkBitmap bm;
-    if (!as_IB(rec.fImage)->getROPixels(&bm)) {
+    if (!as_IB(rec.fImage)->getROPixels(&bm, colorMode)) {
         return false;
     }
 
@@ -226,15 +229,17 @@ bool SkBaseDevice::drawExternallyScaledImage(const SkDraw& draw,
 void SkBaseDevice::drawImage(const SkDraw& draw, const SkImage* image, SkScalar x, SkScalar y,
                              const SkPaint& paint) {
     // Default impl : turns everything into raster bitmap
-
     if (this->drawExternallyScaledImage(draw, image, nullptr,
                                         SkRect::Make(image->bounds()).makeOffset(x, y),
                                         paint, SkCanvas::kFast_SrcRectConstraint)) {
         return;
     }
 
+    SkDestinationSurfaceColorMode colorMode = this->imageInfo().colorSpace()
+        ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
+        : SkDestinationSurfaceColorMode::kLegacy;
     SkBitmap bm;
-    if (as_IB(image)->getROPixels(&bm)) {
+    if (as_IB(image)->getROPixels(&bm, colorMode)) {
         this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint);
     }
 }
@@ -243,13 +248,15 @@ void SkBaseDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const
                                  const SkRect& dst, const SkPaint& paint,
                                  SkCanvas::SrcRectConstraint constraint) {
     // Default impl : turns everything into raster bitmap
-
     if (this->drawExternallyScaledImage(draw, image, src, dst, paint, constraint)) {
         return;
     }
 
+    SkDestinationSurfaceColorMode colorMode = this->imageInfo().colorSpace()
+        ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
+        : SkDestinationSurfaceColorMode::kLegacy;
     SkBitmap bm;
-    if (as_IB(image)->getROPixels(&bm)) {
+    if (as_IB(image)->getROPixels(&bm, colorMode)) {
         this->drawBitmapRect(draw, bm, src, dst, paint, constraint);
     }
 }
index e29124212afcef83875657cfbe0e94874263b370..157cea2bef7d7666c553d33f58c9879726e1dd7f 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "SkBitmap.h"
 #include "SkBitmapCache.h"
+#include "SkColorSpace_Base.h"
 #include "SkImage_Base.h"
 #include "SkImageCacherator.h"
 #include "SkMallocPixelRef.h"
@@ -86,6 +87,17 @@ SkImageCacherator::Validator::Validator(sk_sp<SharedGenerator> gen, const SkIRec
 
     fInfo   = info.makeWH(subset->width(), subset->height());
     fOrigin = SkIPoint::Make(subset->x(), subset->y());
+
+    // If the encoded data is in a strange color space (it's not an XYZ matrix space), we won't be
+    // able to preserve the gamut of the encoded data when we decode it. Instead, we'll have to
+    // decode to a known color space (linear sRGB is a good choice). But we need to adjust the
+    // stored color space, because drawing code will ask the SkImage for its color space, which
+    // will in turn ask the cacherator. If we return the A2B color space, then we will be unable to
+    // construct a source-to-dest gamut transformation matrix.
+    if (fInfo.colorSpace() &&
+        SkColorSpace_Base::Type::kXYZ != as_CSB(fInfo.colorSpace())->type()) {
+        fInfo = fInfo.makeColorSpace(SkColorSpace::MakeNamed(SkColorSpace::kSRGBLinear_Named));
+    }
 }
 
 SkImageCacherator* SkImageCacherator::NewFromGenerator(SkImageGenerator* gen,
@@ -99,8 +111,12 @@ SkImageCacherator::SkImageCacherator(Validator* validator)
     : fSharedGenerator(std::move(validator->fSharedGenerator)) // we take ownership
     , fInfo(validator->fInfo)
     , fOrigin(validator->fOrigin)
-    , fUniqueID(validator->fUniqueID)
 {
+    fUniqueIDs[kLegacy_CachedFormat] = validator->fUniqueID;
+    for (int i = 1; i < kNumCachedFormats; ++i) {
+        // We lazily allocate IDs for non-default caching cases
+        fUniqueIDs[i] = kNeedNewImageUniqueID;
+    }
     SkASSERT(fSharedGenerator);
 }
 
@@ -121,24 +137,26 @@ static bool check_output_bitmap(const SkBitmap& bitmap, uint32_t expectedID) {
 // Note, this returns a new, mutable, bitmap, with a new genID.
 // If you want the immutable bitmap with the same ID as our cacherator, call tryLockAsBitmap()
 //
-bool SkImageCacherator::generateBitmap(SkBitmap* bitmap) {
+bool SkImageCacherator::generateBitmap(SkBitmap* bitmap, const SkImageInfo& decodeInfo) {
     SkBitmap::Allocator* allocator = SkResourceCache::GetAllocator();
 
     ScopedGenerator generator(fSharedGenerator);
     const SkImageInfo& genInfo = generator->getInfo();
-    if (fInfo.dimensions() == genInfo.dimensions()) {
+    if (decodeInfo.dimensions() == genInfo.dimensions()) {
         SkASSERT(fOrigin.x() == 0 && fOrigin.y() == 0);
         // fast-case, no copy needed
-        return generator->tryGenerateBitmap(bitmap, fInfo, allocator);
+        return generator->tryGenerateBitmap(bitmap, decodeInfo, allocator);
     } else {
         // need to handle subsetting, so we first generate the full size version, and then
         // "read" from it to get our subset. See https://bug.skia.org/4213
 
         SkBitmap full;
-        if (!generator->tryGenerateBitmap(&full, genInfo, allocator)) {
+        if (!generator->tryGenerateBitmap(&full,
+                                          decodeInfo.makeWH(genInfo.width(), genInfo.height()),
+                                          allocator)) {
             return false;
         }
-        if (!bitmap->tryAllocPixels(fInfo, nullptr, full.getColorTable())) {
+        if (!bitmap->tryAllocPixels(decodeInfo, nullptr, full.getColorTable())) {
             return false;
         }
         return full.readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
@@ -166,22 +184,28 @@ bool SkImageCacherator::directAccessScaledImage(const SkRect& srcRect,
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
-bool SkImageCacherator::lockAsBitmapOnlyIfAlreadyCached(SkBitmap* bitmap) {
-    return SkBitmapCache::Find(fUniqueID, bitmap) && check_output_bitmap(*bitmap, fUniqueID);
+bool SkImageCacherator::lockAsBitmapOnlyIfAlreadyCached(SkBitmap* bitmap, CachedFormat format) {
+    return kNeedNewImageUniqueID != fUniqueIDs[format] &&
+        SkBitmapCache::Find(fUniqueIDs[format], bitmap) &&
+        check_output_bitmap(*bitmap, fUniqueIDs[format]);
 }
 
 bool SkImageCacherator::tryLockAsBitmap(SkBitmap* bitmap, const SkImage* client,
-                                        SkImage::CachingHint chint) {
-    if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap)) {
+                                        SkImage::CachingHint chint, CachedFormat format,
+                                        const SkImageInfo& info) {
+    if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap, format)) {
         return true;
     }
-    if (!this->generateBitmap(bitmap)) {
+    if (!this->generateBitmap(bitmap, info)) {
         return false;
     }
 
-    bitmap->pixelRef()->setImmutableWithID(fUniqueID);
+    if (kNeedNewImageUniqueID == fUniqueIDs[format]) {
+        fUniqueIDs[format] = SkNextID::ImageID();
+    }
+    bitmap->pixelRef()->setImmutableWithID(fUniqueIDs[format]);
     if (SkImage::kAllow_CachingHint == chint) {
-        SkBitmapCache::Add(fUniqueID, *bitmap);
+        SkBitmapCache::Add(fUniqueIDs[format], *bitmap);
         if (client) {
             as_IB(client)->notifyAddedToCache();
         }
@@ -190,9 +214,17 @@ bool SkImageCacherator::tryLockAsBitmap(SkBitmap* bitmap, const SkImage* client,
 }
 
 bool SkImageCacherator::lockAsBitmap(SkBitmap* bitmap, const SkImage* client,
+                                     SkDestinationSurfaceColorMode colorMode,
                                      SkImage::CachingHint chint) {
-    if (this->tryLockAsBitmap(bitmap, client, chint)) {
-        return check_output_bitmap(*bitmap, fUniqueID);
+    CachedFormat format = this->chooseCacheFormat(colorMode);
+    SkImageInfo cacheInfo = this->buildCacheInfo(format);
+
+    if (kNeedNewImageUniqueID == fUniqueIDs[format]) {
+        fUniqueIDs[format] = SkNextID::ImageID();
+    }
+
+    if (this->tryLockAsBitmap(bitmap, client, chint, format, cacheInfo)) {
+        return check_output_bitmap(*bitmap, fUniqueIDs[format]);
     }
 
 #if SK_SUPPORT_GPU
@@ -201,7 +233,8 @@ bool SkImageCacherator::lockAsBitmap(SkBitmap* bitmap, const SkImage* client,
 
     {
         ScopedGenerator generator(fSharedGenerator);
-        SkIRect subset = SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), fInfo.width(), fInfo.height());
+        SkIRect subset = SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(),
+                                           cacheInfo.width(), cacheInfo.height());
         tex.reset(generator->generateTexture(nullptr, &subset));
     }
     if (!tex) {
@@ -209,27 +242,27 @@ bool SkImageCacherator::lockAsBitmap(SkBitmap* bitmap, const SkImage* client,
         return false;
     }
 
-    if (!bitmap->tryAllocPixels(fInfo)) {
+    if (!bitmap->tryAllocPixels(cacheInfo)) {
         bitmap->reset();
         return false;
     }
 
     const uint32_t pixelOpsFlags = 0;
     if (!tex->readPixels(0, 0, bitmap->width(), bitmap->height(),
-                         SkImageInfo2GrPixelConfig(fInfo, *tex->getContext()->caps()),
+                         SkImageInfo2GrPixelConfig(cacheInfo, *tex->getContext()->caps()),
                          bitmap->getPixels(), bitmap->rowBytes(), pixelOpsFlags)) {
         bitmap->reset();
         return false;
     }
 
-    bitmap->pixelRef()->setImmutableWithID(fUniqueID);
+    bitmap->pixelRef()->setImmutableWithID(fUniqueIDs[format]);
     if (SkImage::kAllow_CachingHint == chint) {
-        SkBitmapCache::Add(fUniqueID, *bitmap);
+        SkBitmapCache::Add(fUniqueIDs[format], *bitmap);
         if (client) {
             as_IB(client)->notifyAddedToCache();
         }
     }
-    return check_output_bitmap(*bitmap, fUniqueID);
+    return check_output_bitmap(*bitmap, fUniqueIDs[format]);
 #else
     return false;
 #endif
@@ -237,8 +270,191 @@ bool SkImageCacherator::lockAsBitmap(SkBitmap* bitmap, const SkImage* client,
 
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
+// 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(
+                                                           SkDestinationSurfaceColorMode colorMode,
+                                                           const GrCaps* grCaps) {
+    SkColorSpace* cs = fInfo.colorSpace();
+    if (!cs || SkDestinationSurfaceColorMode::kLegacy == colorMode) {
+        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:
+            // We can't draw from indexed textures with a color space, so ask the codec to expand
+            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 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 kAsIs_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 kAsIs_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()) {
+                if (caps.supportsSRGB()) {
+                    return kSRGB8888_CachedFormat;
+                } else {
+                    return kLegacy_CachedFormat;
+                }
+            } else if (cs->gammaIsLinear()) {
+                return kAsIs_CachedFormat;
+            } else {
+                return kLinearF16_CachedFormat;
+            }
+    }
+    SkDEBUGFAIL("Unreachable");
+    return kLegacy_CachedFormat;
+}
+
+SkImageInfo SkImageCacherator::buildCacheInfo(CachedFormat format) {
+    switch (format) {
+        case kLegacy_CachedFormat:
+            return fInfo.makeColorSpace(nullptr);
+        case kAsIs_CachedFormat:
+            return fInfo;
+        case kLinearF16_CachedFormat:
+            return fInfo
+                .makeColorType(kRGBA_F16_SkColorType)
+                .makeColorSpace(as_CSB(fInfo.colorSpace())->makeLinearGamma());
+        case kSRGB8888_CachedFormat:
+            return fInfo
+                .makeColorType(kRGBA_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;
+    }
+}
+
 #ifdef SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
 static GrTexture* load_compressed_into_texture(GrContext* ctx, SkData* data, GrSurfaceDesc desc) {
     const void* rawStart;
@@ -275,6 +491,16 @@ static GrTexture* set_key_and_return(GrTexture* tex, const GrUniqueKey& key) {
     return tex;
 }
 
+sk_sp<SkColorSpace> SkImageCacherator::getColorSpace(GrContext* ctx,
+                                                     SkDestinationSurfaceColorMode colorMode) {
+    // 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(colorMode, ctx->caps());
+    SkImageInfo cacheInfo = this->buildCacheInfo(format);
+    return sk_ref_sp(cacheInfo.colorSpace());
+}
+
 /*
  *  We have a 5 ways to try to return a texture (in sorted order)
  *
@@ -284,7 +510,7 @@ static GrTexture* set_key_and_return(GrTexture* tex, const GrUniqueKey& key) {
  *  4. Ask the generator to return YUV planes, which the GPU can convert
  *  5. Ask the generator to return RGB(A) data, which the GPU can convert
  */
-GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& key,
+GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& origKey,
                                           const SkImage* client, SkImage::CachingHint chint,
                                           bool willBeMipped,
                                           SkDestinationSurfaceColorMode colorMode) {
@@ -301,6 +527,14 @@ GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& key
 
     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(colorMode, 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 (GrTexture* tex = ctx->textureProvider()->findAndRefTextureByUniqueKey(key)) {
@@ -321,7 +555,12 @@ GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& key
         }
     }
 
-    const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(fInfo, *ctx->caps());
+    // 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 GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->caps());
 
 #ifdef SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
     // 3. Ask the generator to return a compressed form that the GPU might support
@@ -350,7 +589,7 @@ GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& key
 
     // 5. Ask the generator to return RGB(A) data, which the GPU can convert
     SkBitmap bitmap;
-    if (this->tryLockAsBitmap(&bitmap, client, chint)) {
+    if (this->tryLockAsBitmap(&bitmap, client, chint, format, cacheInfo)) {
         GrTexture* tex = nullptr;
         if (willBeMipped) {
             tex = GrGenerateMipMapsAndUploadToTexture(ctx, bitmap, colorMode);
@@ -373,18 +612,21 @@ GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& key
 
 GrTexture* SkImageCacherator::lockAsTexture(GrContext* ctx, const GrSamplerParams& params,
                                             SkDestinationSurfaceColorMode colorMode,
+                                            sk_sp<SkColorSpace>* texColorSpace,
                                             const SkImage* client, SkImage::CachingHint chint) {
     if (!ctx) {
         return nullptr;
     }
 
-    return GrImageTextureMaker(ctx, this, client, chint).refTextureForParams(params, colorMode);
+    return GrImageTextureMaker(ctx, this, client, chint).refTextureForParams(params, colorMode,
+                                                                             texColorSpace);
 }
 
 #else
 
 GrTexture* SkImageCacherator::lockAsTexture(GrContext* ctx, const GrSamplerParams&,
                                             SkDestinationSurfaceColorMode colorMode,
+                                            sk_sp<SkColorSpace>* texColorSpace,
                                             const SkImage* client, SkImage::CachingHint) {
     return nullptr;
 }
index 3500206459913e51610f37127fb5b1e9430f483c..89ac378883cafb531e1b548a8dac71affd58c432 100644 (file)
@@ -12,6 +12,7 @@
 #include "SkMutex.h"
 #include "SkTemplates.h"
 
+class GrCaps;
 class GrContext;
 class GrSamplerParams;
 class GrUniqueKey;
@@ -29,7 +30,16 @@ public:
     ~SkImageCacherator();
 
     const SkImageInfo& info() const { return fInfo; }
-    uint32_t uniqueID() const { return fUniqueID; }
+    uint32_t uniqueID() const { return fUniqueIDs[kLegacy_CachedFormat]; }
+
+    enum CachedFormat {
+        kLegacy_CachedFormat,    // The format from the generator, with any color space stripped out
+        kAsIs_CachedFormat,      // The format from the generator, with no modification
+        kLinearF16_CachedFormat, // Half float RGBA with linear gamma
+        kSRGB8888_CachedFormat,  // sRGB bytes
+
+        kNumCachedFormats,
+    };
 
     /**
      *  On success (true), bitmap will point to the pixels for this generator. If this returns
@@ -38,7 +48,7 @@ public:
      *  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,
+    bool lockAsBitmap(SkBitmap*, const SkImage* client, SkDestinationSurfaceColorMode colorMode,
                       SkImage::CachingHint = SkImage::kAllow_CachingHint);
 
     /**
@@ -51,7 +61,8 @@ public:
      *  The caller is responsible for calling texture->unref() when they are done.
      */
     GrTexture* lockAsTexture(GrContext*, const GrSamplerParams&,
-                             SkDestinationSurfaceColorMode colorMode, const SkImage* client,
+                             SkDestinationSurfaceColorMode colorMode,
+                             sk_sp<SkColorSpace>* texColorSpace, const SkImage* client,
                              SkImage::CachingHint = SkImage::kAllow_CachingHint);
 
     /**
@@ -64,7 +75,7 @@ public:
     SkData* refEncoded(GrContext*);
 
     // Only return true if the generate has already been cached.
-    bool lockAsBitmapOnlyIfAlreadyCached(SkBitmap*);
+    bool lockAsBitmapOnlyIfAlreadyCached(SkBitmap*, CachedFormat);
     // Call the underlying generator directly
     bool directGeneratePixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
                               int srcX, int srcY);
@@ -105,19 +116,28 @@ private:
 
     SkImageCacherator(Validator*);
 
-    bool generateBitmap(SkBitmap*);
-    bool tryLockAsBitmap(SkBitmap*, const SkImage*, SkImage::CachingHint);
+    CachedFormat chooseCacheFormat(SkDestinationSurfaceColorMode, const GrCaps* = nullptr);
+    SkImageInfo buildCacheInfo(CachedFormat);
+
+    bool generateBitmap(SkBitmap*, const SkImageInfo&);
+    bool tryLockAsBitmap(SkBitmap*, const SkImage*, SkImage::CachingHint, CachedFormat,
+                         const SkImageInfo&);
 #if SK_SUPPORT_GPU
     // Returns the texture. If the cacherator is generating the texture and wants to cache it,
     // it should use the passed in key (if the key is valid).
     GrTexture* lockTexture(GrContext*, const GrUniqueKey& key, const SkImage* client,
                            SkImage::CachingHint, bool willBeMipped, SkDestinationSurfaceColorMode);
+    // 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*, SkDestinationSurfaceColorMode);
+    void makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat, GrUniqueKey* cacheKey);
 #endif
 
     sk_sp<SharedGenerator> fSharedGenerator;
     const SkImageInfo      fInfo;
     const SkIPoint         fOrigin;
-    const uint32_t         fUniqueID;
+    uint32_t               fUniqueIDs[kNumCachedFormats];
 
     friend class GrImageTextureMaker;
     friend class SkImage;
index db04638f02fd38dc017e7bedcc0b6ce679e042b3..2d67ecb13bed67b2334e106e37836d8cbb4eceac 100644 (file)
@@ -139,7 +139,8 @@ public:
                 // gets deleted when the ShadowFP is destroyed, and frees the GrTexture*
                 fTexture[fNumNonAmbLights] = sk_sp<GrTexture>(shadowMap->asTextureRef(context,
                                                            GrSamplerParams::ClampNoFilter(),
-                                                           SkDestinationSurfaceColorMode::kLegacy));
+                                                           SkDestinationSurfaceColorMode::kLegacy,
+                                                           nullptr));
                 fDepthMapSampler[fNumNonAmbLights].reset(fTexture[fNumNonAmbLights].get());
                 this->addTextureSampler(&fDepthMapSampler[fNumNonAmbLights]);
 
index c25079e81626b40b6b3086c5363d6a2fe985e06f..fefe5c2a1c9fe184bcac4426c1c081e338b92562 100644 (file)
@@ -181,6 +181,7 @@ static bool rect_fits(const SkIRect& rect, int width, int height) {
 
 sk_sp<SkSpecialImage> SkSpecialImage::MakeFromImage(const SkIRect& subset,
                                                     sk_sp<SkImage> image,
+                                                    SkDestinationSurfaceColorMode colorMode,
                                                     const SkSurfaceProps* props) {
     SkASSERT(rect_fits(subset, image->width(), image->height()));
 
@@ -192,7 +193,7 @@ sk_sp<SkSpecialImage> SkSpecialImage::MakeFromImage(const SkIRect& subset,
 #endif
     {
         SkBitmap bm;
-        if (as_IB(image)->getROPixels(&bm)) {
+        if (as_IB(image)->getROPixels(&bm, colorMode)) {
             return MakeFromRaster(subset, bm, props);
         }
     }
index 12847df115edab64b54aaf8948d2fa1f270e4ff2..c6afa1d0c7927f108209cb4602a2c8f1027fca54 100644 (file)
@@ -73,6 +73,7 @@ public:
 
     static sk_sp<SkSpecialImage> MakeFromImage(const SkIRect& subset,
                                                sk_sp<SkImage>,
+                                               SkDestinationSurfaceColorMode,
                                                const SkSurfaceProps* = nullptr);
     static sk_sp<SkSpecialImage> MakeFromRaster(const SkIRect& subset,
                                                 const SkBitmap&,
index f96a4a16769b04e27f6032fa4a0f362313ae8138..de849416c8c619e7770e94f2be7ded4cde73b429 100644 (file)
@@ -86,8 +86,12 @@ sk_sp<SkSpecialImage> SkImageSource::onFilterImage(SkSpecialImage* source, const
     if (fSrcRect == bounds && dstRect == bounds) {
         // No regions cropped out or resized; return entire image.
         offset->fX = offset->fY = 0;
+        SkDestinationSurfaceColorMode decodeColorMode = ctx.outputProperties().colorSpace()
+            ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
+            : SkDestinationSurfaceColorMode::kLegacy;
         return SkSpecialImage::MakeFromImage(SkIRect::MakeWH(fImage->width(), fImage->height()),
                                              fImage,
+                                             decodeColorMode,
                                              &source->props());
     }
 
index cc869baf7f2e3486df2537d7631c8ff98037098b..645d1c2a99ef55ffb99166ba1fabf4e36a26cf9a 100644 (file)
@@ -52,7 +52,9 @@ GrTexture* GrBitmapTextureMaker::refOriginalTexture(bool willBeMipped,
     return tex;
 }
 
-void GrBitmapTextureMaker::makeCopyKey(const CopyParams& copyParams, GrUniqueKey* copyKey) {
+void GrBitmapTextureMaker::makeCopyKey(const CopyParams& copyParams, GrUniqueKey* copyKey,
+                                       SkDestinationSurfaceColorMode colorMode) {
+    // Color mode is irrelevant in this case - we always upload the bitmap's contents as-is
     if (fOriginalKey.isValid()) {
         MakeCopyKeyFromOrigKey(fOriginalKey, copyParams, copyKey);
     }
@@ -66,8 +68,9 @@ SkAlphaType GrBitmapTextureMaker::alphaType() const {
     return fBitmap.alphaType();
 }
 
-SkColorSpace* GrBitmapTextureMaker::getColorSpace() {
-    return fBitmap.colorSpace();
+sk_sp<SkColorSpace> GrBitmapTextureMaker::getColorSpace(SkDestinationSurfaceColorMode colorMode) {
+    // Color space doesn't depend on mode - it's just whatever is in the bitmap
+    return sk_ref_sp(fBitmap.colorSpace());
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -93,9 +96,14 @@ GrTexture* GrImageTextureMaker::refOriginalTexture(bool willBeMipped,
                                 colorMode);
 }
 
-void GrImageTextureMaker::makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey) {
+void GrImageTextureMaker::makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey,
+                                      SkDestinationSurfaceColorMode colorMode) {
     if (fOriginalKey.isValid() && SkImage::kAllow_CachingHint == fCachingHint) {
-        MakeCopyKeyFromOrigKey(fOriginalKey, stretch, paramsCopyKey);
+        SkImageCacherator::CachedFormat cacheFormat =
+            fCacher->chooseCacheFormat(colorMode, this->context()->caps());
+        GrUniqueKey cacheKey;
+        fCacher->makeCacheKeyFromOrigKey(fOriginalKey, cacheFormat, &cacheKey);
+        MakeCopyKeyFromOrigKey(cacheKey, stretch, paramsCopyKey);
     }
 }
 
@@ -108,7 +116,6 @@ void GrImageTextureMaker::didCacheCopy(const GrUniqueKey& copyKey) {
 SkAlphaType GrImageTextureMaker::alphaType() const {
     return fCacher->info().alphaType();
 }
-
-SkColorSpace* GrImageTextureMaker::getColorSpace() {
-    return fCacher->info().colorSpace();
+sk_sp<SkColorSpace> GrImageTextureMaker::getColorSpace(SkDestinationSurfaceColorMode colorMode) {
+    return fCacher->getColorSpace(this->context(), colorMode);
 }
index b0b70487e34a68ad5f3b1ccb9d885c76491c85fc..ad5433109276388a2fe878f7107c612798f283c5 100644 (file)
@@ -25,12 +25,13 @@ public:
 protected:
     GrTexture* refOriginalTexture(bool willBeMipped, SkDestinationSurfaceColorMode) override;
 
-    void makeCopyKey(const CopyParams& copyParams, GrUniqueKey* copyKey) override;
+    void makeCopyKey(const CopyParams& copyParams, GrUniqueKey* copyKey,
+                     SkDestinationSurfaceColorMode colorMode) override;
 
     void didCacheCopy(const GrUniqueKey& copyKey) override;
 
     SkAlphaType alphaType() const override;
-    SkColorSpace* getColorSpace() override;
+    sk_sp<SkColorSpace> getColorSpace(SkDestinationSurfaceColorMode) override;
 
 private:
     const SkBitmap  fBitmap;
@@ -52,11 +53,12 @@ protected:
     //          GrTexture* generateTextureForParams(const CopyParams&) override;
 
     GrTexture* refOriginalTexture(bool willBeMipped, SkDestinationSurfaceColorMode) override;
-    void makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey) override;
+    void makeCopyKey(const CopyParams& stretch, GrUniqueKey* paramsCopyKey,
+                     SkDestinationSurfaceColorMode colorMode) override;
     void didCacheCopy(const GrUniqueKey& copyKey) override;
 
     SkAlphaType alphaType() const override;
-    SkColorSpace* getColorSpace() override;
+    sk_sp<SkColorSpace> getColorSpace(SkDestinationSurfaceColorMode) override;
 
 private:
     SkImageCacherator*      fCacher;
index ca273a770f648b18eaa30f3f42afadf9da02cb61..d924f4a976f48ce4c2d71d37653531f27b438716 100644 (file)
@@ -105,7 +105,9 @@ GrTextureAdjuster::GrTextureAdjuster(GrTexture* original, SkAlphaType alphaType,
     }
 }
 
-void GrTextureAdjuster::makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey) {
+void GrTextureAdjuster::makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey,
+                                    SkDestinationSurfaceColorMode) {
+    // Color mode is irrelevant in this case - we already have a texture so we're just sub-setting
     GrUniqueKey baseKey;
     GrMakeKeyFromImageID(&baseKey, fUniqueID, SkIRect::MakeWH(this->width(), this->height()));
     MakeCopyKeyFromOrigKey(baseKey, params, copyKey);
@@ -115,16 +117,12 @@ void GrTextureAdjuster::didCacheCopy(const GrUniqueKey& copyKey) {
     // We don't currently have a mechanism for notifications on Images!
 }
 
-SkColorSpace* GrTextureAdjuster::getColorSpace() {
-    return fColorSpace;
-}
-
 GrTexture* GrTextureAdjuster::refCopy(const CopyParams& copyParams) {
     GrTexture* texture = this->originalTexture();
     GrContext* context = texture->getContext();
     const SkIRect* contentArea = this->contentAreaOrNull();
     GrUniqueKey key;
-    this->makeCopyKey(copyParams, &key);
+    this->makeCopyKey(copyParams, &key, SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware);
     if (key.isValid()) {
         GrTexture* cachedCopy = context->textureProvider()->findAndRefTextureByUniqueKey(key);
         if (cachedCopy) {
@@ -421,7 +419,7 @@ sk_sp<GrFragmentProcessor> GrTextureAdjuster::createFragmentProcessor(
     SkASSERT(kNoDomain_DomainMode == domainMode ||
              (domain.fLeft <= domain.fRight && domain.fTop <= domain.fBottom));
     textureMatrix.postIDiv(texture->width(), texture->height());
-    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(this->getColorSpace(),
+    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace,
                                                                        dstColorSpace);
     return create_fp_for_domain_and_filter(texture.get(), std::move(colorSpaceXform), textureMatrix,
                                            domainMode, domain, filterOrNullForBicubic);
@@ -430,7 +428,8 @@ sk_sp<GrFragmentProcessor> GrTextureAdjuster::createFragmentProcessor(
 //////////////////////////////////////////////////////////////////////////////
 
 GrTexture* GrTextureMaker::refTextureForParams(const GrSamplerParams& params,
-                                               SkDestinationSurfaceColorMode colorMode) {
+                                               SkDestinationSurfaceColorMode colorMode,
+                                               sk_sp<SkColorSpace>* texColorSpace) {
     CopyParams copyParams;
     bool willBeMipped = params.filterMode() == GrSamplerParams::kMipMap_FilterMode;
 
@@ -438,12 +437,16 @@ GrTexture* GrTextureMaker::refTextureForParams(const GrSamplerParams& params,
         willBeMipped = false;
     }
 
+    if (texColorSpace) {
+        *texColorSpace = this->getColorSpace(colorMode);
+    }
+
     if (!fContext->getGpu()->makeCopyForTextureParams(this->width(), this->height(), params,
                                                       &copyParams)) {
         return this->refOriginalTexture(willBeMipped, colorMode);
     }
     GrUniqueKey copyKey;
-    this->makeCopyKey(copyParams, &copyKey);
+    this->makeCopyKey(copyParams, &copyKey, colorMode);
     if (copyKey.isValid()) {
         GrTexture* result = fContext->textureProvider()->findAndRefTextureByUniqueKey(copyKey);
         if (result) {
@@ -491,7 +494,8 @@ sk_sp<GrFragmentProcessor> GrTextureMaker::createFragmentProcessor(
         // Bicubic doesn't use filtering for it's texture accesses.
         params.reset(SkShader::kClamp_TileMode, GrSamplerParams::kNone_FilterMode);
     }
-    sk_sp<GrTexture> texture(this->refTextureForParams(params, colorMode));
+    sk_sp<SkColorSpace> texColorSpace;
+    sk_sp<GrTexture> texture(this->refTextureForParams(params, colorMode, &texColorSpace));
     if (!texture) {
         return nullptr;
     }
@@ -503,7 +507,7 @@ sk_sp<GrFragmentProcessor> GrTextureMaker::createFragmentProcessor(
     SkASSERT(kTightCopy_DomainMode != domainMode);
     SkMatrix normalizedTextureMatrix = textureMatrix;
     normalizedTextureMatrix.postIDiv(texture->width(), texture->height());
-    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(this->getColorSpace(),
+    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(texColorSpace.get(),
                                                                        dstColorSpace);
     return create_fp_for_domain_and_filter(texture.get(), std::move(colorSpaceXform),
                                            normalizedTextureMatrix, domainMode, domain,
index bf3076ebebac0aba60251d2fcd8d603174cdf163..c3f020196cdfdabe58a71e742af62f994274d528 100644 (file)
@@ -74,7 +74,6 @@ public:
     int height() const { return fHeight; }
     bool isAlphaOnly() const { return fIsAlphaOnly; }
     virtual SkAlphaType alphaType() const = 0;
-    virtual SkColorSpace* getColorSpace() = 0;
 
 protected:
     GrTextureProducer(int width, int height, bool isAlphaOnly)
@@ -100,9 +99,11 @@ protected:
     *  If we need to make a copy in order to be compatible with GrTextureParams producer is asked to
     *  return a key that identifies its original content + the CopyParms parameter. If the producer
     *  does not want to cache the stretched version (e.g. the producer is volatile), this should
-    *  simply return without initializing the copyKey.
+    *  simply return without initializing the copyKey. If the texture generated by this producer
+    *  depends on colorMode, then that information should also be incorporated in the key.
     */
-    virtual void makeCopyKey(const CopyParams&, GrUniqueKey* copyKey) = 0;
+    virtual void makeCopyKey(const CopyParams&, GrUniqueKey* copyKey,
+                             SkDestinationSurfaceColorMode colorMode) = 0;
 
     /**
     *  If a stretched version of the texture is generated, it may be cached (assuming that
@@ -150,8 +151,8 @@ public:
 
 protected:
     SkAlphaType alphaType() const override { return fAlphaType; }
-    SkColorSpace* getColorSpace() override;
-    void makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey) override;
+    void makeCopyKey(const CopyParams& params, GrUniqueKey* copyKey,
+                     SkDestinationSurfaceColorMode colorMode) override;
     void didCacheCopy(const GrUniqueKey& copyKey) override;
 
     GrTexture* originalTexture() const { return fOriginal; }
@@ -177,10 +178,13 @@ private:
  */
 class GrTextureMaker : public GrTextureProducer {
 public:
-    /** Returns a texture that is safe for use with the params. If the size of the returned texture
-        does not match width()/height() then the contents of the original must be scaled to fit
-        the texture. */
-    GrTexture* refTextureForParams(const GrSamplerParams&, SkDestinationSurfaceColorMode);
+    /**
+     *  Returns a texture that is safe for use with the params. If the size of the returned texture
+     *  does not match width()/height() then the contents of the original must be scaled to fit
+     *  the texture. Places the color space of the texture in (*texColorSpace).
+     */
+    GrTexture* refTextureForParams(const GrSamplerParams&, SkDestinationSurfaceColorMode,
+                                   sk_sp<SkColorSpace>* texColorSpace);
 
     sk_sp<GrFragmentProcessor> createFragmentProcessor(
                                 const SkMatrix& textureMatrix,
@@ -202,6 +206,12 @@ protected:
      */
     virtual GrTexture* refOriginalTexture(bool willBeMipped, SkDestinationSurfaceColorMode) = 0;
 
+    /**
+     *  Returns the color space of the maker's "original" texture, assuming it was retrieved with
+     *  the same destination color mode.
+     */
+    virtual sk_sp<SkColorSpace> getColorSpace(SkDestinationSurfaceColorMode) = 0;
+
     /**
      *  Return a new (uncached) texture that is the stretch of the maker's original.
      *
index c0ca5cf2a199b09e3faf5d14461a2e1548c47ddc..cc2cd2fd501be6a406c9206325588c53a791ae3a 100644 (file)
@@ -1383,7 +1383,7 @@ void SkGpuDevice::drawImage(const SkDraw& draw, const SkImage* image, SkScalar x
         if (this->shouldTileImage(image, nullptr, SkCanvas::kFast_SrcRectConstraint,
                                   paint.getFilterQuality(), *draw.fMatrix, SkMatrix::I())) {
             // only support tiling as bitmap at the moment, so force raster-version
-            if (!as_IB(image)->getROPixels(&bm)) {
+            if (!as_IB(image)->getROPixels(&bm, fRenderTargetContext->colorMode())) {
                 return;
             }
             this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint);
@@ -1392,7 +1392,7 @@ void SkGpuDevice::drawImage(const SkDraw& draw, const SkImage* image, SkScalar x
             GrImageTextureMaker maker(fContext.get(), cacher, image, SkImage::kAllow_CachingHint);
             this->drawTextureProducer(&maker, nullptr, nullptr, SkCanvas::kFast_SrcRectConstraint,
                                       viewMatrix, fClip, paint);
-        } else if (as_IB(image)->getROPixels(&bm)) {
+        } else if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->colorMode())) {
             this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint);
         }
     }
@@ -1417,7 +1417,7 @@ void SkGpuDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const
     if (this->shouldTileImage(image, src, constraint, paint.getFilterQuality(), *draw.fMatrix,
                               srcToDstRect)) {
         // only support tiling as bitmap at the moment, so force raster-version
-        if (!as_IB(image)->getROPixels(&bm)) {
+        if (!as_IB(image)->getROPixels(&bm, fRenderTargetContext->colorMode())) {
             return;
         }
         this->drawBitmapRect(draw, bm, src, dst, paint, constraint);
@@ -1425,7 +1425,7 @@ void SkGpuDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const
         CHECK_SHOULD_DRAW(draw);
         GrImageTextureMaker maker(fContext.get(), cacher, image, SkImage::kAllow_CachingHint);
         this->drawTextureProducer(&maker, src, &dst, constraint, *draw.fMatrix, fClip, paint);
-    } else if (as_IB(image)->getROPixels(&bm)) {
+    } else if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->colorMode())) {
         this->drawBitmapRect(draw, bm, src, dst, paint, constraint);
     }
 }
@@ -1487,7 +1487,7 @@ void SkGpuDevice::drawImageNine(const SkDraw& draw, const SkImage* image,
         if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) {
             GrImageTextureMaker maker(fContext.get(), cacher, image, SkImage::kAllow_CachingHint);
             this->drawProducerNine(draw, &maker, center, dst, paint);
-        } else if (as_IB(image)->getROPixels(&bm)) {
+        } else if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->colorMode())) {
             this->drawBitmapNine(draw, bm, center, dst, paint);
         }
     }
@@ -1542,7 +1542,7 @@ void SkGpuDevice::drawImageLattice(const SkDraw& draw, const SkImage* image,
         if (SkImageCacherator* cacher = as_IB(image)->peekCacherator()) {
             GrImageTextureMaker maker(fContext.get(), cacher, image, SkImage::kAllow_CachingHint);
             this->drawProducerLattice(draw, &maker, lattice, dst, paint);
-        } else if (as_IB(image)->getROPixels(&bm)) {
+        } else if (as_IB(image)->getROPixels(&bm, fRenderTargetContext->colorMode())) {
             this->drawBitmapLattice(draw, bm, lattice, dst, paint);
         }
     }
index f1a3caaa2ae4bc5146defad555acaf04768dc2e5..471a39305f5693defd7ba23ba054f516194fe2fa 100644 (file)
@@ -426,13 +426,16 @@ GrTexture* GrUploadMipMapToTexture(GrContext* ctx, const SkImageInfo& info,
 GrTexture* GrRefCachedBitmapTexture(GrContext* ctx, const SkBitmap& bitmap,
                                     const GrSamplerParams& params,
                                     SkDestinationSurfaceColorMode colorMode) {
-    return GrBitmapTextureMaker(ctx, bitmap).refTextureForParams(params, colorMode);
+    // Caller doesn't care about the texture's color space (they can always get it from the bitmap)
+    return GrBitmapTextureMaker(ctx, bitmap).refTextureForParams(params, colorMode, nullptr);
 }
 
 sk_sp<GrTexture> GrMakeCachedBitmapTexture(GrContext* ctx, const SkBitmap& bitmap,
                                            const GrSamplerParams& params,
                                            SkDestinationSurfaceColorMode colorMode) {
-    GrTexture* tex = GrBitmapTextureMaker(ctx, bitmap).refTextureForParams(params, colorMode);
+    // Caller doesn't care about the texture's color space (they can always get it from the bitmap)
+    GrTexture* tex = GrBitmapTextureMaker(ctx, bitmap).refTextureForParams(params, colorMode,
+                                                                           nullptr);
     return sk_sp<GrTexture>(tex);
 }
 
index 4d76638cbd327850e1f43beccf56a848d40261dd..f976242eca0086c037c2f4f333136ea3a69199ad 100644 (file)
@@ -65,8 +65,11 @@ bool SkImage::scalePixels(const SkPixmap& dst, SkFilterQuality quality, CachingH
     // Idea: If/when SkImageGenerator supports a native-scaling API (where the generator itself
     //       can scale more efficiently) we should take advantage of it here.
     //
+    SkDestinationSurfaceColorMode decodeColorMode = dst.info().colorSpace()
+        ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
+        : SkDestinationSurfaceColorMode::kLegacy;
     SkBitmap bm;
-    if (as_IB(this)->getROPixels(&bm, chint)) {
+    if (as_IB(this)->getROPixels(&bm, decodeColorMode, chint)) {
         bm.lockPixels();
         SkPixmap pmap;
         // Note: By calling the pixmap scaler, we never cache the final result, so the chint
@@ -83,7 +86,7 @@ void SkImage::preroll(GrContext* ctx) const {
     // to produce a cached raster-bitmap form, so that drawing to a raster canvas should be fast.
     //
     SkBitmap bm;
-    if (as_IB(this)->getROPixels(&bm)) {
+    if (as_IB(this)->getROPixels(&bm, SkDestinationSurfaceColorMode::kLegacy)) {
         bm.lockPixels();
         bm.unlockPixels();
     }
@@ -102,7 +105,10 @@ sk_sp<SkShader> SkImage::makeShader(SkShader::TileMode tileX, SkShader::TileMode
 
 SkData* SkImage::encode(SkImageEncoder::Type type, int quality) const {
     SkBitmap bm;
-    if (as_IB(this)->getROPixels(&bm)) {
+    // TODO: Right now, the encoders don't handle F16 or linearly premultiplied data. Once they do,
+    // we should decode in "color space aware" mode, then re-encode that. For now, work around this
+    // by asking for a legacy decode (which gives us the raw data in N32).
+    if (as_IB(this)->getROPixels(&bm, SkDestinationSurfaceColorMode::kLegacy)) {
         return SkImageEncoder::EncodeData(bm, type, quality);
     }
     return nullptr;
@@ -123,7 +129,11 @@ SkData* SkImage::encode(SkPixelSerializer* serializer) const {
 
     SkBitmap bm;
     SkAutoPixmapUnlock apu;
-    if (as_IB(this)->getROPixels(&bm) && bm.requestLock(&apu)) {
+    // TODO: Right now, the encoders don't handle F16 or linearly premultiplied data. Once they do,
+    // we should decode in "color space aware" mode, then re-encode that. For now, work around this
+    // by asking for a legacy decode (which gives us the raw data in N32).
+    if (as_IB(this)->getROPixels(&bm, SkDestinationSurfaceColorMode::kLegacy) &&
+        bm.requestLock(&apu)) {
         return effectiveSerializer->encode(apu.pixmap());
     }
 
@@ -284,8 +294,7 @@ bool SkImage::asLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode mode) const {
 bool SkImage_Base::onAsLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode mode) const {
     // As the base-class, all we can do is make a copy (regardless of mode).
     // Subclasses that want to be more optimal should override.
-    SkImageInfo info = this->onImageInfo().makeColorType(kN32_SkColorType)
-            .makeAlphaType(this->alphaType());
+    SkImageInfo info = this->onImageInfo().makeColorType(kN32_SkColorType).makeColorSpace(nullptr);
     if (!bitmap->tryAllocPixels(info)) {
         return false;
     }
@@ -315,15 +324,19 @@ sk_sp<SkImage> SkImage::makeWithFilter(const SkImageFilter* filter, const SkIRec
     if (!filter || !outSubset || !offset || !this->bounds().contains(subset)) {
         return nullptr;
     }
+    SkColorSpace* colorSpace = as_IB(this)->onImageInfo().colorSpace();
+    SkDestinationSurfaceColorMode decodeColorMode = colorSpace
+        ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
+        : SkDestinationSurfaceColorMode::kLegacy;
     sk_sp<SkSpecialImage> srcSpecialImage = SkSpecialImage::MakeFromImage(
-        subset, sk_ref_sp(const_cast<SkImage*>(this)));
+        subset, sk_ref_sp(const_cast<SkImage*>(this)), decodeColorMode);
     if (!srcSpecialImage) {
         return nullptr;
     }
 
     sk_sp<SkImageFilterCache> cache(
         SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize));
-    SkImageFilter::OutputProperties outputProperties(as_IB(this)->onImageInfo().colorSpace());
+    SkImageFilter::OutputProperties outputProperties(colorSpace);
     SkImageFilter::Context context(SkMatrix::I(), clipBounds, cache.get(), outputProperties);
 
     sk_sp<SkSpecialImage> result =
index 81b6f76ede8b4cd9684d225bcb3cc55b6b479889..f8b22df41a5ae204e34374fc51747dabf4baf14d 100644 (file)
@@ -52,8 +52,12 @@ size_t SkImageShader::onContextSize(const ContextRec& rec) const {
 }
 
 SkShader::Context* SkImageShader::onCreateContext(const ContextRec& rec, void* storage) const {
+    // TODO: This is wrong. We should be plumbing destination color space to context creation,
+    // and use that to determine the decoding mode of the image.
+    SkDestinationSurfaceColorMode decodeColorMode = SkMipMap::DeduceColorMode(rec);
     return SkBitmapProcLegacyShader::MakeContext(*this, fTileModeX, fTileModeY,
-                                                 SkBitmapProvider(fImage.get()), rec, storage);
+                                                 SkBitmapProvider(fImage.get(), decodeColorMode),
+                                                 rec, storage);
 }
 
 SkImage* SkImageShader::onIsAImage(SkMatrix* texM, TileMode xy[]) const {
@@ -211,13 +215,14 @@ sk_sp<GrFragmentProcessor> SkImageShader::asFragmentProcessor(const AsFPArgs& ar
     GrSkFilterQualityToGrFilterMode(args.fFilterQuality, *args.fViewMatrix, this->getLocalMatrix(),
                                     &doBicubic);
     GrSamplerParams params(tm, textureFilterMode);
-    sk_sp<GrTexture> texture(as_IB(fImage)->asTextureRef(args.fContext, params, args.fColorMode));
+    sk_sp<SkColorSpace> texColorSpace;
+    sk_sp<GrTexture> texture(as_IB(fImage)->asTextureRef(args.fContext, params, args.fColorMode,
+                                                         &texColorSpace));
     if (!texture) {
         return nullptr;
     }
 
-    SkImageInfo info = as_IB(fImage)->onImageInfo();
-    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(info.colorSpace(),
+    sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(texColorSpace.get(),
                                                                        args.fDstColorSpace);
     sk_sp<GrFragmentProcessor> inner;
     if (doBicubic) {
index 09ceb5770a404c4b13676c95ba5ad7809050ee0b..c955d491f75714fb823d03598a8769382be5a2a3 100644 (file)
@@ -52,11 +52,12 @@ public:
 
     // return a read-only copy of the pixels. We promise to not modify them,
     // but only inspect them (or encode them).
-    virtual bool getROPixels(SkBitmap*, CachingHint = kAllow_CachingHint) const = 0;
+    virtual bool getROPixels(SkBitmap*, SkDestinationSurfaceColorMode,
+                             CachingHint = kAllow_CachingHint) const = 0;
 
     // Caller must call unref when they are done.
     virtual GrTexture* asTextureRef(GrContext*, const GrSamplerParams&,
-                                    SkDestinationSurfaceColorMode) const = 0;
+                                    SkDestinationSurfaceColorMode, sk_sp<SkColorSpace>*) const = 0;
 
     virtual sk_sp<SkImage> onMakeSubset(const SkIRect&) const = 0;
 
index 82f395fc95f072674a1e0f7660bdaaa809c19691..efee134a81d8bf943d089e3910435507182c8b20 100644 (file)
@@ -30,9 +30,9 @@ public:
     SkImageCacherator* peekCacherator() const override { return &fCache; }
     SkData* onRefEncoded(GrContext*) const override;
     sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
-    bool getROPixels(SkBitmap*, CachingHint) const override;
+    bool getROPixels(SkBitmap*, SkDestinationSurfaceColorMode, CachingHint) const override;
     GrTexture* asTextureRef(GrContext*, const GrSamplerParams&,
-                            SkDestinationSurfaceColorMode) const override;
+                            SkDestinationSurfaceColorMode, sk_sp<SkColorSpace>*) const override;
     bool onIsLazyGenerated() const override { return true; }
 
 private:
@@ -45,9 +45,13 @@ private:
 
 bool SkImage_Generator::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
                                      int srcX, int srcY, CachingHint chint) const {
+    SkDestinationSurfaceColorMode decodeColorMode = dstInfo.colorSpace()
+        ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
+        : SkDestinationSurfaceColorMode::kLegacy;
     SkBitmap bm;
     if (kDisallow_CachingHint == chint) {
-        if (fCache.lockAsBitmapOnlyIfAlreadyCached(&bm)) {
+        SkImageCacherator::CachedFormat cacheFormat = fCache.chooseCacheFormat(decodeColorMode);
+        if (fCache.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
@@ -60,7 +64,7 @@ bool SkImage_Generator::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels
         }
     }
 
-    if (this->getROPixels(&bm, chint)) {
+    if (this->getROPixels(&bm, decodeColorMode, chint)) {
         return bm.readPixels(dstInfo, dstPixels, dstRB, srcX, srcY);
     }
     return false;
@@ -70,13 +74,15 @@ SkData* SkImage_Generator::onRefEncoded(GrContext* ctx) const {
     return fCache.refEncoded(ctx);
 }
 
-bool SkImage_Generator::getROPixels(SkBitmap* bitmap, CachingHint chint) const {
-    return fCache.lockAsBitmap(bitmap, this, chint);
+bool SkImage_Generator::getROPixels(SkBitmap* bitmap, SkDestinationSurfaceColorMode colorMode,
+                                    CachingHint chint) const {
+    return fCache.lockAsBitmap(bitmap, this, colorMode, chint);
 }
 
 GrTexture* SkImage_Generator::asTextureRef(GrContext* ctx, const GrSamplerParams& params,
-                                           SkDestinationSurfaceColorMode colorMode) const {
-    return fCache.lockAsTexture(ctx, params, colorMode, this);
+                                           SkDestinationSurfaceColorMode colorMode,
+                                           sk_sp<SkColorSpace>* texColorSpace) const {
+    return fCache.lockAsTexture(ctx, params, colorMode, texColorSpace, this);
 }
 
 sk_sp<SkImage> SkImage_Generator::onMakeSubset(const SkIRect& subset) const {
index 84c7b10be1dcbb884c868fe1e77fb3a7feb2669e..02f4d69519de3259d0261f402d8c37f2a3dde722 100644 (file)
@@ -20,6 +20,7 @@
 #include "SkBitmapCache.h"
 #include "SkGrPriv.h"
 #include "SkImage_Gpu.h"
+#include "SkImageCacherator.h"
 #include "SkMipMap.h"
 #include "SkPixelRef.h"
 
@@ -60,7 +61,8 @@ static SkImageInfo make_info(int w, int h, SkAlphaType at, sk_sp<SkColorSpace> c
     return SkImageInfo::MakeN32(w, h, at, std::move(colorSpace));
 }
 
-bool SkImage_Gpu::getROPixels(SkBitmap* dst, CachingHint chint) const {
+bool SkImage_Gpu::getROPixels(SkBitmap* dst, SkDestinationSurfaceColorMode,
+                              CachingHint chint) const {
     if (SkBitmapCache::Find(this->uniqueID(), dst)) {
         SkASSERT(dst->getGenerationID() == this->uniqueID());
         SkASSERT(dst->isImmutable());
@@ -86,9 +88,13 @@ bool SkImage_Gpu::getROPixels(SkBitmap* dst, CachingHint chint) const {
 }
 
 GrTexture* SkImage_Gpu::asTextureRef(GrContext* ctx, const GrSamplerParams& params,
-                                     SkDestinationSurfaceColorMode colorMode) const {
-    GrTextureAdjuster adjuster(this->peekTexture(), this->alphaType(), this->bounds(), this->uniqueID(),
-                               this->onImageInfo().colorSpace());
+                                     SkDestinationSurfaceColorMode colorMode,
+                                     sk_sp<SkColorSpace>* texColorSpace) const {
+    if (texColorSpace) {
+        *texColorSpace = this->fColorSpace;
+    }
+    GrTextureAdjuster adjuster(this->peekTexture(), this->alphaType(), this->bounds(),
+                               this->uniqueID(), this->fColorSpace.get());
     return adjuster.refTextureSafeForParams(params, colorMode, nullptr);
 }
 
@@ -298,14 +304,16 @@ sk_sp<SkImage> SkImage::MakeFromNV12TexturesCopy(GrContext* ctx, SkYUVColorSpace
 }
 
 static sk_sp<SkImage> create_image_from_maker(GrTextureMaker* maker, SkAlphaType at, uint32_t id) {
+    sk_sp<SkColorSpace> texColorSpace;
     sk_sp<GrTexture> texture(
         maker->refTextureForParams(GrSamplerParams::ClampNoFilter(),
-                                   SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware));
+                                   SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware,
+                                   &texColorSpace));
     if (!texture) {
         return nullptr;
     }
     return sk_make_sp<SkImage_Gpu>(texture->width(), texture->height(), id, at, std::move(texture),
-                                   sk_ref_sp(maker->getColorSpace()), SkBudgeted::kNo);
+                                   std::move(texColorSpace), SkBudgeted::kNo);
 }
 
 sk_sp<SkImage> SkImage::makeTextureImage(GrContext *context) const {
@@ -498,7 +506,19 @@ size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& prox
         if (!data && !this->peekPixels(nullptr)) {
             return 0;
         }
-        info = as_IB(this)->onImageInfo().makeWH(scaledSize.width(), scaledSize.height());
+        if (SkImageCacherator* cacher = as_IB(this)->peekCacherator()) {
+            // Generator backed image. Tweak info to trigger correct kind of decode.
+            SkDestinationSurfaceColorMode decodeColorMode = dstColorSpace
+                ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
+                : SkDestinationSurfaceColorMode::kLegacy;
+            SkImageCacherator::CachedFormat cacheFormat = cacher->chooseCacheFormat(
+                decodeColorMode, proxy.fCaps.get());
+            info = cacher->buildCacheInfo(cacheFormat).makeWH(scaledSize.width(),
+                                                              scaledSize.height());
+
+        } else {
+            info = as_IB(this)->onImageInfo().makeWH(scaledSize.width(), scaledSize.height());
+        }
         pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr));
         if (fillMode) {
             pixmap.alloc(info);
index 19e2944283c3669387a0b9bacbd97b28d619afcf..dcde00a8fce5d8bc776e4d384c63e6d96bec6b4e 100644 (file)
@@ -38,9 +38,9 @@ public:
         }
     }
 
-    bool getROPixels(SkBitmap*, CachingHint) const override;
+    bool getROPixels(SkBitmap*, SkDestinationSurfaceColorMode, CachingHint) const override;
     GrTexture* asTextureRef(GrContext* ctx, const GrSamplerParams& params,
-                            SkDestinationSurfaceColorMode) const override;
+                            SkDestinationSurfaceColorMode, sk_sp<SkColorSpace>*) const override;
     sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
 
     GrTexture* peekTexture() const override { return fTexture.get(); }
index a2b12e41cec4176b6ce9e60bbc0f1037addb5262..0875d615b097ade2dbb1c97c9020efa2d22c0a11 100644 (file)
@@ -88,9 +88,9 @@ public:
     const SkBitmap* onPeekBitmap() const override { return &fBitmap; }
 
     SkData* onRefEncoded(GrContext*) const override;
-    bool getROPixels(SkBitmap*, CachingHint) const override;
+    bool getROPixels(SkBitmap*, SkDestinationSurfaceColorMode, CachingHint) const override;
     GrTexture* asTextureRef(GrContext*, const GrSamplerParams&,
-                            SkDestinationSurfaceColorMode) const override;
+                            SkDestinationSurfaceColorMode, sk_sp<SkColorSpace>*) const override;
     sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
 
     // exposed for SkSurface_Raster via SkNewImageFromPixelRef
@@ -192,18 +192,23 @@ SkData* SkImage_Raster::onRefEncoded(GrContext*) const {
     return nullptr;
 }
 
-bool SkImage_Raster::getROPixels(SkBitmap* dst, CachingHint) const {
+bool SkImage_Raster::getROPixels(SkBitmap* dst, SkDestinationSurfaceColorMode, CachingHint) const {
     *dst = fBitmap;
     return true;
 }
 
 GrTexture* SkImage_Raster::asTextureRef(GrContext* ctx, const GrSamplerParams& params,
-                                        SkDestinationSurfaceColorMode colorMode) const {
+                                        SkDestinationSurfaceColorMode colorMode,
+                                        sk_sp<SkColorSpace>* texColorSpace) const {
 #if SK_SUPPORT_GPU
     if (!ctx) {
         return nullptr;
     }
 
+    if (texColorSpace) {
+        *texColorSpace = sk_ref_sp(fBitmap.colorSpace());
+    }
+
     uint32_t uniqueID;
     sk_sp<GrTexture> tex = this->refPinnedTexture(&uniqueID);
     if (tex) {
index 2d789d04d8dca92891e439d0bb22be661d21f1e0..ea6887ac80d784df7a86c7f88865490a7eec0dbd 100644 (file)
@@ -17,7 +17,7 @@
 #include "SkUnPreMultiply.h"
 
 void image_get_ro_pixels(const SkImage* image, SkBitmap* dst) {
-    if(as_IB(image)->getROPixels(dst)
+    if(as_IB(image)->getROPixels(dst, SkDestinationSurfaceColorMode::kLegacy)
        && dst->dimensions() == image->dimensions()) {
         if (dst->colorType() != kIndex_8_SkColorType) {
             return;
@@ -502,7 +502,8 @@ sk_sp<SkPDFObject> SkPDFCreateBitmapObject(sk_sp<SkImage> image,
     if (pixelSerializer) {
         SkBitmap bm;
         SkAutoPixmapUnlock apu;
-        if (as_IB(image.get())->getROPixels(&bm) && bm.requestLock(&apu)) {
+        if (as_IB(image.get())->getROPixels(&bm, SkDestinationSurfaceColorMode::kLegacy) &&
+            bm.requestLock(&apu)) {
             data.reset(pixelSerializer->encode(apu.pixmap()));
             if (data && SkIsJFIF(data.get(), &info)) {
                 bool yuv = info.fType == SkJFIFInfo::kYCbCr;
index a04b38000564fbd685a857b853ecabd86a5335e2..9a70f68c31dfcf5744b5b201fe736942d349ec8c 100644 (file)
@@ -2315,8 +2315,12 @@ sk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkBitmap& bitmap) {
 }
 
 sk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkImage* image) {
+    // TODO: See comment above in drawSpecial. The color mode we use for decode should be driven
+    // by the destination where we're going to draw thing thing (ie this device). But we don't have
+    // a color space, so we always decode in legacy mode for now.
     return SkSpecialImage::MakeFromImage(SkIRect::MakeWH(image->width(), image->height()),
-                                         image->makeNonTextureImage());
+                                         image->makeNonTextureImage(),
+                                         SkDestinationSurfaceColorMode::kLegacy);
 }
 
 sk_sp<SkSpecialImage> SkPDFDevice::snapSpecial() {
index ebd3186e01b0a6904382bcb1dbc40935d11217ec..e3a652b06b482db3c6a2d70a2d2f1d88d5feccf7 100644 (file)
@@ -155,11 +155,13 @@ DEF_TEST(ImageFilterCache_RasterBacked, reporter) {
 static void test_image_backed(skiatest::Reporter* reporter, const sk_sp<SkImage>& srcImage) {
     const SkIRect& full = SkIRect::MakeWH(kFullSize, kFullSize);
 
-    sk_sp<SkSpecialImage> fullImg(SkSpecialImage::MakeFromImage(full, srcImage));
+    sk_sp<SkSpecialImage> fullImg(
+        SkSpecialImage::MakeFromImage(full, srcImage, SkDestinationSurfaceColorMode::kLegacy));
 
     const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
 
-    sk_sp<SkSpecialImage> subsetImg(SkSpecialImage::MakeFromImage(subset, srcImage));
+    sk_sp<SkSpecialImage> subsetImg(
+        SkSpecialImage::MakeFromImage(subset, srcImage, SkDestinationSurfaceColorMode::kLegacy));
 
     test_find_existing(reporter, fullImg, subsetImg);
     test_dont_find_if_diff_key(reporter, fullImg, subsetImg);
index 0acb428d7a3bc85cbd9a2f2d802d97a4fd842e78..18883feded70e61e5664182c112f0bbe760cd6c7 100644 (file)
@@ -43,7 +43,8 @@ static void assert_equal(skiatest::Reporter* reporter, SkImage* a, const SkIRect
     // see https://bug.skia.org/3965
     //REPORTER_ASSERT(reporter, a->isOpaque() == b->isOpaque());
 
-    SkImageInfo info = SkImageInfo::MakeN32(widthA, heightA, a->alphaType());
+    // The codecs may have given us back F16, we can't read from F16 raster to N32, only S32.
+    SkImageInfo info = SkImageInfo::MakeS32(widthA, heightA, a->alphaType());
     SkAutoPixmapStorage pmapA, pmapB;
     pmapA.alloc(info);
     pmapB.alloc(info);
index e6813748522640a03164c4a89fa19061c062fbcf..2ae179c6a9a193954c88fce3d058195bc3fa4d31 100644 (file)
@@ -52,7 +52,7 @@ static void test_blender(std::string resourceName, skiatest::Reporter* reporter)
         return;
     }
     SkBitmap bm;
-    if (!as_IB(image)->getROPixels(&bm)) {
+    if (!as_IB(image)->getROPixels(&bm, SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware)) {
         ERRORF(reporter, "Could not read resource");
         return;
     }
index 62a5da60894c73a24bb345fb759b8f9730004509..8cd874a3c8ca2cb16ff8786efa0bfd2565becef1 100644 (file)
@@ -155,19 +155,20 @@ DEF_TEST(SpecialImage_Raster, reporter) {
     }
 }
 
-DEF_TEST(SpecialImage_Image, reporter) {
+static void test_specialimage_image(skiatest::Reporter* reporter,
+                                    SkDestinationSurfaceColorMode colorMode) {
     SkBitmap bm = create_bm();
 
     sk_sp<SkImage> fullImage(SkImage::MakeFromBitmap(bm));
 
     sk_sp<SkSpecialImage> fullSImage(SkSpecialImage::MakeFromImage(
                                                             SkIRect::MakeWH(kFullSize, kFullSize),
-                                                            fullImage));
+                                                            fullImage, colorMode));
 
     const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize);
 
     {
-        sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeFromImage(subset, fullImage));
+        sk_sp<SkSpecialImage> subSImg1(SkSpecialImage::MakeFromImage(subset, fullImage, colorMode));
         test_image(subSImg1, reporter, nullptr, false, kPad, kFullSize);
     }
 
@@ -177,6 +178,14 @@ DEF_TEST(SpecialImage_Image, reporter) {
     }
 }
 
+DEF_TEST(SpecialImage_Image_Legacy, reporter) {
+    test_specialimage_image(reporter, SkDestinationSurfaceColorMode::kLegacy);
+}
+
+DEF_TEST(SpecialImage_Image_ColorSpaceAware, reporter) {
+    test_specialimage_image(reporter, SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware);
+}
+
 #if SK_SUPPORT_GPU
 
 static void test_texture_backed(skiatest::Reporter* reporter,
index 3a9bb58c4e149c8a63581ba93fd7cb7c7cafd2d4..981bfba337381dc88c2b8ee7274d21d8ef7d4131 100644 (file)
@@ -30,7 +30,12 @@ bool GetResourceAsBitmap(const char* resource, SkBitmap* dst) {
     SkString resourcePath = GetResourcePath(resource);
     sk_sp<SkData> resourceData(SkData::MakeFromFileName(resourcePath.c_str()));
     std::unique_ptr<SkImageGenerator> gen(SkImageGenerator::NewFromEncoded(resourceData.get()));
-    return gen && gen->tryGenerateBitmap(dst);
+    SkPMColor ctStorage[256];
+    sk_sp<SkColorTable> ctable(new SkColorTable(ctStorage, 256));
+    int count = ctable->count();
+    return dst->tryAllocPixels(gen->getInfo(), nullptr, ctable.get()) &&
+        gen->getPixels(gen->getInfo().makeColorSpace(nullptr), dst->getPixels(), dst->rowBytes(),
+                       const_cast<SkPMColor*>(ctable->readColors()), &count);
 }
 
 sk_sp<SkImage> GetResourceAsImage(const char* resource) {