• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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