2424
2525#import < Accelerate/Accelerate.h>
2626
27+ // / Calculate the actual thumnail pixel size
28+ static CGSize SDCalculateThumbnailSize (CGSize fullSize, BOOL preserveAspectRatio, CGSize thumbnailSize) {
29+ CGFloat width = fullSize.width ;
30+ CGFloat height = fullSize.height ;
31+ CGFloat resultWidth;
32+ CGFloat resultHeight;
33+
34+ if (width == 0 || height == 0 || thumbnailSize.width == 0 || thumbnailSize.height == 0 || (width <= thumbnailSize.width && height <= thumbnailSize.height )) {
35+ // Full Pixel
36+ resultWidth = width;
37+ resultHeight = height;
38+ } else {
39+ // Thumbnail
40+ if (preserveAspectRatio) {
41+ CGFloat pixelRatio = width / height;
42+ CGFloat thumbnailRatio = thumbnailSize.width / thumbnailSize.height ;
43+ if (pixelRatio > thumbnailRatio) {
44+ resultWidth = thumbnailSize.width ;
45+ resultHeight = ceil (thumbnailSize.width / pixelRatio);
46+ } else {
47+ resultHeight = thumbnailSize.height ;
48+ resultWidth = ceil (thumbnailSize.height * pixelRatio);
49+ }
50+ } else {
51+ resultWidth = thumbnailSize.width ;
52+ resultHeight = thumbnailSize.height ;
53+ }
54+ }
55+
56+ return CGSizeMake (resultWidth, resultHeight);
57+ }
58+
2759#ifndef SD_LOCK
2860#define SD_LOCK (lock ) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
2961#endif
@@ -64,8 +96,6 @@ @implementation SDImageWebPCoder {
6496 BOOL _hasAnimation;
6597 BOOL _hasAlpha;
6698 BOOL _finished;
67- CGFloat _canvasWidth;
68- CGFloat _canvasHeight;
6999 dispatch_semaphore_t _lock;
70100 NSUInteger _currentBlendIndex;
71101 BOOL _preserveAspectRatio;
@@ -159,7 +189,7 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO
159189 WebPDemuxDelete (demuxer);
160190 return nil ;
161191 }
162- CGColorSpaceRef colorSpace = [self sd_colorSpaceWithDemuxer : demuxer];
192+ CGColorSpaceRef colorSpace = [self sd_createColorSpaceWithDemuxer : demuxer];
163193
164194 if (!hasAnimation || decodeFirstFrame) {
165195 // first frame for animated webp image
@@ -177,19 +207,14 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO
177207 return firstFrameImage;
178208 }
179209
180- int loopCount = WebPDemuxGetI (demuxer, WEBP_FF_LOOP_COUNT);
181- int canvasWidth = WebPDemuxGetI (demuxer, WEBP_FF_CANVAS_WIDTH);
182- int canvasHeight = WebPDemuxGetI (demuxer, WEBP_FF_CANVAS_HEIGHT);
183- BOOL hasAlpha = flags & ALPHA_FLAG;
184- CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host ;
185- bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst ;
186- CGContextRef canvas = CGBitmapContextCreate (NULL , canvasWidth, canvasHeight, 8 , 0 , [SDImageCoderHelper colorSpaceGetDeviceRGB ], bitmapInfo);
210+ CGContextRef canvas = [self sd_createCanvasWithDemuxer: demuxer colorSpace: colorSpace preserveAspectRatio: preserveAspectRatio thumbnailSize: thumbnailSize];
187211 if (!canvas) {
188212 WebPDemuxDelete (demuxer);
189213 CGColorSpaceRelease (colorSpace);
190214 return nil ;
191215 }
192216
217+ int loopCount = WebPDemuxGetI (demuxer, WEBP_FF_LOOP_COUNT);
193218 NSMutableArray <SDImageFrame *> *frames = [NSMutableArray array ];
194219
195220 do {
@@ -305,7 +330,7 @@ - (UIImage *)incrementalDecodedImageWithOptions:(SDImageCoderOptions *)options {
305330 return nil ;
306331 }
307332
308- CGContextRef canvas = CGBitmapContextCreate ( NULL , width, height, 8 , 0 , [SDImageCoderHelper colorSpaceGetDeviceRGB ], bitmapInfo) ;
333+ CGContextRef canvas = [ self sd_createCanvasWithDemuxer: _demux colorSpace: colorSpaceRef preserveAspectRatio: _preserveAspectRatio thumbnailSize: _thumbnailSize] ;
309334 if (!canvas) {
310335 CGImageRelease (imageRef);
311336 return nil ;
@@ -413,25 +438,12 @@ - (nullable CGImageRef)sd_createWebpImageWithData:(WebPData)webpData colorSpace:
413438
414439 int width = config.input .width ;
415440 int height = config.input .height ;
416- if (width == 0 || height == 0 || thumbnailSize.width == 0 || thumbnailSize.height == 0 || (width <= thumbnailSize.width && height <= thumbnailSize.height )) {
417- // Full Pixel
418- } else {
419- // Thumbnail
441+ CGSize resultSize = SDCalculateThumbnailSize (CGSizeMake (width, height), preserveAspectRatio, thumbnailSize);
442+ if (resultSize.width != width || resultSize.height != height) {
443+ // Use scaling
420444 config.options .use_scaling = 1 ;
421- if (preserveAspectRatio) {
422- CGFloat pixelRatio = (CGFloat)width / (CGFloat)height;
423- CGFloat thumbnailRatio = thumbnailSize.width / thumbnailSize.height ;
424- if (pixelRatio > thumbnailRatio) {
425- config.options .scaled_width = thumbnailSize.width ;
426- config.options .scaled_height = thumbnailSize.width / pixelRatio;
427- } else {
428- config.options .scaled_height = thumbnailSize.height ;
429- config.options .scaled_width = thumbnailSize.height * pixelRatio;
430- }
431- } else {
432- config.options .scaled_width = thumbnailSize.width ;
433- config.options .scaled_height = thumbnailSize.height ;
434- }
445+ config.options .scaled_width = resultSize.width ;
446+ config.options .scaled_height = resultSize.height ;
435447 }
436448
437449 // Decode the WebP image data into a RGBA value array
@@ -451,7 +463,7 @@ - (nullable CGImageRef)sd_createWebpImageWithData:(WebPData)webpData colorSpace:
451463 size_t bitsPerPixel = 32 ;
452464 size_t bytesPerRow = config.output .u .RGBA .stride ;
453465 CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault ;
454- CGImageRef imageRef = CGImageCreate (width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL , NO , renderingIntent);
466+ CGImageRef imageRef = CGImageCreate (resultSize. width , resultSize. height , bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL , NO , renderingIntent);
455467
456468 CGDataProviderRelease (provider);
457469
@@ -469,7 +481,7 @@ - (NSTimeInterval)sd_frameDurationWithIterator:(WebPIterator)iter {
469481}
470482
471483// Create and return the correct colorspace by checking the ICC Profile
472- - (nonnull CGColorSpaceRef)sd_colorSpaceWithDemuxer : (nonnull WebPDemuxer *)demuxer CF_RETURNS_RETAINED {
484+ - (nonnull CGColorSpaceRef)sd_createColorSpaceWithDemuxer : (nonnull WebPDemuxer *)demuxer CF_RETURNS_RETAINED {
473485 // WebP contains ICC Profile should use the desired colorspace, instead of default device colorspace
474486 // See: https://developers.google.com/speed/webp/docs/riff_container#color_profile
475487
@@ -508,6 +520,20 @@ - (nonnull CGColorSpaceRef)sd_colorSpaceWithDemuxer:(nonnull WebPDemuxer *)demux
508520 return colorSpaceRef;
509521}
510522
523+ - (CGContextRef)sd_createCanvasWithDemuxer : (nonnull WebPDemuxer *)demuxer colorSpace : (nonnull CGColorSpaceRef)colorSpace preserveAspectRatio : (BOOL )preserveAspectRatio thumbnailSize : (CGSize)thumbnailSize CF_RETURNS_RETAINED {
524+ uint32_t flags = WebPDemuxGetI (demuxer, WEBP_FF_FORMAT_FLAGS);
525+ int canvasWidth = WebPDemuxGetI (demuxer, WEBP_FF_CANVAS_WIDTH);
526+ int canvasHeight = WebPDemuxGetI (demuxer, WEBP_FF_CANVAS_HEIGHT);
527+ BOOL hasAlpha = flags & ALPHA_FLAG;
528+ CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host ;
529+ bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst ;
530+
531+ CGSize canvasSize = SDCalculateThumbnailSize (CGSizeMake (canvasWidth, canvasHeight), preserveAspectRatio, thumbnailSize);
532+ CGContextRef canvas = CGBitmapContextCreate (NULL , canvasSize.width , canvasSize.height , 8 , 0 , colorSpace, bitmapInfo);
533+
534+ return canvas;
535+ }
536+
511537#pragma mark - Encode
512538- (BOOL )canEncodeToFormat : (SDImageFormat)format {
513539 return (format == SDImageFormatWebP);
@@ -784,8 +810,6 @@ - (BOOL)scanAndCheckFramesValidWithDemuxer:(WebPDemuxer *)demuxer {
784810
785811 _hasAnimation = hasAnimation;
786812 _hasAlpha = hasAlpha;
787- _canvasWidth = canvasWidth;
788- _canvasHeight = canvasHeight;
789813 _frameCount = frameCount;
790814 _loopCount = loopCount;
791815
@@ -875,7 +899,7 @@ - (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index {
875899- (UIImage *)safeStaticImageFrame {
876900 UIImage *image;
877901 if (!_colorSpace) {
878- _colorSpace = [self sd_colorSpaceWithDemuxer : _demux];
902+ _colorSpace = [self sd_createColorSpaceWithDemuxer : _demux];
879903 }
880904 // Static WebP image
881905 WebPIterator iter;
@@ -898,18 +922,16 @@ - (UIImage *)safeStaticImageFrame {
898922}
899923
900924- (UIImage *)safeAnimatedImageFrameAtIndex : (NSUInteger )index {
925+ if (!_colorSpace) {
926+ _colorSpace = [self sd_createColorSpaceWithDemuxer: _demux];
927+ }
901928 if (!_canvas) {
902- CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host ;
903- bitmapInfo |= _hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst ;
904- CGContextRef canvas = CGBitmapContextCreate (NULL , _canvasWidth, _canvasHeight, 8 , 0 , [SDImageCoderHelper colorSpaceGetDeviceRGB ], bitmapInfo);
929+ CGContextRef canvas = [self sd_createCanvasWithDemuxer: _demux colorSpace: _colorSpace preserveAspectRatio: _preserveAspectRatio thumbnailSize: _thumbnailSize];
905930 if (!canvas) {
906931 return nil ;
907932 }
908933 _canvas = canvas;
909934 }
910- if (!_colorSpace) {
911- _colorSpace = [self sd_colorSpaceWithDemuxer: _demux];
912- }
913935
914936 SDWebPCoderFrame *frame = _frames[index];
915937 UIImage *image;
@@ -929,7 +951,7 @@ - (UIImage *)safeAnimatedImageFrameAtIndex:(NSUInteger)index {
929951 } else {
930952 // Else, this can happen when one image set to different imageViews or one loop end. So we should clear the canvas. Then draw until the canvas is ready.
931953 if (_currentBlendIndex != NSNotFound ) {
932- CGContextClearRect (_canvas, CGRectMake (0 , 0 , _canvasWidth, _canvasHeight ));
954+ CGContextClearRect (_canvas, CGRectMake (0 , 0 , CGBitmapContextGetWidth (_canvas), CGBitmapContextGetHeight (_canvas) ));
933955 }
934956
935957 // Then, loop from the blend from index, draw each of previous frames on the canvas.
0 commit comments