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 "SkColorSpace_Base.h"
12 #include "SkData.h"
13 #include "SkImageEncoder.h"
14 #include "SkImageFilter.h"
15 #include "SkImageFilterCache.h"
16 #include "SkImageGenerator.h"
17 #include "SkImagePriv.h"
18 #include "SkImageShader.h"
19 #include "SkImage_Base.h"
20 #include "SkNextID.h"
21 #include "SkPicture.h"
22 #include "SkPixelRef.h"
23 #include "SkPixelSerializer.h"
24 #include "SkReadPixelsRec.h"
25 #include "SkSpecialImage.h"
26 #include "SkStream.h"
27 #include "SkString.h"
28 #include "SkSurface.h"
29
30 #if SK_SUPPORT_GPU
31 #include "GrTexture.h"
32 #include "GrContext.h"
33 #include "SkImage_Gpu.h"
34 #endif
35
SkImage(int width,int height,uint32_t uniqueID)36 SkImage::SkImage(int width, int height, uint32_t uniqueID)
37 : fWidth(width)
38 , fHeight(height)
39 , fUniqueID(kNeedNewImageUniqueID == uniqueID ? SkNextID::ImageID() : uniqueID)
40 {
41 SkASSERT(width > 0);
42 SkASSERT(height > 0);
43 }
44
peekPixels(SkPixmap * pm) const45 bool SkImage::peekPixels(SkPixmap* pm) const {
46 SkPixmap tmp;
47 if (!pm) {
48 pm = &tmp;
49 }
50 return as_IB(this)->onPeekPixels(pm);
51 }
52
readPixels(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRowBytes,int srcX,int srcY,CachingHint chint) const53 bool SkImage::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
54 int srcX, int srcY, CachingHint chint) const {
55 return as_IB(this)->onReadPixels(dstInfo, dstPixels, dstRowBytes, srcX, srcY, chint);
56 }
57
scalePixels(const SkPixmap & dst,SkFilterQuality quality,CachingHint chint) const58 bool SkImage::scalePixels(const SkPixmap& dst, SkFilterQuality quality, CachingHint chint) const {
59 if (this->width() == dst.width() && this->height() == dst.height()) {
60 return this->readPixels(dst, 0, 0, chint);
61 }
62
63 // Idea: If/when SkImageGenerator supports a native-scaling API (where the generator itself
64 // can scale more efficiently) we should take advantage of it here.
65 //
66 SkBitmap bm;
67 if (as_IB(this)->getROPixels(&bm, dst.info().colorSpace(), chint)) {
68 SkPixmap pmap;
69 // Note: By calling the pixmap scaler, we never cache the final result, so the chint
70 // is (currently) only being applied to the getROPixels. If we get a request to
71 // also attempt to cache the final (scaled) result, we would add that logic here.
72 //
73 return bm.peekPixels(&pmap) && pmap.scalePixels(dst, quality);
74 }
75 return false;
76 }
77
78 ///////////////////////////////////////////////////////////////////////////////////////////////////
79
alphaType() const80 SkAlphaType SkImage::alphaType() const {
81 return as_IB(this)->onAlphaType();
82 }
83
colorSpace() const84 SkColorSpace* SkImage::colorSpace() const {
85 return as_IB(this)->onImageInfo().colorSpace();
86 }
87
refColorSpace() const88 sk_sp<SkColorSpace> SkImage::refColorSpace() const {
89 return as_IB(this)->onImageInfo().refColorSpace();
90 }
91
makeShader(SkShader::TileMode tileX,SkShader::TileMode tileY,const SkMatrix * localMatrix) const92 sk_sp<SkShader> SkImage::makeShader(SkShader::TileMode tileX, SkShader::TileMode tileY,
93 const SkMatrix* localMatrix) const {
94 return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tileX, tileY, localMatrix);
95 }
96
encodeToData(SkEncodedImageFormat type,int quality) const97 sk_sp<SkData> SkImage::encodeToData(SkEncodedImageFormat type, int quality) const {
98 SkBitmap bm;
99 SkColorSpace* legacyColorSpace = nullptr;
100 if (as_IB(this)->getROPixels(&bm, legacyColorSpace)) {
101 SkDynamicMemoryWStream buf;
102 return SkEncodeImage(&buf, bm, type, quality) ? buf.detachAsData() : nullptr;
103 }
104 return nullptr;
105 }
106
encodeToData(SkPixelSerializer * serializer) const107 sk_sp<SkData> SkImage::encodeToData(SkPixelSerializer* serializer) const {
108 sk_sp<SkData> encoded(this->refEncodedData());
109 if (encoded &&
110 (!serializer || serializer->useEncodedData(encoded->data(), encoded->size()))) {
111 return encoded;
112 }
113
114 SkBitmap bm;
115 SkPixmap pmap;
116 SkColorSpace* legacyColorSpace = nullptr;
117 if (as_IB(this)->getROPixels(&bm, legacyColorSpace) && bm.peekPixels(&pmap)) {
118 if (serializer) {
119 return serializer->encodeToData(pmap);
120 } else {
121 SkDynamicMemoryWStream buf;
122 return SkEncodeImage(&buf, pmap, SkEncodedImageFormat::kPNG, 100)
123 ? buf.detachAsData() : nullptr;
124 }
125 }
126
127 return nullptr;
128 }
129
refEncodedData() const130 sk_sp<SkData> SkImage::refEncodedData() const {
131 return sk_sp<SkData>(as_IB(this)->onRefEncoded());
132 }
133
MakeFromEncoded(sk_sp<SkData> encoded,const SkIRect * subset)134 sk_sp<SkImage> SkImage::MakeFromEncoded(sk_sp<SkData> encoded, const SkIRect* subset) {
135 if (nullptr == encoded || 0 == encoded->size()) {
136 return nullptr;
137 }
138 return SkImage::MakeFromGenerator(SkImageGenerator::MakeFromEncoded(encoded), subset);
139 }
140
141 ///////////////////////////////////////////////////////////////////////////////////////////////////
142
toString(SkString * str) const143 const char* SkImage::toString(SkString* str) const {
144 str->appendf("image: (id:%d (%d, %d) %s)", this->uniqueID(), this->width(), this->height(),
145 this->isOpaque() ? "opaque" : "");
146 return str->c_str();
147 }
148
makeSubset(const SkIRect & subset) const149 sk_sp<SkImage> SkImage::makeSubset(const SkIRect& subset) const {
150 if (subset.isEmpty()) {
151 return nullptr;
152 }
153
154 const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
155 if (!bounds.contains(subset)) {
156 return nullptr;
157 }
158
159 // optimization : return self if the subset == our bounds
160 if (bounds == subset) {
161 return sk_ref_sp(const_cast<SkImage*>(this));
162 }
163 return as_IB(this)->onMakeSubset(subset);
164 }
165
166 #if SK_SUPPORT_GPU
167
getTexture() const168 GrTexture* SkImage::getTexture() const {
169 return as_IB(this)->onGetTexture();
170 }
171
isTextureBacked() const172 bool SkImage::isTextureBacked() const { return SkToBool(as_IB(this)->peekProxy()); }
173
getTextureHandle(bool flushPendingGrContextIO,GrSurfaceOrigin * origin) const174 GrBackendObject SkImage::getTextureHandle(bool flushPendingGrContextIO,
175 GrSurfaceOrigin* origin) const {
176 return as_IB(this)->onGetTextureHandle(flushPendingGrContextIO, origin);
177 }
178
isValid(GrContext * context) const179 bool SkImage::isValid(GrContext* context) const {
180 if (context && context->abandoned()) {
181 return false;
182 }
183 return as_IB(this)->onIsValid(context);
184 }
185
186 #else
187
getTexture() const188 GrTexture* SkImage::getTexture() const { return nullptr; }
189
isTextureBacked() const190 bool SkImage::isTextureBacked() const { return false; }
191
getTextureHandle(bool,GrSurfaceOrigin *) const192 GrBackendObject SkImage::getTextureHandle(bool, GrSurfaceOrigin*) const { return 0; }
193
isValid(GrContext * context) const194 bool SkImage::isValid(GrContext* context) const {
195 if (context) {
196 return false;
197 }
198 return as_IB(this)->onIsValid(context);
199 }
200
201 #endif
202
203 ///////////////////////////////////////////////////////////////////////////////
204
SkImage_Base(int width,int height,uint32_t uniqueID)205 SkImage_Base::SkImage_Base(int width, int height, uint32_t uniqueID)
206 : INHERITED(width, height, uniqueID)
207 , fAddedToCache(false)
208 {}
209
~SkImage_Base()210 SkImage_Base::~SkImage_Base() {
211 if (fAddedToCache.load()) {
212 SkNotifyBitmapGenIDIsStale(this->uniqueID());
213 }
214 }
215
readPixels(const SkPixmap & pmap,int srcX,int srcY,CachingHint chint) const216 bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY, CachingHint chint) const {
217 return this->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX, srcY, chint);
218 }
219
220 ///////////////////////////////////////////////////////////////////////////////////////////////////
221
MakeFromBitmap(const SkBitmap & bm)222 sk_sp<SkImage> SkImage::MakeFromBitmap(const SkBitmap& bm) {
223 SkPixelRef* pr = bm.pixelRef();
224 if (nullptr == pr) {
225 return nullptr;
226 }
227
228 return SkMakeImageFromRasterBitmap(bm, kIfMutable_SkCopyPixelsMode);
229 }
230
asLegacyBitmap(SkBitmap * bitmap,LegacyBitmapMode mode) const231 bool SkImage::asLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode mode) const {
232 return as_IB(this)->onAsLegacyBitmap(bitmap, mode);
233 }
234
onAsLegacyBitmap(SkBitmap * bitmap,LegacyBitmapMode mode) const235 bool SkImage_Base::onAsLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode mode) const {
236 // As the base-class, all we can do is make a copy (regardless of mode).
237 // Subclasses that want to be more optimal should override.
238 SkImageInfo info = this->onImageInfo().makeColorType(kN32_SkColorType).makeColorSpace(nullptr);
239 if (!bitmap->tryAllocPixels(info)) {
240 return false;
241 }
242 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
243 bitmap->reset();
244 return false;
245 }
246
247 if (kRO_LegacyBitmapMode == mode) {
248 bitmap->setImmutable();
249 }
250 return true;
251 }
252
MakeFromPicture(sk_sp<SkPicture> picture,const SkISize & dimensions,const SkMatrix * matrix,const SkPaint * paint,BitDepth bitDepth,sk_sp<SkColorSpace> colorSpace)253 sk_sp<SkImage> SkImage::MakeFromPicture(sk_sp<SkPicture> picture, const SkISize& dimensions,
254 const SkMatrix* matrix, const SkPaint* paint,
255 BitDepth bitDepth, sk_sp<SkColorSpace> colorSpace) {
256 return MakeFromGenerator(SkImageGenerator::MakeFromPicture(dimensions, std::move(picture),
257 matrix, paint, bitDepth,
258 std::move(colorSpace)));
259 }
260
makeWithFilter(const SkImageFilter * filter,const SkIRect & subset,const SkIRect & clipBounds,SkIRect * outSubset,SkIPoint * offset) const261 sk_sp<SkImage> SkImage::makeWithFilter(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 SkColorSpace* colorSpace = as_IB(this)->onImageInfo().colorSpace();
268 sk_sp<SkSpecialImage> srcSpecialImage = SkSpecialImage::MakeFromImage(
269 subset, sk_ref_sp(const_cast<SkImage*>(this)), colorSpace);
270 if (!srcSpecialImage) {
271 return nullptr;
272 }
273
274 sk_sp<SkImageFilterCache> cache(
275 SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize));
276 SkImageFilter::OutputProperties outputProperties(colorSpace);
277 SkImageFilter::Context context(SkMatrix::I(), clipBounds, cache.get(), outputProperties);
278
279 sk_sp<SkSpecialImage> result = filter->filterImage(srcSpecialImage.get(), context, offset);
280 if (!result) {
281 return nullptr;
282 }
283
284 *outSubset = SkIRect::MakeWH(result->width(), result->height());
285 if (!outSubset->intersect(clipBounds.makeOffset(-offset->x(), -offset->y()))) {
286 return nullptr;
287 }
288 offset->fX += outSubset->x();
289 offset->fY += outSubset->y();
290
291 // Note that here we're returning the special image's entire backing store, loose padding
292 // and all!
293 return result->asImage();
294 }
295
isLazyGenerated() const296 bool SkImage::isLazyGenerated() const {
297 return as_IB(this)->onIsLazyGenerated();
298 }
299
isAlphaOnly() const300 bool SkImage::isAlphaOnly() const {
301 return as_IB(this)->onImageInfo().colorType() == kAlpha_8_SkColorType;
302 }
303
makeColorSpace(sk_sp<SkColorSpace> target,SkTransferFunctionBehavior premulBehavior) const304 sk_sp<SkImage> SkImage::makeColorSpace(sk_sp<SkColorSpace> target,
305 SkTransferFunctionBehavior premulBehavior) const {
306 SkColorSpaceTransferFn fn;
307 if (!target || !target->isNumericalTransferFn(&fn)) {
308 return nullptr;
309 }
310
311 // No need to create a new image if:
312 // (1) The color spaces are equal.
313 // (2) The color type is kAlpha8.
314 if (SkColorSpace::Equals(this->colorSpace(), target.get()) ||
315 kAlpha_8_SkColorType == as_IB(this)->onImageInfo().colorType()) {
316 return sk_ref_sp(const_cast<SkImage*>(this));
317 }
318
319 SkColorType targetColorType = kN32_SkColorType;
320 if (SkTransferFunctionBehavior::kRespect == premulBehavior && target->gammaIsLinear()) {
321 targetColorType = kRGBA_F16_SkColorType;
322 }
323
324 // TODO: We might consider making this a deferred conversion?
325 return as_IB(this)->onMakeColorSpace(std::move(target), targetColorType, premulBehavior);
326 }
327
328 //////////////////////////////////////////////////////////////////////////////////////
329
330 #if !SK_SUPPORT_GPU
331
MakeTextureFromMipMap(GrContext *,const SkImageInfo &,const GrMipLevel texels[],int mipLevelCount,SkBudgeted,SkDestinationSurfaceColorMode)332 sk_sp<SkImage> MakeTextureFromMipMap(GrContext*, const SkImageInfo&, const GrMipLevel texels[],
333 int mipLevelCount, SkBudgeted, SkDestinationSurfaceColorMode) {
334 return nullptr;
335 }
336
MakeFromTexture(GrContext *,const GrBackendTextureDesc &,SkAlphaType,sk_sp<SkColorSpace>,TextureReleaseProc,ReleaseContext)337 sk_sp<SkImage> SkImage::MakeFromTexture(GrContext*, const GrBackendTextureDesc&, SkAlphaType,
338 sk_sp<SkColorSpace>, TextureReleaseProc, ReleaseContext) {
339 return nullptr;
340 }
341
MakeFromTexture(GrContext * ctx,const GrBackendTexture & tex,GrSurfaceOrigin origin,SkAlphaType at,sk_sp<SkColorSpace> cs,TextureReleaseProc releaseP,ReleaseContext releaseC)342 sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx,
343 const GrBackendTexture& tex, GrSurfaceOrigin origin,
344 SkAlphaType at, sk_sp<SkColorSpace> cs,
345 TextureReleaseProc releaseP, ReleaseContext releaseC) {
346 return nullptr;
347 }
348
getDeferredTextureImageData(const GrContextThreadSafeProxy &,const DeferredTextureImageUsageParams[],int paramCnt,void * buffer,SkColorSpace * dstColorSpace,SkColorType dstColorType) const349 size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy&,
350 const DeferredTextureImageUsageParams[],
351 int paramCnt, void* buffer,
352 SkColorSpace* dstColorSpace,
353 SkColorType dstColorType) const {
354 return 0;
355 }
356
MakeFromDeferredTextureImageData(GrContext * context,const void *,SkBudgeted)357 sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, const void*,
358 SkBudgeted) {
359 return nullptr;
360 }
361
MakeFromAdoptedTexture(GrContext *,const GrBackendTextureDesc &,SkAlphaType,sk_sp<SkColorSpace>)362 sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext*, const GrBackendTextureDesc&,
363 SkAlphaType, sk_sp<SkColorSpace>) {
364 return nullptr;
365 }
366
MakeFromAdoptedTexture(GrContext * ctx,const GrBackendTexture & tex,GrSurfaceOrigin origin,SkAlphaType at,sk_sp<SkColorSpace> cs)367 sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx,
368 const GrBackendTexture& tex, GrSurfaceOrigin origin,
369 SkAlphaType at, 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
makeTextureImage(GrContext *,SkColorSpace * dstColorSpace) const381 sk_sp<SkImage> SkImage::makeTextureImage(GrContext*, SkColorSpace* dstColorSpace) const {
382 return nullptr;
383 }
384
makeNonTextureImage() const385 sk_sp<SkImage> SkImage::makeNonTextureImage() const {
386 return sk_ref_sp(const_cast<SkImage*>(this));
387 }
388
389 #endif
390
391 ///////////////////////////////////////////////////////////////////////////////////////////////////
392
MakeTextureFromMipMap(GrContext *,const SkImageInfo &,const GrMipLevel texels[],int mipLevelCount,SkBudgeted)393 sk_sp<SkImage> MakeTextureFromMipMap(GrContext*, const SkImageInfo&, const GrMipLevel texels[],
394 int mipLevelCount, SkBudgeted) {
395 return nullptr;
396 }
397
398 ///////////////////////////////////////////////////////////////////////////////////////////////////
399 #include "SkImageDeserializer.h"
400
makeFromData(SkData * data,const SkIRect * subset)401 sk_sp<SkImage> SkImageDeserializer::makeFromData(SkData* data, const SkIRect* subset) {
402 return SkImage::MakeFromEncoded(sk_ref_sp(data), subset);
403 }
makeFromMemory(const void * data,size_t length,const SkIRect * subset)404 sk_sp<SkImage> SkImageDeserializer::makeFromMemory(const void* data, size_t length,
405 const SkIRect* subset) {
406 return SkImage::MakeFromEncoded(SkData::MakeWithCopy(data, length), subset);
407 }
408
409 ///////////////////////////////////////////////////////////////////////////////////////////////////
410
SkImage_pinAsTexture(const SkImage * image,GrContext * ctx)411 bool SkImage_pinAsTexture(const SkImage* image, GrContext* ctx) {
412 SkASSERT(image);
413 SkASSERT(ctx);
414 return as_IB(image)->onPinAsTexture(ctx);
415 }
416
SkImage_unpinAsTexture(const SkImage * image,GrContext * ctx)417 void SkImage_unpinAsTexture(const SkImage* image, GrContext* ctx) {
418 SkASSERT(image);
419 SkASSERT(ctx);
420 as_IB(image)->onUnpinAsTexture(ctx);
421 }
422
423 ///////////////////////////////////////////////////////////////////////////////////////////////////
424
SkImageMakeRasterCopyAndAssignColorSpace(const SkImage * src,SkColorSpace * colorSpace)425 sk_sp<SkImage> SkImageMakeRasterCopyAndAssignColorSpace(const SkImage* src,
426 SkColorSpace* colorSpace) {
427 // Read the pixels out of the source image, with no conversion
428 SkImageInfo info = as_IB(src)->onImageInfo();
429 if (kUnknown_SkColorType == info.colorType()) {
430 SkDEBUGFAIL("Unexpected color type");
431 return nullptr;
432 }
433
434 size_t rowBytes = info.minRowBytes();
435 size_t size = info.getSafeSize(rowBytes);
436 auto data = SkData::MakeUninitialized(size);
437 if (!data) {
438 return nullptr;
439 }
440
441 SkPixmap pm(info, data->writable_data(), rowBytes);
442 if (!src->readPixels(pm, 0, 0, SkImage::kDisallow_CachingHint)) {
443 return nullptr;
444 }
445
446 // Wrap them in a new image with a different color space
447 return SkImage::MakeRasterData(info.makeColorSpace(sk_ref_sp(colorSpace)), data, rowBytes);
448 }
449