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 "SkCachedData.h"
11 #include "SkCanvas.h"
12 #include "SkColorSpacePriv.h"
13 #include "SkData.h"
14 #include "SkImageEncoder.h"
15 #include "SkImageFilter.h"
16 #include "SkImageFilterCache.h"
17 #include "SkImageGenerator.h"
18 #include "SkImagePriv.h"
19 #include "SkImageShader.h"
20 #include "SkImage_Base.h"
21 #include "SkNextID.h"
22 #include "SkPicture.h"
23 #include "SkReadPixelsRec.h"
24 #include "SkSpecialImage.h"
25 #include "SkString.h"
26 #include "SkSurface.h"
27
28 #if SK_SUPPORT_GPU
29 #include "GrTexture.h"
30 #include "GrContext.h"
31 #include "SkImage_Gpu.h"
32 #endif
33 #include "GrBackendSurface.h"
34
SkImage(int width,int height,uint32_t uniqueID)35 SkImage::SkImage(int width, int height, uint32_t uniqueID)
36 : fWidth(width)
37 , fHeight(height)
38 , fUniqueID(kNeedNewImageUniqueID == uniqueID ? SkNextID::ImageID() : uniqueID)
39 {
40 SkASSERT(width > 0);
41 SkASSERT(height > 0);
42 }
43
peekPixels(SkPixmap * pm) const44 bool SkImage::peekPixels(SkPixmap* pm) const {
45 SkPixmap tmp;
46 if (!pm) {
47 pm = &tmp;
48 }
49 return as_IB(this)->onPeekPixels(pm);
50 }
51
readPixels(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRowBytes,int srcX,int srcY,CachingHint chint) const52 bool SkImage::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
53 int srcX, int srcY, CachingHint chint) const {
54 return as_IB(this)->onReadPixels(dstInfo, dstPixels, dstRowBytes, srcX, srcY, chint);
55 }
56
scalePixels(const SkPixmap & dst,SkFilterQuality quality,CachingHint chint) const57 bool SkImage::scalePixels(const SkPixmap& dst, SkFilterQuality quality, CachingHint chint) const {
58 if (this->width() == dst.width() && this->height() == dst.height()) {
59 return this->readPixels(dst, 0, 0, chint);
60 }
61
62 // Idea: If/when SkImageGenerator supports a native-scaling API (where the generator itself
63 // can scale more efficiently) we should take advantage of it here.
64 //
65 SkBitmap bm;
66 if (as_IB(this)->getROPixels(&bm, chint)) {
67 SkPixmap pmap;
68 // Note: By calling the pixmap scaler, we never cache the final result, so the chint
69 // is (currently) only being applied to the getROPixels. If we get a request to
70 // also attempt to cache the final (scaled) result, we would add that logic here.
71 //
72 return bm.peekPixels(&pmap) && pmap.scalePixels(dst, quality);
73 }
74 return false;
75 }
76
77 ///////////////////////////////////////////////////////////////////////////////////////////////////
78
colorType() const79 SkColorType SkImage::colorType() const {
80 return as_IB(this)->onImageInfo().colorType();
81 }
82
alphaType() const83 SkAlphaType SkImage::alphaType() const {
84 return as_IB(this)->onImageInfo().alphaType();
85 }
86
colorSpace() const87 SkColorSpace* SkImage::colorSpace() const {
88 return as_IB(this)->onImageInfo().colorSpace();
89 }
90
refColorSpace() const91 sk_sp<SkColorSpace> SkImage::refColorSpace() const {
92 return as_IB(this)->onImageInfo().refColorSpace();
93 }
94
makeShader(SkShader::TileMode tileX,SkShader::TileMode tileY,const SkMatrix * localMatrix) const95 sk_sp<SkShader> SkImage::makeShader(SkShader::TileMode tileX, SkShader::TileMode tileY,
96 const SkMatrix* localMatrix) const {
97 return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tileX, tileY, localMatrix);
98 }
99
encodeToData(SkEncodedImageFormat type,int quality) const100 sk_sp<SkData> SkImage::encodeToData(SkEncodedImageFormat type, int quality) const {
101 SkBitmap bm;
102 if (as_IB(this)->getROPixels(&bm)) {
103 return SkEncodeBitmap(bm, type, quality);
104 }
105 return nullptr;
106 }
107
encodeToData() const108 sk_sp<SkData> SkImage::encodeToData() const {
109 if (auto encoded = this->refEncodedData()) {
110 return encoded;
111 }
112
113 return this->encodeToData(SkEncodedImageFormat::kPNG, 100);
114 }
115
refEncodedData() const116 sk_sp<SkData> SkImage::refEncodedData() const {
117 return sk_sp<SkData>(as_IB(this)->onRefEncoded());
118 }
119
MakeFromEncoded(sk_sp<SkData> encoded,const SkIRect * subset)120 sk_sp<SkImage> SkImage::MakeFromEncoded(sk_sp<SkData> encoded, const SkIRect* subset) {
121 if (nullptr == encoded || 0 == encoded->size()) {
122 return nullptr;
123 }
124 return SkImage::MakeFromGenerator(SkImageGenerator::MakeFromEncoded(std::move(encoded)),
125 subset);
126 }
127
128 ///////////////////////////////////////////////////////////////////////////////////////////////////
129
makeSubset(const SkIRect & subset) const130 sk_sp<SkImage> SkImage::makeSubset(const SkIRect& subset) const {
131 if (subset.isEmpty()) {
132 return nullptr;
133 }
134
135 const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
136 if (!bounds.contains(subset)) {
137 return nullptr;
138 }
139
140 // optimization : return self if the subset == our bounds
141 if (bounds == subset) {
142 return sk_ref_sp(const_cast<SkImage*>(this));
143 }
144 return as_IB(this)->onMakeSubset(subset);
145 }
146
147 #if SK_SUPPORT_GPU
148
getTexture() const149 GrTexture* SkImage::getTexture() const {
150 return as_IB(this)->onGetTexture();
151 }
152
isTextureBacked() const153 bool SkImage::isTextureBacked() const { return as_IB(this)->onIsTextureBacked(); }
154
getBackendTexture(bool flushPendingGrContextIO,GrSurfaceOrigin * origin) const155 GrBackendTexture SkImage::getBackendTexture(bool flushPendingGrContextIO,
156 GrSurfaceOrigin* origin) const {
157 return as_IB(this)->onGetBackendTexture(flushPendingGrContextIO, origin);
158 }
159
isValid(GrContext * context) const160 bool SkImage::isValid(GrContext* context) const {
161 if (context && context->abandoned()) {
162 return false;
163 }
164 return as_IB(this)->onIsValid(context);
165 }
166
167 #else
168
getTexture() const169 GrTexture* SkImage::getTexture() const { return nullptr; }
170
isTextureBacked() const171 bool SkImage::isTextureBacked() const { return false; }
172
getBackendTexture(bool flushPendingGrContextIO,GrSurfaceOrigin * origin) const173 GrBackendTexture SkImage::getBackendTexture(bool flushPendingGrContextIO,
174 GrSurfaceOrigin* origin) const {
175 return GrBackendTexture(); // invalid
176 }
177
isValid(GrContext * context) const178 bool SkImage::isValid(GrContext* context) const {
179 if (context) {
180 return false;
181 }
182 return as_IB(this)->onIsValid(context);
183 }
184
185 #endif
186
187 ///////////////////////////////////////////////////////////////////////////////
188
SkImage_Base(int width,int height,uint32_t uniqueID)189 SkImage_Base::SkImage_Base(int width, int height, uint32_t uniqueID)
190 : INHERITED(width, height, uniqueID)
191 , fAddedToRasterCache(false)
192 {}
193
~SkImage_Base()194 SkImage_Base::~SkImage_Base() {
195 if (fAddedToRasterCache.load()) {
196 SkNotifyBitmapGenIDIsStale(this->uniqueID());
197 }
198 }
199
onGetBackendTexture(bool flushPendingGrContextIO,GrSurfaceOrigin * origin) const200 GrBackendTexture SkImage_Base::onGetBackendTexture(bool flushPendingGrContextIO,
201 GrSurfaceOrigin* origin) const {
202 return GrBackendTexture(); // invalid
203 }
204
readPixels(const SkPixmap & pmap,int srcX,int srcY,CachingHint chint) const205 bool 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
MakeFromBitmap(const SkBitmap & bm)211 sk_sp<SkImage> SkImage::MakeFromBitmap(const SkBitmap& bm) {
212 if (!bm.pixelRef()) {
213 return nullptr;
214 }
215
216 return SkMakeImageFromRasterBitmap(bm, kIfMutable_SkCopyPixelsMode);
217 }
218
asLegacyBitmap(SkBitmap * bitmap,LegacyBitmapMode) const219 bool SkImage::asLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode ) const {
220 return as_IB(this)->onAsLegacyBitmap(bitmap);
221 }
222
getPlanes(SkYUVASizeInfo *,SkYUVAIndex[4],SkYUVColorSpace *,const void * [4])223 sk_sp<SkCachedData> SkImage_Base::getPlanes(SkYUVASizeInfo*, SkYUVAIndex[4],
224 SkYUVColorSpace*, const void*[4]) {
225 return nullptr;
226 }
227
onAsLegacyBitmap(SkBitmap * bitmap) const228 bool 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 = this->onImageInfo().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
MakeFromPicture(sk_sp<SkPicture> picture,const SkISize & dimensions,const SkMatrix * matrix,const SkPaint * paint,BitDepth bitDepth,sk_sp<SkColorSpace> colorSpace)244 sk_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
makeWithFilter(const SkImageFilter * filter,const SkIRect & subset,const SkIRect & clipBounds,SkIRect * outSubset,SkIPoint * offset) const252 sk_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
makeWithFilter(GrContext * grContext,const SkImageFilter * filter,const SkIRect & subset,const SkIRect & clipBounds,SkIRect * outSubset,SkIPoint * offset) const260 sk_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 = SkSpecialImage::MakeFromImage(
268 grContext, subset, sk_ref_sp(const_cast<SkImage*>(this)));
269 if (!srcSpecialImage) {
270 return nullptr;
271 }
272
273 sk_sp<SkImageFilterCache> cache(
274 SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize));
275 SkImageFilter::OutputProperties outputProperties(as_IB(this)->onImageInfo().colorType(),
276 as_IB(this)->onImageInfo().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) const304 sk_sp<SkImage> SkImage::makeColorSpace(sk_sp<SkColorSpace> target) const {
305 if (!target) {
306 return nullptr;
307 }
308
309 // No need to create a new image if:
310 // (1) The color spaces are equal.
311 // (2) The color type is kAlpha8.
312 SkColorSpace* colorSpace = this->colorSpace();
313 if (!colorSpace) {
314 colorSpace = sk_srgb_singleton();
315 }
316 if (SkColorSpace::Equals(colorSpace, target.get()) || this->isAlphaOnly()) {
317 return sk_ref_sp(const_cast<SkImage*>(this));
318 }
319
320 return as_IB(this)->onMakeColorTypeAndColorSpace(this->colorType(), std::move(target));
321 }
322
makeColorTypeAndColorSpace(SkColorType targetColorType,sk_sp<SkColorSpace> targetColorSpace) const323 sk_sp<SkImage> SkImage::makeColorTypeAndColorSpace(SkColorType targetColorType,
324 sk_sp<SkColorSpace> targetColorSpace) const {
325 if (kUnknown_SkColorType == targetColorType || !targetColorSpace) {
326 return nullptr;
327 }
328
329 SkColorType colorType = this->colorType();
330 SkColorSpace* colorSpace = this->colorSpace();
331 if (!colorSpace) {
332 colorSpace = sk_srgb_singleton();
333 }
334 if (colorType == targetColorType &&
335 (SkColorSpace::Equals(colorSpace, targetColorSpace.get()) || this->isAlphaOnly())) {
336 return sk_ref_sp(const_cast<SkImage*>(this));
337 }
338
339 return as_IB(this)->onMakeColorTypeAndColorSpace(targetColorType, std::move(targetColorSpace));
340 }
341
makeNonTextureImage() const342 sk_sp<SkImage> SkImage::makeNonTextureImage() const {
343 if (!this->isTextureBacked()) {
344 return sk_ref_sp(const_cast<SkImage*>(this));
345 }
346 return this->makeRasterImage();
347 }
348
makeRasterImage() const349 sk_sp<SkImage> SkImage::makeRasterImage() const {
350 SkPixmap pm;
351 if (this->peekPixels(&pm)) {
352 return sk_ref_sp(const_cast<SkImage*>(this));
353 }
354
355 const SkImageInfo info = as_IB(this)->onImageInfo();
356 const size_t rowBytes = info.minRowBytes();
357 size_t size = info.computeByteSize(rowBytes);
358 if (SkImageInfo::ByteSizeOverflowed(size)) {
359 return nullptr;
360 }
361
362 sk_sp<SkData> data = SkData::MakeUninitialized(size);
363 pm = { info.makeColorSpace(nullptr), data->writable_data(), info.minRowBytes() };
364 if (!this->readPixels(pm, 0, 0)) {
365 return nullptr;
366 }
367
368 return SkImage::MakeRasterData(info, std::move(data), rowBytes);
369 }
370
371 //////////////////////////////////////////////////////////////////////////////////////
372
373 #if !SK_SUPPORT_GPU
374
MakeFromTexture(GrContext * ctx,const GrBackendTexture & tex,GrSurfaceOrigin origin,SkColorType ct,SkAlphaType at,sk_sp<SkColorSpace> cs,TextureReleaseProc releaseP,ReleaseContext releaseC)375 sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx,
376 const GrBackendTexture& tex, GrSurfaceOrigin origin,
377 SkColorType ct, SkAlphaType at, sk_sp<SkColorSpace> cs,
378 TextureReleaseProc releaseP, ReleaseContext releaseC) {
379 return nullptr;
380 }
381
MakeBackendTextureFromSkImage(GrContext *,sk_sp<SkImage>,GrBackendTexture *,BackendTextureReleaseProc *)382 bool SkImage::MakeBackendTextureFromSkImage(GrContext*,
383 sk_sp<SkImage>,
384 GrBackendTexture*,
385 BackendTextureReleaseProc*) {
386 return false;
387 }
388
MakeFromAdoptedTexture(GrContext * ctx,const GrBackendTexture & tex,GrSurfaceOrigin origin,SkColorType ct,SkAlphaType at,sk_sp<SkColorSpace> cs)389 sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext* ctx,
390 const GrBackendTexture& tex, GrSurfaceOrigin origin,
391 SkColorType ct, SkAlphaType at,
392 sk_sp<SkColorSpace> cs) {
393 return nullptr;
394 }
395
MakeFromYUVATexturesCopy(GrContext * context,SkYUVColorSpace yuvColorSpace,const GrBackendTexture yuvaTextures[],const SkYUVAIndex yuvaIndices[4],SkISize imageSize,GrSurfaceOrigin imageOrigin,sk_sp<SkColorSpace> imageColorSpace)396 sk_sp<SkImage> SkImage::MakeFromYUVATexturesCopy(GrContext* context,
397 SkYUVColorSpace yuvColorSpace,
398 const GrBackendTexture yuvaTextures[],
399 const SkYUVAIndex yuvaIndices[4],
400 SkISize imageSize,
401 GrSurfaceOrigin imageOrigin,
402 sk_sp<SkColorSpace> imageColorSpace) {
403 return nullptr;
404 }
405
MakeFromYUVATexturesCopyWithExternalBackend(GrContext * context,SkYUVColorSpace yuvColorSpace,const GrBackendTexture yuvaTextures[],const SkYUVAIndex yuvaIndices[4],SkISize imageSize,GrSurfaceOrigin imageOrigin,const GrBackendTexture & backendTexture,sk_sp<SkColorSpace> imageColorSpace)406 sk_sp<SkImage> SkImage::MakeFromYUVATexturesCopyWithExternalBackend(
407 GrContext* context,
408 SkYUVColorSpace yuvColorSpace,
409 const GrBackendTexture yuvaTextures[],
410 const SkYUVAIndex yuvaIndices[4],
411 SkISize imageSize,
412 GrSurfaceOrigin imageOrigin,
413 const GrBackendTexture& backendTexture,
414 sk_sp<SkColorSpace> imageColorSpace) {
415 return nullptr;
416 }
417
MakeFromYUVTexturesCopy(GrContext * ctx,SkYUVColorSpace space,const GrBackendTexture[3],GrSurfaceOrigin origin,sk_sp<SkColorSpace> imageColorSpace)418 sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace space,
419 const GrBackendTexture[3],
420 GrSurfaceOrigin origin,
421 sk_sp<SkColorSpace> imageColorSpace) {
422 return nullptr;
423 }
424
MakeFromYUVTexturesCopyWithExternalBackend(GrContext * context,SkYUVColorSpace yuvColorSpace,const GrBackendTexture yuvTextures[3],GrSurfaceOrigin surfaceOrigin,const GrBackendTexture & backendTexture,sk_sp<SkColorSpace> colorSpace)425 sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopyWithExternalBackend(
426 GrContext* context, SkYUVColorSpace yuvColorSpace, const GrBackendTexture yuvTextures[3],
427 GrSurfaceOrigin surfaceOrigin, const GrBackendTexture& backendTexture,
428 sk_sp<SkColorSpace> colorSpace) {
429 return nullptr;
430 }
431
MakeFromNV12TexturesCopy(GrContext * ctx,SkYUVColorSpace space,const GrBackendTexture[2],GrSurfaceOrigin origin,sk_sp<SkColorSpace> imageColorSpace)432 sk_sp<SkImage> SkImage::MakeFromNV12TexturesCopy(GrContext* ctx, SkYUVColorSpace space,
433 const GrBackendTexture[2],
434 GrSurfaceOrigin origin,
435 sk_sp<SkColorSpace> imageColorSpace) {
436 return nullptr;
437 }
438
makeTextureImage(GrContext *,SkColorSpace * dstColorSpace,GrMipMapped mipMapped) const439 sk_sp<SkImage> SkImage::makeTextureImage(GrContext*, SkColorSpace* dstColorSpace,
440 GrMipMapped mipMapped) const {
441 return nullptr;
442 }
443
MakeFromNV12TexturesCopyWithExternalBackend(GrContext * context,SkYUVColorSpace yuvColorSpace,const GrBackendTexture nv12Textures[2],GrSurfaceOrigin surfaceOrigin,const GrBackendTexture & backendTexture,sk_sp<SkColorSpace> colorSpace)444 sk_sp<SkImage> MakeFromNV12TexturesCopyWithExternalBackend(GrContext* context,
445 SkYUVColorSpace yuvColorSpace,
446 const GrBackendTexture nv12Textures[2],
447 GrSurfaceOrigin surfaceOrigin,
448 const GrBackendTexture& backendTexture,
449 sk_sp<SkColorSpace> colorSpace) {
450 return nullptr;
451 }
452
453 #endif
454
455 ///////////////////////////////////////////////////////////////////////////////////////////////////
456
SkImage_pinAsTexture(const SkImage * image,GrContext * ctx)457 bool SkImage_pinAsTexture(const SkImage* image, GrContext* ctx) {
458 SkASSERT(image);
459 SkASSERT(ctx);
460 return as_IB(image)->onPinAsTexture(ctx);
461 }
462
SkImage_unpinAsTexture(const SkImage * image,GrContext * ctx)463 void SkImage_unpinAsTexture(const SkImage* image, GrContext* ctx) {
464 SkASSERT(image);
465 SkASSERT(ctx);
466 as_IB(image)->onUnpinAsTexture(ctx);
467 }
468
SkImage_getSubset(const SkImage * image)469 SkIRect SkImage_getSubset(const SkImage* image) {
470 SkASSERT(image);
471 return as_IB(image)->onGetSubset();
472 }
473
474 ///////////////////////////////////////////////////////////////////////////////////////////////////
475
SkImageMakeRasterCopyAndAssignColorSpace(const SkImage * src,SkColorSpace * colorSpace)476 sk_sp<SkImage> SkImageMakeRasterCopyAndAssignColorSpace(const SkImage* src,
477 SkColorSpace* colorSpace) {
478 // Read the pixels out of the source image, with no conversion
479 SkImageInfo info = as_IB(src)->onImageInfo();
480 if (kUnknown_SkColorType == info.colorType()) {
481 SkDEBUGFAIL("Unexpected color type");
482 return nullptr;
483 }
484
485 size_t rowBytes = info.minRowBytes();
486 size_t size = info.computeByteSize(rowBytes);
487 if (SkImageInfo::ByteSizeOverflowed(size)) {
488 return nullptr;
489 }
490 auto data = SkData::MakeUninitialized(size);
491 if (!data) {
492 return nullptr;
493 }
494
495 SkPixmap pm(info, data->writable_data(), rowBytes);
496 if (!src->readPixels(pm, 0, 0, SkImage::kDisallow_CachingHint)) {
497 return nullptr;
498 }
499
500 // Wrap them in a new image with a different color space
501 return SkImage::MakeRasterData(info.makeColorSpace(sk_ref_sp(colorSpace)), data, rowBytes);
502 }
503