Skip to content

Commit a99c0de

Browse files
committed
Fix the iOS 15+ force-decode hack break Apple's HEIF and JPEG YUV420 optimization
The lazyDecode logic effect the static image as well. Should roll back and only check for animated image
1 parent 0274aa8 commit a99c0de

File tree

3 files changed

+41
-39
lines changed

3 files changed

+41
-39
lines changed

SDWebImage/Core/SDImageIOAnimatedCoder.m

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,22 @@ + (NSTimeInterval)frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRe
218218
return frameDuration;
219219
}
220220

221-
+ (UIImage *)createFrameAtIndex:(NSUInteger)index source:(CGImageSourceRef)source scale:(CGFloat)scale preserveAspectRatio:(BOOL)preserveAspectRatio thumbnailSize:(CGSize)thumbnailSize lazyDecode:(BOOL)lazyDecode options:(NSDictionary *)options {
221+
+ (UIImage *)createFrameAtIndex:(NSUInteger)index source:(CGImageSourceRef)source scale:(CGFloat)scale preserveAspectRatio:(BOOL)preserveAspectRatio thumbnailSize:(CGSize)thumbnailSize lazyDecode:(BOOL)lazyDecode animatedImage:(BOOL)animatedImage {
222+
// `animatedImage` means called from `SDAnimatedImageProvider.animatedImageFrameAtIndex`
223+
NSDictionary *options;
224+
if (animatedImage) {
225+
if (!lazyDecode) {
226+
options = @{
227+
// image decoding and caching should happen at image creation time.
228+
(__bridge NSString *)kCGImageSourceShouldCacheImmediately : @(YES),
229+
};
230+
} else {
231+
options = @{
232+
// image decoding will happen at rendering time
233+
(__bridge NSString *)kCGImageSourceShouldCacheImmediately : @(NO),
234+
};
235+
}
236+
}
222237
// Some options need to pass to `CGImageSourceCopyPropertiesAtIndex` before `CGImageSourceCreateImageAtIndex`, or ImageIO will ignore them because they parse once :)
223238
// Parse the image properties
224239
NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(source, index, (__bridge CFDictionaryRef)options);
@@ -287,7 +302,7 @@ + (UIImage *)createFrameAtIndex:(NSUInteger)index source:(CGImageSourceRef)sourc
287302
isDecoded = YES;
288303
}
289304
}
290-
} else {
305+
} else if (animatedImage) {
291306
// iOS 15+, CGImageRef now retains CGImageSourceRef internally. To workaround its thread-safe issue, we have to strip CGImageSourceRef, using Force-Decode (or have to use SPI `CGImageSetImageSource`), See: https://github.com/SDWebImage/SDWebImage/issues/3273
292307
if (@available(iOS 15, tvOS 15, *)) {
293308
// User pass `lazyDecode == YES`, but we still have to strip the CGImageSourceRef
@@ -297,21 +312,19 @@ + (UIImage *)createFrameAtIndex:(NSUInteger)index source:(CGImageSourceRef)sourc
297312
CGImageRelease(imageRef);
298313
imageRef = newImageRef;
299314
}
300-
}
301-
}
302315
#if SD_CHECK_CGIMAGE_RETAIN_SOURCE
303-
if (@available(iOS 15, tvOS 15, *)) {
304-
// Assert here to check CGImageRef should not retain the CGImageSourceRef and has possible thread-safe issue (this is behavior on iOS 15+)
305-
// If assert hit, fire issue to https://github.com/SDWebImage/SDWebImage/issues and we update the condition for this behavior check
306-
static dispatch_once_t onceToken;
307-
dispatch_once(&onceToken, ^{
308-
SDCGImageGetImageSource = dlsym(RTLD_DEFAULT, "CGImageGetImageSource");
309-
});
310-
if (SDCGImageGetImageSource) {
311-
NSCAssert(!SDCGImageGetImageSource(imageRef), @"Animated Coder created CGImageRef should not retain CGImageSourceRef, which may cause thread-safe issue without lock");
316+
// Assert here to check CGImageRef should not retain the CGImageSourceRef and has possible thread-safe issue (this is behavior on iOS 15+)
317+
// If assert hit, fire issue to https://github.com/SDWebImage/SDWebImage/issues and we update the condition for this behavior check
318+
static dispatch_once_t onceToken;
319+
dispatch_once(&onceToken, ^{
320+
SDCGImageGetImageSource = dlsym(RTLD_DEFAULT, "CGImageGetImageSource");
321+
});
322+
if (SDCGImageGetImageSource) {
323+
NSCAssert(!SDCGImageGetImageSource(imageRef), @"Animated Coder created CGImageRef should not retain CGImageSourceRef, which may cause thread-safe issue without lock");
324+
}
325+
#endif
312326
}
313327
}
314-
#endif
315328

316329
#if SD_UIKIT || SD_WATCH
317330
UIImageOrientation imageOrientation = [SDImageCoderHelper imageOrientationFromEXIFOrientation:exifOrientation];
@@ -412,12 +425,12 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO
412425

413426
BOOL decodeFirstFrame = [options[SDImageCoderDecodeFirstFrameOnly] boolValue];
414427
if (decodeFirstFrame || count <= 1) {
415-
animatedImage = [self.class createFrameAtIndex:0 source:source scale:scale preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize lazyDecode:lazyDecode options:nil];
428+
animatedImage = [self.class createFrameAtIndex:0 source:source scale:scale preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize lazyDecode:lazyDecode animatedImage:NO];
416429
} else {
417430
NSMutableArray<SDImageFrame *> *frames = [NSMutableArray arrayWithCapacity:count];
418431

419432
for (size_t i = 0; i < count; i++) {
420-
UIImage *image = [self.class createFrameAtIndex:i source:source scale:scale preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize lazyDecode:lazyDecode options:nil];
433+
UIImage *image = [self.class createFrameAtIndex:i source:source scale:scale preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize lazyDecode:lazyDecode animatedImage:NO];
421434
if (!image) {
422435
continue;
423436
}
@@ -473,7 +486,7 @@ - (instancetype)initIncrementalWithOptions:(nullable SDImageCoderOptions *)optio
473486
preserveAspectRatio = preserveAspectRatioValue.boolValue;
474487
}
475488
_preserveAspectRatio = preserveAspectRatio;
476-
BOOL lazyDecode = YES; // Defaults YES for static image coder
489+
BOOL lazyDecode = NO; // Defaults NO for animated image coder
477490
NSNumber *lazyDecodeValue = options[SDImageCoderDecodeUseLazyDecoding];
478491
if (lazyDecodeValue != nil) {
479492
lazyDecode = lazyDecodeValue.boolValue;
@@ -533,7 +546,7 @@ - (UIImage *)incrementalDecodedImageWithOptions:(SDImageCoderOptions *)options {
533546
if (scaleFactor != nil) {
534547
scale = MAX([scaleFactor doubleValue], 1);
535548
}
536-
image = [self.class createFrameAtIndex:0 source:_imageSource scale:scale preserveAspectRatio:_preserveAspectRatio thumbnailSize:_thumbnailSize lazyDecode:_lazyDecode options:nil];
549+
image = [self.class createFrameAtIndex:0 source:_imageSource scale:scale preserveAspectRatio:_preserveAspectRatio thumbnailSize:_thumbnailSize lazyDecode:_lazyDecode animatedImage:NO];
537550
if (image) {
538551
image.sd_imageFormat = self.class.imageFormat;
539552
}
@@ -695,6 +708,12 @@ - (nullable instancetype)initWithAnimatedImageData:(nullable NSData *)data optio
695708
preserveAspectRatio = preserveAspectRatioValue.boolValue;
696709
}
697710
_preserveAspectRatio = preserveAspectRatio;
711+
BOOL lazyDecode = NO; // Defaults NO for animated image coder
712+
NSNumber *lazyDecodeValue = options[SDImageCoderDecodeUseLazyDecoding];
713+
if (lazyDecodeValue != nil) {
714+
lazyDecode = lazyDecodeValue.boolValue;
715+
}
716+
_lazyDecode = lazyDecode;
698717
_imageSource = imageSource;
699718
_imageData = data;
700719
#if SD_UIKIT
@@ -785,24 +804,7 @@ - (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index {
785804
}
786805

787806
- (UIImage *)safeAnimatedImageFrameAtIndex:(NSUInteger)index {
788-
NSDictionary *options;
789-
BOOL lazyDecode = NO; // Defaults NO for animated image coder
790-
NSNumber *lazyDecodeValue = options[SDImageCoderDecodeUseLazyDecoding];
791-
if (lazyDecodeValue != nil) {
792-
lazyDecode = lazyDecodeValue.boolValue;
793-
}
794-
if (!lazyDecode) {
795-
options = @{
796-
(__bridge NSString *)kCGImageSourceShouldCacheImmediately : @(NO),
797-
(__bridge NSString *)kCGImageSourceShouldCache : @(NO)
798-
};
799-
} else {
800-
options = @{
801-
(__bridge NSString *)kCGImageSourceShouldCacheImmediately : @(YES),
802-
(__bridge NSString *)kCGImageSourceShouldCache : @(YES) // Always cache to reduce CPU usage
803-
};
804-
}
805-
UIImage *image = [self.class createFrameAtIndex:index source:_imageSource scale:_scale preserveAspectRatio:_preserveAspectRatio thumbnailSize:_thumbnailSize lazyDecode:lazyDecode options:options];
807+
UIImage *image = [self.class createFrameAtIndex:index source:_imageSource scale:_scale preserveAspectRatio:_preserveAspectRatio thumbnailSize:_thumbnailSize lazyDecode:_lazyDecode animatedImage:YES];
806808
if (!image) {
807809
return nil;
808810
}

SDWebImage/Core/SDImageIOCoder.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO
211211
CFStringRef uttype = CGImageSourceGetType(source);
212212
SDImageFormat imageFormat = [NSData sd_imageFormatFromUTType:uttype];
213213

214-
UIImage *image = [SDImageIOAnimatedCoder createFrameAtIndex:0 source:source scale:scale preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize lazyDecode:lazyDecode options:nil];
214+
UIImage *image = [SDImageIOAnimatedCoder createFrameAtIndex:0 source:source scale:scale preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize lazyDecode:lazyDecode animatedImage:NO];
215215
CFRelease(source);
216216

217217
image.sd_imageFormat = imageFormat;
@@ -306,7 +306,7 @@ - (UIImage *)incrementalDecodedImageWithOptions:(SDImageCoderOptions *)options {
306306
if (scaleFactor != nil) {
307307
scale = MAX([scaleFactor doubleValue], 1);
308308
}
309-
image = [SDImageIOAnimatedCoder createFrameAtIndex:0 source:_imageSource scale:scale preserveAspectRatio:_preserveAspectRatio thumbnailSize:_thumbnailSize lazyDecode:_lazyDecode options:nil];
309+
image = [SDImageIOAnimatedCoder createFrameAtIndex:0 source:_imageSource scale:scale preserveAspectRatio:_preserveAspectRatio thumbnailSize:_thumbnailSize lazyDecode:_lazyDecode animatedImage:NO];
310310
if (image) {
311311
CFStringRef uttype = CGImageSourceGetType(_imageSource);
312312
image.sd_imageFormat = [NSData sd_imageFormatFromUTType:uttype];

SDWebImage/Private/SDImageIOAnimatedCoderInternal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
+ (NSTimeInterval)frameDurationAtIndex:(NSUInteger)index source:(nonnull CGImageSourceRef)source;
3434
+ (NSUInteger)imageLoopCountWithSource:(nonnull CGImageSourceRef)source;
35-
+ (nullable UIImage *)createFrameAtIndex:(NSUInteger)index source:(nonnull CGImageSourceRef)source scale:(CGFloat)scale preserveAspectRatio:(BOOL)preserveAspectRatio thumbnailSize:(CGSize)thumbnailSize lazyDecode:(BOOL)lazyDecode options:(nullable NSDictionary *)options;
35+
+ (nullable UIImage *)createFrameAtIndex:(NSUInteger)index source:(nonnull CGImageSourceRef)source scale:(CGFloat)scale preserveAspectRatio:(BOOL)preserveAspectRatio thumbnailSize:(CGSize)thumbnailSize lazyDecode:(BOOL)lazyDecode animatedImage:(BOOL)animatedImage;
3636
+ (BOOL)canEncodeToFormat:(SDImageFormat)format;
3737
+ (BOOL)canDecodeFromFormat:(SDImageFormat)format;
3838

0 commit comments

Comments
 (0)