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