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