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/SkBitmap.h"
9#include "include/core/SkCanvas.h"
10#include "include/core/SkData.h"
11#include "include/core/SkImageEncoder.h"
12#include "include/core/SkImageFilter.h"
13#include "include/core/SkImageGenerator.h"
14#include "include/core/SkPicture.h"
15#include "include/core/SkString.h"
16#include "include/core/SkSurface.h"
17#include "src/core/SkBitmapCache.h"
18#include "src/core/SkCachedData.h"
19#include "src/core/SkColorSpacePriv.h"
20#include "src/core/SkImageFilterCache.h"
21#include "src/core/SkImageFilter_Base.h"
22#include "src/core/SkImagePriv.h"
23#include "src/core/SkNextID.h"
24#include "src/core/SkSpecialImage.h"
25#include "src/image/SkImage_Base.h"
26#include "src/image/SkReadPixelsRec.h"
27#include "src/shaders/SkImageShader.h"
28
29#if SK_SUPPORT_GPU
30#include "include/gpu/GrContext.h"
31#include "src/image/SkImage_Gpu.h"
32#endif
33#include "include/gpu/GrBackendSurface.h"
34
35SkImage::SkImage(const SkImageInfo& info, uint32_t uniqueID)
36 : fInfo(info)
37 , fUniqueID(kNeedNewImageUniqueID == uniqueID ? SkNextID::ImageID() : uniqueID) {
38 SkASSERT(info.width() > 0);
39 SkASSERT(info.height() > 0);
40}
41
42bool SkImage::peekPixels(SkPixmap* pm) const {
43 SkPixmap tmp;
44 if (!pm) {
45 pm = &tmp;
46 }
47 return as_IB(this)->onPeekPixels(pm);
48}
49
50bool SkImage::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, int srcX,
51 int srcY, CachingHint chint) const {
52 return as_IB(this)->onReadPixels(dstInfo, dstPixels, dstRowBytes, srcX, srcY, chint);
53}
54
55bool 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, 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
77SkColorType SkImage::colorType() const { return fInfo.colorType(); }
78
79SkAlphaType SkImage::alphaType() const { return fInfo.alphaType(); }
80
81SkColorSpace* SkImage::colorSpace() const { return fInfo.colorSpace(); }
82
83sk_sp<SkColorSpace> SkImage::refColorSpace() const { return fInfo.refColorSpace(); }
84
85sk_sp<SkShader> SkImage::makeShader(SkTileMode tmx, SkTileMode tmy,
86 const SkMatrix* localMatrix) const {
87 return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tmx, tmy, localMatrix);
88}
89
90sk_sp<SkData> SkImage::encodeToData(SkEncodedImageFormat type, int quality) const {
91 SkBitmap bm;
92 if (as_IB(this)->getROPixels(&bm)) {
93 return SkEncodeBitmap(bm, type, quality);
94 }
95 return nullptr;
96}
97
98sk_sp<SkData> SkImage::encodeToData() const {
99 if (auto encoded = this->refEncodedData()) {
100 return encoded;
101 }
102
103 return this->encodeToData(SkEncodedImageFormat::kPNG, 100);
104}
105
106sk_sp<SkData> SkImage::refEncodedData() const {
107 return sk_sp<SkData>(as_IB(this)->onRefEncoded());
108}
109
110sk_sp<SkImage> SkImage::MakeFromEncoded(sk_sp<SkData> encoded, const SkIRect* subset) {
111 if (nullptr == encoded || 0 == encoded->size()) {
112 return nullptr;
113 }
114 return SkImage::MakeFromGenerator(SkImageGenerator::MakeFromEncoded(std::move(encoded)),
115 subset);
116}
117
118///////////////////////////////////////////////////////////////////////////////////////////////////
119
120sk_sp<SkImage> SkImage::makeSubset(const SkIRect& subset) const {
121 if (subset.isEmpty()) {
122 return nullptr;
123 }
124
125 const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
126 if (!bounds.contains(subset)) {
127 return nullptr;
128 }
129
130 // optimization : return self if the subset == our bounds
131 if (bounds == subset) {
132 return sk_ref_sp(const_cast<SkImage*>(this));
133 }
134
135 // CONTEXT TODO: propagate the context parameter to the top-level API
136#if SK_SUPPORT_GPU
137 return as_IB(this)->onMakeSubset(as_IB(this)->context(), subset);
138#else
139 return as_IB(this)->onMakeSubset(nullptr, subset);
140#endif
141}
142
143#if SK_SUPPORT_GPU
144
145bool SkImage::isTextureBacked() const { return as_IB(this)->onIsTextureBacked(); }
146
147GrBackendTexture SkImage::getBackendTexture(bool flushPendingGrContextIO,
148 GrSurfaceOrigin* origin) const {
149 return as_IB(this)->onGetBackendTexture(flushPendingGrContextIO, origin);
150}
151
152bool SkImage::isValid(GrContext* context) const {
153 if (context && context->abandoned()) {
154 return false;
155 }
156 return as_IB(this)->onIsValid(context);
157}
158
159GrSemaphoresSubmitted SkImage::flush(GrContext* context, const GrFlushInfo& flushInfo) {
160 return as_IB(this)->onFlush(context, flushInfo);
161}
162
163void SkImage::flush(GrContext* context) { as_IB(this)->onFlush(context, {}); }
164
165#else
166
167bool SkImage::isTextureBacked() const { return false; }
168
169GrBackendTexture SkImage::getBackendTexture(bool flushPendingGrContextIO,
170 GrSurfaceOrigin* origin) const {
171 return GrBackendTexture(); // invalid
172}
173
174bool SkImage::isValid(GrContext* context) const {
175 if (context) {
176 return false;
177 }
178 return as_IB(this)->onIsValid(context);
179}
180
181GrSemaphoresSubmitted SkImage::flush(GrContext*, const GrFlushInfo&) {
182 return GrSemaphoresSubmitted::kNo;
183}
184
185void SkImage::flush(GrContext*) {}
186
187#endif
188
189///////////////////////////////////////////////////////////////////////////////
190
191SkImage_Base::SkImage_Base(const SkImageInfo& info, uint32_t uniqueID)
192 : INHERITED(info, uniqueID), fAddedToRasterCache(false) {}
193
194SkImage_Base::~SkImage_Base() {
195 if (fAddedToRasterCache.load()) {
196 SkNotifyBitmapGenIDIsStale(this->uniqueID());
197 }
198}
199
200GrBackendTexture SkImage_Base::onGetBackendTexture(bool flushPendingGrContextIO,
201 GrSurfaceOrigin* origin) const {
202 return GrBackendTexture(); // invalid
203}
204
205bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY, CachingHint chint) const {
206 return this->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX, srcY, chint);
207}
208
209///////////////////////////////////////////////////////////////////////////////////////////////////
210
211sk_sp<SkImage> SkImage::MakeFromBitmap(const SkBitmap& bm) {
212 if (!bm.pixelRef()) {
213 return nullptr;
214 }
215
216 return SkMakeImageFromRasterBitmap(bm, kIfMutable_SkCopyPixelsMode);
217}
218
219bool SkImage::asLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode ) const {
220 return as_IB(this)->onAsLegacyBitmap(bitmap);
221}
222
223sk_sp<SkCachedData> SkImage_Base::getPlanes(SkYUVASizeInfo*, SkYUVAIndex[4],
224 SkYUVColorSpace*, const void*[4]) {
225 return nullptr;
226}
227
228bool SkImage_Base::onAsLegacyBitmap(SkBitmap* bitmap) const {
229 // As the base-class, all we can do is make a copy (regardless of mode).
230 // Subclasses that want to be more optimal should override.
231 SkImageInfo info = fInfo.makeColorType(kN32_SkColorType).makeColorSpace(nullptr);
232 if (!bitmap->tryAllocPixels(info)) {
233 return false;
234 }
235 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
236 bitmap->reset();
237 return false;
238 }
239
240 bitmap->setImmutable();
241 return true;
242}
243
244sk_sp<SkImage> SkImage::MakeFromPicture(sk_sp<SkPicture> picture, const SkISize& dimensions,
245 const SkMatrix* matrix, const SkPaint* paint,
246 BitDepth bitDepth, sk_sp<SkColorSpace> colorSpace) {
247 return MakeFromGenerator(SkImageGenerator::MakeFromPicture(dimensions, std::move(picture),
248 matrix, paint, bitDepth,
249 std::move(colorSpace)));
250}
251
252sk_sp<SkImage> SkImage::makeWithFilter(const SkImageFilter* filter, const SkIRect& subset,
253 const SkIRect& clipBounds, SkIRect* outSubset,
254 SkIPoint* offset) const {
255 GrContext* context = as_IB(this)->context();
256
257 return this->makeWithFilter(context, filter, subset, clipBounds, outSubset, offset);
258}
259
260sk_sp<SkImage> SkImage::makeWithFilter(GrContext* grContext,
261 const SkImageFilter* filter, const SkIRect& subset,
262 const SkIRect& clipBounds, SkIRect* outSubset,
263 SkIPoint* offset) const {
264 if (!filter || !outSubset || !offset || !this->bounds().contains(subset)) {
265 return nullptr;
266 }
267 sk_sp<SkSpecialImage> srcSpecialImage =
268#if SK_SUPPORT_GPU
269 SkSpecialImage::MakeFromImage(grContext, subset, sk_ref_sp(const_cast<SkImage*>(this)));
270#else
271 SkSpecialImage::MakeFromImage(nullptr, subset, sk_ref_sp(const_cast<SkImage*>(this)));
272#endif
273 if (!srcSpecialImage) {
274 return nullptr;
275 }
276
277 sk_sp<SkImageFilterCache> cache(
278 SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize));
279
280 // The filters operate in the local space of the src image, where (0,0) corresponds to the
281 // subset's top left corner. But the clip bounds and any crop rects on the filters are in the
282 // original coordinate system, so configure the CTM to correct crop rects and explicitly adjust
283 // the clip bounds (since it is assumed to already be in image space).
284 SkImageFilter_Base::Context context(SkMatrix::MakeTrans(-subset.x(), -subset.y()),
285 clipBounds.makeOffset(-subset.topLeft()),
286 cache.get(), fInfo.colorType(), fInfo.colorSpace(),
287 srcSpecialImage.get());
288
289 sk_sp<SkSpecialImage> result = as_IFB(filter)->filterImage(context).imageAndOffset(offset);
290 if (!result) {
291 return nullptr;
292 }
293
294 // The output image and offset are relative to the subset rectangle, so the offset needs to
295 // be shifted to put it in the correct spot with respect to the original coordinate system
296 offset->fX += subset.x();
297 offset->fY += subset.y();
298
299 // Final clip against the exact clipBounds (the clip provided in the context gets adjusted
300 // to account for pixel-moving filters so doesn't always exactly match when finished). The
301 // clipBounds are translated into the clippedDstRect coordinate space, including the
302 // result->subset() ensures that the result's image pixel origin does not affect results.
303 SkIRect dstRect = result->subset();
304 SkIRect clippedDstRect = dstRect;
305 if (!clippedDstRect.intersect(clipBounds.makeOffset(result->subset().topLeft() - *offset))) {
306 return nullptr;
307 }
308
309 // Adjust the geometric offset if the top-left corner moved as well
310 offset->fX += (clippedDstRect.x() - dstRect.x());
311 offset->fY += (clippedDstRect.y() - dstRect.y());
312 *outSubset = clippedDstRect;
313 return result->asImage();
314}
315
316bool SkImage::isLazyGenerated() const {
317 return as_IB(this)->onIsLazyGenerated();
318}
319
320bool SkImage::isAlphaOnly() const { return SkColorTypeIsAlphaOnly(fInfo.colorType()); }
321
322sk_sp<SkImage> SkImage::makeColorSpace(sk_sp<SkColorSpace> target) const {
323 if (!target) {
324 return nullptr;
325 }
326
327 // No need to create a new image if:
328 // (1) The color spaces are equal.
329 // (2) The color type is kAlpha8.
330 SkColorSpace* colorSpace = this->colorSpace();
331 if (!colorSpace) {
332 colorSpace = sk_srgb_singleton();
333 }
334 if (SkColorSpace::Equals(colorSpace, target.get()) || this->isAlphaOnly()) {
335 return sk_ref_sp(const_cast<SkImage*>(this));
336 }
337
338 // CONTEXT TODO: propagate the context parameter to the top-level API
339#if SK_SUPPORT_GPU
340 return as_IB(this)->onMakeColorTypeAndColorSpace(as_IB(this)->context(),
341#else
342 return as_IB(this)->onMakeColorTypeAndColorSpace(nullptr,
343#endif
344 this->colorType(), std::move(target));
345}
346
347sk_sp<SkImage> SkImage::makeColorTypeAndColorSpace(SkColorType targetColorType,
348 sk_sp<SkColorSpace> targetColorSpace) const {
349 if (kUnknown_SkColorType == targetColorType || !targetColorSpace) {
350 return nullptr;
351 }
352
353 SkColorType colorType = this->colorType();
354 SkColorSpace* colorSpace = this->colorSpace();
355 if (!colorSpace) {
356 colorSpace = sk_srgb_singleton();
357 }
358 if (colorType == targetColorType &&
359 (SkColorSpace::Equals(colorSpace, targetColorSpace.get()) || this->isAlphaOnly())) {
360 return sk_ref_sp(const_cast<SkImage*>(this));
361 }
362
363 // CONTEXT TODO: propagate the context parameter to the top-level API
364#if SK_SUPPORT_GPU
365 return as_IB(this)->onMakeColorTypeAndColorSpace(as_IB(this)->context(),
366#else
367 return as_IB(this)->onMakeColorTypeAndColorSpace(nullptr,
368#endif
369 targetColorType, std::move(targetColorSpace));
370}
371
372sk_sp<SkImage> SkImage::reinterpretColorSpace(sk_sp<SkColorSpace> target) const {
373 if (!target) {
374 return nullptr;
375 }
376
377 // No need to create a new image if:
378 // (1) The color spaces are equal.
379 // (2) The color type is kAlpha8.
380 SkColorSpace* colorSpace = this->colorSpace();
381 if (!colorSpace) {
382 colorSpace = sk_srgb_singleton();
383 }
384 if (SkColorSpace::Equals(colorSpace, target.get()) || this->isAlphaOnly()) {
385 return sk_ref_sp(const_cast<SkImage*>(this));
386 }
387
388 return as_IB(this)->onReinterpretColorSpace(std::move(target));
389}
390
391sk_sp<SkImage> SkImage::makeNonTextureImage() const {
392 if (!this->isTextureBacked()) {
393 return sk_ref_sp(const_cast<SkImage*>(this));
394 }
395 return this->makeRasterImage();
396}
397
398sk_sp<SkImage> SkImage::makeRasterImage(CachingHint chint) const {
399 SkPixmap pm;
400 if (this->peekPixels(&pm)) {
401 return sk_ref_sp(const_cast<SkImage*>(this));
402 }
403
404 const size_t rowBytes = fInfo.minRowBytes();
405 size_t size = fInfo.computeByteSize(rowBytes);
406 if (SkImageInfo::ByteSizeOverflowed(size)) {
407 return nullptr;
408 }
409
410 sk_sp<SkData> data = SkData::MakeUninitialized(size);
411 pm = {fInfo.makeColorSpace(nullptr), data->writable_data(), fInfo.minRowBytes()};
412 if (!this->readPixels(pm, 0, 0, chint)) {
413 return nullptr;
414 }
415
416 return SkImage::MakeRasterData(fInfo, std::move(data), rowBytes);
417}
418
419//////////////////////////////////////////////////////////////////////////////////////
420
421#if !SK_SUPPORT_GPU
422
423sk_sp<SkImage> SkImage::DecodeToTexture(GrContext*, const void*, size_t, const SkIRect*) {
424 return nullptr;
425}
426
427sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx,
428 const GrBackendTexture& tex, GrSurfaceOrigin origin,
429 SkColorType ct, SkAlphaType at, sk_sp<SkColorSpace> cs,
430 TextureReleaseProc releaseP, ReleaseContext releaseC) {
431 return nullptr;
432}
433
434sk_sp<SkImage> SkImage::MakeFromCompressedTexture(GrContext* ctx,
435 const GrBackendTexture& tex,
436 GrSurfaceOrigin origin,
437 SkAlphaType at,
438 sk_sp<SkColorSpace> cs,
439 TextureReleaseProc releaseP,
440 ReleaseContext releaseC) {
441 return nullptr;
442}
443
444bool SkImage::MakeBackendTextureFromSkImage(GrContext*,
445 sk_sp<SkImage>,
446 GrBackendTexture*,
447 BackendTextureReleaseProc*) {
448 return false;
449}
450
451sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx,
452 const GrBackendTexture& tex, GrSurfaceOrigin origin,
453 SkColorType ct, SkAlphaType at,
454 sk_sp<SkColorSpace> cs) {
455 return nullptr;
456}
457
458sk_sp<SkImage> SkImage::MakeFromYUVATexturesCopy(GrContext* context,
459 SkYUVColorSpace yuvColorSpace,
460 const GrBackendTexture yuvaTextures[],
461 const SkYUVAIndex yuvaIndices[4],
462 SkISize imageSize,
463 GrSurfaceOrigin imageOrigin,
464 sk_sp<SkColorSpace> imageColorSpace) {
465 return nullptr;
466}
467
468sk_sp<SkImage> SkImage::MakeFromYUVATexturesCopyWithExternalBackend(
469 GrContext* context,
470 SkYUVColorSpace yuvColorSpace,
471 const GrBackendTexture yuvaTextures[],
472 const SkYUVAIndex yuvaIndices[4],
473 SkISize imageSize,
474 GrSurfaceOrigin imageOrigin,
475 const GrBackendTexture& backendTexture,
476 sk_sp<SkColorSpace> imageColorSpace,
477 TextureReleaseProc textureReleaseProc,
478 ReleaseContext releaseContext) {
479 return nullptr;
480}
481
482sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace space,
483 const GrBackendTexture[3],
484 GrSurfaceOrigin origin,
485 sk_sp<SkColorSpace> imageColorSpace) {
486 return nullptr;
487}
488
489sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopyWithExternalBackend(
490 GrContext* context, SkYUVColorSpace yuvColorSpace, const GrBackendTexture yuvTextures[3],
491 GrSurfaceOrigin surfaceOrigin, const GrBackendTexture& backendTexture,
492 sk_sp<SkColorSpace> colorSpace) {
493 return nullptr;
494}
495
496sk_sp<SkImage> SkImage::MakeFromNV12TexturesCopy(GrContext* ctx, SkYUVColorSpace space,
497 const GrBackendTexture[2],
498 GrSurfaceOrigin origin,
499 sk_sp<SkColorSpace> imageColorSpace) {
500 return nullptr;
501}
502
503sk_sp<SkImage> SkImage::makeTextureImage(GrContext*, GrMipMapped, SkBudgeted) const {
504 return nullptr;
505}
506
507sk_sp<SkImage> SkImage::MakeFromNV12TexturesCopyWithExternalBackend(
508 GrContext* context,
509 SkYUVColorSpace yuvColorSpace,
510 const GrBackendTexture nv12Textures[2],
511 GrSurfaceOrigin imageOrigin,
512 const GrBackendTexture& backendTexture,
513 sk_sp<SkColorSpace> imageColorSpace,
514 TextureReleaseProc textureReleaseProc,
515 ReleaseContext releaseContext) {
516 return nullptr;
517}
518
519#endif
520
521///////////////////////////////////////////////////////////////////////////////////////////////////
522
523bool SkImage_pinAsTexture(const SkImage* image, GrContext* ctx) {
524 SkASSERT(image);
525 SkASSERT(ctx);
526 return as_IB(image)->onPinAsTexture(ctx);
527}
528
529void SkImage_unpinAsTexture(const SkImage* image, GrContext* ctx) {
530 SkASSERT(image);
531 SkASSERT(ctx);
532 as_IB(image)->onUnpinAsTexture(ctx);
533}
534
535SkIRect SkImage_getSubset(const SkImage* image) {
536 SkASSERT(image);
537 return as_IB(image)->onGetSubset();
538}
539