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 "SkImage_Base.h"
9 #include "SkImageCacherator.h"
10
11 #include "SkBitmap.h"
12 #include "SkBitmapCache.h"
13 #include "SkData.h"
14 #include "SkImageGenerator.h"
15 #include "SkImagePriv.h"
16 #include "SkNextID.h"
17 #include "SkPixelRef.h"
18
19 #if SK_SUPPORT_GPU
20 #include "GrContext.h"
21 #include "GrContextPriv.h"
22 #include "GrGpuResourcePriv.h"
23 #include "GrImageTextureMaker.h"
24 #include "GrResourceKey.h"
25 #include "GrProxyProvider.h"
26 #include "GrSamplerState.h"
27 #include "GrYUVProvider.h"
28 #include "SkGr.h"
29 #endif
30
31 // Ref-counted tuple(SkImageGenerator, SkMutex) which allows sharing one generator among N images
32 class SharedGenerator final : public SkNVRefCnt<SharedGenerator> {
33 public:
Make(std::unique_ptr<SkImageGenerator> gen)34 static sk_sp<SharedGenerator> Make(std::unique_ptr<SkImageGenerator> gen) {
35 return gen ? sk_sp<SharedGenerator>(new SharedGenerator(std::move(gen))) : nullptr;
36 }
37
38 // This is thread safe. It is a const field set in the constructor.
getInfo()39 const SkImageInfo& getInfo() { return fGenerator->getInfo(); }
40
41 private:
SharedGenerator(std::unique_ptr<SkImageGenerator> gen)42 explicit SharedGenerator(std::unique_ptr<SkImageGenerator> gen)
43 : fGenerator(std::move(gen)) {
44 SkASSERT(fGenerator);
45 }
46
47 friend class ScopedGenerator;
48 friend class SkImage_Lazy;
49
50 std::unique_ptr<SkImageGenerator> fGenerator;
51 SkMutex fMutex;
52 };
53
54 class SkImage_Lazy : public SkImage_Base, public SkImageCacherator {
55 public:
56 struct Validator {
57 Validator(sk_sp<SharedGenerator>, const SkIRect* subset, sk_sp<SkColorSpace> colorSpace);
58
operator boolSkImage_Lazy::Validator59 operator bool() const { return fSharedGenerator.get(); }
60
61 sk_sp<SharedGenerator> fSharedGenerator;
62 SkImageInfo fInfo;
63 SkIPoint fOrigin;
64 sk_sp<SkColorSpace> fColorSpace;
65 uint32_t fUniqueID;
66 };
67
68 SkImage_Lazy(Validator* validator);
69
onImageInfo() const70 SkImageInfo onImageInfo() const override {
71 return fInfo;
72 }
onAlphaType() const73 SkAlphaType onAlphaType() const override {
74 return fInfo.alphaType();
75 }
76
77 bool onReadPixels(const SkImageInfo&, void*, size_t, int srcX, int srcY,
78 CachingHint) const override;
79 #if SK_SUPPORT_GPU
80 sk_sp<GrTextureProxy> asTextureProxyRef(GrContext*,
81 const GrSamplerState&, SkColorSpace*,
82 sk_sp<SkColorSpace>*,
83 SkScalar scaleAdjust[2]) const override;
84 #endif
85 SkData* onRefEncoded() const override;
86 sk_sp<SkImage> onMakeSubset(const SkIRect&) const override;
87 bool getROPixels(SkBitmap*, SkColorSpace* dstColorSpace, CachingHint) const override;
onIsLazyGenerated() const88 bool onIsLazyGenerated() const override { return true; }
89 bool onCanLazyGenerateOnGPU() const override;
90 sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>, SkColorType,
91 SkTransferFunctionBehavior) const override;
92
93 bool onIsValid(GrContext*) const override;
94
peekCacherator() const95 SkImageCacherator* peekCacherator() const override {
96 return const_cast<SkImage_Lazy*>(this);
97 }
98
99 // Only return true if the generate has already been cached.
100 bool lockAsBitmapOnlyIfAlreadyCached(SkBitmap*, CachedFormat) const;
101 // Call the underlying generator directly
102 bool directGeneratePixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
103 int srcX, int srcY, SkTransferFunctionBehavior behavior) const;
104
105 // SkImageCacherator interface
106 #if SK_SUPPORT_GPU
107 // Returns the texture proxy. If the cacherator is generating the texture and wants to cache it,
108 // it should use the passed in key (if the key is valid).
109 sk_sp<GrTextureProxy> lockTextureProxy(GrContext*,
110 const GrUniqueKey& key,
111 SkImage::CachingHint,
112 bool willBeMipped,
113 SkColorSpace* dstColorSpace,
114 GrTextureMaker::AllowedTexGenType genType) override;
115
116 // Returns the color space of the texture that would be returned if you called lockTexture.
117 // Separate code path to allow querying of the color space for textures that cached (even
118 // externally).
119 sk_sp<SkColorSpace> getColorSpace(GrContext*, SkColorSpace* dstColorSpace) override;
120 void makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat,
121 GrUniqueKey* cacheKey) override;
122 #endif
123
124 CachedFormat chooseCacheFormat(SkColorSpace* dstColorSpace,
125 const GrCaps* = nullptr) const override;
126 SkImageInfo buildCacheInfo(CachedFormat) const override;
127
128 private:
129 class ScopedGenerator;
130
131 /**
132 * On success (true), bitmap will point to the pixels for this generator. If this returns
133 * false, the bitmap will be reset to empty.
134 */
135 bool lockAsBitmap(SkBitmap*, SkImage::CachingHint, CachedFormat, const SkImageInfo&,
136 SkTransferFunctionBehavior) const;
137
138 /**
139 * Populates parameters to pass to the generator for reading pixels or generating a texture.
140 * For image generators, legacy versus true color blending is indicated using a
141 * SkTransferFunctionBehavior, and the target color space is specified on the SkImageInfo.
142 * If generatorImageInfo has no color space set, set its color space to this SkImage's color
143 * space, and return "ignore" behavior, indicating legacy mode. If generatorImageInfo has a
144 * color space set, return "respect" behavior, indicating linear blending mode.
145 */
146 SkTransferFunctionBehavior getGeneratorBehaviorAndInfo(SkImageInfo* generatorImageInfo) const;
147
148 sk_sp<SharedGenerator> fSharedGenerator;
149 // Note that fInfo is not necessarily the info from the generator. It may be cropped by
150 // onMakeSubset and its color space may be changed by onMakeColorSpace.
151 const SkImageInfo fInfo;
152 const SkIPoint fOrigin;
153
154 struct IDRec {
155 SkOnce fOnce;
156 uint32_t fUniqueID;
157 };
158 mutable IDRec fIDRecs[kNumCachedFormats];
159
160 uint32_t getUniqueID(CachedFormat) const;
161
162 // Repeated calls to onMakeColorSpace will result in a proliferation of unique IDs and
163 // SkImage_Lazy instances. Cache the result of the last successful onMakeColorSpace call.
164 mutable SkMutex fOnMakeColorSpaceMutex;
165 mutable sk_sp<SkColorSpace> fOnMakeColorSpaceTarget;
166 mutable sk_sp<SkImage> fOnMakeColorSpaceResult;
167
168 typedef SkImage_Base INHERITED;
169 };
170
171 ///////////////////////////////////////////////////////////////////////////////
172
Validator(sk_sp<SharedGenerator> gen,const SkIRect * subset,sk_sp<SkColorSpace> colorSpace)173 SkImage_Lazy::Validator::Validator(sk_sp<SharedGenerator> gen, const SkIRect* subset,
174 sk_sp<SkColorSpace> colorSpace)
175 : fSharedGenerator(std::move(gen)) {
176 if (!fSharedGenerator) {
177 return;
178 }
179
180 // The following generator accessors are safe without acquiring the mutex (const getters).
181 // TODO: refactor to use a ScopedGenerator instead, for clarity.
182 const SkImageInfo& info = fSharedGenerator->fGenerator->getInfo();
183 if (info.isEmpty()) {
184 fSharedGenerator.reset();
185 return;
186 }
187
188 fUniqueID = fSharedGenerator->fGenerator->uniqueID();
189 const SkIRect bounds = SkIRect::MakeWH(info.width(), info.height());
190 if (subset) {
191 if (!bounds.contains(*subset)) {
192 fSharedGenerator.reset();
193 return;
194 }
195 if (*subset != bounds) {
196 // we need a different uniqueID since we really are a subset of the raw generator
197 fUniqueID = SkNextID::ImageID();
198 }
199 } else {
200 subset = &bounds;
201 }
202
203 fInfo = info.makeWH(subset->width(), subset->height());
204 fOrigin = SkIPoint::Make(subset->x(), subset->y());
205 if (colorSpace) {
206 fInfo = fInfo.makeColorSpace(colorSpace);
207 fUniqueID = SkNextID::ImageID();
208 }
209 }
210
211 ///////////////////////////////////////////////////////////////////////////////
212
213 // Helper for exclusive access to a shared generator.
214 class SkImage_Lazy::ScopedGenerator {
215 public:
ScopedGenerator(const sk_sp<SharedGenerator> & gen)216 ScopedGenerator(const sk_sp<SharedGenerator>& gen)
217 : fSharedGenerator(gen)
218 , fAutoAquire(gen->fMutex) {}
219
operator ->() const220 SkImageGenerator* operator->() const {
221 fSharedGenerator->fMutex.assertHeld();
222 return fSharedGenerator->fGenerator.get();
223 }
224
operator SkImageGenerator*() const225 operator SkImageGenerator*() const {
226 fSharedGenerator->fMutex.assertHeld();
227 return fSharedGenerator->fGenerator.get();
228 }
229
230 private:
231 const sk_sp<SharedGenerator>& fSharedGenerator;
232 SkAutoExclusive fAutoAquire;
233 };
234
235 ///////////////////////////////////////////////////////////////////////////////
236
SkImage_Lazy(Validator * validator)237 SkImage_Lazy::SkImage_Lazy(Validator* validator)
238 : INHERITED(validator->fInfo.width(), validator->fInfo.height(), validator->fUniqueID)
239 , fSharedGenerator(std::move(validator->fSharedGenerator))
240 , fInfo(validator->fInfo)
241 , fOrigin(validator->fOrigin) {
242 SkASSERT(fSharedGenerator);
243 // We explicit set the legacy format slot, but leave the others uninitialized (via SkOnce)
244 // and only resolove them to IDs as needed (by calling getUniqueID()).
245 fIDRecs[kLegacy_CachedFormat].fOnce([this, validator] {
246 fIDRecs[kLegacy_CachedFormat].fUniqueID = validator->fUniqueID;
247 });
248 }
249
getUniqueID(CachedFormat format) const250 uint32_t SkImage_Lazy::getUniqueID(CachedFormat format) const {
251 IDRec* rec = &fIDRecs[format];
252 rec->fOnce([rec] {
253 rec->fUniqueID = SkNextID::ImageID();
254 });
255 return rec->fUniqueID;
256 }
257
258 //////////////////////////////////////////////////////////////////////////////////////////////////
259
260 // Abstraction of GrCaps that handles the cases where we don't have a caps pointer (because
261 // we're in raster mode), or where GPU support is entirely missing. In theory, we only need the
262 // chosen format to be texturable, but that lets us choose F16 on GLES implemenations where we
263 // won't be able to read the texture back. We'd like to ensure that SkImake::makeNonTextureImage
264 // works, so we require that the formats we choose are renderable (as a proxy for being readable).
265 struct CacheCaps {
CacheCapsCacheCaps266 CacheCaps(const GrCaps* caps) : fCaps(caps) {}
267
268 #if SK_SUPPORT_GPU
supportsHalfFloatCacheCaps269 bool supportsHalfFloat() const {
270 return !fCaps ||
271 (fCaps->isConfigTexturable(kRGBA_half_GrPixelConfig) &&
272 fCaps->isConfigRenderable(kRGBA_half_GrPixelConfig, false));
273 }
274
supportsSRGBCacheCaps275 bool supportsSRGB() const {
276 return !fCaps ||
277 (fCaps->srgbSupport() && fCaps->isConfigTexturable(kSRGBA_8888_GrPixelConfig));
278 }
279
supportsSBGRCacheCaps280 bool supportsSBGR() const {
281 return !fCaps || fCaps->srgbSupport();
282 }
283 #else
supportsHalfFloatCacheCaps284 bool supportsHalfFloat() const { return true; }
supportsSRGBCacheCaps285 bool supportsSRGB() const { return true; }
supportsSBGRCacheCaps286 bool supportsSBGR() const { return true; }
287 #endif
288
289 const GrCaps* fCaps;
290 };
291
chooseCacheFormat(SkColorSpace * dstColorSpace,const GrCaps * grCaps) const292 SkImageCacherator::CachedFormat SkImage_Lazy::chooseCacheFormat(SkColorSpace* dstColorSpace,
293 const GrCaps* grCaps) const {
294 SkColorSpace* cs = fInfo.colorSpace();
295 if (!cs || !dstColorSpace) {
296 return kLegacy_CachedFormat;
297 }
298
299 CacheCaps caps(grCaps);
300 switch (fInfo.colorType()) {
301 case kUnknown_SkColorType:
302 case kAlpha_8_SkColorType:
303 case kRGB_565_SkColorType:
304 case kARGB_4444_SkColorType:
305 case kRGB_888x_SkColorType:
306 case kRGBA_1010102_SkColorType:
307 case kRGB_101010x_SkColorType:
308 // We don't support color space on these formats, so always decode in legacy mode:
309 // TODO: Ask the codec to decode these to something else (at least sRGB 8888)?
310 return kLegacy_CachedFormat;
311
312 case kGray_8_SkColorType:
313 // TODO: What do we do with grayscale sources that have strange color spaces attached?
314 // The codecs and color space xform don't handle this correctly (yet), so drop it on
315 // the floor. (Also, inflating by a factor of 8 is going to be unfortunate).
316 // As it is, we don't directly support sRGB grayscale, so ask the codec to convert
317 // it for us. This bypasses some really sketchy code GrUploadPixmapToTexture.
318 if (cs->gammaCloseToSRGB() && caps.supportsSRGB()) {
319 return kSRGB8888_CachedFormat;
320 } else {
321 return kLegacy_CachedFormat;
322 }
323
324 case kRGBA_8888_SkColorType:
325 if (cs->gammaCloseToSRGB()) {
326 if (caps.supportsSRGB()) {
327 return kSRGB8888_CachedFormat;
328 } else if (caps.supportsHalfFloat()) {
329 return kLinearF16_CachedFormat;
330 } else {
331 return kLegacy_CachedFormat;
332 }
333 } else {
334 if (caps.supportsHalfFloat()) {
335 return kLinearF16_CachedFormat;
336 } else if (caps.supportsSRGB()) {
337 return kSRGB8888_CachedFormat;
338 } else {
339 return kLegacy_CachedFormat;
340 }
341 }
342
343 case kBGRA_8888_SkColorType:
344 // Odd case. sBGRA isn't a real thing, so we may not have this texturable.
345 if (caps.supportsSBGR()) {
346 if (cs->gammaCloseToSRGB()) {
347 return kSBGR8888_CachedFormat;
348 } else if (caps.supportsHalfFloat()) {
349 return kLinearF16_CachedFormat;
350 } else if (caps.supportsSRGB()) {
351 return kSRGB8888_CachedFormat;
352 } else {
353 // sBGRA support without sRGBA is highly unlikely (impossible?) Nevertheless.
354 return kLegacy_CachedFormat;
355 }
356 } else {
357 if (cs->gammaCloseToSRGB()) {
358 if (caps.supportsSRGB()) {
359 return kSRGB8888_CachedFormat;
360 } else if (caps.supportsHalfFloat()) {
361 return kLinearF16_CachedFormat;
362 } else {
363 return kLegacy_CachedFormat;
364 }
365 } else {
366 if (caps.supportsHalfFloat()) {
367 return kLinearF16_CachedFormat;
368 } else if (caps.supportsSRGB()) {
369 return kSRGB8888_CachedFormat;
370 } else {
371 return kLegacy_CachedFormat;
372 }
373 }
374 }
375
376 case kRGBA_F16_SkColorType:
377 if (caps.supportsHalfFloat()) {
378 return kLinearF16_CachedFormat;
379 } else if (caps.supportsSRGB()) {
380 return kSRGB8888_CachedFormat;
381 } else {
382 return kLegacy_CachedFormat;
383 }
384 }
385 SkDEBUGFAIL("Unreachable");
386 return kLegacy_CachedFormat;
387 }
388
buildCacheInfo(CachedFormat format) const389 SkImageInfo SkImage_Lazy::buildCacheInfo(CachedFormat format) const {
390 switch (format) {
391 case kLegacy_CachedFormat:
392 return fInfo.makeColorSpace(nullptr);
393 case kLinearF16_CachedFormat:
394 return fInfo.makeColorType(kRGBA_F16_SkColorType)
395 .makeColorSpace(fInfo.colorSpace()->makeLinearGamma());
396 case kSRGB8888_CachedFormat:
397 // If the transfer function is nearly (but not exactly) sRGB, we don't want the codec
398 // to bother trans-coding. It would be slow, and do more harm than good visually,
399 // so we make sure to leave the colorspace as-is.
400 if (fInfo.colorSpace()->gammaCloseToSRGB()) {
401 return fInfo.makeColorType(kRGBA_8888_SkColorType);
402 } else {
403 return fInfo.makeColorType(kRGBA_8888_SkColorType)
404 .makeColorSpace(fInfo.colorSpace()->makeSRGBGamma());
405 }
406 case kSBGR8888_CachedFormat:
407 // See note above about not-quite-sRGB transfer functions.
408 if (fInfo.colorSpace()->gammaCloseToSRGB()) {
409 return fInfo.makeColorType(kBGRA_8888_SkColorType);
410 } else {
411 return fInfo.makeColorType(kBGRA_8888_SkColorType)
412 .makeColorSpace(fInfo.colorSpace()->makeSRGBGamma());
413 }
414 default:
415 SkDEBUGFAIL("Invalid cached format");
416 return fInfo;
417 }
418 }
419
420 //////////////////////////////////////////////////////////////////////////////////////////////////
421
check_output_bitmap(const SkBitmap & bitmap,uint32_t expectedID)422 static bool check_output_bitmap(const SkBitmap& bitmap, uint32_t expectedID) {
423 SkASSERT(bitmap.getGenerationID() == expectedID);
424 SkASSERT(bitmap.isImmutable());
425 SkASSERT(bitmap.getPixels());
426 return true;
427 }
428
directGeneratePixels(const SkImageInfo & info,void * pixels,size_t rb,int srcX,int srcY,SkTransferFunctionBehavior behavior) const429 bool SkImage_Lazy::directGeneratePixels(const SkImageInfo& info, void* pixels, size_t rb,
430 int srcX, int srcY,
431 SkTransferFunctionBehavior behavior) const {
432 ScopedGenerator generator(fSharedGenerator);
433 const SkImageInfo& genInfo = generator->getInfo();
434 // Currently generators do not natively handle subsets, so check that first.
435 if (srcX || srcY || genInfo.width() != info.width() || genInfo.height() != info.height()) {
436 return false;
437 }
438
439 SkImageGenerator::Options opts;
440 // TODO: This should respect the behavior argument.
441 opts.fBehavior = SkTransferFunctionBehavior::kIgnore;
442 return generator->getPixels(info, pixels, rb, &opts);
443 }
444
445 //////////////////////////////////////////////////////////////////////////////////////////////////
446
lockAsBitmapOnlyIfAlreadyCached(SkBitmap * bitmap,CachedFormat format) const447 bool SkImage_Lazy::lockAsBitmapOnlyIfAlreadyCached(SkBitmap* bitmap, CachedFormat format) const {
448 uint32_t uniqueID = this->getUniqueID(format);
449 return SkBitmapCache::Find(SkBitmapCacheDesc::Make(uniqueID,
450 fInfo.width(), fInfo.height()), bitmap) &&
451 check_output_bitmap(*bitmap, uniqueID);
452 }
453
generate_pixels(SkImageGenerator * gen,const SkPixmap & pmap,int originX,int originY,SkTransferFunctionBehavior behavior)454 static bool generate_pixels(SkImageGenerator* gen, const SkPixmap& pmap, int originX, int originY,
455 SkTransferFunctionBehavior behavior) {
456 const int genW = gen->getInfo().width();
457 const int genH = gen->getInfo().height();
458 const SkIRect srcR = SkIRect::MakeWH(genW, genH);
459 const SkIRect dstR = SkIRect::MakeXYWH(originX, originY, pmap.width(), pmap.height());
460 if (!srcR.contains(dstR)) {
461 return false;
462 }
463
464 // If they are requesting a subset, we have to have a temp allocation for full image, and
465 // then copy the subset into their allocation
466 SkBitmap full;
467 SkPixmap fullPM;
468 const SkPixmap* dstPM = &pmap;
469 if (srcR != dstR) {
470 if (!full.tryAllocPixels(pmap.info().makeWH(genW, genH))) {
471 return false;
472 }
473 if (!full.peekPixels(&fullPM)) {
474 return false;
475 }
476 dstPM = &fullPM;
477 }
478
479 SkImageGenerator::Options opts;
480 opts.fBehavior = behavior;
481 if (!gen->getPixels(dstPM->info(), dstPM->writable_addr(), dstPM->rowBytes(), &opts)) {
482 return false;
483 }
484
485 if (srcR != dstR) {
486 if (!full.readPixels(pmap, originX, originY)) {
487 return false;
488 }
489 }
490 return true;
491 }
492
lockAsBitmap(SkBitmap * bitmap,SkImage::CachingHint chint,CachedFormat format,const SkImageInfo & info,SkTransferFunctionBehavior behavior) const493 bool SkImage_Lazy::lockAsBitmap(SkBitmap* bitmap, SkImage::CachingHint chint, CachedFormat format,
494 const SkImageInfo& info,
495 SkTransferFunctionBehavior behavior) const {
496 if (this->lockAsBitmapOnlyIfAlreadyCached(bitmap, format)) {
497 return true;
498 }
499
500 uint32_t uniqueID = this->getUniqueID(format);
501
502 SkBitmap tmpBitmap;
503 SkBitmapCache::RecPtr cacheRec;
504 SkPixmap pmap;
505 if (SkImage::kAllow_CachingHint == chint) {
506 auto desc = SkBitmapCacheDesc::Make(uniqueID, info.width(), info.height());
507 cacheRec = SkBitmapCache::Alloc(desc, info, &pmap);
508 if (!cacheRec) {
509 return false;
510 }
511 } else {
512 if (!tmpBitmap.tryAllocPixels(info)) {
513 return false;
514 }
515 if (!tmpBitmap.peekPixels(&pmap)) {
516 return false;
517 }
518 }
519
520 ScopedGenerator generator(fSharedGenerator);
521 if (!generate_pixels(generator, pmap, fOrigin.x(), fOrigin.y(), behavior)) {
522 return false;
523 }
524
525 if (cacheRec) {
526 SkBitmapCache::Add(std::move(cacheRec), bitmap);
527 SkASSERT(bitmap->getPixels()); // we're locked
528 SkASSERT(bitmap->isImmutable());
529 SkASSERT(bitmap->getGenerationID() == uniqueID);
530 this->notifyAddedToCache();
531 } else {
532 *bitmap = tmpBitmap;
533 bitmap->pixelRef()->setImmutableWithID(uniqueID);
534 }
535
536 check_output_bitmap(*bitmap, uniqueID);
537 return true;
538 }
539
540 //////////////////////////////////////////////////////////////////////////////////////////////////
541
onReadPixels(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRB,int srcX,int srcY,CachingHint chint) const542 bool SkImage_Lazy::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
543 int srcX, int srcY, CachingHint chint) const {
544 SkColorSpace* dstColorSpace = dstInfo.colorSpace();
545 SkBitmap bm;
546 if (kDisallow_CachingHint == chint) {
547 CachedFormat cacheFormat = this->chooseCacheFormat(dstColorSpace);
548 SkImageInfo genPixelsInfo = dstInfo;
549 SkTransferFunctionBehavior behavior = getGeneratorBehaviorAndInfo(&genPixelsInfo);
550 if (this->lockAsBitmapOnlyIfAlreadyCached(&bm, cacheFormat)) {
551 return bm.readPixels(dstInfo, dstPixels, dstRB, srcX, srcY);
552 } else {
553 // Try passing the caller's buffer directly down to the generator. If this fails we
554 // may still succeed in the general case, as the generator may prefer some other
555 // config, which we could then convert via SkBitmap::readPixels.
556 if (this->directGeneratePixels(genPixelsInfo, dstPixels, dstRB, srcX, srcY, behavior)) {
557 return true;
558 }
559 // else fall through
560 }
561 }
562
563 if (this->getROPixels(&bm, dstColorSpace, chint)) {
564 return bm.readPixels(dstInfo, dstPixels, dstRB, srcX, srcY);
565 }
566 return false;
567 }
568
onRefEncoded() const569 SkData* SkImage_Lazy::onRefEncoded() const {
570 ScopedGenerator generator(fSharedGenerator);
571 return generator->refEncodedData();
572 }
573
getROPixels(SkBitmap * bitmap,SkColorSpace * dstColorSpace,CachingHint chint) const574 bool SkImage_Lazy::getROPixels(SkBitmap* bitmap, SkColorSpace* dstColorSpace,
575 CachingHint chint) const {
576 CachedFormat cacheFormat = this->chooseCacheFormat(dstColorSpace);
577 const SkImageInfo cacheInfo = this->buildCacheInfo(cacheFormat);
578 SkImageInfo genPixelsInfo = cacheInfo;
579 SkTransferFunctionBehavior behavior = getGeneratorBehaviorAndInfo(&genPixelsInfo);
580 return this->lockAsBitmap(bitmap, chint, cacheFormat, genPixelsInfo, behavior);
581 }
582
onIsValid(GrContext * context) const583 bool SkImage_Lazy::onIsValid(GrContext* context) const {
584 ScopedGenerator generator(fSharedGenerator);
585 return generator->isValid(context);
586 }
587
onCanLazyGenerateOnGPU() const588 bool SkImage_Lazy::onCanLazyGenerateOnGPU() const {
589 #if SK_SUPPORT_GPU
590 ScopedGenerator generator(fSharedGenerator);
591 return SkImageGenerator::TexGenType::kNone != generator->onCanGenerateTexture();
592 #else
593 return false;
594 #endif
595 }
596
getGeneratorBehaviorAndInfo(SkImageInfo * generatorImageInfo) const597 SkTransferFunctionBehavior SkImage_Lazy::getGeneratorBehaviorAndInfo(SkImageInfo* generatorImageInfo) const {
598 if (generatorImageInfo->colorSpace()) {
599 return SkTransferFunctionBehavior::kRespect;
600 }
601 // Only specify an output color space if color conversion can be done on the color type.
602 switch (generatorImageInfo->colorType()) {
603 case kRGBA_8888_SkColorType:
604 case kBGRA_8888_SkColorType:
605 case kRGBA_F16_SkColorType:
606 case kRGB_565_SkColorType:
607 *generatorImageInfo = generatorImageInfo->makeColorSpace(fInfo.refColorSpace());
608 break;
609 default:
610 break;
611 }
612 return SkTransferFunctionBehavior::kIgnore;
613 }
614
615 ///////////////////////////////////////////////////////////////////////////////////////////////////
616
617 #if SK_SUPPORT_GPU
asTextureProxyRef(GrContext * context,const GrSamplerState & params,SkColorSpace * dstColorSpace,sk_sp<SkColorSpace> * texColorSpace,SkScalar scaleAdjust[2]) const618 sk_sp<GrTextureProxy> SkImage_Lazy::asTextureProxyRef(GrContext* context,
619 const GrSamplerState& params,
620 SkColorSpace* dstColorSpace,
621 sk_sp<SkColorSpace>* texColorSpace,
622 SkScalar scaleAdjust[2]) const {
623 if (!context) {
624 return nullptr;
625 }
626
627 GrImageTextureMaker textureMaker(context, this, kAllow_CachingHint);
628 return textureMaker.refTextureProxyForParams(params, dstColorSpace, texColorSpace, scaleAdjust);
629 }
630 #endif
631
onMakeSubset(const SkIRect & subset) const632 sk_sp<SkImage> SkImage_Lazy::onMakeSubset(const SkIRect& subset) const {
633 SkASSERT(fInfo.bounds().contains(subset));
634 SkASSERT(fInfo.bounds() != subset);
635
636 const SkIRect generatorSubset = subset.makeOffset(fOrigin.x(), fOrigin.y());
637 Validator validator(fSharedGenerator, &generatorSubset, fInfo.refColorSpace());
638 return validator ? sk_sp<SkImage>(new SkImage_Lazy(&validator)) : nullptr;
639 }
640
onMakeColorSpace(sk_sp<SkColorSpace> target,SkColorType targetColorType,SkTransferFunctionBehavior premulBehavior) const641 sk_sp<SkImage> SkImage_Lazy::onMakeColorSpace(sk_sp<SkColorSpace> target,
642 SkColorType targetColorType,
643 SkTransferFunctionBehavior premulBehavior) const {
644 SkAutoExclusive autoAquire(fOnMakeColorSpaceMutex);
645 if (target && fOnMakeColorSpaceTarget &&
646 SkColorSpace::Equals(target.get(), fOnMakeColorSpaceTarget.get())) {
647 return fOnMakeColorSpaceResult;
648 }
649 const SkIRect generatorSubset =
650 SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), fInfo.width(), fInfo.height());
651 Validator validator(fSharedGenerator, &generatorSubset, target);
652 sk_sp<SkImage> result = validator ? sk_sp<SkImage>(new SkImage_Lazy(&validator)) : nullptr;
653 if (result) {
654 fOnMakeColorSpaceTarget = target;
655 fOnMakeColorSpaceResult = result;
656 }
657 return result;
658 }
659
MakeFromGenerator(std::unique_ptr<SkImageGenerator> generator,const SkIRect * subset)660 sk_sp<SkImage> SkImage::MakeFromGenerator(std::unique_ptr<SkImageGenerator> generator,
661 const SkIRect* subset) {
662 SkImage_Lazy::Validator validator(SharedGenerator::Make(std::move(generator)), subset, nullptr);
663
664 return validator ? sk_make_sp<SkImage_Lazy>(&validator) : nullptr;
665 }
666
667 //////////////////////////////////////////////////////////////////////////////////////////////////
668
669 /**
670 * Implementation of SkImageCacherator interface, as needed by GrImageTextureMaker
671 */
672
673 #if SK_SUPPORT_GPU
674
makeCacheKeyFromOrigKey(const GrUniqueKey & origKey,CachedFormat format,GrUniqueKey * cacheKey)675 void SkImage_Lazy::makeCacheKeyFromOrigKey(const GrUniqueKey& origKey, CachedFormat format,
676 GrUniqueKey* cacheKey) {
677 SkASSERT(!cacheKey->isValid());
678 if (origKey.isValid()) {
679 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
680 GrUniqueKey::Builder builder(cacheKey, origKey, kDomain, 1);
681 builder[0] = format;
682 }
683 }
684
685 class Generator_GrYUVProvider : public GrYUVProvider {
686 SkImageGenerator* fGen;
687
688 public:
Generator_GrYUVProvider(SkImageGenerator * gen)689 Generator_GrYUVProvider(SkImageGenerator* gen) : fGen(gen) {}
690
onGetID()691 uint32_t onGetID() override { return fGen->uniqueID(); }
onQueryYUV8(SkYUVSizeInfo * sizeInfo,SkYUVColorSpace * colorSpace) const692 bool onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const override {
693 return fGen->queryYUV8(sizeInfo, colorSpace);
694 }
onGetYUV8Planes(const SkYUVSizeInfo & sizeInfo,void * planes[3])695 bool onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) override {
696 return fGen->getYUV8Planes(sizeInfo, planes);
697 }
698 };
699
set_key_on_proxy(GrProxyProvider * proxyProvider,GrTextureProxy * proxy,GrTextureProxy * originalProxy,const GrUniqueKey & key)700 static void set_key_on_proxy(GrProxyProvider* proxyProvider,
701 GrTextureProxy* proxy, GrTextureProxy* originalProxy,
702 const GrUniqueKey& key) {
703 if (key.isValid()) {
704 SkASSERT(proxy->origin() == kTopLeft_GrSurfaceOrigin);
705 if (originalProxy) {
706 SkASSERT(GrMipMapped::kYes == proxy->mipMapped() &&
707 GrMipMapped::kNo == originalProxy->mipMapped());
708 // If we had an originalProxy, that means there already is a proxy in the cache which
709 // matches the key, but it does not have mip levels and we require them. Thus we must
710 // remove the unique key from that proxy.
711 proxyProvider->removeUniqueKeyFromProxy(key, originalProxy);
712 }
713 proxyProvider->assignUniqueKeyToProxy(key, proxy);
714 }
715 }
716
getColorSpace(GrContext * ctx,SkColorSpace * dstColorSpace)717 sk_sp<SkColorSpace> SkImage_Lazy::getColorSpace(GrContext* ctx, SkColorSpace* dstColorSpace) {
718 if (!dstColorSpace) {
719 // In legacy mode, we do no modification to the image's color space or encoding.
720 // Subsequent legacy drawing is likely to ignore the color space, but some clients
721 // may want to know what space the image data is in, so return it.
722 return fInfo.refColorSpace();
723 } else {
724 CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
725 SkImageInfo cacheInfo = this->buildCacheInfo(format);
726 return cacheInfo.refColorSpace();
727 }
728 }
729
730 /*
731 * We have 4 ways to try to return a texture (in sorted order)
732 *
733 * 1. Check the cache for a pre-existing one
734 * 2. Ask the generator to natively create one
735 * 3. Ask the generator to return YUV planes, which the GPU can convert
736 * 4. Ask the generator to return RGB(A) data, which the GPU can convert
737 */
lockTextureProxy(GrContext * ctx,const GrUniqueKey & origKey,SkImage::CachingHint chint,bool willBeMipped,SkColorSpace * dstColorSpace,GrTextureMaker::AllowedTexGenType genType)738 sk_sp<GrTextureProxy> SkImage_Lazy::lockTextureProxy(GrContext* ctx,
739 const GrUniqueKey& origKey,
740 SkImage::CachingHint chint,
741 bool willBeMipped,
742 SkColorSpace* dstColorSpace,
743 GrTextureMaker::AllowedTexGenType genType) {
744 // Values representing the various texture lock paths we can take. Used for logging the path
745 // taken to a histogram.
746 enum LockTexturePath {
747 kFailure_LockTexturePath,
748 kPreExisting_LockTexturePath,
749 kNative_LockTexturePath,
750 kCompressed_LockTexturePath, // Deprecated
751 kYUV_LockTexturePath,
752 kRGBA_LockTexturePath,
753 };
754
755 enum { kLockTexturePathCount = kRGBA_LockTexturePath + 1 };
756
757 // Determine which cached format we're going to use (which may involve decoding to a different
758 // info than the generator provides).
759 CachedFormat format = this->chooseCacheFormat(dstColorSpace, ctx->caps());
760
761 // Fold the cache format into our texture key
762 GrUniqueKey key;
763 this->makeCacheKeyFromOrigKey(origKey, format, &key);
764
765 GrProxyProvider* proxyProvider = ctx->contextPriv().proxyProvider();
766 sk_sp<GrTextureProxy> proxy;
767
768 // 1. Check the cache for a pre-existing one
769 if (key.isValid()) {
770 proxy = proxyProvider->findOrCreateProxyByUniqueKey(key, kTopLeft_GrSurfaceOrigin);
771 if (proxy) {
772 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kPreExisting_LockTexturePath,
773 kLockTexturePathCount);
774 if (!willBeMipped || GrMipMapped::kYes == proxy->mipMapped()) {
775 return proxy;
776 }
777 }
778 }
779
780 // The CachedFormat is both an index for which cache "slot" we'll use to store this particular
781 // decoded variant of the encoded data, and also a recipe for how to transform the original
782 // info to get the one that we're going to decode to.
783 const SkImageInfo cacheInfo = this->buildCacheInfo(format);
784 SkImageInfo genPixelsInfo = cacheInfo;
785 SkTransferFunctionBehavior behavior = getGeneratorBehaviorAndInfo(&genPixelsInfo);
786
787 // 2. Ask the generator to natively create one
788 if (!proxy) {
789 ScopedGenerator generator(fSharedGenerator);
790 if (GrTextureMaker::AllowedTexGenType::kCheap == genType &&
791 SkImageGenerator::TexGenType::kCheap != generator->onCanGenerateTexture()) {
792 return nullptr;
793 }
794 if ((proxy = generator->generateTexture(ctx, genPixelsInfo, fOrigin, behavior,
795 willBeMipped))) {
796 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath,
797 kLockTexturePathCount);
798 set_key_on_proxy(proxyProvider, proxy.get(), nullptr, key);
799 if (!willBeMipped || GrMipMapped::kYes == proxy->mipMapped()) {
800 return proxy;
801 }
802 }
803 }
804
805 // 3. Ask the generator to return YUV planes, which the GPU can convert. If we will be mipping
806 // the texture we fall through here and have the CPU generate the mip maps for us.
807 if (!proxy && !willBeMipped && !ctx->contextPriv().disableGpuYUVConversion()) {
808 const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(cacheInfo, *ctx->caps());
809 ScopedGenerator generator(fSharedGenerator);
810 Generator_GrYUVProvider provider(generator);
811
812 // The pixels in the texture will be in the generator's color space. If onMakeColorSpace
813 // has been called then this will not match this image's color space. To correct this, apply
814 // a color space conversion from the generator's color space to this image's color space.
815 const SkColorSpace* generatorColorSpace =
816 fSharedGenerator->fGenerator->getInfo().colorSpace();
817 const SkColorSpace* thisColorSpace = fInfo.colorSpace();
818
819 // TODO: Update to create the mipped surface in the YUV generator and draw the base layer
820 // directly into the mipped surface.
821 proxy = provider.refAsTextureProxy(ctx, desc, generatorColorSpace, thisColorSpace);
822 if (proxy) {
823 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath,
824 kLockTexturePathCount);
825 set_key_on_proxy(proxyProvider, proxy.get(), nullptr, key);
826 return proxy;
827 }
828 }
829
830 // 4. Ask the generator to return RGB(A) data, which the GPU can convert
831 SkBitmap bitmap;
832 if (!proxy && this->lockAsBitmap(&bitmap, chint, format, genPixelsInfo, behavior)) {
833 if (willBeMipped) {
834 proxy = GrGenerateMipMapsAndUploadToTextureProxy(proxyProvider, bitmap, dstColorSpace);
835 }
836 if (!proxy) {
837 proxy = GrUploadBitmapToTextureProxy(proxyProvider, bitmap, dstColorSpace);
838 }
839 if (proxy && (!willBeMipped || GrMipMapped::kYes == proxy->mipMapped())) {
840 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath,
841 kLockTexturePathCount);
842 set_key_on_proxy(proxyProvider, proxy.get(), nullptr, key);
843 return proxy;
844 }
845 }
846
847 if (proxy) {
848 // We need a mipped proxy, but we either found a proxy earlier that wasn't mipped, generated
849 // a native non mipped proxy, or generated a non-mipped yuv proxy. Thus we generate a new
850 // mipped surface and copy the original proxy into the base layer. We will then let the gpu
851 // generate the rest of the mips.
852 SkASSERT(willBeMipped);
853 SkASSERT(GrMipMapped::kNo == proxy->mipMapped());
854 if (auto mippedProxy = GrCopyBaseMipMapToTextureProxy(ctx, proxy.get())) {
855 set_key_on_proxy(proxyProvider, mippedProxy.get(), proxy.get(), key);
856 return mippedProxy;
857 }
858 // We failed to make a mipped proxy with the base copied into it. This could have
859 // been from failure to make the proxy or failure to do the copy. Thus we will fall
860 // back to just using the non mipped proxy; See skbug.com/7094.
861 return proxy;
862 }
863
864 SK_HISTOGRAM_ENUMERATION("LockTexturePath", kFailure_LockTexturePath,
865 kLockTexturePathCount);
866 return nullptr;
867 }
868
869 ///////////////////////////////////////////////////////////////////////////////////////////////////
870
871 #endif
872