xref: /aosp_15_r20/external/skia/src/image/SkImage.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2012 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 "include/core/SkImage.h"
9 
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkBlendMode.h"
12 #include "include/core/SkCanvas.h"
13 #include "include/core/SkColorSpace.h"
14 #include "include/core/SkData.h"
15 #include "include/core/SkPaint.h"
16 #include "include/core/SkPixmap.h"
17 #include "include/core/SkSurface.h"
18 #include "include/core/SkTileMode.h"
19 #include "include/core/SkTypes.h"
20 #include "src/core/SkColorSpacePriv.h"
21 #include "src/core/SkImageInfoPriv.h"
22 #include "src/core/SkMipmap.h"
23 #include "src/core/SkNextID.h"
24 #include "src/image/SkImage_Base.h"
25 #include "src/shaders/SkImageShader.h"
26 
27 #include <utility>
28 
29 class SkShader;
30 
SkImage(const SkImageInfo & info,uint32_t uniqueID)31 SkImage::SkImage(const SkImageInfo& info, uint32_t uniqueID)
32         : fInfo(info)
33         , fUniqueID(kNeedNewImageUniqueID == uniqueID ? SkNextID::ImageID() : uniqueID) {
34     SkASSERT(info.width() > 0);
35     SkASSERT(info.height() > 0);
36 }
37 
peekPixels(SkPixmap * pm) const38 bool SkImage::peekPixels(SkPixmap* pm) const {
39     SkPixmap tmp;
40     if (!pm) {
41         pm = &tmp;
42     }
43     return as_IB(this)->onPeekPixels(pm);
44 }
45 
readPixels(GrDirectContext * dContext,const SkImageInfo & dstInfo,void * dstPixels,size_t dstRowBytes,int srcX,int srcY,CachingHint chint) const46 bool SkImage::readPixels(GrDirectContext* dContext, const SkImageInfo& dstInfo, void* dstPixels,
47                          size_t dstRowBytes, int srcX, int srcY, CachingHint chint) const {
48     return as_IB(this)->onReadPixels(dContext, dstInfo, dstPixels, dstRowBytes, srcX, srcY, chint);
49 }
50 
makeScaled(skgpu::graphite::Recorder * recorder,const SkImageInfo & newInfo,const SkSamplingOptions & sampling) const51 sk_sp<SkImage> SkImage::makeScaled(skgpu::graphite::Recorder* recorder,
52                                    const SkImageInfo& newInfo,
53                                    const SkSamplingOptions& sampling) const {
54     if (!SkImageInfoIsValid(newInfo)) {
55         return nullptr;
56     }
57     if (newInfo == this->imageInfo()) {
58         return sk_ref_sp(this);
59     }
60 
61     auto surf = as_IB(this)->onMakeSurface(recorder, newInfo);
62     if (!surf) {
63         return nullptr;
64     }
65 
66     SkPaint paint;
67     paint.setBlendMode(SkBlendMode::kSrc);
68     surf->getCanvas()->drawImageRect(this,
69                                      SkRect::MakeIWH(newInfo.width(), newInfo.height()),
70                                      sampling,
71                                      &paint);
72     return surf->makeImageSnapshot();
73 }
74 
75 #ifndef SK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API
readPixels(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRowBytes,int srcX,int srcY,CachingHint chint) const76 bool SkImage::readPixels(const SkImageInfo& dstInfo, void* dstPixels,
77                          size_t dstRowBytes, int srcX, int srcY, CachingHint chint) const {
78     auto dContext = as_IB(this)->directContext();
79     return this->readPixels(dContext, dstInfo, dstPixels, dstRowBytes, srcX, srcY, chint);
80 }
81 #endif
82 
asyncRescaleAndReadPixels(const SkImageInfo & info,const SkIRect & srcRect,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context) const83 void SkImage::asyncRescaleAndReadPixels(const SkImageInfo& info,
84                                         const SkIRect& srcRect,
85                                         RescaleGamma rescaleGamma,
86                                         RescaleMode rescaleMode,
87                                         ReadPixelsCallback callback,
88                                         ReadPixelsContext context) const {
89     if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) ||
90         !SkImageInfoIsValid(info)) {
91         callback(context, nullptr);
92         return;
93     }
94     as_IB(this)->onAsyncRescaleAndReadPixels(
95             info, srcRect, rescaleGamma, rescaleMode, callback, context);
96 }
97 
asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,sk_sp<SkColorSpace> dstColorSpace,const SkIRect & srcRect,const SkISize & dstSize,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context) const98 void SkImage::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
99                                               sk_sp<SkColorSpace> dstColorSpace,
100                                               const SkIRect& srcRect,
101                                               const SkISize& dstSize,
102                                               RescaleGamma rescaleGamma,
103                                               RescaleMode rescaleMode,
104                                               ReadPixelsCallback callback,
105                                               ReadPixelsContext context) const {
106     if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) || dstSize.isZero() ||
107         (dstSize.width() & 0b1) || (dstSize.height() & 0b1)) {
108         callback(context, nullptr);
109         return;
110     }
111     as_IB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace,
112                                                    /*readAlpha=*/false,
113                                                    std::move(dstColorSpace),
114                                                    srcRect,
115                                                    dstSize,
116                                                    rescaleGamma,
117                                                    rescaleMode,
118                                                    callback,
119                                                    context);
120 }
121 
asyncRescaleAndReadPixelsYUVA420(SkYUVColorSpace yuvColorSpace,sk_sp<SkColorSpace> dstColorSpace,const SkIRect & srcRect,const SkISize & dstSize,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext context) const122 void SkImage::asyncRescaleAndReadPixelsYUVA420(SkYUVColorSpace yuvColorSpace,
123                                                sk_sp<SkColorSpace> dstColorSpace,
124                                                const SkIRect& srcRect,
125                                                const SkISize& dstSize,
126                                                RescaleGamma rescaleGamma,
127                                                RescaleMode rescaleMode,
128                                                ReadPixelsCallback callback,
129                                                ReadPixelsContext context) const {
130     if (!SkIRect::MakeWH(this->width(), this->height()).contains(srcRect) || dstSize.isZero() ||
131         (dstSize.width() & 0b1) || (dstSize.height() & 0b1)) {
132         callback(context, nullptr);
133         return;
134     }
135     as_IB(this)->onAsyncRescaleAndReadPixelsYUV420(yuvColorSpace,
136                                                    /*readAlpha=*/true,
137                                                    std::move(dstColorSpace),
138                                                    srcRect,
139                                                    dstSize,
140                                                    rescaleGamma,
141                                                    rescaleMode,
142                                                    callback,
143                                                    context);
144 }
145 
scalePixels(const SkPixmap & dst,const SkSamplingOptions & sampling,CachingHint chint) const146 bool SkImage::scalePixels(const SkPixmap& dst, const SkSamplingOptions& sampling,
147                           CachingHint chint) const {
148     // Context TODO: Elevate GrDirectContext requirement to public API.
149     auto dContext = as_IB(this)->directContext();
150     if (this->width() == dst.width() && this->height() == dst.height()) {
151         return this->readPixels(dContext, dst, 0, 0, chint);
152     }
153 
154     // Idea: If/when SkImageGenerator supports a native-scaling API (where the generator itself
155     //       can scale more efficiently) we should take advantage of it here.
156     //
157     SkBitmap bm;
158     if (as_IB(this)->getROPixels(dContext, &bm, chint)) {
159         SkPixmap pmap;
160         // Note: By calling the pixmap scaler, we never cache the final result, so the chint
161         //       is (currently) only being applied to the getROPixels. If we get a request to
162         //       also attempt to cache the final (scaled) result, we would add that logic here.
163         //
164         return bm.peekPixels(&pmap) && pmap.scalePixels(dst, sampling);
165     }
166     return false;
167 }
168 
169 ///////////////////////////////////////////////////////////////////////////////////////////////////
170 
colorType() const171 SkColorType SkImage::colorType() const { return fInfo.colorType(); }
172 
alphaType() const173 SkAlphaType SkImage::alphaType() const { return fInfo.alphaType(); }
174 
colorSpace() const175 SkColorSpace* SkImage::colorSpace() const { return fInfo.colorSpace(); }
176 
refColorSpace() const177 sk_sp<SkColorSpace> SkImage::refColorSpace() const { return fInfo.refColorSpace(); }
178 
makeShader(const SkSamplingOptions & sampling,const SkMatrix & lm) const179 sk_sp<SkShader> SkImage::makeShader(const SkSamplingOptions& sampling, const SkMatrix& lm) const {
180     return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)),
181                                SkTileMode::kClamp, SkTileMode::kClamp,
182                                sampling, &lm);
183 }
184 
makeShader(const SkSamplingOptions & sampling,const SkMatrix * lm) const185 sk_sp<SkShader> SkImage::makeShader(const SkSamplingOptions& sampling, const SkMatrix* lm) const {
186     return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)),
187                                SkTileMode::kClamp, SkTileMode::kClamp,
188                                sampling, lm);
189 }
190 
makeShader(SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & sampling,const SkMatrix & lm) const191 sk_sp<SkShader> SkImage::makeShader(SkTileMode tmx, SkTileMode tmy,
192                                     const SkSamplingOptions& sampling,
193                                     const SkMatrix& lm) const {
194     return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy,
195                                sampling, &lm);
196 }
197 
makeShader(SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & sampling,const SkMatrix * localMatrix) const198 sk_sp<SkShader> SkImage::makeShader(SkTileMode tmx, SkTileMode tmy,
199                                     const SkSamplingOptions& sampling,
200                                     const SkMatrix* localMatrix) const {
201     return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy,
202                                sampling, localMatrix);
203 }
204 
makeRawShader(SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & sampling,const SkMatrix & lm) const205 sk_sp<SkShader> SkImage::makeRawShader(SkTileMode tmx, SkTileMode tmy,
206                                        const SkSamplingOptions& sampling,
207                                        const SkMatrix& lm) const {
208     return SkImageShader::MakeRaw(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy,
209                                   sampling, &lm);
210 }
211 
makeRawShader(const SkSamplingOptions & sampling,const SkMatrix & lm) const212 sk_sp<SkShader> SkImage::makeRawShader(const SkSamplingOptions& sampling,
213                                        const SkMatrix& lm) const {
214     return SkImageShader::MakeRaw(sk_ref_sp(const_cast<SkImage*>(this)),
215                                   SkTileMode::kClamp, SkTileMode::kClamp,
216                                   sampling, &lm);
217 }
218 
makeRawShader(const SkSamplingOptions & sampling,const SkMatrix * localMatrix) const219 sk_sp<SkShader> SkImage::makeRawShader(const SkSamplingOptions& sampling,
220                                        const SkMatrix* localMatrix) const {
221     return SkImageShader::MakeRaw(sk_ref_sp(const_cast<SkImage*>(this)),
222                                   SkTileMode::kClamp, SkTileMode::kClamp,
223                                   sampling, localMatrix);
224 }
225 
makeRawShader(SkTileMode tmx,SkTileMode tmy,const SkSamplingOptions & sampling,const SkMatrix * localMatrix) const226 sk_sp<SkShader> SkImage::makeRawShader(SkTileMode tmx, SkTileMode tmy,
227                                        const SkSamplingOptions& sampling,
228                                        const SkMatrix* localMatrix) const {
229     return SkImageShader::MakeRaw(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy,
230                                   sampling, localMatrix);
231 }
232 
refEncodedData() const233 sk_sp<SkData> SkImage::refEncodedData() const {
234     return sk_sp<SkData>(as_IB(this)->onRefEncoded());
235 }
236 
237 ///////////////////////////////////////////////////////////////////////////////////////////////////
238 
readPixels(GrDirectContext * dContext,const SkPixmap & pmap,int srcX,int srcY,CachingHint chint) const239 bool SkImage::readPixels(GrDirectContext* dContext, const SkPixmap& pmap, int srcX, int srcY,
240                          CachingHint chint) const {
241     return this->readPixels(dContext, pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX,
242                             srcY, chint);
243 }
244 
245 #ifndef SK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API
readPixels(const SkPixmap & pmap,int srcX,int srcY,CachingHint chint) const246 bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY, CachingHint chint) const {
247     auto dContext = as_IB(this)->directContext();
248     return this->readPixels(dContext, pmap, srcX, srcY, chint);
249 }
250 #endif
251 
asLegacyBitmap(SkBitmap * bitmap,LegacyBitmapMode) const252 bool SkImage::asLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode ) const {
253     // Context TODO: Elevate GrDirectContext requirement to public API.
254     auto dContext = as_IB(this)->directContext();
255     return as_IB(this)->onAsLegacyBitmap(dContext, bitmap);
256 }
257 
isAlphaOnly() const258 bool SkImage::isAlphaOnly() const { return SkColorTypeIsAlphaOnly(fInfo.colorType()); }
259 
reinterpretColorSpace(sk_sp<SkColorSpace> target) const260 sk_sp<SkImage> SkImage::reinterpretColorSpace(sk_sp<SkColorSpace> target) const {
261     if (!target) {
262         return nullptr;
263     }
264 
265     // No need to create a new image if:
266     // (1) The color spaces are equal.
267     // (2) The color type is kAlpha8.
268     SkColorSpace* colorSpace = this->colorSpace();
269     if (!colorSpace) {
270         colorSpace = sk_srgb_singleton();
271     }
272     if (SkColorSpace::Equals(colorSpace, target.get()) || this->isAlphaOnly()) {
273         return sk_ref_sp(const_cast<SkImage*>(this));
274     }
275 
276     return as_IB(this)->onReinterpretColorSpace(std::move(target));
277 }
278 
makeNonTextureImage(GrDirectContext * dContext) const279 sk_sp<SkImage> SkImage::makeNonTextureImage(GrDirectContext* dContext) const {
280     if (!this->isTextureBacked()) {
281         return sk_ref_sp(const_cast<SkImage*>(this));
282     }
283     return this->makeRasterImage(dContext, kDisallow_CachingHint);
284 }
285 
makeRasterImage(GrDirectContext * dContext,CachingHint chint) const286 sk_sp<SkImage> SkImage::makeRasterImage(GrDirectContext* dContext, CachingHint chint) const {
287     SkPixmap pm;
288     if (this->peekPixels(&pm)) {
289         return sk_ref_sp(const_cast<SkImage*>(this));
290     }
291 
292     const size_t rowBytes = fInfo.minRowBytes();
293     size_t size = fInfo.computeByteSize(rowBytes);
294     if (SkImageInfo::ByteSizeOverflowed(size)) {
295         return nullptr;
296     }
297 
298     if (!dContext) {
299         // Try to get the saved context if the client didn't pass it in (but they really should).
300         dContext = as_IB(this)->directContext();
301     }
302     sk_sp<SkData> data = SkData::MakeUninitialized(size);
303     pm = {fInfo.makeColorSpace(nullptr), data->writable_data(), fInfo.minRowBytes()};
304     if (!this->readPixels(dContext, pm, 0, 0, chint)) {
305         return nullptr;
306     }
307 
308     return SkImages::RasterFromData(fInfo, std::move(data), rowBytes);
309 }
310 
hasMipmaps() const311 bool SkImage::hasMipmaps() const { return as_IB(this)->onHasMipmaps(); }
312 
isProtected() const313 bool SkImage::isProtected() const { return as_IB(this)->onIsProtected(); }
314 
withMipmaps(sk_sp<SkMipmap> mips) const315 sk_sp<SkImage> SkImage::withMipmaps(sk_sp<SkMipmap> mips) const {
316     if (mips == nullptr || mips->validForRootLevel(this->imageInfo())) {
317         if (auto result = as_IB(this)->onMakeWithMipmaps(std::move(mips))) {
318             return result;
319         }
320     }
321     return sk_ref_sp((const_cast<SkImage*>(this)));
322 }
323 
withDefaultMipmaps() const324 sk_sp<SkImage> SkImage::withDefaultMipmaps() const {
325     return this->withMipmaps(nullptr);
326 }
327