diff options
author | Brian Osman <brianosman@google.com> | 2016-11-18 11:28:24 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2016-11-21 14:58:32 +0000 |
commit | 7992da32f02f90e0ac9ab6914eb31676b502eb71 (patch) | |
tree | eb993b2a03662c692d79013088ba18ebcd793ef3 | |
parent | dc940a63c4a2b5bfc8765fcdbeb097ac35e7bbe9 (diff) |
Support decoding images to multiple formats, depending on usage
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, 522 insertions, 141 deletions
diff --git a/bench/SkBlend_optsBench.cpp b/bench/SkBlend_optsBench.cpp index 6a4593e664..b5827ef03b 100644 --- a/bench/SkBlend_optsBench.cpp +++ b/bench/SkBlend_optsBench.cpp @@ -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); diff --git a/gm/image_pict.cpp b/gm/image_pict.cpp index 148426da5a..11a56da9fc 100644 --- a/gm/image_pict.cpp +++ b/gm/image_pict.cpp @@ -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 diff --git a/src/codec/SkCodecImageGenerator.cpp b/src/codec/SkCodecImageGenerator.cpp index 8108f0de44..9f85971f89 100644 --- a/src/codec/SkCodecImageGenerator.cpp +++ b/src/codec/SkCodecImageGenerator.cpp @@ -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: diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp index e44b76926a..1e618accb0 100644 --- a/src/core/SkBitmapDevice.cpp +++ b/src/core/SkBitmapDevice.cpp @@ -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() { diff --git a/src/core/SkBitmapProvider.cpp b/src/core/SkBitmapProvider.cpp index 11b1ac98d1..3ec4748c90 100644 --- a/src/core/SkBitmapProvider.cpp +++ b/src/core/SkBitmapProvider.cpp @@ -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, diff --git a/src/core/SkBitmapProvider.h b/src/core/SkBitmapProvider.h index fd5b66219e..4437889fb0 100644 --- a/src/core/SkBitmapProvider.h +++ b/src/core/SkBitmapProvider.h @@ -13,9 +13,14 @@ 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 diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp index 4caa8756cb..73f412d1d5 100644 --- a/src/core/SkDevice.cpp +++ b/src/core/SkDevice.cpp @@ -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); } } diff --git a/src/core/SkImageCacherator.cpp b/src/core/SkImageCacherator.cpp index e29124212a..157cea2bef 100644 --- a/src/core/SkImageCacherator.cpp +++ b/src/core/SkImageCacherator.cpp @@ -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; } diff --git a/src/core/SkImageCacherator.h b/src/core/SkImageCacherator.h index 3500206459..89ac378883 100644 --- a/src/core/SkImageCacherator.h +++ b/src/core/SkImageCacherator.h @@ -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; diff --git a/src/core/SkShadowShader.cpp b/src/core/SkShadowShader.cpp index db04638f02..2d67ecb13b 100644 --- a/src/core/SkShadowShader.cpp +++ b/src/core/SkShadowShader.cpp @@ -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]); diff --git a/src/core/SkSpecialImage.cpp b/src/core/SkSpecialImage.cpp index c25079e816..fefe5c2a1c 100644 --- a/src/core/SkSpecialImage.cpp +++ b/src/core/SkSpecialImage.cpp @@ -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); } } diff --git a/src/core/SkSpecialImage.h b/src/core/SkSpecialImage.h index 12847df115..c6afa1d0c7 100644 --- a/src/core/SkSpecialImage.h +++ b/src/core/SkSpecialImage.h @@ -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&, diff --git a/src/effects/SkImageSource.cpp b/src/effects/SkImageSource.cpp index f96a4a1676..de849416c8 100644 --- a/src/effects/SkImageSource.cpp +++ b/src/effects/SkImageSource.cpp @@ -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()); } diff --git a/src/gpu/GrImageIDTextureAdjuster.cpp b/src/gpu/GrImageIDTextureAdjuster.cpp index cc869baf7f..645d1c2a99 100644 --- a/src/gpu/GrImageIDTextureAdjuster.cpp +++ b/src/gpu/GrImageIDTextureAdjuster.cpp @@ -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); } diff --git a/src/gpu/GrImageIDTextureAdjuster.h b/src/gpu/GrImageIDTextureAdjuster.h index b0b70487e3..ad54331092 100644 --- a/src/gpu/GrImageIDTextureAdjuster.h +++ b/src/gpu/GrImageIDTextureAdjuster.h @@ -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; diff --git a/src/gpu/GrTextureParamsAdjuster.cpp b/src/gpu/GrTextureParamsAdjuster.cpp index ca273a770f..d924f4a976 100644 --- a/src/gpu/GrTextureParamsAdjuster.cpp +++ b/src/gpu/GrTextureParamsAdjuster.cpp @@ -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, ©Params)) { return this->refOriginalTexture(willBeMipped, colorMode); } GrUniqueKey copyKey; - this->makeCopyKey(copyParams, ©Key); + this->makeCopyKey(copyParams, ©Key, 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, diff --git a/src/gpu/GrTextureParamsAdjuster.h b/src/gpu/GrTextureParamsAdjuster.h index bf3076ebeb..c3f020196c 100644 --- a/src/gpu/GrTextureParamsAdjuster.h +++ b/src/gpu/GrTextureParamsAdjuster.h @@ -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, @@ -203,6 +207,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. * * The base-class handles general logic for this, and only needs access to the following diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index c0ca5cf2a1..cc2cd2fd50 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -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); } } diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp index f1a3caaa2a..471a39305f 100644 --- a/src/gpu/SkGr.cpp +++ b/src/gpu/SkGr.cpp @@ -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); } diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp index 4d76638cbd..f976242eca 100644 --- a/src/image/SkImage.cpp +++ b/src/image/SkImage.cpp @@ -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 = diff --git a/src/image/SkImageShader.cpp b/src/image/SkImageShader.cpp index 81b6f76ede..f8b22df41a 100644 --- a/src/image/SkImageShader.cpp +++ b/src/image/SkImageShader.cpp @@ -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) { diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h index 09ceb5770a..c955d491f7 100644 --- a/src/image/SkImage_Base.h +++ b/src/image/SkImage_Base.h @@ -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; diff --git a/src/image/SkImage_Generator.cpp b/src/image/SkImage_Generator.cpp index 82f395fc95..efee134a81 100644 --- a/src/image/SkImage_Generator.cpp +++ b/src/image/SkImage_Generator.cpp @@ -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 { diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp index 84c7b10be1..02f4d69519 100644 --- a/src/image/SkImage_Gpu.cpp +++ b/src/image/SkImage_Gpu.cpp @@ -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); diff --git a/src/image/SkImage_Gpu.h b/src/image/SkImage_Gpu.h index 19e2944283..dcde00a8fc 100644 --- a/src/image/SkImage_Gpu.h +++ b/src/image/SkImage_Gpu.h @@ -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(); } diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp index a2b12e41ce..0875d615b0 100644 --- a/src/image/SkImage_Raster.cpp +++ b/src/image/SkImage_Raster.cpp @@ -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) { diff --git a/src/pdf/SkPDFBitmap.cpp b/src/pdf/SkPDFBitmap.cpp index 2d789d04d8..ea6887ac80 100644 --- a/src/pdf/SkPDFBitmap.cpp +++ b/src/pdf/SkPDFBitmap.cpp @@ -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; diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index a04b380005..9a70f68c31 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -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() { diff --git a/tests/ImageFilterCacheTest.cpp b/tests/ImageFilterCacheTest.cpp index ebd3186e01..e3a652b06b 100644 --- a/tests/ImageFilterCacheTest.cpp +++ b/tests/ImageFilterCacheTest.cpp @@ -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); diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp index 0acb428d7a..18883feded 100644 --- a/tests/ImageTest.cpp +++ b/tests/ImageTest.cpp @@ -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); diff --git a/tests/SkBlend_optsTest.cpp b/tests/SkBlend_optsTest.cpp index e681374852..2ae179c6a9 100644 --- a/tests/SkBlend_optsTest.cpp +++ b/tests/SkBlend_optsTest.cpp @@ -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; } diff --git a/tests/SpecialImageTest.cpp b/tests/SpecialImageTest.cpp index 62a5da6089..8cd874a3c8 100644 --- a/tests/SpecialImageTest.cpp +++ b/tests/SpecialImageTest.cpp @@ -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, diff --git a/tools/Resources.cpp b/tools/Resources.cpp index 3a9bb58c4e..981bfba337 100644 --- a/tools/Resources.cpp +++ b/tools/Resources.cpp @@ -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) { |