2 * Copyright 2015 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
9 #include "SkBitmapCache.h"
10 #include "SkColorSpace_Base.h"
11 #include "SkImage_Base.h"
12 #include "SkImageCacherator.h"
13 #include "SkMallocPixelRef.h"
15 #include "SkPixelRef.h"
16 #include "SkResourceCache.h"
19 #include "GrContext.h"
20 #include "GrContextPriv.h"
21 #include "GrGpuResourcePriv.h"
22 #include "GrImageTextureMaker.h"
23 #include "GrResourceKey.h"
24 #include "GrResourceProvider.h"
25 #include "GrSamplerParams.h"
26 #include "GrYUVProvider.h"
30 // Until we actually have codecs/etc. that can contain/support a GPU texture format
31 // skip this step, since for some generators, returning their encoded data as a SkData
32 // can be somewhat expensive, and this call doesn't indicate to the generator that we're
33 // only interested in GPU datas...
34 // see skbug.com/ 4971, 5128, ...
35 //#define SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
37 // Helper for exclusive access to a shared generator.
38 class SkImageCacherator::ScopedGenerator {
40 ScopedGenerator(const sk_sp<SharedGenerator>& gen)
41 : fSharedGenerator(gen)
42 , fAutoAquire(gen->fMutex) {}
44 SkImageGenerator* operator->() const {
45 fSharedGenerator->fMutex.assertHeld();
46 return fSharedGenerator->fGenerator.get();
49 operator SkImageGenerator*() const {
50 fSharedGenerator->fMutex.assertHeld();
51 return fSharedGenerator->fGenerator.get();
55 const sk_sp<SharedGenerator>& fSharedGenerator;
56 SkAutoExclusive fAutoAquire;
59 SkImageCacherator::Validator::Validator(sk_sp<SharedGenerator> gen, const SkIRect* subset)
60 : fSharedGenerator(std::move(gen)) {
62 if (!fSharedGenerator) {
66 // The following generator accessors are safe without acquiring the mutex (const getters).
67 // TODO: refactor to use a ScopedGenerator instead, for clarity.
68 const SkImageInfo& info = fSharedGenerator->fGenerator->getInfo();
70 fSharedGenerator.reset();
74 fUniqueID = fSharedGenerator->fGenerator->uniqueID();
75 const SkIRect bounds = SkIRect::MakeWH(info.width(), info.height());
77 if (!bounds.contains(*subset)) {
78 fSharedGenerator.reset();
81 if (*subset != bounds) {
82 // we need a different uniqueID since we really are a subset of the raw generator
83 fUniqueID = SkNextID::ImageID();
89 fInfo = info.makeWH(subset->width(), subset->height());
90 fOrigin = SkIPoint::Make(subset->x(), subset->y());
92 // If the encoded data is in a strange color space (it's not an XYZ matrix space), we won't be
93 // able to preserve the gamut of the encoded data when we decode it. Instead, we'll have to
94 // decode to a known color space (linear sRGB is a good choice). But we need to adjust the
95 // stored color space, because drawing code will ask the SkImage for its color space, which
96 // will in turn ask the cacherator. If we return the A2B color space, then we will be unable to
97 // construct a source-to-dest gamut transformation matrix.
98 if (fInfo.colorSpace() &&
99 SkColorSpace_Base::Type::kXYZ != as_CSB(fInfo.colorSpace())->type()) {
100 fInfo = fInfo.makeColorSpace(SkColorSpace::MakeSRGBLinear());
104 SkImageCacherator* SkImageCacherator::NewFromGenerator(std::unique_ptr<SkImageGenerator> gen,
105 const SkIRect* subset) {
106 Validator validator(SharedGenerator::Make(std::move(gen)), subset);
108 return validator ? new SkImageCacherator(&validator) : nullptr;
111 SkImageCacherator::SkImageCacherator(Validator* validator)
112 : fSharedGenerator(std::move(validator->fSharedGenerator)) // we take ownership
113 , fInfo(validator->fInfo)
114 , fOrigin(validator->fOrigin)
116 fUniqueIDs[kLegacy_CachedFormat] = validator->fUniqueID;
117 for (int i = 1; i < kNumCachedFormats; ++i) {
118 // We lazily allocate IDs for non-default caching cases
119 fUniqueIDs[i] = kNeedNewImageUniqueID;
121 SkASSERT(fSharedGenerator);
124 SkImageCacherator::~SkImageCacherator() {}
126 SkData* SkImageCacherator::refEncoded(GrContext* ctx) {
127 ScopedGenerator generator(fSharedGenerator);
128 return generator->refEncodedData(ctx);
131 static bool check_output_bitmap(const SkBitmap& bitmap, uint32_t expectedID) {
132 SkASSERT(bitmap.getGenerationID() == expectedID);
133 SkASSERT(bitmap.isImmutable());
134 SkASSERT(bitmap.getPixels());
138 // Note, this returns a new, mutable, bitmap, with a new genID.
139 // If you want the immutable bitmap with the same ID as our cacherator, call tryLockAsBitmap()
141 bool SkImageCacherator::generateBitmap(SkBitmap* bitmap, const SkImageInfo& decodeInfo) {
142 SkBitmap::Allocator* allocator = SkResourceCache::GetAllocator();
144 ScopedGenerator generator(fSharedGenerator);
145 const SkImageInfo& genInfo = generator->getInfo();
146 if (decodeInfo.dimensions() == genInfo.dimensions()) {
147 SkASSERT(fOrigin.x() == 0 && fOrigin.y() == 0);
148 // fast-case, no copy needed
149 return generator->tryGenerateBitmap(bitmap, decodeInfo, allocator);
151 // need to handle subsetting, so we first generate the full size version, and then
152 // "read" from it to get our subset. See https://bug.skia.org/4213
155 if (!generator->tryGenerateBitmap(&full,
156 decodeInfo.makeWH(genInfo.width(), genInfo.height()),
160 if (!bitmap->tryAllocPixels(decodeInfo, sk_ref_sp(full.getColorTable()))) {
163 return full.readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
164 fOrigin.x(), fOrigin.y());
168 bool SkImageCacherator::directGeneratePixels(const SkImageInfo& info, void* pixels, size_t rb,
170 SkTransferFunctionBehavior behavior) {
171 ScopedGenerator generator(fSharedGenerator);
172 const SkImageInfo& genInfo = generator->getInfo();
173 // Currently generators do not natively handle subsets, so check that first.
174 if (srcX || srcY || genInfo.width() != info.width() || genInfo.height() != info.height()) {
178 SkImageGenerator::Options opts;
179 opts.fBehavior = behavior;
180 return generator->getPixels(info, pixels, rb, &opts);
183 //////////////////////////////////////////////////////////////////////////////////////////////////
185 bool SkImageCacherator::lockAsBitmapOnlyIfAlreadyCached(SkBitmap* bitmap, CachedFormat format) {
186 return kNeedNewImageUniqueID != fUniqueIDs[format] &&
187 SkBitmapCache::Find(SkBitmapCacheDesc::Make(fUniqueIDs[format],
188 fInfo.width(), fInfo.height()), bitmap) &&
189 check_output_bitmap(*bitmap, fUniqueIDs[format]);
192 bool SkImageCacherator::tryLockAsBitmap(SkBitmap* bitmap, const SkImage* client,
193 SkImage::CachingHint chint, CachedFormat format,
194 const SkImageInfo& info) {
195 if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap, format)) {
198 if (!this->generateBitmap(bitmap, info)) {
202 if (kNeedNewImageUniqueID == fUniqueIDs[format]) {
203 fUniqueIDs[format] = SkNextID::ImageID();
205 bitmap->pixelRef()->setImmutableWithID(fUniqueIDs[format]);
206 if (SkImage::kAllow_CachingHint == chint) {
207 SkBitmapCache::Add(SkBitmapCacheDesc::Make(fUniqueIDs[format],
208 fInfo.width(), fInfo.height()), *bitmap);
210 as_IB(client)->notifyAddedToCache();
216 bool SkImageCacherator::lockAsBitmap(GrContext* context, SkBitmap* bitmap, const SkImage* client,
217 SkColorSpace* dstColorSpace,
218 SkImage::CachingHint chint) {
219 CachedFormat format = this->chooseCacheFormat(dstColorSpace);
220 SkImageInfo cacheInfo = this->buildCacheInfo(format);
222 if (kNeedNewImageUniqueID == fUniqueIDs[format]) {
223 fUniqueIDs[format] = SkNextID::ImageID();
226 if (this->tryLockAsBitmap(bitmap, client, chint, format, cacheInfo)) {
227 return check_output_bitmap(*bitmap, fUniqueIDs[format]);
236 // Try to get a texture and read it back to raster (and then cache that with our ID)
237 sk_sp<GrTextureProxy> proxy;
240 ScopedGenerator generator(fSharedGenerator);
241 proxy = generator->generateTexture(context, cacheInfo, fOrigin);
248 if (!bitmap->tryAllocPixels(cacheInfo)) {
253 sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeWrappedSurfaceContext(
255 fInfo.refColorSpace())); // src colorSpace
261 if (!sContext->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
266 bitmap->pixelRef()->setImmutableWithID(fUniqueIDs[format]);
267 if (SkImage::kAllow_CachingHint == chint) {
268 SkBitmapCache::Add(SkBitmapCacheDesc::Make(fUniqueIDs[format],
269 fInfo.width(), fInfo.height()), *bitmap);
271 as_IB(client)->notifyAddedToCache();
274 return check_output_bitmap(*bitmap, fUniqueIDs[format]);
280 //////////////////////////////////////////////////////////////////////////////////////////////////
282 // Abstraction of GrCaps that handles the cases where we don't have a caps pointer (because
283 // we're in raster mode), or where GPU support is entirely missing. In theory, we only need the
284 // chosen format to be texturable, but that lets us choose F16 on GLES implemenations where we
285 // won't be able to read the texture back. We'd like to ensure that SkImake::makeNonTextureImage
286 // works, so we require that the formats we choose are renderable (as a proxy for being readable).
288 CacheCaps(const GrCaps* caps) : fCaps(caps) {}
291 bool supportsHalfFloat() const {
293 (fCaps->isConfigTexturable(kRGBA_half_GrPixelConfig) &&
294 fCaps->isConfigRenderable(kRGBA_half_GrPixelConfig, false));
297 bool supportsSRGB() const {
299 (fCaps->srgbSupport() && fCaps->isConfigTexturable(kSRGBA_8888_GrPixelConfig));
302 bool supportsSBGR() const {
303 return !fCaps || fCaps->srgbSupport();
306 bool supportsHalfFloat() const { return true; }
307 bool supportsSRGB() const { return true; }
308 bool supportsSBGR() const { return true; }
314 SkImageCacherator::CachedFormat SkImageCacherator::chooseCacheFormat(SkColorSpace* dstColorSpace,
315 const GrCaps* grCaps) {
316 SkColorSpace* cs = fInfo.colorSpace();
317 if (!cs || !dstColorSpace) {
318 return kLegacy_CachedFormat;
321 CacheCaps caps(grCaps);
322 switch (fInfo.colorType()) {
323 case kUnknown_SkColorType:
324 case kAlpha_8_SkColorType:
325 case kRGB_565_SkColorType:
326 case kARGB_4444_SkColorType:
327 // We don't support color space on these formats, so always decode in legacy mode:
328 // TODO: Ask the codec to decode these to something else (at least sRGB 8888)?
329 return kLegacy_CachedFormat;
331 case kIndex_8_SkColorType:
332 // We can't draw from indexed textures with a color space, so ask the codec to expand
333 if (cs->gammaCloseToSRGB()) {
334 if (caps.supportsSRGB()) {
335 return kSRGB8888_CachedFormat;
336 } else if (caps.supportsHalfFloat()) {
337 return kLinearF16_CachedFormat;
339 return kLegacy_CachedFormat;
342 if (caps.supportsHalfFloat()) {
343 return kLinearF16_CachedFormat;
344 } else if (caps.supportsSRGB()) {
345 return kSRGB8888_CachedFormat;
347 return kLegacy_CachedFormat;
351 case kGray_8_SkColorType:
352 // TODO: What do we do with grayscale sources that have strange color spaces attached?
353 // The codecs and color space xform don't handle this correctly (yet), so drop it on
354 // the floor. (Also, inflating by a factor of 8 is going to be unfortunate).
355 // As it is, we don't directly support sRGB grayscale, so ask the codec to convert
356 // it for us. This bypasses some really sketchy code GrUploadPixmapToTexture.
357 if (cs->gammaCloseToSRGB() && caps.supportsSRGB()) {
358 return kSRGB8888_CachedFormat;
360 return kLegacy_CachedFormat;
363 case kRGBA_8888_SkColorType:
364 if (cs->gammaCloseToSRGB()) {
365 if (caps.supportsSRGB()) {
366 return kSRGB8888_CachedFormat;
367 } else if (caps.supportsHalfFloat()) {
368 return kLinearF16_CachedFormat;
370 return kLegacy_CachedFormat;
373 if (caps.supportsHalfFloat()) {
374 return kLinearF16_CachedFormat;
375 } else if (caps.supportsSRGB()) {
376 return kSRGB8888_CachedFormat;
378 return kLegacy_CachedFormat;
382 case kBGRA_8888_SkColorType:
383 // Odd case. sBGRA isn't a real thing, so we may not have this texturable.
384 if (caps.supportsSBGR()) {
385 if (cs->gammaCloseToSRGB()) {
386 return kSBGR8888_CachedFormat;
387 } else if (caps.supportsHalfFloat()) {
388 return kLinearF16_CachedFormat;
389 } else if (caps.supportsSRGB()) {
390 return kSRGB8888_CachedFormat;
392 // sBGRA support without sRGBA is highly unlikely (impossible?) Nevertheless.
393 return kLegacy_CachedFormat;
396 if (cs->gammaCloseToSRGB()) {
397 if (caps.supportsSRGB()) {
398 return kSRGB8888_CachedFormat;
399 } else if (caps.supportsHalfFloat()) {
400 return kLinearF16_CachedFormat;
402 return kLegacy_CachedFormat;
405 if (caps.supportsHalfFloat()) {
406 return kLinearF16_CachedFormat;
407 } else if (caps.supportsSRGB()) {
408 return kSRGB8888_CachedFormat;
410 return kLegacy_CachedFormat;
415 case kRGBA_F16_SkColorType:
416 if (caps.supportsHalfFloat()) {
417 return kLinearF16_CachedFormat;
418 } else if (caps.supportsSRGB()) {
419 return kSRGB8888_CachedFormat;
421 return kLegacy_CachedFormat;
424 SkDEBUGFAIL("Unreachable");
425 return kLegacy_CachedFormat;
428 SkImageInfo SkImageCacherator::buildCacheInfo(CachedFormat format) {
430 case kLegacy_CachedFormat:
431 return fInfo.makeColorSpace(nullptr);
432 case kLinearF16_CachedFormat:
433 return fInfo.makeColorType(kRGBA_F16_SkColorType)
434 .makeColorSpace(as_CSB(fInfo.colorSpace())->makeLinearGamma());
435 case kSRGB8888_CachedFormat:
436 // If the transfer function is nearly (but not exactly) sRGB, we don't want the codec
437 // to bother trans-coding. It would be slow, and do more harm than good visually,
438 // so we make sure to leave the colorspace as-is.
439 if (fInfo.colorSpace()->gammaCloseToSRGB()) {
440 return fInfo.makeColorType(kRGBA_8888_SkColorType);
442 return fInfo.makeColorType(kRGBA_8888_SkColorType)
443 .makeColorSpace(as_CSB(fInfo.colorSpace())->makeSRGBGamma());
445 case kSBGR8888_CachedFormat:
446 // See note above about not-quite-sRGB transfer functions.
447 if (fInfo.colorSpace()->gammaCloseToSRGB()) {
448 return fInfo.makeColorType(kBGRA_8888_SkColorType);
450 return fInfo.makeColorType(kBGRA_8888_SkColorType)
451 .makeColorSpace(as_CSB(fInfo.colorSpace())->makeSRGBGamma());
454 SkDEBUGFAIL("Invalid cached format");
459 //////////////////////////////////////////////////////////////////////////////////////////////////
463 void SkImageCacherator::makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat format,
464 GrUniqueKey* cacheKey) {
465 SkASSERT(!cacheKey->isValid());
466 if (origKey.isValid()) {
467 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
468 GrUniqueKey::Builder builder(cacheKey, origKey, kDomain, 1);
473 #ifdef SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
474 static GrTexture* load_compressed_into_texture(GrContext* ctx, SkData* data, GrSurfaceDesc desc) {
475 const void* rawStart;
476 GrPixelConfig config = GrIsCompressedTextureDataSupported(ctx, data, desc.fWidth, desc.fHeight,
478 if (kUnknown_GrPixelConfig == config) {
482 desc.fConfig = config;
483 return ctx->resourceProvider()->createTexture(desc, SkBudgeted::kYes, rawStart, 0);
487 class Generator_GrYUVProvider : public GrYUVProvider {
488 SkImageGenerator* fGen;
491 Generator_GrYUVProvider(SkImageGenerator* gen) : fGen(gen) {}
493 uint32_t onGetID() override { return fGen->uniqueID(); }
494 bool onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const override {
495 return fGen->queryYUV8(sizeInfo, colorSpace);
497 bool onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) override {
498 return fGen->getYUV8Planes(sizeInfo, planes);
502 static void set_key_on_proxy(GrResourceProvider* resourceProvider,
503 GrTextureProxy* proxy, const GrUniqueKey& key) {
505 resourceProvider->assignUniqueKeyToProxy(key, proxy);
509 sk_sp<SkColorSpace> SkImageCacherator::getColorSpace(GrContext* ctx, SkColorSpace* dstColorSpace) {
510 // TODO: This isn't always correct. Picture generator currently produces textures in N32,
511 // and will (soon) emit them in an arbitrary (destination) space. We will need to stash that
512 // information in/on the key so we can return the correct space in case #1 of lockTexture.
513 CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
514 SkImageInfo cacheInfo = this->buildCacheInfo(format);
515 return sk_ref_sp(cacheInfo.colorSpace());
519 * We have a 5 ways to try to return a texture (in sorted order)
521 * 1. Check the cache for a pre-existing one
522 * 2. Ask the generator to natively create one
523 * 3. Ask the generator to return a compressed form that the GPU might support
524 * 4. Ask the generator to return YUV planes, which the GPU can convert
525 * 5. Ask the generator to return RGB(A) data, which the GPU can convert
527 sk_sp<GrTextureProxy> SkImageCacherator::lockTextureProxy(GrContext* ctx,
528 const GrUniqueKey& origKey,
529 const SkImage* client,
530 SkImage::CachingHint chint,
532 SkColorSpace* dstColorSpace) {
533 // Values representing the various texture lock paths we can take. Used for logging the path
534 // taken to a histogram.
535 enum LockTexturePath {
536 kFailure_LockTexturePath,
537 kPreExisting_LockTexturePath,
538 kNative_LockTexturePath,
539 kCompressed_LockTexturePath,
540 kYUV_LockTexturePath,
541 kRGBA_LockTexturePath,
544 enum { kLockTexturePathCount = kRGBA_LockTexturePath + 1 };
546 // Determine which cached format we're going to use (which may involve decoding to a different
547 // info than the generator provides).
548 CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
550 // Fold the cache format into our texture key
552 this->makeCacheKeyFromOrigKey(origKey, format, &key);
554 // 1. Check the cache for a pre-existing one
556 if (sk_sp<GrTextureProxy> proxy = ctx->resourceProvider()->findProxyByUniqueKey(key)) {
557 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kPreExisting_LockTexturePath,
558 kLockTexturePathCount);
563 // The CachedFormat is both an index for which cache "slot" we'll use to store this particular
564 // decoded variant of the encoded data, and also a recipe for how to transform the original
565 // info to get the one that we're going to decode to.
566 SkImageInfo cacheInfo = this->buildCacheInfo(format);
568 // 2. Ask the generator to natively create one
570 ScopedGenerator generator(fSharedGenerator);
571 if (sk_sp<GrTextureProxy> proxy = generator->generateTexture(ctx, cacheInfo, fOrigin)) {
572 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath,
573 kLockTexturePathCount);
574 set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
579 const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->caps());
581 #ifdef SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
582 // 3. Ask the generator to return a compressed form that the GPU might support
583 sk_sp<SkData> data(this->refEncoded(ctx));
585 GrTexture* tex = load_compressed_into_texture(ctx, data, desc);
587 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kCompressed_LockTexturePath,
588 kLockTexturePathCount);
589 return set_key_and_return(tex, key);
594 // 4. Ask the generator to return YUV planes, which the GPU can convert
595 if (!ctx->contextPriv().disableGpuYUVConversion()) {
596 ScopedGenerator generator(fSharedGenerator);
597 Generator_GrYUVProvider provider(generator);
598 if (sk_sp<GrTextureProxy> proxy = provider.refAsTextureProxy(ctx, desc, true)) {
599 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath,
600 kLockTexturePathCount);
601 set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
606 // 5. Ask the generator to return RGB(A) data, which the GPU can convert
608 if (this->tryLockAsBitmap(&bitmap, client, chint, format, cacheInfo)) {
609 sk_sp<GrTextureProxy> proxy;
611 proxy = GrGenerateMipMapsAndUploadToTextureProxy(ctx, bitmap, dstColorSpace);
614 proxy = GrUploadBitmapToTextureProxy(ctx->resourceProvider(), bitmap);
617 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath,
618 kLockTexturePathCount);
619 set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
623 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kFailure_LockTexturePath,
624 kLockTexturePathCount);
628 ///////////////////////////////////////////////////////////////////////////////////////////////////
630 sk_sp<GrTextureProxy> SkImageCacherator::lockAsTextureProxy(GrContext* ctx,
631 const GrSamplerParams& params,
632 SkColorSpace* dstColorSpace,
633 sk_sp<SkColorSpace>* texColorSpace,
634 const SkImage* client,
635 SkScalar scaleAdjust[2],
636 SkImage::CachingHint chint) {
641 return GrImageTextureMaker(ctx, this, client, chint).refTextureProxyForParams(params,