@@ -68,6 +68,8 @@ @implementation SDImageWebPCoder {
6868 CGFloat _canvasHeight;
6969 dispatch_semaphore_t _lock;
7070 NSUInteger _currentBlendIndex;
71+ BOOL _preserveAspectRatio;
72+ CGSize _thumbnailSize;
7173}
7274
7375- (void )dealloc {
@@ -133,6 +135,22 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO
133135 }
134136 }
135137
138+ CGSize thumbnailSize = CGSizeZero;
139+ NSValue *thumbnailSizeValue = options[SDImageCoderDecodeThumbnailPixelSize];
140+ if (thumbnailSizeValue != nil ) {
141+ #if SD_MAC
142+ thumbnailSize = thumbnailSizeValue.sizeValue ;
143+ #else
144+ thumbnailSize = thumbnailSizeValue.CGSizeValue ;
145+ #endif
146+ }
147+
148+ BOOL preserveAspectRatio = YES ;
149+ NSNumber *preserveAspectRatioValue = options[SDImageCoderDecodePreserveAspectRatio];
150+ if (preserveAspectRatioValue != nil ) {
151+ preserveAspectRatio = preserveAspectRatioValue.boolValue ;
152+ }
153+
136154 // for animated webp image
137155 WebPIterator iter;
138156 // libwebp's index start with 1
@@ -145,7 +163,7 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO
145163
146164 if (!hasAnimation || decodeFirstFrame) {
147165 // first frame for animated webp image
148- CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment colorSpace: colorSpace];
166+ CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment colorSpace: colorSpace preserveAspectRatio: preserveAspectRatio thumbnailSize: thumbnailSize ];
149167 CGColorSpaceRelease (colorSpace);
150168#if SD_UIKIT || SD_WATCH
151169 UIImage *firstFrameImage = [[UIImage alloc ] initWithCGImage: imageRef scale: scale orientation: UIImageOrientationUp];
@@ -176,7 +194,7 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO
176194
177195 do {
178196 @autoreleasepool {
179- CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas: canvas iterator: iter colorSpace: colorSpace];
197+ CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas: canvas iterator: iter colorSpace: colorSpace preserveAspectRatio: preserveAspectRatio thumbnailSize: thumbnailSize ];
180198 if (!imageRef) {
181199 continue ;
182200 }
@@ -221,6 +239,22 @@ - (instancetype)initIncrementalWithOptions:(nullable SDImageCoderOptions *)optio
221239 }
222240 }
223241 _scale = scale;
242+ CGSize thumbnailSize = CGSizeZero;
243+ NSValue *thumbnailSizeValue = options[SDImageCoderDecodeThumbnailPixelSize];
244+ if (thumbnailSizeValue != nil ) {
245+ #if SD_MAC
246+ thumbnailSize = thumbnailSizeValue.sizeValue ;
247+ #else
248+ thumbnailSize = thumbnailSizeValue.CGSizeValue ;
249+ #endif
250+ }
251+ _thumbnailSize = thumbnailSize;
252+ BOOL preserveAspectRatio = YES ;
253+ NSNumber *preserveAspectRatioValue = options[SDImageCoderDecodePreserveAspectRatio];
254+ if (preserveAspectRatioValue != nil ) {
255+ preserveAspectRatio = preserveAspectRatioValue.boolValue ;
256+ }
257+ _preserveAspectRatio = preserveAspectRatio;
224258 }
225259 return self;
226260}
@@ -308,7 +342,7 @@ - (UIImage *)incrementalDecodedImageWithOptions:(SDImageCoderOptions *)options {
308342 return image;
309343}
310344
311- - (void )sd_blendWebpImageWithCanvas : (CGContextRef)canvas iterator : (WebPIterator)iter colorSpace : (nonnull CGColorSpaceRef)colorSpaceRef {
345+ - (void )sd_blendWebpImageWithCanvas : (CGContextRef)canvas iterator : (WebPIterator)iter colorSpace : (nonnull CGColorSpaceRef)colorSpaceRef preserveAspectRatio : ( BOOL ) preserveAspectRatio thumbnailSize : (CGSize) thumbnailSize {
312346 size_t canvasHeight = CGBitmapContextGetHeight (canvas);
313347 CGFloat tmpX = iter.x_offset ;
314348 CGFloat tmpY = canvasHeight - iter.height - iter.y_offset ;
@@ -317,7 +351,7 @@ - (void)sd_blendWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)
317351 if (iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
318352 CGContextClearRect (canvas, imageRect);
319353 } else {
320- CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment colorSpace: colorSpaceRef];
354+ CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment colorSpace: colorSpaceRef preserveAspectRatio: preserveAspectRatio thumbnailSize: thumbnailSize ];
321355 if (!imageRef) {
322356 return ;
323357 }
@@ -331,8 +365,8 @@ - (void)sd_blendWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)
331365 }
332366}
333367
334- - (nullable CGImageRef)sd_drawnWebpImageWithCanvas : (CGContextRef)canvas iterator : (WebPIterator)iter colorSpace : (nonnull CGColorSpaceRef)colorSpaceRef CF_RETURNS_RETAINED {
335- CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment colorSpace: colorSpaceRef];
368+ - (nullable CGImageRef)sd_drawnWebpImageWithCanvas : (CGContextRef)canvas iterator : (WebPIterator)iter colorSpace : (nonnull CGColorSpaceRef)colorSpaceRef preserveAspectRatio : ( BOOL ) preserveAspectRatio thumbnailSize : (CGSize) thumbnailSize CF_RETURNS_RETAINED {
369+ CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment colorSpace: colorSpaceRef preserveAspectRatio: preserveAspectRatio thumbnailSize: thumbnailSize ];
336370 if (!imageRef) {
337371 return nil ;
338372 }
@@ -359,7 +393,7 @@ - (nullable CGImageRef)sd_drawnWebpImageWithCanvas:(CGContextRef)canvas iterator
359393 return newImageRef;
360394}
361395
362- - (nullable CGImageRef)sd_createWebpImageWithData : (WebPData)webpData colorSpace : (nonnull CGColorSpaceRef)colorSpaceRef CF_RETURNS_RETAINED {
396+ - (nullable CGImageRef)sd_createWebpImageWithData : (WebPData)webpData colorSpace : (nonnull CGColorSpaceRef)colorSpaceRef preserveAspectRatio : ( BOOL ) preserveAspectRatio thumbnailSize : (CGSize) thumbnailSize CF_RETURNS_RETAINED {
363397 WebPDecoderConfig config;
364398 if (!WebPInitDecoderConfig (&config)) {
365399 return nil ;
@@ -377,13 +411,34 @@ - (nullable CGImageRef)sd_createWebpImageWithData:(WebPData)webpData colorSpace:
377411 config.options .use_threads = 1 ;
378412 config.output .colorspace = MODE_bgrA;
379413
414+ int width = config.input .width ;
415+ 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
420+ 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+ }
435+ }
436+
380437 // Decode the WebP image data into a RGBA value array
381438 if (WebPDecode (webpData.bytes , webpData.size , &config) != VP8_STATUS_OK) {
382439 return nil ;
383440 }
384441
385- int width = config.input .width ;
386- int height = config.input .height ;
387442 if (config.options .use_scaling ) {
388443 width = config.options .scaled_width ;
389444 height = config.options .scaled_height ;
@@ -681,6 +736,22 @@ - (instancetype)initWithAnimatedImageData:(NSData *)data options:(nullable SDIma
681736 scale = 1 ;
682737 }
683738 }
739+ CGSize thumbnailSize = CGSizeZero;
740+ NSValue *thumbnailSizeValue = options[SDImageCoderDecodeThumbnailPixelSize];
741+ if (thumbnailSizeValue != nil ) {
742+ #if SD_MAC
743+ thumbnailSize = thumbnailSizeValue.sizeValue ;
744+ #else
745+ thumbnailSize = thumbnailSizeValue.CGSizeValue ;
746+ #endif
747+ }
748+ _thumbnailSize = thumbnailSize;
749+ BOOL preserveAspectRatio = YES ;
750+ NSNumber *preserveAspectRatioValue = options[SDImageCoderDecodePreserveAspectRatio];
751+ if (preserveAspectRatioValue != nil ) {
752+ preserveAspectRatio = preserveAspectRatioValue.boolValue ;
753+ }
754+ _preserveAspectRatio = preserveAspectRatio;
684755 _scale = scale;
685756 _demux = demuxer;
686757 _imageData = data;
@@ -812,7 +883,7 @@ - (UIImage *)safeStaticImageFrame {
812883 WebPDemuxReleaseIterator (&iter);
813884 return nil ;
814885 }
815- CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment colorSpace: _colorSpace];
886+ CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment colorSpace: _colorSpace preserveAspectRatio: _preserveAspectRatio thumbnailSize: _thumbnailSize ];
816887 if (!imageRef) {
817888 return nil ;
818889 }
@@ -874,7 +945,7 @@ - (UIImage *)safeAnimatedImageFrameAtIndex:(NSUInteger)index {
874945 if (endIndex > startIndex) {
875946 do {
876947 @autoreleasepool {
877- [self sd_blendWebpImageWithCanvas: _canvas iterator: iter colorSpace: _colorSpace];
948+ [self sd_blendWebpImageWithCanvas: _canvas iterator: iter colorSpace: _colorSpace preserveAspectRatio: _preserveAspectRatio thumbnailSize: _thumbnailSize ];
878949 }
879950 } while ((size_t )iter.frame_num < endIndex && WebPDemuxNextFrame (&iter));
880951 }
@@ -887,7 +958,7 @@ - (UIImage *)safeAnimatedImageFrameAtIndex:(NSUInteger)index {
887958 _currentBlendIndex = index;
888959
889960 // Now the canvas is ready, which respects of dispose method behavior. Just do normal decoding and produce image.
890- CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas: _canvas iterator: iter colorSpace: _colorSpace];
961+ CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas: _canvas iterator: iter colorSpace: _colorSpace preserveAspectRatio: _preserveAspectRatio thumbnailSize: _thumbnailSize ];
891962 if (!imageRef) {
892963 return nil ;
893964 }
0 commit comments