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);
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;
// 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
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:
}
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() {
}
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,
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;
// 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
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;
}
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);
}
}
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);
}
}
#include "SkBitmap.h"
#include "SkBitmapCache.h"
+#include "SkColorSpace_Base.h"
#include "SkImage_Base.h"
#include "SkImageCacherator.h"
#include "SkMallocPixelRef.h"
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,
: 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);
}
// 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(),
//////////////////////////////////////////////////////////////////////////////////////////////////
-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();
}
}
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
{
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) {
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
//////////////////////////////////////////////////////////////////////////////////////////////////
+// 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;
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)
*
* 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) {
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)) {
}
}
- 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
// 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);
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;
}
#include "SkMutex.h"
#include "SkTemplates.h"
+class GrCaps;
class GrContext;
class GrSamplerParams;
class GrUniqueKey;
~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
* 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);
/**
* 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);
/**
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);
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;
// 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]);
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()));
#endif
{
SkBitmap bm;
- if (as_IB(image)->getROPixels(&bm)) {
+ if (as_IB(image)->getROPixels(&bm, colorMode)) {
return MakeFromRaster(subset, bm, props);
}
}
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&,
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());
}
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);
}
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());
}
//////////////////////////////////////////////////////////////////////////////
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);
}
}
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);
}
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;
// 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;
}
}
-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);
// 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) {
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);
//////////////////////////////////////////////////////////////////////////////
GrTexture* GrTextureMaker::refTextureForParams(const GrSamplerParams& params,
- SkDestinationSurfaceColorMode colorMode) {
+ SkDestinationSurfaceColorMode colorMode,
+ sk_sp<SkColorSpace>* texColorSpace) {
CopyParams copyParams;
bool willBeMipped = params.filterMode() == GrSamplerParams::kMipMap_FilterMode;
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) {
// 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;
}
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,
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)
* 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
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; }
*/
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,
*/
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.
*
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);
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);
}
}
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);
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);
}
}
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);
}
}
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);
}
}
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);
}
// 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
// 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();
}
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;
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());
}
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;
}
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 =
}
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 {
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) {
// 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;
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:
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
}
}
- if (this->getROPixels(&bm, chint)) {
+ if (this->getROPixels(&bm, decodeColorMode, chint)) {
return bm.readPixels(dstInfo, dstPixels, dstRB, srcX, srcY);
}
return false;
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 {
#include "SkBitmapCache.h"
#include "SkGrPriv.h"
#include "SkImage_Gpu.h"
+#include "SkImageCacherator.h"
#include "SkMipMap.h"
#include "SkPixelRef.h"
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());
}
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);
}
}
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 {
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);
}
}
- 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(); }
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
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) {
#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;
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;
}
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() {
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);
// 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);
return;
}
SkBitmap bm;
- if (!as_IB(image)->getROPixels(&bm)) {
+ if (!as_IB(image)->getROPixels(&bm, SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware)) {
ERRORF(reporter, "Could not read resource");
return;
}
}
}
-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);
}
}
}
+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,
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) {