1 /*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "SkBitmap.h"
9 #include "SkBitmapCache.h"
10 #include "SkColorSpace_Base.h"
11 #include "SkImage_Base.h"
12 #include "SkImageCacherator.h"
13 #include "SkMallocPixelRef.h"
14 #include "SkNextID.h"
15 #include "SkPixelRef.h"
16 #include "SkResourceCache.h"
17
18 #if SK_SUPPORT_GPU
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"
27 #include "SkGr.h"
28 #endif
29
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
36
37 // Helper for exclusive access to a shared generator.
38 class SkImageCacherator::ScopedGenerator {
39 public:
ScopedGenerator(const sk_sp<SharedGenerator> & gen)40 ScopedGenerator(const sk_sp<SharedGenerator>& gen)
41 : fSharedGenerator(gen)
42 , fAutoAquire(gen->fMutex) {}
43
operator ->() const44 SkImageGenerator* operator->() const {
45 fSharedGenerator->fMutex.assertHeld();
46 return fSharedGenerator->fGenerator.get();
47 }
48
operator SkImageGenerator*() const49 operator SkImageGenerator*() const {
50 fSharedGenerator->fMutex.assertHeld();
51 return fSharedGenerator->fGenerator.get();
52 }
53
54 private:
55 const sk_sp<SharedGenerator>& fSharedGenerator;
56 SkAutoExclusive fAutoAquire;
57 };
58
Validator(sk_sp<SharedGenerator> gen,const SkIRect * subset)59 SkImageCacherator::Validator::Validator(sk_sp<SharedGenerator> gen, const SkIRect* subset)
60 : fSharedGenerator(std::move(gen)) {
61
62 if (!fSharedGenerator) {
63 return;
64 }
65
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();
69 if (info.isEmpty()) {
70 fSharedGenerator.reset();
71 return;
72 }
73
74 fUniqueID = fSharedGenerator->fGenerator->uniqueID();
75 const SkIRect bounds = SkIRect::MakeWH(info.width(), info.height());
76 if (subset) {
77 if (!bounds.contains(*subset)) {
78 fSharedGenerator.reset();
79 return;
80 }
81 if (*subset != bounds) {
82 // we need a different uniqueID since we really are a subset of the raw generator
83 fUniqueID = SkNextID::ImageID();
84 }
85 } else {
86 subset = &bounds;
87 }
88
89 fInfo = info.makeWH(subset->width(), subset->height());
90 fOrigin = SkIPoint::Make(subset->x(), subset->y());
91
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());
101 }
102 }
103
NewFromGenerator(std::unique_ptr<SkImageGenerator> gen,const SkIRect * subset)104 SkImageCacherator* SkImageCacherator::NewFromGenerator(std::unique_ptr<SkImageGenerator> gen,
105 const SkIRect* subset) {
106 Validator validator(SharedGenerator::Make(std::move(gen)), subset);
107
108 return validator ? new SkImageCacherator(&validator) : nullptr;
109 }
110
SkImageCacherator(Validator * validator)111 SkImageCacherator::SkImageCacherator(Validator* validator)
112 : fSharedGenerator(std::move(validator->fSharedGenerator)) // we take ownership
113 , fInfo(validator->fInfo)
114 , fOrigin(validator->fOrigin)
115 {
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;
120 }
121 SkASSERT(fSharedGenerator);
122 }
123
~SkImageCacherator()124 SkImageCacherator::~SkImageCacherator() {}
125
refEncoded(GrContext * ctx)126 SkData* SkImageCacherator::refEncoded(GrContext* ctx) {
127 ScopedGenerator generator(fSharedGenerator);
128 return generator->refEncodedData(ctx);
129 }
130
check_output_bitmap(const SkBitmap & bitmap,uint32_t expectedID)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());
135 return true;
136 }
137
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()
140 //
generateBitmap(SkBitmap * bitmap,const SkImageInfo & decodeInfo)141 bool SkImageCacherator::generateBitmap(SkBitmap* bitmap, const SkImageInfo& decodeInfo) {
142 SkBitmap::Allocator* allocator = SkResourceCache::GetAllocator();
143
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);
150 } else {
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
153
154 SkBitmap full;
155 if (!generator->tryGenerateBitmap(&full,
156 decodeInfo.makeWH(genInfo.width(), genInfo.height()),
157 allocator)) {
158 return false;
159 }
160 if (!bitmap->tryAllocPixels(decodeInfo, nullptr, full.getColorTable())) {
161 return false;
162 }
163 return full.readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(),
164 fOrigin.x(), fOrigin.y());
165 }
166 }
167
directGeneratePixels(const SkImageInfo & info,void * pixels,size_t rb,int srcX,int srcY)168 bool SkImageCacherator::directGeneratePixels(const SkImageInfo& info, void* pixels, size_t rb,
169 int srcX, int srcY) {
170 ScopedGenerator generator(fSharedGenerator);
171 const SkImageInfo& genInfo = generator->getInfo();
172 // Currently generators do not natively handle subsets, so check that first.
173 if (srcX || srcY || genInfo.width() != info.width() || genInfo.height() != info.height()) {
174 return false;
175 }
176 return generator->getPixels(info, pixels, rb);
177 }
178
179 //////////////////////////////////////////////////////////////////////////////////////////////////
180
lockAsBitmapOnlyIfAlreadyCached(SkBitmap * bitmap,CachedFormat format)181 bool SkImageCacherator::lockAsBitmapOnlyIfAlreadyCached(SkBitmap* bitmap, CachedFormat format) {
182 return kNeedNewImageUniqueID != fUniqueIDs[format] &&
183 SkBitmapCache::Find(SkBitmapCacheDesc::Make(fUniqueIDs[format],
184 fInfo.width(), fInfo.height()), bitmap) &&
185 check_output_bitmap(*bitmap, fUniqueIDs[format]);
186 }
187
tryLockAsBitmap(SkBitmap * bitmap,const SkImage * client,SkImage::CachingHint chint,CachedFormat format,const SkImageInfo & info)188 bool SkImageCacherator::tryLockAsBitmap(SkBitmap* bitmap, const SkImage* client,
189 SkImage::CachingHint chint, CachedFormat format,
190 const SkImageInfo& info) {
191 if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap, format)) {
192 return true;
193 }
194 if (!this->generateBitmap(bitmap, info)) {
195 return false;
196 }
197
198 if (kNeedNewImageUniqueID == fUniqueIDs[format]) {
199 fUniqueIDs[format] = SkNextID::ImageID();
200 }
201 bitmap->pixelRef()->setImmutableWithID(fUniqueIDs[format]);
202 if (SkImage::kAllow_CachingHint == chint) {
203 SkBitmapCache::Add(SkBitmapCacheDesc::Make(fUniqueIDs[format],
204 fInfo.width(), fInfo.height()), *bitmap);
205 if (client) {
206 as_IB(client)->notifyAddedToCache();
207 }
208 }
209 return true;
210 }
211
lockAsBitmap(GrContext * context,SkBitmap * bitmap,const SkImage * client,SkColorSpace * dstColorSpace,SkImage::CachingHint chint)212 bool SkImageCacherator::lockAsBitmap(GrContext* context, SkBitmap* bitmap, const SkImage* client,
213 SkColorSpace* dstColorSpace,
214 SkImage::CachingHint chint) {
215 CachedFormat format = this->chooseCacheFormat(dstColorSpace);
216 SkImageInfo cacheInfo = this->buildCacheInfo(format);
217
218 if (kNeedNewImageUniqueID == fUniqueIDs[format]) {
219 fUniqueIDs[format] = SkNextID::ImageID();
220 }
221
222 if (this->tryLockAsBitmap(bitmap, client, chint, format, cacheInfo)) {
223 return check_output_bitmap(*bitmap, fUniqueIDs[format]);
224 }
225
226 #if SK_SUPPORT_GPU
227 if (!context) {
228 bitmap->reset();
229 return false;
230 }
231
232 // Try to get a texture and read it back to raster (and then cache that with our ID)
233 sk_sp<GrTextureProxy> proxy;
234
235 {
236 ScopedGenerator generator(fSharedGenerator);
237 proxy = generator->generateTexture(context, cacheInfo, fOrigin);
238 }
239 if (!proxy) {
240 bitmap->reset();
241 return false;
242 }
243
244 if (!bitmap->tryAllocPixels(cacheInfo)) {
245 bitmap->reset();
246 return false;
247 }
248
249 sk_sp<GrSurfaceContext> sContext(context->contextPriv().makeWrappedSurfaceContext(
250 proxy,
251 fInfo.refColorSpace())); // src colorSpace
252 if (!sContext) {
253 bitmap->reset();
254 return false;
255 }
256
257 if (!sContext->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
258 bitmap->reset();
259 return false;
260 }
261
262 bitmap->pixelRef()->setImmutableWithID(fUniqueIDs[format]);
263 if (SkImage::kAllow_CachingHint == chint) {
264 SkBitmapCache::Add(SkBitmapCacheDesc::Make(fUniqueIDs[format],
265 fInfo.width(), fInfo.height()), *bitmap);
266 if (client) {
267 as_IB(client)->notifyAddedToCache();
268 }
269 }
270 return check_output_bitmap(*bitmap, fUniqueIDs[format]);
271 #else
272 return false;
273 #endif
274 }
275
276 //////////////////////////////////////////////////////////////////////////////////////////////////
277
278 // Abstraction of GrCaps that handles the cases where we don't have a caps pointer (because
279 // we're in raster mode), or where GPU support is entirely missing. In theory, we only need the
280 // chosen format to be texturable, but that lets us choose F16 on GLES implemenations where we
281 // won't be able to read the texture back. We'd like to ensure that SkImake::makeNonTextureImage
282 // works, so we require that the formats we choose are renderable (as a proxy for being readable).
283 struct CacheCaps {
CacheCapsCacheCaps284 CacheCaps(const GrCaps* caps) : fCaps(caps) {}
285
286 #if SK_SUPPORT_GPU
supportsHalfFloatCacheCaps287 bool supportsHalfFloat() const {
288 return !fCaps ||
289 (fCaps->isConfigTexturable(kRGBA_half_GrPixelConfig) &&
290 fCaps->isConfigRenderable(kRGBA_half_GrPixelConfig, false));
291 }
292
supportsSRGBCacheCaps293 bool supportsSRGB() const {
294 return !fCaps ||
295 (fCaps->srgbSupport() && fCaps->isConfigTexturable(kSRGBA_8888_GrPixelConfig));
296 }
297
supportsSBGRCacheCaps298 bool supportsSBGR() const {
299 return !fCaps || fCaps->srgbSupport();
300 }
301 #else
supportsHalfFloatCacheCaps302 bool supportsHalfFloat() const { return true; }
supportsSRGBCacheCaps303 bool supportsSRGB() const { return true; }
supportsSBGRCacheCaps304 bool supportsSBGR() const { return true; }
305 #endif
306
307 const GrCaps* fCaps;
308 };
309
chooseCacheFormat(SkColorSpace * dstColorSpace,const GrCaps * grCaps)310 SkImageCacherator::CachedFormat SkImageCacherator::chooseCacheFormat(SkColorSpace* dstColorSpace,
311 const GrCaps* grCaps) {
312 SkColorSpace* cs = fInfo.colorSpace();
313 if (!cs || !dstColorSpace) {
314 return kLegacy_CachedFormat;
315 }
316
317 CacheCaps caps(grCaps);
318 switch (fInfo.colorType()) {
319 case kUnknown_SkColorType:
320 case kAlpha_8_SkColorType:
321 case kRGB_565_SkColorType:
322 case kARGB_4444_SkColorType:
323 // We don't support color space on these formats, so always decode in legacy mode:
324 // TODO: Ask the codec to decode these to something else (at least sRGB 8888)?
325 return kLegacy_CachedFormat;
326
327 case kIndex_8_SkColorType:
328 // We can't draw from indexed textures with a color space, so ask the codec to expand
329 if (cs->gammaCloseToSRGB()) {
330 if (caps.supportsSRGB()) {
331 return kSRGB8888_CachedFormat;
332 } else if (caps.supportsHalfFloat()) {
333 return kLinearF16_CachedFormat;
334 } else {
335 return kLegacy_CachedFormat;
336 }
337 } else {
338 if (caps.supportsHalfFloat()) {
339 return kLinearF16_CachedFormat;
340 } else if (caps.supportsSRGB()) {
341 return kSRGB8888_CachedFormat;
342 } else {
343 return kLegacy_CachedFormat;
344 }
345 }
346
347 case kGray_8_SkColorType:
348 // TODO: What do we do with grayscale sources that have strange color spaces attached?
349 // The codecs and color space xform don't handle this correctly (yet), so drop it on
350 // the floor. (Also, inflating by a factor of 8 is going to be unfortunate).
351 // As it is, we don't directly support sRGB grayscale, so ask the codec to convert
352 // it for us. This bypasses some really sketchy code GrUploadPixmapToTexture.
353 if (cs->gammaCloseToSRGB() && caps.supportsSRGB()) {
354 return kSRGB8888_CachedFormat;
355 } else {
356 return kLegacy_CachedFormat;
357 }
358
359 case kRGBA_8888_SkColorType:
360 if (cs->gammaCloseToSRGB()) {
361 if (caps.supportsSRGB()) {
362 return kAsIs_CachedFormat;
363 } else if (caps.supportsHalfFloat()) {
364 return kLinearF16_CachedFormat;
365 } else {
366 return kLegacy_CachedFormat;
367 }
368 } else {
369 if (caps.supportsHalfFloat()) {
370 return kLinearF16_CachedFormat;
371 } else if (caps.supportsSRGB()) {
372 return kSRGB8888_CachedFormat;
373 } else {
374 return kLegacy_CachedFormat;
375 }
376 }
377
378 case kBGRA_8888_SkColorType:
379 // Odd case. sBGRA isn't a real thing, so we may not have this texturable.
380 if (caps.supportsSBGR()) {
381 if (cs->gammaCloseToSRGB()) {
382 return kAsIs_CachedFormat;
383 } else if (caps.supportsHalfFloat()) {
384 return kLinearF16_CachedFormat;
385 } else if (caps.supportsSRGB()) {
386 return kSRGB8888_CachedFormat;
387 } else {
388 // sBGRA support without sRGBA is highly unlikely (impossible?) Nevertheless.
389 return kLegacy_CachedFormat;
390 }
391 } else {
392 if (cs->gammaCloseToSRGB()) {
393 if (caps.supportsSRGB()) {
394 return kSRGB8888_CachedFormat;
395 } else if (caps.supportsHalfFloat()) {
396 return kLinearF16_CachedFormat;
397 } else {
398 return kLegacy_CachedFormat;
399 }
400 } else {
401 if (caps.supportsHalfFloat()) {
402 return kLinearF16_CachedFormat;
403 } else if (caps.supportsSRGB()) {
404 return kSRGB8888_CachedFormat;
405 } else {
406 return kLegacy_CachedFormat;
407 }
408 }
409 }
410
411 case kRGBA_F16_SkColorType:
412 if (!caps.supportsHalfFloat()) {
413 if (caps.supportsSRGB()) {
414 return kSRGB8888_CachedFormat;
415 } else {
416 return kLegacy_CachedFormat;
417 }
418 } else if (cs->gammaIsLinear()) {
419 return kAsIs_CachedFormat;
420 } else {
421 return kLinearF16_CachedFormat;
422 }
423 }
424 SkDEBUGFAIL("Unreachable");
425 return kLegacy_CachedFormat;
426 }
427
buildCacheInfo(CachedFormat format)428 SkImageInfo SkImageCacherator::buildCacheInfo(CachedFormat format) {
429 switch (format) {
430 case kLegacy_CachedFormat:
431 return fInfo.makeColorSpace(nullptr);
432 case kAsIs_CachedFormat:
433 return fInfo;
434 case kLinearF16_CachedFormat:
435 return fInfo
436 .makeColorType(kRGBA_F16_SkColorType)
437 .makeColorSpace(as_CSB(fInfo.colorSpace())->makeLinearGamma());
438 case kSRGB8888_CachedFormat:
439 return fInfo
440 .makeColorType(kRGBA_8888_SkColorType)
441 .makeColorSpace(as_CSB(fInfo.colorSpace())->makeSRGBGamma());
442 default:
443 SkDEBUGFAIL("Invalid cached format");
444 return fInfo;
445 }
446 }
447
448 //////////////////////////////////////////////////////////////////////////////////////////////////
449
450 #if SK_SUPPORT_GPU
451
makeCacheKeyFromOrigKey(const GrUniqueKey & origKey,CachedFormat format,GrUniqueKey * cacheKey)452 void SkImageCacherator::makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat format,
453 GrUniqueKey* cacheKey) {
454 SkASSERT(!cacheKey->isValid());
455 if (origKey.isValid()) {
456 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
457 GrUniqueKey::Builder builder(cacheKey, origKey, kDomain, 1);
458 builder[0] = format;
459 }
460 }
461
462 #ifdef SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
load_compressed_into_texture(GrContext * ctx,SkData * data,GrSurfaceDesc desc)463 static GrTexture* load_compressed_into_texture(GrContext* ctx, SkData* data, GrSurfaceDesc desc) {
464 const void* rawStart;
465 GrPixelConfig config = GrIsCompressedTextureDataSupported(ctx, data, desc.fWidth, desc.fHeight,
466 &rawStart);
467 if (kUnknown_GrPixelConfig == config) {
468 return nullptr;
469 }
470
471 desc.fConfig = config;
472 return ctx->resourceProvider()->createTexture(desc, SkBudgeted::kYes, rawStart, 0);
473 }
474 #endif
475
476 class Generator_GrYUVProvider : public GrYUVProvider {
477 SkImageGenerator* fGen;
478
479 public:
Generator_GrYUVProvider(SkImageGenerator * gen)480 Generator_GrYUVProvider(SkImageGenerator* gen) : fGen(gen) {}
481
onGetID()482 uint32_t onGetID() override { return fGen->uniqueID(); }
onQueryYUV8(SkYUVSizeInfo * sizeInfo,SkYUVColorSpace * colorSpace) const483 bool onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const override {
484 return fGen->queryYUV8(sizeInfo, colorSpace);
485 }
onGetYUV8Planes(const SkYUVSizeInfo & sizeInfo,void * planes[3])486 bool onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) override {
487 return fGen->getYUV8Planes(sizeInfo, planes);
488 }
489 };
490
set_key_on_proxy(GrResourceProvider * resourceProvider,GrTextureProxy * proxy,const GrUniqueKey & key)491 static void set_key_on_proxy(GrResourceProvider* resourceProvider,
492 GrTextureProxy* proxy, const GrUniqueKey& key) {
493 if (key.isValid()) {
494 resourceProvider->assignUniqueKeyToProxy(key, proxy);
495 }
496 }
497
getColorSpace(GrContext * ctx,SkColorSpace * dstColorSpace)498 sk_sp<SkColorSpace> SkImageCacherator::getColorSpace(GrContext* ctx, SkColorSpace* dstColorSpace) {
499 // TODO: This isn't always correct. Picture generator currently produces textures in N32,
500 // and will (soon) emit them in an arbitrary (destination) space. We will need to stash that
501 // information in/on the key so we can return the correct space in case #1 of lockTexture.
502 CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
503 SkImageInfo cacheInfo = this->buildCacheInfo(format);
504 return sk_ref_sp(cacheInfo.colorSpace());
505 }
506
507 /*
508 * We have a 5 ways to try to return a texture (in sorted order)
509 *
510 * 1. Check the cache for a pre-existing one
511 * 2. Ask the generator to natively create one
512 * 3. Ask the generator to return a compressed form that the GPU might support
513 * 4. Ask the generator to return YUV planes, which the GPU can convert
514 * 5. Ask the generator to return RGB(A) data, which the GPU can convert
515 */
lockTextureProxy(GrContext * ctx,const GrUniqueKey & origKey,const SkImage * client,SkImage::CachingHint chint,bool willBeMipped,SkColorSpace * dstColorSpace)516 sk_sp<GrTextureProxy> SkImageCacherator::lockTextureProxy(GrContext* ctx,
517 const GrUniqueKey& origKey,
518 const SkImage* client,
519 SkImage::CachingHint chint,
520 bool willBeMipped,
521 SkColorSpace* dstColorSpace) {
522 // Values representing the various texture lock paths we can take. Used for logging the path
523 // taken to a histogram.
524 enum LockTexturePath {
525 kFailure_LockTexturePath,
526 kPreExisting_LockTexturePath,
527 kNative_LockTexturePath,
528 kCompressed_LockTexturePath,
529 kYUV_LockTexturePath,
530 kRGBA_LockTexturePath,
531 };
532
533 enum { kLockTexturePathCount = kRGBA_LockTexturePath + 1 };
534
535 // Determine which cached format we're going to use (which may involve decoding to a different
536 // info than the generator provides).
537 CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
538
539 // Fold the cache format into our texture key
540 GrUniqueKey key;
541 this->makeCacheKeyFromOrigKey(origKey, format, &key);
542
543 // 1. Check the cache for a pre-existing one
544 if (key.isValid()) {
545 if (sk_sp<GrTextureProxy> proxy = ctx->resourceProvider()->findProxyByUniqueKey(key)) {
546 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kPreExisting_LockTexturePath,
547 kLockTexturePathCount);
548 return proxy;
549 }
550 }
551
552 // The CachedFormat is both an index for which cache "slot" we'll use to store this particular
553 // decoded variant of the encoded data, and also a recipe for how to transform the original
554 // info to get the one that we're going to decode to.
555 SkImageInfo cacheInfo = this->buildCacheInfo(format);
556
557 // 2. Ask the generator to natively create one
558 {
559 ScopedGenerator generator(fSharedGenerator);
560 if (sk_sp<GrTextureProxy> proxy = generator->generateTexture(ctx, cacheInfo, fOrigin)) {
561 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath,
562 kLockTexturePathCount);
563 set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
564 return proxy;
565 }
566 }
567
568 const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->caps());
569
570 #ifdef SK_SUPPORT_COMPRESSED_TEXTURES_IN_CACHERATOR
571 // 3. Ask the generator to return a compressed form that the GPU might support
572 sk_sp<SkData> data(this->refEncoded(ctx));
573 if (data) {
574 GrTexture* tex = load_compressed_into_texture(ctx, data, desc);
575 if (tex) {
576 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kCompressed_LockTexturePath,
577 kLockTexturePathCount);
578 return set_key_and_return(tex, key);
579 }
580 }
581 #endif
582
583 // 4. Ask the generator to return YUV planes, which the GPU can convert
584 if (!ctx->contextPriv().disableGpuYUVConversion()) {
585 ScopedGenerator generator(fSharedGenerator);
586 Generator_GrYUVProvider provider(generator);
587 if (sk_sp<GrTextureProxy> proxy = provider.refAsTextureProxy(ctx, desc, true)) {
588 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath,
589 kLockTexturePathCount);
590 set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
591 return proxy;
592 }
593 }
594
595 // 5. Ask the generator to return RGB(A) data, which the GPU can convert
596 SkBitmap bitmap;
597 if (this->tryLockAsBitmap(&bitmap, client, chint, format, cacheInfo)) {
598 sk_sp<GrTextureProxy> proxy;
599 if (willBeMipped) {
600 proxy = GrGenerateMipMapsAndUploadToTextureProxy(ctx, bitmap, dstColorSpace);
601 }
602 if (!proxy) {
603 proxy = GrUploadBitmapToTextureProxy(ctx->resourceProvider(), bitmap);
604 }
605 if (proxy) {
606 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath,
607 kLockTexturePathCount);
608 set_key_on_proxy(ctx->resourceProvider(), proxy.get(), key);
609 return proxy;
610 }
611 }
612 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kFailure_LockTexturePath,
613 kLockTexturePathCount);
614 return nullptr;
615 }
616
617 ///////////////////////////////////////////////////////////////////////////////////////////////////
618
lockAsTextureProxy(GrContext * ctx,const GrSamplerParams & params,SkColorSpace * dstColorSpace,sk_sp<SkColorSpace> * texColorSpace,const SkImage * client,SkScalar scaleAdjust[2],SkImage::CachingHint chint)619 sk_sp<GrTextureProxy> SkImageCacherator::lockAsTextureProxy(GrContext* ctx,
620 const GrSamplerParams& params,
621 SkColorSpace* dstColorSpace,
622 sk_sp<SkColorSpace>* texColorSpace,
623 const SkImage* client,
624 SkScalar scaleAdjust[2],
625 SkImage::CachingHint chint) {
626 if (!ctx) {
627 return nullptr;
628 }
629
630 return GrImageTextureMaker(ctx, this, client, chint).refTextureProxyForParams(params,
631 dstColorSpace,
632 texColorSpace,
633 scaleAdjust);
634 }
635
636 #endif
637