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