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 "SkBitmap.h"
9 #include "SkBitmapCache.h"
10 #include "SkCanvas.h"
11 #include "SkData.h"
12 #include "SkImageEncoder.h"
13 #include "SkImageFilter.h"
14 #include "SkImageFilterCache.h"
15 #include "SkImageGenerator.h"
16 #include "SkImagePriv.h"
17 #include "SkImageShader.h"
18 #include "SkImage_Base.h"
19 #include "SkNextID.h"
20 #include "SkPicture.h"
21 #include "SkPixelRef.h"
22 #include "SkReadPixelsRec.h"
23 #include "SkSpecialImage.h"
24 #include "SkString.h"
25 #include "SkSurface.h"
26
27 #if SK_SUPPORT_GPU
28 #include "GrTexture.h"
29 #include "GrContext.h"
30 #include "SkImage_Gpu.h"
31 #endif
32
SkImage(int width,int height,uint32_t uniqueID)33 SkImage::SkImage(int width, int height, uint32_t uniqueID)
34 : fWidth(width)
35 , fHeight(height)
36 , fUniqueID(kNeedNewImageUniqueID == uniqueID ? SkNextID::ImageID() : uniqueID)
37 {
38 SkASSERT(width > 0);
39 SkASSERT(height > 0);
40 }
41
peekPixels(SkPixmap * pm) const42 bool SkImage::peekPixels(SkPixmap* pm) const {
43 SkPixmap tmp;
44 if (!pm) {
45 pm = &tmp;
46 }
47 return as_IB(this)->onPeekPixels(pm);
48 }
49
readPixels(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRowBytes,int srcX,int srcY,CachingHint chint) const50 bool SkImage::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
51 int srcX, int srcY, CachingHint chint) const {
52 return as_IB(this)->onReadPixels(dstInfo, dstPixels, dstRowBytes, srcX, srcY, chint);
53 }
54
scalePixels(const SkPixmap & dst,SkFilterQuality quality,CachingHint chint) const55 bool SkImage::scalePixels(const SkPixmap& dst, SkFilterQuality quality, CachingHint chint) const {
56 if (this->width() == dst.width() && this->height() == dst.height()) {
57 return this->readPixels(dst, 0, 0, chint);
58 }
59
60 // Idea: If/when SkImageGenerator supports a native-scaling API (where the generator itself
61 // can scale more efficiently) we should take advantage of it here.
62 //
63 SkBitmap bm;
64 if (as_IB(this)->getROPixels(&bm, dst.info().colorSpace(), chint)) {
65 SkPixmap pmap;
66 // Note: By calling the pixmap scaler, we never cache the final result, so the chint
67 // is (currently) only being applied to the getROPixels. If we get a request to
68 // also attempt to cache the final (scaled) result, we would add that logic here.
69 //
70 return bm.peekPixels(&pmap) && pmap.scalePixels(dst, quality);
71 }
72 return false;
73 }
74
75 ///////////////////////////////////////////////////////////////////////////////////////////////////
76
alphaType() const77 SkAlphaType SkImage::alphaType() const {
78 return as_IB(this)->onAlphaType();
79 }
80
colorSpace() const81 SkColorSpace* SkImage::colorSpace() const {
82 return as_IB(this)->onImageInfo().colorSpace();
83 }
84
refColorSpace() const85 sk_sp<SkColorSpace> SkImage::refColorSpace() const {
86 return as_IB(this)->onImageInfo().refColorSpace();
87 }
88
makeShader(SkShader::TileMode tileX,SkShader::TileMode tileY,const SkMatrix * localMatrix) const89 sk_sp<SkShader> SkImage::makeShader(SkShader::TileMode tileX, SkShader::TileMode tileY,
90 const SkMatrix* localMatrix) const {
91 return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tileX, tileY, localMatrix);
92 }
93
encodeToData(SkEncodedImageFormat type,int quality) const94 sk_sp<SkData> SkImage::encodeToData(SkEncodedImageFormat type, int quality) const {
95 SkBitmap bm;
96 SkColorSpace* legacyColorSpace = nullptr;
97 if (as_IB(this)->getROPixels(&bm, legacyColorSpace)) {
98 return SkEncodeBitmap(bm, type, quality);
99 }
100 return nullptr;
101 }
102
encodeToData() const103 sk_sp<SkData> SkImage::encodeToData() const {
104 if (auto encoded = this->refEncodedData()) {
105 return encoded;
106 }
107
108 SkBitmap bm;
109 SkPixmap pmap;
110 SkColorSpace* legacyColorSpace = nullptr;
111 if (as_IB(this)->getROPixels(&bm, legacyColorSpace) && bm.peekPixels(&pmap)) {
112 return SkEncodePixmap(pmap, SkEncodedImageFormat::kPNG, 100);
113 }
114 return nullptr;
115 }
116
refEncodedData() const117 sk_sp<SkData> SkImage::refEncodedData() const {
118 return sk_sp<SkData>(as_IB(this)->onRefEncoded());
119 }
120
MakeFromEncoded(sk_sp<SkData> encoded,const SkIRect * subset)121 sk_sp<SkImage> SkImage::MakeFromEncoded(sk_sp<SkData> encoded, const SkIRect* subset) {
122 if (nullptr == encoded || 0 == encoded->size()) {
123 return nullptr;
124 }
125 return SkImage::MakeFromGenerator(SkImageGenerator::MakeFromEncoded(encoded), subset);
126 }
127
128 ///////////////////////////////////////////////////////////////////////////////////////////////////
129
toString(SkString * str) const130 const char* SkImage::toString(SkString* str) const {
131 str->appendf("image: (id:%d (%d, %d) %s)", this->uniqueID(), this->width(), this->height(),
132 this->isOpaque() ? "opaque" : "");
133 return str->c_str();
134 }
135
makeSubset(const SkIRect & subset) const136 sk_sp<SkImage> SkImage::makeSubset(const SkIRect& subset) const {
137 if (subset.isEmpty()) {
138 return nullptr;
139 }
140
141 const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
142 if (!bounds.contains(subset)) {
143 return nullptr;
144 }
145
146 // optimization : return self if the subset == our bounds
147 if (bounds == subset) {
148 return sk_ref_sp(const_cast<SkImage*>(this));
149 }
150 return as_IB(this)->onMakeSubset(subset);
151 }
152
153 #if SK_SUPPORT_GPU
154
getTexture() const155 GrTexture* SkImage::getTexture() const {
156 return as_IB(this)->onGetTexture();
157 }
158
isTextureBacked() const159 bool SkImage::isTextureBacked() const { return SkToBool(as_IB(this)->peekProxy()); }
160
getTextureHandle(bool flushPendingGrContextIO,GrSurfaceOrigin * origin) const161 GrBackendObject SkImage::getTextureHandle(bool flushPendingGrContextIO,
162 GrSurfaceOrigin* origin) const {
163 return as_IB(this)->onGetTextureHandle(flushPendingGrContextIO, origin);
164 }
165
isValid(GrContext * context) const166 bool SkImage::isValid(GrContext* context) const {
167 if (context && context->abandoned()) {
168 return false;
169 }
170 return as_IB(this)->onIsValid(context);
171 }
172
173 #else
174
getTexture() const175 GrTexture* SkImage::getTexture() const { return nullptr; }
176
isTextureBacked() const177 bool SkImage::isTextureBacked() const { return false; }
178
getTextureHandle(bool,GrSurfaceOrigin *) const179 GrBackendObject SkImage::getTextureHandle(bool, GrSurfaceOrigin*) const { return 0; }
180
isValid(GrContext * context) const181 bool SkImage::isValid(GrContext* context) const {
182 if (context) {
183 return false;
184 }
185 return as_IB(this)->onIsValid(context);
186 }
187
188 #endif
189
190 ///////////////////////////////////////////////////////////////////////////////
191
SkImage_Base(int width,int height,uint32_t uniqueID)192 SkImage_Base::SkImage_Base(int width, int height, uint32_t uniqueID)
193 : INHERITED(width, height, uniqueID)
194 , fAddedToCache(false)
195 {}
196
~SkImage_Base()197 SkImage_Base::~SkImage_Base() {
198 if (fAddedToCache.load()) {
199 SkNotifyBitmapGenIDIsStale(this->uniqueID());
200 }
201 }
202
readPixels(const SkPixmap & pmap,int srcX,int srcY,CachingHint chint) const203 bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY, CachingHint chint) const {
204 return this->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX, srcY, chint);
205 }
206
207 ///////////////////////////////////////////////////////////////////////////////////////////////////
208
MakeFromBitmap(const SkBitmap & bm)209 sk_sp<SkImage> SkImage::MakeFromBitmap(const SkBitmap& bm) {
210 SkPixelRef* pr = bm.pixelRef();
211 if (nullptr == pr) {
212 return nullptr;
213 }
214
215 return SkMakeImageFromRasterBitmap(bm, kIfMutable_SkCopyPixelsMode);
216 }
217
asLegacyBitmap(SkBitmap * bitmap,LegacyBitmapMode) const218 bool SkImage::asLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode ) const {
219 return as_IB(this)->onAsLegacyBitmap(bitmap);
220 }
221
onAsLegacyBitmap(SkBitmap * bitmap) const222 bool SkImage_Base::onAsLegacyBitmap(SkBitmap* bitmap) const {
223 // As the base-class, all we can do is make a copy (regardless of mode).
224 // Subclasses that want to be more optimal should override.
225 SkImageInfo info = this->onImageInfo().makeColorType(kN32_SkColorType).makeColorSpace(nullptr);
226 if (!bitmap->tryAllocPixels(info)) {
227 return false;
228 }
229 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
230 bitmap->reset();
231 return false;
232 }
233
234 bitmap->setImmutable();
235 return true;
236 }
237
MakeFromPicture(sk_sp<SkPicture> picture,const SkISize & dimensions,const SkMatrix * matrix,const SkPaint * paint,BitDepth bitDepth,sk_sp<SkColorSpace> colorSpace)238 sk_sp<SkImage> SkImage::MakeFromPicture(sk_sp<SkPicture> picture, const SkISize& dimensions,
239 const SkMatrix* matrix, const SkPaint* paint,
240 BitDepth bitDepth, sk_sp<SkColorSpace> colorSpace) {
241 return MakeFromGenerator(SkImageGenerator::MakeFromPicture(dimensions, std::move(picture),
242 matrix, paint, bitDepth,
243 std::move(colorSpace)));
244 }
245
makeWithFilter(const SkImageFilter * filter,const SkIRect & subset,const SkIRect & clipBounds,SkIRect * outSubset,SkIPoint * offset) const246 sk_sp<SkImage> SkImage::makeWithFilter(const SkImageFilter* filter, const SkIRect& subset,
247 const SkIRect& clipBounds, SkIRect* outSubset,
248 SkIPoint* offset) const {
249 if (!filter || !outSubset || !offset || !this->bounds().contains(subset)) {
250 return nullptr;
251 }
252 SkColorSpace* colorSpace = as_IB(this)->onImageInfo().colorSpace();
253 sk_sp<SkSpecialImage> srcSpecialImage = SkSpecialImage::MakeFromImage(
254 subset, sk_ref_sp(const_cast<SkImage*>(this)), colorSpace);
255 if (!srcSpecialImage) {
256 return nullptr;
257 }
258
259 sk_sp<SkImageFilterCache> cache(
260 SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize));
261 SkImageFilter::OutputProperties outputProperties(colorSpace);
262 SkImageFilter::Context context(SkMatrix::I(), clipBounds, cache.get(), outputProperties);
263
264 sk_sp<SkSpecialImage> result = filter->filterImage(srcSpecialImage.get(), context, offset);
265 if (!result) {
266 return nullptr;
267 }
268
269 *outSubset = SkIRect::MakeWH(result->width(), result->height());
270 if (!outSubset->intersect(clipBounds.makeOffset(-offset->x(), -offset->y()))) {
271 return nullptr;
272 }
273 offset->fX += outSubset->x();
274 offset->fY += outSubset->y();
275
276 // Note that here we're returning the special image's entire backing store, loose padding
277 // and all!
278 return result->asImage();
279 }
280
isLazyGenerated() const281 bool SkImage::isLazyGenerated() const {
282 return as_IB(this)->onIsLazyGenerated();
283 }
284
isAlphaOnly() const285 bool SkImage::isAlphaOnly() const {
286 return as_IB(this)->onImageInfo().colorType() == kAlpha_8_SkColorType;
287 }
288
makeColorSpace(sk_sp<SkColorSpace> target,SkTransferFunctionBehavior premulBehavior) const289 sk_sp<SkImage> SkImage::makeColorSpace(sk_sp<SkColorSpace> target,
290 SkTransferFunctionBehavior premulBehavior) const {
291 SkColorSpaceTransferFn fn;
292 if (!target || !target->isNumericalTransferFn(&fn)) {
293 return nullptr;
294 }
295
296 // No need to create a new image if:
297 // (1) The color spaces are equal.
298 // (2) The color type is kAlpha8.
299 if (SkColorSpace::Equals(this->colorSpace(), target.get()) ||
300 kAlpha_8_SkColorType == as_IB(this)->onImageInfo().colorType()) {
301 return sk_ref_sp(const_cast<SkImage*>(this));
302 }
303
304 SkColorType targetColorType = kN32_SkColorType;
305 if (SkTransferFunctionBehavior::kRespect == premulBehavior && target->gammaIsLinear()) {
306 targetColorType = kRGBA_F16_SkColorType;
307 }
308
309 // TODO: We might consider making this a deferred conversion?
310 return as_IB(this)->onMakeColorSpace(std::move(target), targetColorType, premulBehavior);
311 }
312
makeNonTextureImage() const313 sk_sp<SkImage> SkImage::makeNonTextureImage() const {
314 if (!this->isTextureBacked()) {
315 return sk_ref_sp(const_cast<SkImage*>(this));
316 }
317 return this->makeRasterImage();
318 }
319
makeRasterImage() const320 sk_sp<SkImage> SkImage::makeRasterImage() const {
321 SkPixmap pm;
322 if (this->peekPixels(&pm)) {
323 return sk_ref_sp(const_cast<SkImage*>(this));
324 }
325
326 const SkImageInfo info = as_IB(this)->onImageInfo();
327 const size_t rowBytes = info.minRowBytes();
328 size_t size = info.computeByteSize(rowBytes);
329 if (SkImageInfo::ByteSizeOverflowed(size)) {
330 return nullptr;
331 }
332
333 sk_sp<SkData> data = SkData::MakeUninitialized(size);
334 pm = { info.makeColorSpace(nullptr), data->writable_data(), info.minRowBytes() };
335 if (!this->readPixels(pm, 0, 0)) {
336 return nullptr;
337 }
338
339 return SkImage::MakeRasterData(info, std::move(data), rowBytes);
340 }
341
342 //////////////////////////////////////////////////////////////////////////////////////
343
344 #if !SK_SUPPORT_GPU
345
MakeFromTexture(GrContext * ctx,const GrBackendTexture & tex,GrSurfaceOrigin origin,SkAlphaType at,sk_sp<SkColorSpace> cs,TextureReleaseProc releaseP,ReleaseContext releaseC)346 sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx,
347 const GrBackendTexture& tex, GrSurfaceOrigin origin,
348 SkAlphaType at, sk_sp<SkColorSpace> cs,
349 TextureReleaseProc releaseP, ReleaseContext releaseC) {
350 return nullptr;
351 }
352
MakeBackendTextureFromSkImage(GrContext *,sk_sp<SkImage>,GrBackendTexture *,BackendTextureReleaseProc *)353 bool SkImage::MakeBackendTextureFromSkImage(GrContext*,
354 sk_sp<SkImage>,
355 GrBackendTexture*,
356 BackendTextureReleaseProc*) {
357 return false;
358 }
359
MakeFromAdoptedTexture(GrContext * ctx,const GrBackendTexture & tex,GrSurfaceOrigin origin,SkAlphaType at,sk_sp<SkColorSpace> cs)360 sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx,
361 const GrBackendTexture& tex, GrSurfaceOrigin origin,
362 SkAlphaType at, sk_sp<SkColorSpace> cs) {
363 return nullptr;
364 }
365
MakeFromAdoptedTexture(GrContext * ctx,const GrBackendTexture & tex,GrSurfaceOrigin origin,SkColorType ct,SkAlphaType at,sk_sp<SkColorSpace> cs)366 sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx,
367 const GrBackendTexture& tex, GrSurfaceOrigin origin,
368 SkColorType ct, SkAlphaType at,
369 sk_sp<SkColorSpace> cs) {
370 return nullptr;
371 }
372
MakeFromYUVTexturesCopy(GrContext * ctx,SkYUVColorSpace space,const GrBackendObject yuvTextureHandles[3],const SkISize yuvSizes[3],GrSurfaceOrigin origin,sk_sp<SkColorSpace> imageColorSpace)373 sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace space,
374 const GrBackendObject yuvTextureHandles[3],
375 const SkISize yuvSizes[3],
376 GrSurfaceOrigin origin,
377 sk_sp<SkColorSpace> imageColorSpace) {
378 return nullptr;
379 }
380
MakeFromYUVTexturesCopy(GrContext * ctx,SkYUVColorSpace space,const GrBackendTexture yuvTextureHandles[3],const SkISize yuvSizes[3],GrSurfaceOrigin origin,sk_sp<SkColorSpace> imageColorSpace)381 sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace space,
382 const GrBackendTexture yuvTextureHandles[3],
383 const SkISize yuvSizes[3],
384 GrSurfaceOrigin origin,
385 sk_sp<SkColorSpace> imageColorSpace) {
386 return nullptr;
387 }
388
makeTextureImage(GrContext *,SkColorSpace * dstColorSpace) const389 sk_sp<SkImage> SkImage::makeTextureImage(GrContext*, SkColorSpace* dstColorSpace) const {
390 return nullptr;
391 }
392
393 #endif
394
395 ///////////////////////////////////////////////////////////////////////////////////////////////////
396
SkImage_pinAsTexture(const SkImage * image,GrContext * ctx)397 bool SkImage_pinAsTexture(const SkImage* image, GrContext* ctx) {
398 SkASSERT(image);
399 SkASSERT(ctx);
400 return as_IB(image)->onPinAsTexture(ctx);
401 }
402
SkImage_unpinAsTexture(const SkImage * image,GrContext * ctx)403 void SkImage_unpinAsTexture(const SkImage* image, GrContext* ctx) {
404 SkASSERT(image);
405 SkASSERT(ctx);
406 as_IB(image)->onUnpinAsTexture(ctx);
407 }
408
409 ///////////////////////////////////////////////////////////////////////////////////////////////////
410
SkImageMakeRasterCopyAndAssignColorSpace(const SkImage * src,SkColorSpace * colorSpace)411 sk_sp<SkImage> SkImageMakeRasterCopyAndAssignColorSpace(const SkImage* src,
412 SkColorSpace* colorSpace) {
413 // Read the pixels out of the source image, with no conversion
414 SkImageInfo info = as_IB(src)->onImageInfo();
415 if (kUnknown_SkColorType == info.colorType()) {
416 SkDEBUGFAIL("Unexpected color type");
417 return nullptr;
418 }
419
420 size_t rowBytes = info.minRowBytes();
421 size_t size = info.computeByteSize(rowBytes);
422 if (SkImageInfo::ByteSizeOverflowed(size)) {
423 return nullptr;
424 }
425 auto data = SkData::MakeUninitialized(size);
426 if (!data) {
427 return nullptr;
428 }
429
430 SkPixmap pm(info, data->writable_data(), rowBytes);
431 if (!src->readPixels(pm, 0, 0, SkImage::kDisallow_CachingHint)) {
432 return nullptr;
433 }
434
435 // Wrap them in a new image with a different color space
436 return SkImage::MakeRasterData(info.makeColorSpace(sk_ref_sp(colorSpace)), data, rowBytes);
437 }
438