xref: /aosp_15_r20/external/skia/src/image/SkImage_Lazy.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 "src/image/SkImage_Lazy.h"
9 
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkColorSpace.h"
12 #include "include/core/SkData.h"
13 #include "include/core/SkImageGenerator.h"
14 #include "include/core/SkPixmap.h"
15 #include "include/core/SkSize.h"
16 #include "include/core/SkSurface.h"
17 #include "include/core/SkYUVAInfo.h"
18 #include "src/core/SkBitmapCache.h"
19 #include "src/core/SkCachedData.h"
20 #include "src/core/SkNextID.h"
21 #include "src/core/SkResourceCache.h"
22 #include "src/core/SkYUVPlanesCache.h"
23 
24 #include <utility>
25 
26 class SkSurfaceProps;
27 
28 enum SkColorType : int;
29 
Make(std::unique_ptr<SkImageGenerator> gen)30 sk_sp<SharedGenerator> SharedGenerator::Make(std::unique_ptr<SkImageGenerator> gen) {
31     return gen ? sk_sp<SharedGenerator>(new SharedGenerator(std::move(gen))) : nullptr;
32 }
33 
SharedGenerator(std::unique_ptr<SkImageGenerator> gen)34 SharedGenerator::SharedGenerator(std::unique_ptr<SkImageGenerator> gen)
35         : fGenerator(std::move(gen)) {
36     SkASSERT(fGenerator);
37 }
38 
getInfo() const39 const SkImageInfo& SharedGenerator::getInfo() const { return fGenerator->getInfo(); }
40 
isTextureGenerator()41 bool SharedGenerator::isTextureGenerator() { return fGenerator->isTextureGenerator(); }
42 
43 ///////////////////////////////////////////////////////////////////////////////
44 
Validator(sk_sp<SharedGenerator> gen,const SkColorType * colorType,sk_sp<SkColorSpace> colorSpace)45 SkImage_Lazy::Validator::Validator(sk_sp<SharedGenerator> gen, const SkColorType* colorType,
46                                    sk_sp<SkColorSpace> colorSpace)
47         : fSharedGenerator(std::move(gen)) {
48     if (!fSharedGenerator) {
49         return;
50     }
51 
52     // The following generator accessors are safe without acquiring the mutex (const getters).
53     // TODO: refactor to use a ScopedGenerator instead, for clarity.
54     fInfo = fSharedGenerator->fGenerator->getInfo();
55     if (fInfo.isEmpty()) {
56         fSharedGenerator.reset();
57         return;
58     }
59 
60     fUniqueID = fSharedGenerator->fGenerator->uniqueID();
61 
62     if (colorType && (*colorType == fInfo.colorType())) {
63         colorType = nullptr;
64     }
65 
66     if (colorType || colorSpace) {
67         if (colorType) {
68             fInfo = fInfo.makeColorType(*colorType);
69         }
70         if (colorSpace) {
71             fInfo = fInfo.makeColorSpace(colorSpace);
72         }
73         fUniqueID = SkNextID::ImageID();
74     }
75 }
76 
77 ///////////////////////////////////////////////////////////////////////////////
78 
79 // Helper for exclusive access to a shared generator.
80 class SkImage_Lazy::ScopedGenerator {
81 public:
ScopedGenerator(const sk_sp<SharedGenerator> & gen)82     ScopedGenerator(const sk_sp<SharedGenerator>& gen)
83       : fSharedGenerator(gen)
84       , fAutoAcquire(gen->fMutex) {}
85 
operator ->() const86     SkImageGenerator* operator->() const {
87         fSharedGenerator->fMutex.assertHeld();
88         return fSharedGenerator->fGenerator.get();
89     }
90 
operator SkImageGenerator*() const91     operator SkImageGenerator*() const {
92         fSharedGenerator->fMutex.assertHeld();
93         return fSharedGenerator->fGenerator.get();
94     }
95 
96 private:
97     const sk_sp<SharedGenerator>& fSharedGenerator;
98     SkAutoMutexExclusive          fAutoAcquire;
99 };
100 
101 ///////////////////////////////////////////////////////////////////////////////
102 
SkImage_Lazy(Validator * validator)103 SkImage_Lazy::SkImage_Lazy(Validator* validator)
104     : SkImage_Base(validator->fInfo, validator->fUniqueID)
105     , fSharedGenerator(std::move(validator->fSharedGenerator))
106 {
107     SkASSERT(fSharedGenerator);
108 }
109 
getROPixels(GrDirectContext * ctx,SkBitmap * bitmap,SkImage::CachingHint chint) const110 bool SkImage_Lazy::getROPixels(GrDirectContext* ctx, SkBitmap* bitmap,
111                                SkImage::CachingHint chint) const {
112     auto check_output_bitmap = [bitmap]() {
113         SkASSERT(bitmap->isImmutable());
114         SkASSERT(bitmap->getPixels());
115         (void)bitmap;
116     };
117 
118     auto desc = SkBitmapCacheDesc::Make(this);
119     if (SkBitmapCache::Find(desc, bitmap)) {
120         check_output_bitmap();
121         return true;
122     }
123 
124     if (SkImage::kAllow_CachingHint == chint) {
125         SkPixmap pmap;
126         SkBitmapCache::RecPtr cacheRec = SkBitmapCache::Alloc(desc, this->imageInfo(), &pmap);
127         if (!cacheRec) {
128             return false;
129         }
130         bool success = false;
131         {   // make sure ScopedGenerator goes out of scope before we try readPixelsProxy
132             success = ScopedGenerator(fSharedGenerator)->getPixels(pmap);
133         }
134         if (!success && !this->readPixelsProxy(ctx, pmap)) {
135             return false;
136         }
137         SkBitmapCache::Add(std::move(cacheRec), bitmap);
138         this->notifyAddedToRasterCache();
139     } else {
140         if (!bitmap->tryAllocPixels(this->imageInfo())) {
141             return false;
142         }
143         bool success = false;
144         {   // make sure ScopedGenerator goes out of scope before we try readPixelsProxy
145             success = ScopedGenerator(fSharedGenerator)->getPixels(bitmap->pixmap());
146         }
147         if (!success && !this->readPixelsProxy(ctx, bitmap->pixmap())) {
148             return false;
149         }
150         bitmap->setImmutable();
151     }
152     check_output_bitmap();
153     return true;
154 }
155 
generator() const156 sk_sp<SharedGenerator> SkImage_Lazy::generator() const {
157     return fSharedGenerator;
158 }
159 
onIsProtected() const160 bool SkImage_Lazy::onIsProtected() const {
161     ScopedGenerator generator(fSharedGenerator);
162     return generator->isProtected();
163 }
164 
onReadPixels(GrDirectContext * dContext,const SkImageInfo & dstInfo,void * dstPixels,size_t dstRB,int srcX,int srcY,CachingHint chint) const165 bool SkImage_Lazy::onReadPixels(GrDirectContext* dContext,
166                                 const SkImageInfo& dstInfo,
167                                 void* dstPixels,
168                                 size_t dstRB,
169                                 int srcX,
170                                 int srcY,
171                                 CachingHint chint) const {
172     SkBitmap bm;
173     if (this->getROPixels(dContext, &bm, chint)) {
174         return bm.readPixels(dstInfo, dstPixels, dstRB, srcX, srcY);
175     }
176     return false;
177 }
178 
onRefEncoded() const179 sk_sp<SkData> SkImage_Lazy::onRefEncoded() const {
180     // check that we aren't a subset or colortype/etc modification of the original
181     if (fSharedGenerator->fGenerator->uniqueID() == this->uniqueID()) {
182         ScopedGenerator generator(fSharedGenerator);
183         return generator->refEncodedData();
184     }
185     return nullptr;
186 }
187 
isValid(GrRecordingContext * context) const188 bool SkImage_Lazy::isValid(GrRecordingContext* context) const {
189     ScopedGenerator generator(fSharedGenerator);
190     return generator->isValid(context);
191 }
192 
193 
onMakeSubset(GrDirectContext *,const SkIRect & subset) const194 sk_sp<SkImage> SkImage_Lazy::onMakeSubset(GrDirectContext*, const SkIRect& subset) const {
195     // neither picture-backed nor codec-backed lazy images need the context to do readbacks.
196     // The subclass for cross-context images *does* use the direct context.
197     auto pixels = this->makeRasterImage(nullptr);
198     return pixels ? pixels->makeSubset(nullptr, subset) : nullptr;
199 }
200 
onMakeSubset(skgpu::graphite::Recorder *,const SkIRect & subset,RequiredProperties props) const201 sk_sp<SkImage> SkImage_Lazy::onMakeSubset(skgpu::graphite::Recorder*,
202                                           const SkIRect& subset,
203                                           RequiredProperties props) const {
204     // TODO: can we do this more efficiently, by telling the generator we want to
205     //       "realize" a subset?
206     sk_sp<SkImage> nonLazyImg = this->makeRasterImage(nullptr);
207     if (!nonLazyImg) {
208         return nullptr;
209     }
210     return nonLazyImg->makeSubset(nullptr, subset, props);
211 }
212 
onMakeSurface(skgpu::graphite::Recorder *,const SkImageInfo & info) const213 sk_sp<SkSurface> SkImage_Lazy::onMakeSurface(skgpu::graphite::Recorder*,
214                                              const SkImageInfo& info) const {
215     const SkSurfaceProps* props = nullptr;
216     const size_t rowBytes = 0;
217     return SkSurfaces::Raster(info, rowBytes, props);
218 }
219 
onMakeColorTypeAndColorSpace(SkColorType targetCT,sk_sp<SkColorSpace> targetCS,GrDirectContext *) const220 sk_sp<SkImage> SkImage_Lazy::onMakeColorTypeAndColorSpace(SkColorType targetCT,
221                                                           sk_sp<SkColorSpace> targetCS,
222                                                           GrDirectContext*) const {
223     SkAutoMutexExclusive autoAquire(fOnMakeColorTypeAndSpaceMutex);
224     if (fOnMakeColorTypeAndSpaceResult &&
225         targetCT == fOnMakeColorTypeAndSpaceResult->colorType() &&
226         SkColorSpace::Equals(targetCS.get(), fOnMakeColorTypeAndSpaceResult->colorSpace())) {
227         return fOnMakeColorTypeAndSpaceResult;
228     }
229     Validator validator(fSharedGenerator, &targetCT, targetCS);
230     sk_sp<SkImage> result = validator ? sk_sp<SkImage>(new SkImage_Lazy(&validator)) : nullptr;
231     if (result) {
232         fOnMakeColorTypeAndSpaceResult = result;
233     }
234     return result;
235 }
236 
onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const237 sk_sp<SkImage> SkImage_Lazy::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
238     // TODO: The correct thing is to clone the generator, and modify its color space. That's hard,
239     // because we don't have a clone method, and generator is public (and derived-from by clients).
240     // So do the simple/inefficient thing here, and fallback to raster when this is called.
241 
242     // We allocate the bitmap with the new color space, then generate the image using the original.
243     SkBitmap bitmap;
244     if (bitmap.tryAllocPixels(this->imageInfo().makeColorSpace(std::move(newCS)))) {
245         SkPixmap pixmap = bitmap.pixmap();
246         pixmap.setColorSpace(this->refColorSpace());
247         if (ScopedGenerator(fSharedGenerator)->getPixels(pixmap)) {
248             bitmap.setImmutable();
249             return bitmap.asImage();
250         }
251     }
252     return nullptr;
253 }
254 
getPlanes(const SkYUVAPixmapInfo::SupportedDataTypes & supportedDataTypes,SkYUVAPixmaps * yuvaPixmaps) const255 sk_sp<SkCachedData> SkImage_Lazy::getPlanes(
256         const SkYUVAPixmapInfo::SupportedDataTypes& supportedDataTypes,
257         SkYUVAPixmaps* yuvaPixmaps) const {
258     ScopedGenerator generator(fSharedGenerator);
259 
260     sk_sp<SkCachedData> data(SkYUVPlanesCache::FindAndRef(generator->uniqueID(), yuvaPixmaps));
261 
262     if (data) {
263         SkASSERT(yuvaPixmaps->isValid());
264         SkASSERT(yuvaPixmaps->yuvaInfo().dimensions() == this->dimensions());
265         return data;
266     }
267     SkYUVAPixmapInfo yuvaPixmapInfo;
268     if (!generator->queryYUVAInfo(supportedDataTypes, &yuvaPixmapInfo) ||
269         yuvaPixmapInfo.yuvaInfo().dimensions() != this->dimensions()) {
270         return nullptr;
271     }
272     data.reset(SkResourceCache::NewCachedData(yuvaPixmapInfo.computeTotalBytes()));
273     SkYUVAPixmaps tempPixmaps = SkYUVAPixmaps::FromExternalMemory(yuvaPixmapInfo,
274                                                                   data->writable_data());
275     SkASSERT(tempPixmaps.isValid());
276     if (!generator->getYUVAPlanes(tempPixmaps)) {
277         return nullptr;
278     }
279     // Decoding is done, cache the resulting YUV planes
280     *yuvaPixmaps = tempPixmaps;
281     SkYUVPlanesCache::Add(this->uniqueID(), data.get(), *yuvaPixmaps);
282     return data;
283 }
284 
addUniqueIDListener(sk_sp<SkIDChangeListener> listener) const285 void SkImage_Lazy::addUniqueIDListener(sk_sp<SkIDChangeListener> listener) const {
286     fUniqueIDListeners.add(std::move(listener));
287 }
288 
289 // TODO(kjlubick) move SharedGenerate to SkImage_Lazy.h and this to SkImage_LazyFactories
290 namespace SkImages {
291 
DeferredFromGenerator(std::unique_ptr<SkImageGenerator> generator)292 sk_sp<SkImage> DeferredFromGenerator(std::unique_ptr<SkImageGenerator> generator) {
293     SkImage_Lazy::Validator validator(
294             SharedGenerator::Make(std::move(generator)), nullptr, nullptr);
295 
296     return validator ? sk_make_sp<SkImage_Lazy>(&validator) : nullptr;
297 }
298 
299 }  // namespace SkImages
300