Skip to content

Commit 50136be

Browse files
committed
Fix the issue that animated image (which use a canvas) should also scale the canvas size
1 parent 9db3358 commit 50136be

File tree

1 file changed

+63
-41
lines changed

1 file changed

+63
-41
lines changed

SDWebImageWebPCoder/Classes/SDImageWebPCoder.m

Lines changed: 63 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,38 @@
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

Comments
 (0)