Creating FLAnimatedImage in global queue to improve displaying gif performance (#2047)
* Creating FLAnimatedImage in global queue to improve displaying gif performance * Added `context` dictionary param to `sd_internalSetImageWithURL` that allows sending context options. For now, one can pass `SDWebImageInternalSetImageInGlobalQueueKey` with true value so the `setImage` op is executed on a global queue.
This commit is contained in:
parent
6bab618acc
commit
85cb2fd6ad
|
@ -52,17 +52,25 @@
|
|||
options:options
|
||||
operationKey:nil
|
||||
setImageBlock:^(UIImage *image, NSData *imageData) {
|
||||
// This setImageBlock may not called from main queue
|
||||
SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:imageData];
|
||||
FLAnimatedImage *animatedImage;
|
||||
if (imageFormat == SDImageFormatGIF) {
|
||||
weakSelf.animatedImage = [FLAnimatedImage animatedImageWithGIFData:imageData];
|
||||
animatedImage = [FLAnimatedImage animatedImageWithGIFData:imageData];
|
||||
}
|
||||
dispatch_main_async_safe(^{
|
||||
if (animatedImage) {
|
||||
weakSelf.animatedImage = animatedImage;
|
||||
weakSelf.image = nil;
|
||||
} else {
|
||||
weakSelf.image = image;
|
||||
weakSelf.animatedImage = nil;
|
||||
}
|
||||
});
|
||||
}
|
||||
progress:progressBlock
|
||||
completed:completedBlock];
|
||||
completed:completedBlock
|
||||
context:@{SDWebImageInternalSetImageInGlobalQueueKey: @(YES)}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -11,13 +11,13 @@
|
|||
|
||||
/**
|
||||
Global object holding the array of coders, so that we avoid passing them from object to object.
|
||||
Uses a priority queue behind scenes, which means the latest added coders have priority.
|
||||
Uses a priority queue behind scenes, which means the latest added coders have the highest priority.
|
||||
This is done so when encoding/decoding something, we go through the list and ask each coder if they can handle the current data.
|
||||
That way, users can add their custom coders while preserving our existing prebuilt ones
|
||||
|
||||
Note: the `coders` getter will return the coders in their reversed order
|
||||
Example:
|
||||
- by default we internally set coders = `IOCoder`, `WebPCoder`
|
||||
- by default we internally set coders = `IOCoder`, `WebPCoder`. (`GIFCoder` is not recommended to add only if you want to get GIF support without `FLAnimatedImage`)
|
||||
- calling `coders` will return `@[WebPCoder, IOCoder]`
|
||||
- call `[addCoder:[MyCrazyCoder new]]`
|
||||
- calling `coders` now returns `@[MyCrazyCoder, WebPCoder, IOCoder]`
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
Built in coder using ImageIO that supports GIF encoding/decoding
|
||||
@note `SDWebImageIOCoder` supports GIF but only as static (will use the 1st frame).
|
||||
@note Use `SDWebImageGIFCoder` for fully animated GIFs - less performant than `FLAnimatedImage`
|
||||
@note The recommended approach for animated GIFs is using `FLAnimatedImage`
|
||||
@note If you decide to make all `UIImageView`(including `FLAnimatedImageView`) instance support GIF. You should add this coder to `SDWebImageCodersManager` and make sure that it has a higher priority than `SDWebImageIOCoder`
|
||||
@note The recommended approach for animated GIFs is using `FLAnimatedImage`. It's more performant than `UIImageView` for GIF displaying
|
||||
*/
|
||||
@interface SDWebImageGIFCoder : NSObject <SDWebImageCoder>
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
|
|||
|
||||
SDImageFormat format = [NSData sd_imageFormatForImageData:data];
|
||||
if (format == SDImageFormatGIF) {
|
||||
// static single GIF need to be created animated for FLAnimatedImageView logic
|
||||
// static single GIF need to be created animated for `FLAnimatedImage` logic
|
||||
// GIF does not support EXIF image orientation
|
||||
image = [UIImage animatedImageWithImages:@[image] duration:image.duration];
|
||||
return image;
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
#import "SDWebImageManager.h"
|
||||
|
||||
FOUNDATION_EXPORT NSString * _Nonnull const SDWebImageInternalSetImageInGlobalQueueKey;
|
||||
|
||||
typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable imageData);
|
||||
|
||||
@interface UIView (WebCache)
|
||||
|
@ -50,6 +52,34 @@ typedef void(^SDSetImageBlock)(UIImage * _Nullable image, NSData * _Nullable ima
|
|||
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock;
|
||||
|
||||
/**
|
||||
* Set the imageView `image` with an `url` and optionally a placeholder image.
|
||||
*
|
||||
* The download is asynchronous and cached.
|
||||
*
|
||||
* @param url The url for the image.
|
||||
* @param placeholder The image to be set initially, until the image request finishes.
|
||||
* @param options The options to use when downloading the image. @see SDWebImageOptions for the possible values.
|
||||
* @param operationKey A string to be used as the operation key. If nil, will use the class name
|
||||
* @param setImageBlock Block used for custom set image code
|
||||
* @param progressBlock A block called while image is downloading
|
||||
* @note the progress block is executed on a background queue
|
||||
* @param completedBlock A block called when operation has been completed. This block has no return value
|
||||
* and takes the requested UIImage as first parameter. In case of error the image parameter
|
||||
* is nil and the second parameter may contain an NSError. The third parameter is a Boolean
|
||||
* indicating if the image was retrieved from the local cache or from the network.
|
||||
* The fourth parameter is the original image url.
|
||||
* @param context A context with extra information to perform specify changes or processes.
|
||||
*/
|
||||
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
|
||||
placeholderImage:(nullable UIImage *)placeholder
|
||||
options:(SDWebImageOptions)options
|
||||
operationKey:(nullable NSString *)operationKey
|
||||
setImageBlock:(nullable SDSetImageBlock)setImageBlock
|
||||
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock
|
||||
context:(nullable NSDictionary *)context;
|
||||
|
||||
/**
|
||||
* Cancel the current download
|
||||
*/
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#import "objc/runtime.h"
|
||||
#import "UIView+WebCacheOperation.h"
|
||||
|
||||
NSString * const SDWebImageInternalSetImageInGlobalQueueKey = @"setImageInGlobalQueue";
|
||||
|
||||
static char imageURLKey;
|
||||
|
||||
#if SD_UIKIT
|
||||
|
@ -34,6 +36,17 @@ static char TAG_ACTIVITY_SHOW;
|
|||
setImageBlock:(nullable SDSetImageBlock)setImageBlock
|
||||
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock {
|
||||
return [self sd_internalSetImageWithURL:url placeholderImage:placeholder options:options operationKey:operationKey setImageBlock:setImageBlock progress:progressBlock completed:completedBlock context:nil];
|
||||
}
|
||||
|
||||
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
|
||||
placeholderImage:(nullable UIImage *)placeholder
|
||||
options:(SDWebImageOptions)options
|
||||
operationKey:(nullable NSString *)operationKey
|
||||
setImageBlock:(nullable SDSetImageBlock)setImageBlock
|
||||
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
|
||||
completed:(nullable SDExternalCompletionBlock)completedBlock
|
||||
context:(nullable NSDictionary *)context {
|
||||
NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
|
||||
[self sd_cancelImageLoadOperationWithKey:validOperationKey];
|
||||
objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
|
@ -54,28 +67,48 @@ static char TAG_ACTIVITY_SHOW;
|
|||
id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
|
||||
__strong __typeof (wself) sself = wself;
|
||||
[sself sd_removeActivityIndicator];
|
||||
if (!sself) {
|
||||
return;
|
||||
}
|
||||
dispatch_main_async_safe(^{
|
||||
if (!sself) {
|
||||
return;
|
||||
}
|
||||
if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
|
||||
completedBlock(image, error, cacheType, url);
|
||||
return;
|
||||
} else if (image) {
|
||||
[sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock];
|
||||
[sself sd_setNeedsLayout];
|
||||
} else {
|
||||
if ((options & SDWebImageDelayPlaceholder)) {
|
||||
[sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
|
||||
if (!sself) { return; }
|
||||
BOOL shouldCallCompletedBlock = finished || (options & SDWebImageAvoidAutoSetImage);
|
||||
BOOL shouldNotSetImage = ((image && (options & SDWebImageAvoidAutoSetImage)) ||
|
||||
(!image && !(options & SDWebImageDelayPlaceholder)));
|
||||
SDWebImageNoParamsBlock callCompletedBlockClojure = ^{
|
||||
if (!sself) { return; }
|
||||
if (!shouldNotSetImage) {
|
||||
[sself sd_setNeedsLayout];
|
||||
}
|
||||
}
|
||||
if (completedBlock && finished) {
|
||||
if (completedBlock && shouldCallCompletedBlock) {
|
||||
completedBlock(image, error, cacheType, url);
|
||||
}
|
||||
};
|
||||
|
||||
// case 1a: we got an image, but the SDWebImageAvoidAutoSetImage flag is set
|
||||
// OR
|
||||
// case 1b: we got no image and the SDWebImageDelayPlaceholder is not set
|
||||
if (shouldNotSetImage) {
|
||||
dispatch_main_async_safe(callCompletedBlockClojure);
|
||||
return;
|
||||
}
|
||||
|
||||
UIImage *targetImage = nil;
|
||||
NSData *targetData = nil;
|
||||
if (image) {
|
||||
// case 2a: we got an image and the SDWebImageAvoidAutoSetImage is not set
|
||||
targetImage = image;
|
||||
targetData = data;
|
||||
} else if (options & SDWebImageDelayPlaceholder) {
|
||||
// case 2b: we got no image and the SDWebImageDelayPlaceholder flag is set
|
||||
targetImage = placeholder;
|
||||
targetData = nil;
|
||||
}
|
||||
BOOL shouldUseGlobalQueue = NO;
|
||||
if (context && [context valueForKey:SDWebImageInternalSetImageInGlobalQueueKey]) {
|
||||
shouldUseGlobalQueue = [[context valueForKey:SDWebImageInternalSetImageInGlobalQueueKey] boolValue];
|
||||
}
|
||||
dispatch_queue_t targetQueue = shouldUseGlobalQueue ? dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0) : dispatch_get_main_queue();
|
||||
|
||||
dispatch_async(targetQueue, ^{
|
||||
[sself sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock];
|
||||
dispatch_main_async_safe(callCompletedBlockClojure);
|
||||
});
|
||||
}];
|
||||
[self sd_setImageLoadOperation:operation forKey:validOperationKey];
|
||||
|
|
Loading…
Reference in New Issue