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