Merge pull request #2453 from dreampiggy/improvement_animation_class_protocol

Add one initializer with extra arg for custom animated image class to use possible coder options, to make it extensible
This commit is contained in:
Bogdan Poplauschi 2018-08-28 12:03:06 +03:00 committed by GitHub
commit 6892f9e818
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 48 additions and 32 deletions

View File

@ -16,9 +16,20 @@
@protocol SDAnimatedImage <SDAnimatedImageProvider> @protocol SDAnimatedImage <SDAnimatedImageProvider>
@required @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. 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 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. @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<SDAnimatedImageCoder>)animatedCoder scale:(CGFloat)scale; - (nullable instancetype)initWithAnimatedCoder:(nonnull id<SDAnimatedImageCoder>)animatedCoder scale:(CGFloat)scale;
@optional @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. 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. This method may be called on background thread.
@ -63,7 +75,6 @@
- (nullable instancetype)initWithContentsOfFile:(nonnull NSString *)path; - (nullable instancetype)initWithContentsOfFile:(nonnull NSString *)path;
- (nullable instancetype)initWithData:(nonnull NSData *)data; - (nullable instancetype)initWithData:(nonnull NSData *)data;
- (nullable instancetype)initWithData:(nonnull NSData *)data scale:(CGFloat)scale; - (nullable instancetype)initWithData:(nonnull NSData *)data scale:(CGFloat)scale;
- (nullable instancetype)initWithAnimatedCoder:(nonnull id<SDAnimatedImageCoder>)animatedCoder scale:(CGFloat)scale;
/** /**
Current animated image format. Current animated image format.

View File

@ -276,6 +276,10 @@ static NSArray *SDBundlePreferredScales() {
} }
- (instancetype)initWithData:(NSData *)data scale:(CGFloat)scale { - (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) { if (!data || data.length == 0) {
return nil; return nil;
} }
@ -284,7 +288,10 @@ static NSArray *SDBundlePreferredScales() {
for (id<SDImageCoder>coder in [SDImageCodersManager sharedManager].coders) { for (id<SDImageCoder>coder in [SDImageCodersManager sharedManager].coders) {
if ([coder conformsToProtocol:@protocol(SDAnimatedImageCoder)]) { if ([coder conformsToProtocol:@protocol(SDAnimatedImageCoder)]) {
if ([coder canDecodeFromData:data]) { 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; break;
} }
} }

View File

@ -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. 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 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. Default is YES. Set to NO to only render the static poster for incremental animated image.
*/ */
@property (nonatomic, assign) BOOL shouldIncrementalLoad; @property (nonatomic, assign) BOOL shouldIncrementalLoad;

View File

@ -20,23 +20,24 @@ UIImage * _Nullable SDImageCacheDecodeImageData(NSData * _Nonnull imageData, NSS
if (scale < 1) { if (scale < 1) {
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) { if (!decodeFirstFrame) {
Class animatedImageClass = context[SDWebImageContextAnimatedImageClass]; Class animatedImageClass = context[SDWebImageContextAnimatedImageClass];
// check whether we should use `SDAnimatedImage` // check whether we should use `SDAnimatedImage`
if ([animatedImageClass isSubclassOfClass:[UIImage class]] && [animatedImageClass conformsToProtocol:@protocol(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)]) { if (options & SDWebImagePreloadAllFrames && [image respondsToSelector:@selector(preloadAllFrames)]) {
[((id<SDAnimatedImage>)image) preloadAllFrames]; [((id<SDAnimatedImage>)image) preloadAllFrames];
} }
} }
} }
if (!image) { 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]; image = [[SDImageCodersManager sharedManager] decodedImageWithData:imageData options:coderOptions];
} }
if (image) { if (image) {

View File

@ -51,7 +51,6 @@ FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderWebImageContext;
/** /**
This is the image coder protocol to provide custom image decoding/encoding. This is the image coder protocol to provide custom image decoding/encoding.
These methods are all required to implement. 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. @note Pay attention that these methods are not called from main queue.
*/ */
@protocol SDImageCoder <NSObject> @protocol SDImageCoder <NSObject>
@ -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. @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 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 @return The decoded image from data
*/ */
- (nullable UIImage *)decodedImageWithData:(nullable NSData *)data - (nullable UIImage *)decodedImageWithData:(nullable NSData *)data

View File

@ -34,23 +34,24 @@ UIImage * _Nullable SDImageLoaderDecodeImageData(NSData * _Nonnull imageData, NS
if (scale < 1) { if (scale < 1) {
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) { if (!decodeFirstFrame) {
// check whether we should use `SDAnimatedImage` // check whether we should use `SDAnimatedImage`
Class animatedImageClass = context[SDWebImageContextAnimatedImageClass]; Class animatedImageClass = context[SDWebImageContextAnimatedImageClass];
if ([animatedImageClass isSubclassOfClass:[UIImage class]] && [animatedImageClass conformsToProtocol:@protocol(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)]) { if (options & SDWebImagePreloadAllFrames && [image respondsToSelector:@selector(preloadAllFrames)]) {
[((id<SDAnimatedImage>)image) preloadAllFrames]; [((id<SDAnimatedImage>)image) preloadAllFrames];
} }
} }
} }
if (!image) { 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]; image = [[SDImageCodersManager sharedManager] decodedImageWithData:imageData options:coderOptions];
} }
if (image) { if (image) {
@ -95,13 +96,20 @@ UIImage * _Nullable SDImageLoaderDecodeProgressiveImageData(NSData * _Nonnull im
if (scale < 1) { if (scale < 1) {
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<SDProgressiveImageCoder> progressiveCoder = objc_getAssociatedObject(operation, SDImageLoaderProgressiveCoderKey); id<SDProgressiveImageCoder> progressiveCoder = objc_getAssociatedObject(operation, SDImageLoaderProgressiveCoderKey);
if (!progressiveCoder) { if (!progressiveCoder) {
// We need to create a new instance for progressive decoding to avoid conflicts // We need to create a new instance for progressive decoding to avoid conflicts
for (id<SDImageCoder>coder in [SDImageCodersManager sharedManager].coders.reverseObjectEnumerator) { for (id<SDImageCoder>coder in [SDImageCodersManager sharedManager].coders.reverseObjectEnumerator) {
if ([coder conformsToProtocol:@protocol(SDProgressiveImageCoder)] && if ([coder conformsToProtocol:@protocol(SDProgressiveImageCoder)] &&
[((id<SDProgressiveImageCoder>)coder) canIncrementalDecodeFromData:imageData]) { [((id<SDProgressiveImageCoder>)coder) canIncrementalDecodeFromData:imageData]) {
progressiveCoder = [[[coder class] alloc] initIncrementalWithOptions:@{SDImageCoderDecodeScaleFactor : @(scale)}]; progressiveCoder = [[[coder class] alloc] initIncrementalWithOptions:coderOptions];
break; break;
} }
} }
@ -121,12 +129,6 @@ UIImage * _Nullable SDImageLoaderDecodeProgressiveImageData(NSData * _Nonnull im
} }
} }
if (!image) { 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]; image = [progressiveCoder incrementalDecodedImageWithOptions:coderOptions];
} }
if (image) { if (image) {

View File

@ -208,7 +208,7 @@ FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextImageS
FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextStoreCacheType; 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). 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; FOUNDATION_EXPORT SDWebImageContextOption _Nonnull const SDWebImageContextAnimatedImageClass;

View File

@ -108,10 +108,6 @@ static id<SDImageLoader> _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 { - (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]; return [self loadImageWithURL:url options:options context:nil progress:progressBlock completed:completedBlock];
} }