@@ -194,11 +194,11 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO
194194 CGColorSpaceRef colorSpace = [self sd_createColorSpaceWithDemuxer: demuxer];
195195 int canvasWidth = WebPDemuxGetI (demuxer, WEBP_FF_CANVAS_WIDTH);
196196 int canvasHeight = WebPDemuxGetI (demuxer, WEBP_FF_CANVAS_HEIGHT);
197+ // Check whether we need to use thumbnail
198+ CGSize scaledSize = SDCalculateThumbnailSize (CGSizeMake (canvasWidth, canvasHeight), preserveAspectRatio, thumbnailSize);
197199
198200 if (!hasAnimation || decodeFirstFrame) {
199201 // first frame for animated webp image
200- CGSize scaledSize = SDCalculateThumbnailSize (CGSizeMake (canvasWidth, canvasHeight), preserveAspectRatio, thumbnailSize);
201- // Create thumbnail if need
202202 CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment colorSpace: colorSpace scaledSize: scaledSize];
203203 CGColorSpaceRelease (colorSpace);
204204#if SD_UIKIT || SD_WATCH
@@ -228,10 +228,11 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO
228228
229229 do {
230230 @autoreleasepool {
231- CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas: canvas iterator: iter colorSpace: colorSpace];
231+ CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas: canvas iterator: iter colorSpace: colorSpace scaledSize: scaledSize ];
232232 if (!imageRef) {
233233 continue ;
234234 }
235+
235236#if SD_UIKIT || SD_WATCH
236237 UIImage *image = [[UIImage alloc ] initWithCGImage: imageRef scale: scale orientation: UIImageOrientationUp];
237238#else
@@ -399,12 +400,13 @@ - (void)sd_blendWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)
399400 }
400401}
401402
402- - (nullable CGImageRef)sd_drawnWebpImageWithCanvas : (CGContextRef)canvas iterator : (WebPIterator)iter colorSpace : (nonnull CGColorSpaceRef)colorSpaceRef CF_RETURNS_RETAINED {
403+ - (nullable CGImageRef)sd_drawnWebpImageWithCanvas : (CGContextRef)canvas iterator : (WebPIterator)iter colorSpace : (nonnull CGColorSpaceRef)colorSpaceRef scaledSize : (CGSize) scaledSize CF_RETURNS_RETAINED {
403404 CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment colorSpace: colorSpaceRef scaledSize: CGSizeZero];
404405 if (!imageRef) {
405406 return nil ;
406407 }
407408
409+ size_t canvasWidth = CGBitmapContextGetWidth (canvas);
408410 size_t canvasHeight = CGBitmapContextGetHeight (canvas);
409411 CGFloat tmpX = iter.x_offset ;
410412 CGFloat tmpY = canvasHeight - iter.height - iter.y_offset ;
@@ -425,6 +427,17 @@ - (nullable CGImageRef)sd_drawnWebpImageWithCanvas:(CGContextRef)canvas iterator
425427 CGContextClearRect (canvas, imageRect);
426428 }
427429
430+ // Check whether we need to use thumbnail
431+ if (!CGSizeEqualToSize (CGSizeMake (canvasWidth, canvasHeight), scaledSize)) {
432+ // Important: For Animated WebP thumbnail generation, we can not just use a scaled small canvas and draw each thumbnail frame
433+ // This works **On Theory**. However, image scale down loss details. Animated WebP use the partial pixels with blend mode / dispose method with offset, to cover previous canvas status
434+ // Because of this reason, even each frame contains small zigzag, the final animation contains visible glitch, this is not we want.
435+ // So, always create the full pixels canvas (even though this consume more RAM), after drawn on the canvas, re-scale again with the final size
436+ CGImageRef scaledImageRef = [SDImageCoderHelper CGImageCreateScaled: newImageRef size: scaledSize];
437+ CGImageRelease (newImageRef);
438+ newImageRef = scaledImageRef;
439+ }
440+
428441 return newImageRef;
429442}
430443
@@ -899,6 +912,7 @@ - (UIImage *)safeStaticImageFrame {
899912 WebPDemuxReleaseIterator (&iter);
900913 return nil ;
901914 }
915+ // Check whether we need to use thumbnail
902916 CGSize scaledSize = SDCalculateThumbnailSize (CGSizeMake (_canvasWidth, _canvasHeight), _preserveAspectRatio, _thumbnailSize);
903917 CGImageRef imageRef = [self sd_createWebpImageWithData: iter.fragment colorSpace: _colorSpace scaledSize: scaledSize];
904918 if (!imageRef) {
@@ -915,9 +929,6 @@ - (UIImage *)safeStaticImageFrame {
915929}
916930
917931- (UIImage *)safeAnimatedImageFrameAtIndex : (NSUInteger )index {
918- if (!_colorSpace) {
919- _colorSpace = [self sd_createColorSpaceWithDemuxer: _demux];
920- }
921932 if (!_canvas) {
922933 CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host ;
923934 bitmapInfo |= _hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst ;
@@ -927,6 +938,9 @@ - (UIImage *)safeAnimatedImageFrameAtIndex:(NSUInteger)index {
927938 }
928939 _canvas = canvas;
929940 }
941+ if (!_colorSpace) {
942+ _colorSpace = [self sd_createColorSpaceWithDemuxer: _demux];
943+ }
930944
931945 SDWebPCoderFrame *frame = _frames[index];
932946 UIImage *image;
@@ -946,7 +960,7 @@ - (UIImage *)safeAnimatedImageFrameAtIndex:(NSUInteger)index {
946960 } else {
947961 // 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.
948962 if (_currentBlendIndex != NSNotFound ) {
949- CGContextClearRect (_canvas, CGRectMake (0 , 0 , CGBitmapContextGetWidth (_canvas), CGBitmapContextGetHeight (_canvas) ));
963+ CGContextClearRect (_canvas, CGRectMake (0 , 0 , _canvasWidth, _canvasHeight ));
950964 }
951965
952966 // Then, loop from the blend from index, draw each of previous frames on the canvas.
@@ -975,7 +989,9 @@ - (UIImage *)safeAnimatedImageFrameAtIndex:(NSUInteger)index {
975989 _currentBlendIndex = index;
976990
977991 // Now the canvas is ready, which respects of dispose method behavior. Just do normal decoding and produce image.
978- CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas: _canvas iterator: iter colorSpace: _colorSpace];
992+ // Check whether we need to use thumbnail
993+ CGSize scaledSize = SDCalculateThumbnailSize (CGSizeMake (_canvasWidth, _canvasHeight), _preserveAspectRatio, _thumbnailSize);
994+ CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas: _canvas iterator: iter colorSpace: _colorSpace scaledSize: scaledSize];
979995 if (!imageRef) {
980996 return nil ;
981997 }
0 commit comments