diff --git a/SDWebImage/Core/SDAnimatedImage.h b/SDWebImage/Core/SDAnimatedImage.h index 39b3d909..78739c57 100644 --- a/SDWebImage/Core/SDAnimatedImage.h +++ b/SDWebImage/Core/SDAnimatedImage.h @@ -72,7 +72,8 @@ // This class override these methods from UIImage(NSImage), and it supports NSSecureCoding. // You should use these methods to create a new animated image. Use other methods just call super instead. -// Pay attention, when the animated image frame count <= 1, all the `SDAnimatedImageProvider` protocol methods will return nil or 0 value, you'd better check the frame count before usage and keep fallback. +// @note Before 5.19, these initializer will return nil for static image (when all candidate SDAnimatedImageCoder returns nil instance), like JPEG data. After 5.19, these initializer will retry for static image as well, so JPEG data will return non-nil instance. +// @note When the animated image frame count <= 1, all the `SDAnimatedImageProvider` protocol methods will return nil or 0 value, you'd better check the frame count before usage and keep fallback. + (nullable instancetype)imageNamed:(nonnull NSString *)name; // Cache in memory, no Asset Catalog support #if __has_include() + (nullable instancetype)imageNamed:(nonnull NSString *)name inBundle:(nullable NSBundle *)bundle compatibleWithTraitCollection:(nullable UITraitCollection *)traitCollection; // Cache in memory, no Asset Catalog support @@ -88,7 +89,8 @@ /** Current animated image format. - @note This format is only valid when `animatedImageData` not nil + @note This format is only valid when `animatedImageData` not nil. + @note This actually just call `[NSData sd_imageFormatForImageData:self.animatedImageData]` */ @property (nonatomic, assign, readonly) SDImageFormat animatedImageFormat; diff --git a/SDWebImage/Core/SDAnimatedImage.m b/SDWebImage/Core/SDAnimatedImage.m index 10f5eb9d..75ba2236 100644 --- a/SDWebImage/Core/SDAnimatedImage.m +++ b/SDWebImage/Core/SDAnimatedImage.m @@ -34,7 +34,6 @@ static CGFloat SDImageScaleFromPath(NSString *string) { @interface SDAnimatedImage () @property (nonatomic, strong) id animatedCoder; -@property (nonatomic, assign, readwrite) SDImageFormat animatedImageFormat; @property (atomic, copy) NSArray *loadedAnimatedImageFrames; // Mark as atomic to keep thread-safe @property (nonatomic, assign, getter=isAllFramesLoaded) BOOL allFramesLoaded; @@ -115,7 +114,19 @@ static CGFloat SDImageScaleFromPath(NSString *string) { - (instancetype)initWithContentsOfFile:(NSString *)path { NSData *data = [NSData dataWithContentsOfFile:path]; - return [self initWithData:data scale:SDImageScaleFromPath(path)]; + if (!data) { + return nil; + } + CGFloat scale = SDImageScaleFromPath(path); + // path extension may be useful for coder like raw-image + NSString *fileExtensionHint = path.pathExtension; // without dot + if (fileExtensionHint.length == 0) { + // Ignore file extension which is empty + fileExtensionHint = nil; + } + SDImageCoderMutableOptions *mutableCoderOptions = [NSMutableDictionary dictionaryWithCapacity:1]; + mutableCoderOptions[SDImageCoderDecodeFileExtensionHint] = fileExtensionHint; + return [self initWithData:data scale:scale options:[mutableCoderOptions copy]]; } - (instancetype)initWithData:(NSData *)data { @@ -131,21 +142,38 @@ static CGFloat SDImageScaleFromPath(NSString *string) { return nil; } id animatedCoder = nil; + SDImageCoderMutableOptions *mutableCoderOptions; + if (options != nil) { + mutableCoderOptions = [NSMutableDictionary dictionaryWithDictionary:options]; + } else { + mutableCoderOptions = [NSMutableDictionary dictionaryWithCapacity:1]; + } + mutableCoderOptions[SDImageCoderDecodeScaleFactor] = @(scale); + options = [mutableCoderOptions copy]; for (idcoder in [SDImageCodersManager sharedManager].coders.reverseObjectEnumerator) { if ([coder conformsToProtocol:@protocol(SDAnimatedImageCoder)]) { if ([coder canDecodeFromData:data]) { - if (!options) { - options = @{SDImageCoderDecodeScaleFactor : @(scale)}; - } animatedCoder = [[[coder class] alloc] initWithAnimatedImageData:data options:options]; break; } } } - if (!animatedCoder) { - return nil; + if (animatedCoder) { + // Animated Image + return [self initWithAnimatedCoder:animatedCoder scale:scale]; + } else { + // Static Image (Before 5.19 this code path return nil) + UIImage *image = [[SDImageCodersManager sharedManager] decodedImageWithData:data options:options]; + if (!image) { + return nil; + } +#if SD_MAC + self = [super initWithCGImage:image.CGImage scale:MAX(scale, 1) orientation:kCGImagePropertyOrientationUp]; +#else + self = [super initWithCGImage:image.CGImage scale:MAX(scale, 1) orientation:image.imageOrientation]; +#endif + return self; } - return [self initWithAnimatedCoder:animatedCoder scale:scale]; } - (instancetype)initWithAnimatedCoder:(id)animatedCoder scale:(CGFloat)scale { @@ -166,13 +194,14 @@ static CGFloat SDImageScaleFromPath(NSString *string) { if (animatedCoder.animatedImageFrameCount > 1) { _animatedCoder = animatedCoder; } - NSData *data = [animatedCoder animatedImageData]; - SDImageFormat format = [NSData sd_imageFormatForImageData:data]; - _animatedImageFormat = format; } return self; } +- (SDImageFormat)animatedImageFormat { + return [NSData sd_imageFormatForImageData:self.animatedImageData]; +} + #pragma mark - Preload - (void)preloadAllFrames { if (!_animatedCoder) { @@ -205,7 +234,6 @@ static CGFloat SDImageScaleFromPath(NSString *string) { - (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { - _animatedImageFormat = [aDecoder decodeIntegerForKey:NSStringFromSelector(@selector(animatedImageFormat))]; NSData *animatedImageData = [aDecoder decodeObjectOfClass:[NSData class] forKey:NSStringFromSelector(@selector(animatedImageData))]; if (!animatedImageData) { return self; @@ -232,7 +260,6 @@ static CGFloat SDImageScaleFromPath(NSString *string) { - (void)encodeWithCoder:(NSCoder *)aCoder { [super encodeWithCoder:aCoder]; - [aCoder encodeInteger:self.animatedImageFormat forKey:NSStringFromSelector(@selector(animatedImageFormat))]; NSData *animatedImageData = self.animatedImageData; if (animatedImageData) { [aCoder encodeObject:animatedImageData forKey:NSStringFromSelector(@selector(animatedImageData))];