Skip to content

Commit 7e2e3db

Browse files
committed
Added support for SDWebImage thumbnail decoding
Use the coder helper's re-scale on CGImage currently May optimize using vImageScale in the future
1 parent ad88f81 commit 7e2e3db

File tree

2 files changed

+78
-8
lines changed

2 files changed

+78
-8
lines changed

Example/SDWebImageAVIFCoder/SDViewController.m

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ - (void)viewDidLoad
3333
[self.view addSubview:imageView1];
3434
[self.view addSubview:imageView2];
3535

36-
[imageView1 sd_setImageWithURL:AVIFURL placeholderImage:nil options:0 completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
36+
[imageView1 sd_setImageWithURL:AVIFURL completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
3737
if (image) {
3838
NSLog(@"Static AVIF load success");
3939
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@@ -44,7 +44,10 @@ - (void)viewDidLoad
4444
});
4545
}
4646
}];
47-
[imageView2 sd_setImageWithURL:animatedAVIFSURL completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
47+
CGSize animatedThumbnailSize = CGSizeMake(100, 100);
48+
[imageView2 sd_setImageWithURL:animatedAVIFSURL placeholderImage:nil options:0 context:@{SDWebImageContextImageThumbnailPixelSize : @(animatedThumbnailSize)} progress:nil completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
49+
NSCAssert(image.size.width == 100, @"Thumbnail width should be 100");
50+
NSCAssert(image.size.height == 100, @"Thumbnail height should be 100");
4851
if (image) {
4952
NSLog(@"Animated AVIFS load success");
5053
}

SDWebImageAVIFCoder/Classes/SDImageAVIFCoder.m

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ @implementation SDImageAVIFCoder {
6767
CGFloat _scale;
6868
NSUInteger _loopCount;
6969
NSUInteger _frameCount;
70+
BOOL _hasAnimation;
7071
SD_LOCK_DECLARE(_lock);
72+
BOOL _preserveAspectRatio;
73+
CGSize _thumbnailSize;
7174
}
7275

7376
- (void)dealloc {
@@ -93,14 +96,32 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)
9396
if (!data) {
9497
return nil;
9598
}
99+
BOOL decodeFirstFrame = [options[SDImageCoderDecodeFirstFrameOnly] boolValue];
96100
CGFloat scale = 1;
97-
if ([options valueForKey:SDImageCoderDecodeScaleFactor]) {
98-
scale = [[options valueForKey:SDImageCoderDecodeScaleFactor] doubleValue];
101+
NSNumber *scaleFactor = options[SDImageCoderDecodeScaleFactor];
102+
if (scaleFactor != nil) {
103+
scale = [scaleFactor doubleValue];
99104
if (scale < 1) {
100105
scale = 1;
101106
}
102107
}
103108

109+
CGSize thumbnailSize = CGSizeZero;
110+
NSValue *thumbnailSizeValue = options[SDImageCoderDecodeThumbnailPixelSize];
111+
if (thumbnailSizeValue != nil) {
112+
#if SD_MAC
113+
thumbnailSize = thumbnailSizeValue.sizeValue;
114+
#else
115+
thumbnailSize = thumbnailSizeValue.CGSizeValue;
116+
#endif
117+
}
118+
119+
BOOL preserveAspectRatio = YES;
120+
NSNumber *preserveAspectRatioValue = options[SDImageCoderDecodePreserveAspectRatio];
121+
if (preserveAspectRatioValue != nil) {
122+
preserveAspectRatio = preserveAspectRatioValue.boolValue;
123+
}
124+
104125
// Decode it
105126
avifDecoder * decoder = avifDecoderCreate();
106127
avifDecoderSetIOMemory(decoder, data.bytes, data.length);
@@ -113,15 +134,27 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)
113134
return nil;
114135
}
115136

137+
BOOL hasAnimation = decoder->imageCount > 1;
138+
uint32_t width = decoder->image->width;
139+
uint32_t height = decoder->image->height;
140+
CGSize scaledSize = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(width, height) scaleSize:thumbnailSize preserveAspectRatio:preserveAspectRatio shouldScaleUp:NO];
141+
116142
// Static image
117-
if (decoder->imageCount <= 1) {
143+
if (!hasAnimation || decodeFirstFrame) {
118144
avifResult nextImageResult = avifDecoderNextImage(decoder);
119145
if (nextImageResult != AVIF_RESULT_OK) {
120146
NSLog(@"Failed to decode image: %s", avifResultToString(nextImageResult));
121147
avifDecoderDestroy(decoder);
122148
return nil;
123149
}
124-
CGImageRef imageRef = SDCreateCGImageFromAVIF(decoder->image);
150+
CGImageRef originImageRef = SDCreateCGImageFromAVIF(decoder->image);
151+
if (!originImageRef) {
152+
avifDecoderDestroy(decoder);
153+
return nil;
154+
}
155+
// TODO: optimization using vImageScale directly during transform
156+
CGImageRef imageRef = [SDImageCoderHelper CGImageCreateScaled:originImageRef size:scaledSize];
157+
CGImageRelease(originImageRef);
125158
if (!imageRef) {
126159
avifDecoderDestroy(decoder);
127160
return nil;
@@ -140,7 +173,13 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)
140173
NSMutableArray<SDImageFrame *> *frames = [NSMutableArray array];
141174
while (avifDecoderNextImage(decoder) == AVIF_RESULT_OK) {
142175
@autoreleasepool {
143-
CGImageRef imageRef = SDCreateCGImageFromAVIF(decoder->image);
176+
CGImageRef originImageRef = SDCreateCGImageFromAVIF(decoder->image);
177+
if (!originImageRef) {
178+
continue;
179+
}
180+
// TODO: optimization using vImageScale directly during transform
181+
CGImageRef imageRef = [SDImageCoderHelper CGImageCreateScaled:originImageRef size:scaledSize];
182+
CGImageRelease(originImageRef);
144183
if (!imageRef) {
145184
continue;
146185
}
@@ -296,6 +335,7 @@ - (instancetype)initWithAnimatedImageData:(NSData *)data options:(SDImageCoderOp
296335
// TODO: Optimize the performance like WebPCoder (frame meta cache, etc)
297336
_frameCount = decoder->imageCount;
298337
_loopCount = 0;
338+
_hasAnimation = decoder->imageCount > 1;
299339
CGFloat scale = 1;
300340
NSNumber *scaleFactor = options[SDImageCoderDecodeScaleFactor];
301341
if (scaleFactor != nil) {
@@ -305,6 +345,22 @@ - (instancetype)initWithAnimatedImageData:(NSData *)data options:(SDImageCoderOp
305345
}
306346
}
307347
_scale = scale;
348+
CGSize thumbnailSize = CGSizeZero;
349+
NSValue *thumbnailSizeValue = options[SDImageCoderDecodeThumbnailPixelSize];
350+
if (thumbnailSizeValue != nil) {
351+
#if SD_MAC
352+
thumbnailSize = thumbnailSizeValue.sizeValue;
353+
#else
354+
thumbnailSize = thumbnailSizeValue.CGSizeValue;
355+
#endif
356+
}
357+
_thumbnailSize = thumbnailSize;
358+
BOOL preserveAspectRatio = YES;
359+
NSNumber *preserveAspectRatioValue = options[SDImageCoderDecodePreserveAspectRatio];
360+
if (preserveAspectRatioValue != nil) {
361+
preserveAspectRatio = preserveAspectRatioValue.boolValue;
362+
}
363+
_preserveAspectRatio = preserveAspectRatio;
308364
_decoder = decoder;
309365
_imageData = data;
310366
SD_LOCK_INIT(_lock);
@@ -345,14 +401,25 @@ - (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index {
345401
if (index >= _frameCount) {
346402
return nil;
347403
}
404+
uint32_t width = 0;
405+
uint32_t height = 0;
348406
SD_LOCK(_lock);
349407
avifResult decodeResult = avifDecoderNthImage(_decoder, (uint32_t)index);
350408
if (decodeResult != AVIF_RESULT_OK) {
351409
SD_UNLOCK(_lock);
352410
return nil;
353411
}
354-
CGImageRef imageRef = SDCreateCGImageFromAVIF(_decoder->image);
412+
width = _decoder->image->width;
413+
height = _decoder->image->height;
414+
CGImageRef originImageRef = SDCreateCGImageFromAVIF(_decoder->image);
355415
SD_UNLOCK(_lock);
416+
if (!originImageRef) {
417+
return nil;
418+
}
419+
CGSize scaledSize = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(width, height) scaleSize:_thumbnailSize preserveAspectRatio:_preserveAspectRatio shouldScaleUp:NO];
420+
// TODO: optimization using vImageScale directly during transform
421+
CGImageRef imageRef = [SDImageCoderHelper CGImageCreateScaled:originImageRef size:scaledSize];
422+
CGImageRelease(originImageRef);
356423
if (!imageRef) {
357424
return nil;
358425
}

0 commit comments

Comments
 (0)