From c99ddbfac99ffc69b3adb69c84ccd1ed313d8472 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Tue, 21 Aug 2018 12:48:40 +0800 Subject: [PATCH 1/2] Add one initializer with extra arg for custom animated image class to use possible coder options, to make it extensible --- SDWebImage/SDAnimatedImage.h | 15 +++++++++++++-- SDWebImage/SDAnimatedImage.m | 9 ++++++++- SDWebImage/SDAnimatedImageView.h | 2 +- SDWebImage/SDImageCacheDefine.m | 15 ++++++++------- SDWebImage/SDImageLoader.m | 30 ++++++++++++++++-------------- SDWebImage/SDWebImageDefine.h | 2 +- 6 files changed, 47 insertions(+), 26 deletions(-) diff --git a/SDWebImage/SDAnimatedImage.h b/SDWebImage/SDAnimatedImage.h index 6ae98571..2112685a 100644 --- a/SDWebImage/SDAnimatedImage.h +++ b/SDWebImage/SDAnimatedImage.h @@ -16,9 +16,20 @@ @protocol SDAnimatedImage @required +/** + Initializes and returns the image object with the specified data, scale factor and possible animation decoding options. + @note We use this to create animated image instance for normal animation decoding. + + @param data The data object containing the image data. + @param scale The scale factor to assume when interpreting the image data. Applying a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a different scale factor changes the size of the image as reported by the `size` property. + @param options A dictionary containing any animation decoding options. + @return An initialized object + */ +- (nullable instancetype)initWithData:(nonnull NSData *)data scale:(CGFloat)scale options:(nullable SDImageCoderOptions *)options; + /** Initializes the image with an animated coder. You can use the coder to decode the image frame later. - @note Normally we use `initWithData:scale:` to create custom animated image class. However, for progressive image decoding, we will use this with animated coder which conforms to `SDProgressiveImageCoder` as well instead. + @note We use this with animated coder which conforms to `SDProgressiveImageCoder` for progressive animation decoding. @param animatedCoder An animated coder which conform `SDAnimatedImageCoder` protocol @param scale The scale factor to assume when interpreting the image data. Applying a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a different scale factor changes the size of the image as reported by the `size` property. @@ -27,6 +38,7 @@ - (nullable instancetype)initWithAnimatedCoder:(nonnull id)animatedCoder scale:(CGFloat)scale; @optional +// These methods are used for optional advanced feature, like image frame preloading. /** Pre-load all animated image frame into memory. Then later frame image request can directly return the frame for index without decoding. This method may be called on background thread. @@ -63,7 +75,6 @@ - (nullable instancetype)initWithContentsOfFile:(nonnull NSString *)path; - (nullable instancetype)initWithData:(nonnull NSData *)data; - (nullable instancetype)initWithData:(nonnull NSData *)data scale:(CGFloat)scale; -- (nullable instancetype)initWithAnimatedCoder:(nonnull id)animatedCoder scale:(CGFloat)scale; /** Current animated image format. diff --git a/SDWebImage/SDAnimatedImage.m b/SDWebImage/SDAnimatedImage.m index fa20c898..2ad9b72e 100644 --- a/SDWebImage/SDAnimatedImage.m +++ b/SDWebImage/SDAnimatedImage.m @@ -276,6 +276,10 @@ static NSArray *SDBundlePreferredScales() { } - (instancetype)initWithData:(NSData *)data scale:(CGFloat)scale { + return [self initWithData:data scale:scale options:nil]; +} + +- (instancetype)initWithData:(NSData *)data scale:(CGFloat)scale options:(SDImageCoderOptions *)options { if (!data || data.length == 0) { return nil; } @@ -284,7 +288,10 @@ static NSArray *SDBundlePreferredScales() { for (idcoder in [SDImageCodersManager sharedManager].coders) { if ([coder conformsToProtocol:@protocol(SDAnimatedImageCoder)]) { if ([coder canDecodeFromData:data]) { - animatedCoder = [[[coder class] alloc] initWithAnimatedImageData:data options:@{SDImageCoderDecodeScaleFactor : @(scale)}]; + if (!options) { + options = @{SDImageCoderDecodeScaleFactor : @(scale)}; + } + animatedCoder = [[[coder class] alloc] initWithAnimatedImageData:data options:options]; break; } } diff --git a/SDWebImage/SDAnimatedImageView.h b/SDWebImage/SDAnimatedImageView.h index e822c4ea..ec5bda13 100644 --- a/SDWebImage/SDAnimatedImageView.h +++ b/SDWebImage/SDAnimatedImageView.h @@ -53,7 +53,7 @@ /** Whehter or not to enable incremental image load for animated image. This is for the animated image which `sd_isIncremental` is YES (See `UIImage+Metadata.h`). If enable, animated image rendering will stop at the last frame available currently, and continue when another `setImage:` trigger, where the new animated image's `animatedImageData` should be updated from the previous one. If the `sd_isIncremental` is NO. The incremental image load stop. @note If you are confused about this description, open Chrome browser to view some large GIF images with low network speed to see the animation behavior. - @note The best practice to use incremental load is using `initWithAnimatedCoder:scale` in `SDAnimatedImage` with animated coder which conform to `SDProgressiveImageCoder` as well. Then call incremental update and incremental decode method to produce the image. + @note The best practice to use incremental load is using `initWithAnimatedCoder:scale:` in `SDAnimatedImage` with animated coder which conform to `SDProgressiveImageCoder` as well. Then call incremental update and incremental decode method to produce the image. Default is YES. Set to NO to only render the static poster for incremental animated image. */ @property (nonatomic, assign) BOOL shouldIncrementalLoad; diff --git a/SDWebImage/SDImageCacheDefine.m b/SDWebImage/SDImageCacheDefine.m index 3bef6dbe..423e0583 100644 --- a/SDWebImage/SDImageCacheDefine.m +++ b/SDWebImage/SDImageCacheDefine.m @@ -20,23 +20,24 @@ UIImage * _Nullable SDImageCacheDecodeImageData(NSData * _Nonnull imageData, NSS if (scale < 1) { scale = 1; } + SDImageCoderOptions *coderOptions = @{SDImageCoderDecodeFirstFrameOnly : @(decodeFirstFrame), SDImageCoderDecodeScaleFactor : @(scale)}; + if (context) { + SDImageCoderMutableOptions *mutableCoderOptions = [coderOptions mutableCopy]; + [mutableCoderOptions setValue:context forKey:SDImageCoderWebImageContext]; + coderOptions = [mutableCoderOptions copy]; + } + if (!decodeFirstFrame) { Class animatedImageClass = context[SDWebImageContextAnimatedImageClass]; // check whether we should use `SDAnimatedImage` if ([animatedImageClass isSubclassOfClass:[UIImage class]] && [animatedImageClass conformsToProtocol:@protocol(SDAnimatedImage)]) { - image = [[animatedImageClass alloc] initWithData:imageData scale:scale]; + image = [[animatedImageClass alloc] initWithData:imageData scale:scale options:coderOptions]; if (options & SDWebImagePreloadAllFrames && [image respondsToSelector:@selector(preloadAllFrames)]) { [((id)image) preloadAllFrames]; } } } if (!image) { - SDImageCoderOptions *coderOptions = @{SDImageCoderDecodeFirstFrameOnly : @(decodeFirstFrame), SDImageCoderDecodeScaleFactor : @(scale)}; - if (context) { - SDImageCoderMutableOptions *mutableCoderOptions = [coderOptions mutableCopy]; - [mutableCoderOptions setValue:context forKey:SDImageCoderWebImageContext]; - coderOptions = [mutableCoderOptions copy]; - } image = [[SDImageCodersManager sharedManager] decodedImageWithData:imageData options:coderOptions]; } if (image) { diff --git a/SDWebImage/SDImageLoader.m b/SDWebImage/SDImageLoader.m index 87f6c84f..7d3db1a9 100644 --- a/SDWebImage/SDImageLoader.m +++ b/SDWebImage/SDImageLoader.m @@ -34,23 +34,24 @@ UIImage * _Nullable SDImageLoaderDecodeImageData(NSData * _Nonnull imageData, NS if (scale < 1) { scale = 1; } + SDImageCoderOptions *coderOptions = @{SDImageCoderDecodeFirstFrameOnly : @(decodeFirstFrame), SDImageCoderDecodeScaleFactor : @(scale)}; + if (context) { + SDImageCoderMutableOptions *mutableCoderOptions = [coderOptions mutableCopy]; + [mutableCoderOptions setValue:context forKey:SDImageCoderWebImageContext]; + coderOptions = [mutableCoderOptions copy]; + } + if (!decodeFirstFrame) { // check whether we should use `SDAnimatedImage` Class animatedImageClass = context[SDWebImageContextAnimatedImageClass]; if ([animatedImageClass isSubclassOfClass:[UIImage class]] && [animatedImageClass conformsToProtocol:@protocol(SDAnimatedImage)]) { - image = [[animatedImageClass alloc] initWithData:imageData scale:scale]; + image = [[animatedImageClass alloc] initWithData:imageData scale:scale options:coderOptions]; if (options & SDWebImagePreloadAllFrames && [image respondsToSelector:@selector(preloadAllFrames)]) { [((id)image) preloadAllFrames]; } } } if (!image) { - SDImageCoderOptions *coderOptions = @{SDImageCoderDecodeFirstFrameOnly : @(decodeFirstFrame), SDImageCoderDecodeScaleFactor : @(scale)}; - if (context) { - SDImageCoderMutableOptions *mutableCoderOptions = [coderOptions mutableCopy]; - [mutableCoderOptions setValue:context forKey:SDImageCoderWebImageContext]; - coderOptions = [mutableCoderOptions copy]; - } image = [[SDImageCodersManager sharedManager] decodedImageWithData:imageData options:coderOptions]; } if (image) { @@ -95,13 +96,20 @@ UIImage * _Nullable SDImageLoaderDecodeProgressiveImageData(NSData * _Nonnull im if (scale < 1) { scale = 1; } + SDImageCoderOptions *coderOptions = @{SDImageCoderDecodeFirstFrameOnly : @(decodeFirstFrame), SDImageCoderDecodeScaleFactor : @(scale)}; + if (context) { + SDImageCoderMutableOptions *mutableCoderOptions = [coderOptions mutableCopy]; + [mutableCoderOptions setValue:context forKey:SDImageCoderWebImageContext]; + coderOptions = [mutableCoderOptions copy]; + } + id progressiveCoder = objc_getAssociatedObject(operation, SDImageLoaderProgressiveCoderKey); if (!progressiveCoder) { // We need to create a new instance for progressive decoding to avoid conflicts for (idcoder in [SDImageCodersManager sharedManager].coders.reverseObjectEnumerator) { if ([coder conformsToProtocol:@protocol(SDProgressiveImageCoder)] && [((id)coder) canIncrementalDecodeFromData:imageData]) { - progressiveCoder = [[[coder class] alloc] initIncrementalWithOptions:@{SDImageCoderDecodeScaleFactor : @(scale)}]; + progressiveCoder = [[[coder class] alloc] initIncrementalWithOptions:coderOptions]; break; } } @@ -121,12 +129,6 @@ UIImage * _Nullable SDImageLoaderDecodeProgressiveImageData(NSData * _Nonnull im } } if (!image) { - SDImageCoderOptions *coderOptions = @{SDImageCoderDecodeFirstFrameOnly : @(decodeFirstFrame), SDImageCoderDecodeScaleFactor : @(scale)}; - if (context) { - SDImageCoderMutableOptions *mutableCoderOptions = [coderOptions mutableCopy]; - [mutableCoderOptions setValue:context forKey:SDImageCoderWebImageContext]; - coderOptions = [mutableCoderOptions copy]; - } image = [progressiveCoder incrementalDecodedImageWithOptions:coderOptions]; } if (image) { diff --git a/SDWebImage/SDWebImageDefine.h b/SDWebImage/SDWebImageDefine.h index 15e25726..999c3b92 100644 --- a/SDWebImage/SDWebImageDefine.h +++ b/SDWebImage/SDWebImageDefine.h @@ -208,7 +208,7 @@ FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageS FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextStoreCacheType; /** - A Class object which the instance is a `UIImage/NSImage` subclass and adopt `SDAnimatedImage` protocol. We will call `initWithData:scale:` to create the instance (or `initWithAnimatedCoder:scale` when using progressive download) . If the instance create failed, fallback to normal `UIImage/NSImage`. + A Class object which the instance is a `UIImage/NSImage` subclass and adopt `SDAnimatedImage` protocol. We will call `initWithData:scale:options:` to create the instance (or `initWithAnimatedCoder:scale:` when using progressive download) . If the instance create failed, fallback to normal `UIImage/NSImage`. This can be used to improve animated images rendering performance (especially memory usage on big animated images) with `SDAnimatedImageView` (Class). */ FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextAnimatedImageClass; From 7c5c114aa597ce36808a5067adf8c09a6044c18f Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Tue, 28 Aug 2018 15:21:59 +0800 Subject: [PATCH 2/2] Update the comment about scale factor for coders --- SDWebImage/SDImageCoder.h | 3 +-- SDWebImage/SDWebImageManager.m | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/SDWebImage/SDImageCoder.h b/SDWebImage/SDImageCoder.h index 36a78fb2..ff28a5cd 100644 --- a/SDWebImage/SDImageCoder.h +++ b/SDWebImage/SDImageCoder.h @@ -51,7 +51,6 @@ FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderWebImageContext; /** This is the image coder protocol to provide custom image decoding/encoding. These methods are all required to implement. - You do not need to specify image scale during decoding because we may scale image later. @note Pay attention that these methods are not called from main queue. */ @protocol SDImageCoder @@ -71,7 +70,7 @@ FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderWebImageContext; @note This protocol may supports decode animated image frames. You can use `+[SDImageCoderHelper animatedImageWithFrames:]` to produce an animated image with frames. @param data The image data to be decoded - @param options A dictionary containing any decoding options. Pass @{SDImageCoderDecodeFirstFrameOnly: @(YES)} to decode the first frame only. + @param options A dictionary containing any decoding options. Pass @{SDImageCoderDecodeScaleFactor: @(1.0)} to specify scale factor for image. Pass @{SDImageCoderDecodeFirstFrameOnly: @(YES)} to decode the first frame only. @return The decoded image from data */ - (nullable UIImage *)decodedImageWithData:(nullable NSData *)data diff --git a/SDWebImage/SDWebImageManager.m b/SDWebImage/SDWebImageManager.m index 0cde8804..31f4145a 100644 --- a/SDWebImage/SDWebImageManager.m +++ b/SDWebImage/SDWebImageManager.m @@ -108,10 +108,6 @@ static id _defaultImageLoader; } } -- (nullable UIImage *)scaledImageForKey:(nullable NSString *)key image:(nullable UIImage *)image { - return SDScaledImageForKey(key, image); -} - - (SDWebImageCombinedOperation *)loadImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDImageLoaderProgressBlock)progressBlock completed:(SDInternalCompletionBlock)completedBlock { return [self loadImageWithURL:url options:options context:nil progress:progressBlock completed:completedBlock]; }