@@ -64,6 +64,7 @@ @implementation SDImageWebPCoder {
64
64
NSUInteger _frameCount;
65
65
NSArray <SDWebPCoderFrame *> *_frames;
66
66
CGContextRef _canvas;
67
+ CGColorSpaceRef _colorSpace;
67
68
BOOL _hasAnimation;
68
69
BOOL _hasAlpha;
69
70
BOOL _finished;
@@ -86,6 +87,10 @@ - (void)dealloc {
86
87
CGContextRelease (_canvas);
87
88
_canvas = NULL ;
88
89
}
90
+ if (_colorSpace) {
91
+ CGColorSpaceRelease (_colorSpace);
92
+ _colorSpace = NULL ;
93
+ }
89
94
}
90
95
91
96
+ (instancetype )sharedCoder {
@@ -131,21 +136,6 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO
131
136
scale = 1 ;
132
137
}
133
138
}
134
- if (!hasAnimation) {
135
- // for static single webp image
136
- CGImageRef imageRef = [self sd_createWebpImageWithData: webpData];
137
- if (!imageRef) {
138
- return nil ;
139
- }
140
- #if SD_UIKIT || SD_WATCH
141
- UIImage *staticImage = [[UIImage alloc ] initWithCGImage: imageRef scale: scale orientation: UIImageOrientationUp];
142
- #else
143
- UIImage *staticImage = [[UIImage alloc ] initWithCGImage: imageRef scale: scale orientation: kCGImagePropertyOrientationUp ];
144
- #endif
145
- CGImageRelease (imageRef);
146
- WebPDemuxDelete (demuxer);
147
- return staticImage;
148
- }
149
139
150
140
// for animated webp image
151
141
WebPIterator iter;
@@ -155,10 +145,12 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO
155
145
WebPDemuxDelete (demuxer);
156
146
return nil ;
157
147
}
148
+ CGColorSpaceRef colorSpace = [self sd_colorSpaceWithDemuxer: demuxer];
158
149
159
- if (decodeFirstFrame) {
150
+ if (!hasAnimation || decodeFirstFrame) {
160
151
// first frame for animated webp image
161
- CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment];
152
+ CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment colorSpace: colorSpace];
153
+ CGColorSpaceRelease (colorSpace);
162
154
#if SD_UIKIT || SD_WATCH
163
155
UIImage *firstFrameImage = [[UIImage alloc ] initWithCGImage: imageRef scale: scale orientation: UIImageOrientationUp];
164
156
#else
@@ -180,14 +172,15 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO
180
172
CGContextRef canvas = CGBitmapContextCreate (NULL , canvasWidth, canvasHeight, 8 , 0 , [SDImageCoderHelper colorSpaceGetDeviceRGB ], bitmapInfo);
181
173
if (!canvas) {
182
174
WebPDemuxDelete (demuxer);
175
+ CGColorSpaceRelease (colorSpace);
183
176
return nil ;
184
177
}
185
178
186
179
NSMutableArray <SDImageFrame *> *frames = [NSMutableArray array ];
187
180
188
181
do {
189
182
@autoreleasepool {
190
- CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas: canvas iterator: iter];
183
+ CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas: canvas iterator: iter colorSpace: colorSpace ];
191
184
if (!imageRef) {
192
185
continue ;
193
186
}
@@ -208,6 +201,7 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO
208
201
WebPDemuxReleaseIterator (&iter);
209
202
WebPDemuxDelete (demuxer);
210
203
CGContextRelease (canvas);
204
+ CGColorSpaceRelease (colorSpace);
211
205
212
206
UIImage *animatedImage = [SDImageCoderHelper animatedImageWithFrames: frames];
213
207
animatedImage.sd_imageLoopCount = loopCount;
@@ -318,7 +312,7 @@ - (UIImage *)incrementalDecodedImageWithOptions:(SDImageCoderOptions *)options {
318
312
return image;
319
313
}
320
314
321
- - (void )sd_blendWebpImageWithCanvas : (CGContextRef)canvas iterator : (WebPIterator)iter {
315
+ - (void )sd_blendWebpImageWithCanvas : (CGContextRef)canvas iterator : (WebPIterator)iter colorSpace : (nonnull CGColorSpaceRef) colorSpaceRef {
322
316
size_t canvasHeight = CGBitmapContextGetHeight (canvas);
323
317
CGFloat tmpX = iter.x_offset ;
324
318
CGFloat tmpY = canvasHeight - iter.height - iter.y_offset ;
@@ -327,7 +321,7 @@ - (void)sd_blendWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)
327
321
if (iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
328
322
CGContextClearRect (canvas, imageRect);
329
323
} else {
330
- CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment];
324
+ CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment colorSpace: colorSpaceRef ];
331
325
if (!imageRef) {
332
326
return ;
333
327
}
@@ -341,8 +335,8 @@ - (void)sd_blendWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)
341
335
}
342
336
}
343
337
344
- - (nullable CGImageRef)sd_drawnWebpImageWithCanvas : (CGContextRef)canvas iterator : (WebPIterator)iter CF_RETURNS_RETAINED {
345
- CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment];
338
+ - (nullable CGImageRef)sd_drawnWebpImageWithCanvas : (CGContextRef)canvas iterator : (WebPIterator)iter colorSpace : (nonnull CGColorSpaceRef) colorSpaceRef CF_RETURNS_RETAINED {
339
+ CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment colorSpace: colorSpaceRef ];
346
340
if (!imageRef) {
347
341
return nil ;
348
342
}
@@ -369,7 +363,7 @@ - (nullable CGImageRef)sd_drawnWebpImageWithCanvas:(CGContextRef)canvas iterator
369
363
return newImageRef;
370
364
}
371
365
372
- - (nullable CGImageRef)sd_createWebpImageWithData : (WebPData)webpData CF_RETURNS_RETAINED {
366
+ - (nullable CGImageRef)sd_createWebpImageWithData : (WebPData)webpData colorSpace : (nonnull CGColorSpaceRef) colorSpaceRef CF_RETURNS_RETAINED {
373
367
WebPDecoderConfig config;
374
368
if (!WebPInitDecoderConfig (&config)) {
375
369
return nil ;
@@ -382,11 +376,10 @@ - (nullable CGImageRef)sd_createWebpImageWithData:(WebPData)webpData CF_RETURNS_
382
376
BOOL hasAlpha = config.input .has_alpha ;
383
377
// iOS prefer BGRA8888 (premultiplied) or BGRX8888 bitmapInfo for screen rendering, which is same as `UIGraphicsBeginImageContext()` or `- [CALayer drawInContext:]`
384
378
// use this bitmapInfo, combined with right colorspace, even without decode, can still avoid extra CA::Render::copy_image(which marked `Color Copied Images` from Instruments)
385
- WEBP_CSP_MODE colorspace = MODE_bgrA;
386
379
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host ;
387
380
bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst ;
388
381
config.options .use_threads = 1 ;
389
- config.output .colorspace = colorspace ;
382
+ config.output .colorspace = MODE_bgrA ;
390
383
391
384
// Decode the WebP image data into a RGBA value array
392
385
if (WebPDecode (webpData.bytes , webpData.size , &config) != VP8_STATUS_OK) {
@@ -406,7 +399,6 @@ - (nullable CGImageRef)sd_createWebpImageWithData:(WebPData)webpData CF_RETURNS_
406
399
size_t bitsPerComponent = 8 ;
407
400
size_t bitsPerPixel = 32 ;
408
401
size_t bytesPerRow = config.output .u .RGBA .stride ;
409
- CGColorSpaceRef colorSpaceRef = [SDImageCoderHelper colorSpaceGetDeviceRGB ];
410
402
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault ;
411
403
CGImageRef imageRef = CGImageCreate (width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL , NO , renderingIntent);
412
404
@@ -425,6 +417,28 @@ - (NSTimeInterval)sd_frameDurationWithIterator:(WebPIterator)iter {
425
417
return duration / 1000.0 ;
426
418
}
427
419
420
+ // Create and return the correct colorspace by checking the ICC Profile
421
+ - (nonnull CGColorSpaceRef)sd_colorSpaceWithDemuxer : (nonnull WebPDemuxer *)demuxer CF_RETURNS_RETAINED {
422
+ // WebP contains ICC Profile should use the desired colorspace, instead of default device colorspace
423
+ // See: https://developers.google.com/speed/webp/docs/riff_container#color_profile
424
+
425
+ WebPChunkIterator chunk_iter;
426
+ CGColorSpaceRef colorSpaceRef = NULL ;
427
+
428
+ int result = WebPDemuxGetChunk (demuxer, " ICCP" , 1 , &chunk_iter);
429
+ if (result) {
430
+ NSData *profileData = [NSData dataWithBytes: chunk_iter.chunk.bytes length: chunk_iter.chunk.size];
431
+ colorSpaceRef = CGColorSpaceCreateWithICCProfile ((__bridge CFDataRef)profileData);
432
+ }
433
+
434
+ if (!colorSpaceRef) {
435
+ colorSpaceRef = [SDImageCoderHelper colorSpaceGetDeviceRGB ];
436
+ CGColorSpaceRetain (colorSpaceRef);
437
+ }
438
+
439
+ return colorSpaceRef;
440
+ }
441
+
428
442
#pragma mark - Encode
429
443
- (BOOL )canEncodeToFormat : (SDImageFormat)format {
430
444
return (format == SDImageFormatWebP);
@@ -770,6 +784,9 @@ - (UIImage *)safeAnimatedImageFrameAtIndex:(NSUInteger)index {
770
784
}
771
785
_canvas = canvas;
772
786
}
787
+ if (!_colorSpace) {
788
+ _colorSpace = [self sd_colorSpaceWithDemuxer: _demux];
789
+ }
773
790
774
791
SDWebPCoderFrame *frame = _frames[index];
775
792
UIImage *image;
@@ -782,7 +799,7 @@ - (UIImage *)safeAnimatedImageFrameAtIndex:(NSUInteger)index {
782
799
WebPDemuxReleaseIterator (&iter);
783
800
return nil ;
784
801
}
785
- CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas: _canvas iterator: iter];
802
+ CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas: _canvas iterator: iter colorSpace: _colorSpace ];
786
803
if (!imageRef) {
787
804
return nil ;
788
805
}
@@ -810,9 +827,9 @@ - (UIImage *)safeAnimatedImageFrameAtIndex:(NSUInteger)index {
810
827
do {
811
828
@autoreleasepool {
812
829
if ((size_t )iter.frame_num == endIndex) {
813
- [self sd_blendWebpImageWithCanvas: _canvas iterator: iter];
830
+ [self sd_blendWebpImageWithCanvas: _canvas iterator: iter colorSpace: _colorSpace ];
814
831
} else {
815
- CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas: _canvas iterator: iter];
832
+ CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas: _canvas iterator: iter colorSpace: _colorSpace ];
816
833
if (!imageRef) {
817
834
return nil ;
818
835
}
0 commit comments