Use the new solution, store the original decode options, when multiple loading pipeline share the same URL callback, check it and re-decode if needed
This commit is contained in:
parent
911328c1b4
commit
ecac56b726
|
@ -10,6 +10,7 @@
|
|||
#import "SDWebImageCompat.h"
|
||||
#import "SDWebImageOperation.h"
|
||||
#import "SDWebImageDefine.h"
|
||||
#import "SDImageCoder.h"
|
||||
|
||||
/// Image Cache Type
|
||||
typedef NS_ENUM(NSInteger, SDImageCacheType) {
|
||||
|
@ -54,6 +55,12 @@ typedef void(^SDImageCacheContainsCompletionBlock)(SDImageCacheType containsCach
|
|||
*/
|
||||
FOUNDATION_EXPORT UIImage * _Nullable SDImageCacheDecodeImageData(NSData * _Nonnull imageData, NSString * _Nonnull cacheKey, SDWebImageOptions options, SDWebImageContext * _Nullable context);
|
||||
|
||||
/// Get the decode options from the loading context options and cache key. This is the built-in translate between the web loading part to the decoding part (which does not depens on).
|
||||
/// @param context The options arg from the input
|
||||
/// @param options The context arg from the input
|
||||
/// @param cacheKey The image cache key from the input. Should not be nil
|
||||
FOUNDATION_EXPORT SDImageCoderOptions * _Nonnull SDGetDecodeOptionsFromContext(SDWebImageContext * _Nullable context, SDWebImageOptions options, NSString * _Nonnull cacheKey);
|
||||
|
||||
/**
|
||||
This is the image cache protocol to provide custom image cache for `SDWebImageManager`.
|
||||
Though the best practice to custom image cache, is to write your own class which conform `SDMemoryCache` or `SDDiskCache` protocol for `SDImageCache` class (See more on `SDImageCacheConfig.memoryCacheClass & SDImageCacheConfig.diskCacheClass`).
|
||||
|
|
|
@ -13,10 +13,7 @@
|
|||
#import "UIImage+Metadata.h"
|
||||
#import "SDInternalMacros.h"
|
||||
|
||||
UIImage * _Nullable SDImageCacheDecodeImageData(NSData * _Nonnull imageData, NSString * _Nonnull cacheKey, SDWebImageOptions options, SDWebImageContext * _Nullable context) {
|
||||
NSCParameterAssert(imageData);
|
||||
NSCParameterAssert(cacheKey);
|
||||
UIImage *image;
|
||||
SDImageCoderOptions * _Nonnull SDGetDecodeOptionsFromContext(SDWebImageContext * _Nullable context, SDWebImageOptions options, NSString * _Nonnull cacheKey) {
|
||||
BOOL decodeFirstFrame = SD_OPTIONS_CONTAINS(options, SDWebImageDecodeFirstFrameOnly);
|
||||
NSNumber *scaleValue = context[SDWebImageContextImageScaleFactor];
|
||||
CGFloat scale = scaleValue.doubleValue >= 1 ? scaleValue.doubleValue : SDImageScaleFactorForKey(cacheKey);
|
||||
|
@ -40,6 +37,17 @@ UIImage * _Nullable SDImageCacheDecodeImageData(NSData * _Nonnull imageData, NSS
|
|||
mutableCoderOptions[SDImageCoderWebImageContext] = context;
|
||||
SDImageCoderOptions *coderOptions = [mutableCoderOptions copy];
|
||||
|
||||
return coderOptions;
|
||||
}
|
||||
|
||||
UIImage * _Nullable SDImageCacheDecodeImageData(NSData * _Nonnull imageData, NSString * _Nonnull cacheKey, SDWebImageOptions options, SDWebImageContext * _Nullable context) {
|
||||
NSCParameterAssert(imageData);
|
||||
NSCParameterAssert(cacheKey);
|
||||
UIImage *image;
|
||||
SDImageCoderOptions *coderOptions = SDGetDecodeOptionsFromContext(context, options, cacheKey);
|
||||
BOOL decodeFirstFrame = SD_OPTIONS_CONTAINS(options, SDWebImageDecodeFirstFrameOnly);
|
||||
CGFloat scale = [coderOptions[SDImageCoderDecodeScaleFactor] doubleValue];
|
||||
|
||||
// Grab the image coder
|
||||
id<SDImageCoder> imageCoder;
|
||||
if ([context[SDWebImageContextImageCoder] conformsToProtocol:@protocol(SDImageCoder)]) {
|
||||
|
@ -81,8 +89,8 @@ UIImage * _Nullable SDImageCacheDecodeImageData(NSData * _Nonnull imageData, NSS
|
|||
if (shouldDecode) {
|
||||
image = [SDImageCoderHelper decodedImageWithImage:image];
|
||||
}
|
||||
// mark the image as thumbnail, to let manager check whether to re-decode if needed
|
||||
image.sd_isThumbnail = thumbnailSizeValue != nil;
|
||||
// assign the decode options, to let manager check whether to re-decode if needed
|
||||
image.sd_decodeOptions = coderOptions;
|
||||
}
|
||||
|
||||
return image;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#import "SDAnimatedImage.h"
|
||||
#import "UIImage+Metadata.h"
|
||||
#import "SDInternalMacros.h"
|
||||
#import "SDImageCacheDefine.h"
|
||||
#import "objc/runtime.h"
|
||||
|
||||
SDWebImageContextOption const SDWebImageContextLoaderCachedImage = @"loaderCachedImage";
|
||||
|
@ -41,28 +42,9 @@ UIImage * _Nullable SDImageLoaderDecodeImageData(NSData * _Nonnull imageData, NS
|
|||
} else {
|
||||
cacheKey = imageURL.absoluteString;
|
||||
}
|
||||
SDImageCoderOptions *coderOptions = SDGetDecodeOptionsFromContext(context, options, cacheKey);
|
||||
BOOL decodeFirstFrame = SD_OPTIONS_CONTAINS(options, SDWebImageDecodeFirstFrameOnly);
|
||||
NSNumber *scaleValue = context[SDWebImageContextImageScaleFactor];
|
||||
CGFloat scale = scaleValue.doubleValue >= 1 ? scaleValue.doubleValue : SDImageScaleFactorForKey(cacheKey);
|
||||
NSNumber *preserveAspectRatioValue = context[SDWebImageContextImagePreserveAspectRatio];
|
||||
NSValue *thumbnailSizeValue;
|
||||
BOOL shouldScaleDown = SD_OPTIONS_CONTAINS(options, SDWebImageScaleDownLargeImages);
|
||||
if (shouldScaleDown) {
|
||||
CGFloat thumbnailPixels = SDImageCoderHelper.defaultScaleDownLimitBytes / 4;
|
||||
CGFloat dimension = ceil(sqrt(thumbnailPixels));
|
||||
thumbnailSizeValue = @(CGSizeMake(dimension, dimension));
|
||||
}
|
||||
if (context[SDWebImageContextImageThumbnailPixelSize]) {
|
||||
thumbnailSizeValue = context[SDWebImageContextImageThumbnailPixelSize];
|
||||
}
|
||||
|
||||
SDImageCoderMutableOptions *mutableCoderOptions = [NSMutableDictionary dictionaryWithCapacity:2];
|
||||
mutableCoderOptions[SDImageCoderDecodeFirstFrameOnly] = @(decodeFirstFrame);
|
||||
mutableCoderOptions[SDImageCoderDecodeScaleFactor] = @(scale);
|
||||
mutableCoderOptions[SDImageCoderDecodePreserveAspectRatio] = preserveAspectRatioValue;
|
||||
mutableCoderOptions[SDImageCoderDecodeThumbnailPixelSize] = thumbnailSizeValue;
|
||||
mutableCoderOptions[SDImageCoderWebImageContext] = context;
|
||||
SDImageCoderOptions *coderOptions = [mutableCoderOptions copy];
|
||||
CGFloat scale = [coderOptions[SDImageCoderDecodeScaleFactor] doubleValue];
|
||||
|
||||
// Grab the image coder
|
||||
id<SDImageCoder> imageCoder;
|
||||
|
@ -106,8 +88,8 @@ UIImage * _Nullable SDImageLoaderDecodeImageData(NSData * _Nonnull imageData, NS
|
|||
if (shouldDecode) {
|
||||
image = [SDImageCoderHelper decodedImageWithImage:image];
|
||||
}
|
||||
// mark the image as thumbnail, to let manager check whether to re-decode if needed
|
||||
image.sd_isThumbnail = thumbnailSizeValue != nil;
|
||||
// assign the decode options, to let manager check whether to re-decode if needed
|
||||
image.sd_decodeOptions = coderOptions;
|
||||
}
|
||||
|
||||
return image;
|
||||
|
@ -206,8 +188,8 @@ UIImage * _Nullable SDImageLoaderDecodeProgressiveImageData(NSData * _Nonnull im
|
|||
}
|
||||
// mark the image as progressive (completed one are not mark as progressive)
|
||||
image.sd_isIncremental = !finished;
|
||||
// mark the image as thumbnail, to let manager check whether to re-decode if needed
|
||||
image.sd_isThumbnail = thumbnailSizeValue != nil;
|
||||
// assign the decode options, to let manager check whether to re-decode if needed
|
||||
image.sd_decodeOptions = coderOptions;
|
||||
}
|
||||
|
||||
return image;
|
||||
|
|
|
@ -568,7 +568,11 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
// thumbnail check
|
||||
// This exist when previous thumbnail pipeline callback into next full size pipeline, because we share the same URL download but need different image
|
||||
// Actually this is a hack, we attach the metadata into image object, which should design a better concept like `ImageInfo` and keep that around
|
||||
BOOL shouldDecodeFullImage = originalImage.sd_isThumbnail && context[SDWebImageContextImageThumbnailPixelSize] == nil;
|
||||
// Should match impl in `SDImageCacheDecodeImageData/SDImageLoaderDecode[Progressive]ImageData`
|
||||
SDImageCoderOptions *decodeOptions = SDGetDecodeOptionsFromContext(context, options, url.absoluteString);
|
||||
SDImageCoderOptions *returnedDecodeOptions = originalImage.sd_decodeOptions;
|
||||
// If the retuened image decode options exist (some loaders impl does not use `SDImageLoaderDecode`) but does not match the options we provide, redecode
|
||||
BOOL shouldRedecodeFullImage = returnedDecodeOptions && ![returnedDecodeOptions isEqualToDictionary:decodeOptions];
|
||||
|
||||
if (shouldTransformImage) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||
|
@ -577,10 +581,8 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
NSString *key = [self cacheKeyForURL:url context:context];
|
||||
// Case that transformer one thumbnail, which this time need full pixel image
|
||||
UIImage *fullSizeImage = originalImage;
|
||||
if (shouldDecodeFullImage) {
|
||||
SDWebImageMutableContext *tempContext = [context mutableCopy];
|
||||
tempContext[SDWebImageContextImageThumbnailPixelSize] = nil;
|
||||
fullSizeImage = SDImageCacheDecodeImageData(originalData, key, options, tempContext) ?: originalImage;
|
||||
if (shouldRedecodeFullImage) {
|
||||
fullSizeImage = SDImageCacheDecodeImageData(originalData, key, options, context) ?: originalImage;
|
||||
}
|
||||
UIImage *transformedImage = [transformer transformedImageWithImage:fullSizeImage forKey:key];
|
||||
if (transformedImage && finished) {
|
||||
|
@ -600,15 +602,11 @@ static id<SDImageLoader> _defaultImageLoader;
|
|||
}
|
||||
}
|
||||
});
|
||||
} else if (shouldDecodeFullImage) {
|
||||
} else if (shouldRedecodeFullImage) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||
@autoreleasepool {
|
||||
// transformed/thumbnailed cache key
|
||||
NSString *key = [self cacheKeyForURL:url context:context];
|
||||
// disable thumbnail decoding
|
||||
SDWebImageMutableContext *tempContext = [context mutableCopy];
|
||||
tempContext[SDWebImageContextImageThumbnailPixelSize] = nil;
|
||||
UIImage *fullSizeImage = SDImageCacheDecodeImageData(originalData, key, options, tempContext) ?: originalImage;
|
||||
// Re-decode because the returned image does not match current request pipeline's context
|
||||
UIImage *fullSizeImage = SDImageCacheDecodeImageData(originalData, url.absoluteString, options, context) ?: originalImage;
|
||||
// Continue store transform cache process
|
||||
[self callStoreTransformCacheProcessForOperation:operation url:url options:options context:context image:fullSizeImage data:originalData cacheType:cacheType transformed:NO finished:finished completed:completedBlock];
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#import "SDWebImageCompat.h"
|
||||
#import "NSData+ImageContentType.h"
|
||||
#import "SDImageCoder.h"
|
||||
|
||||
/**
|
||||
UIImage category for image metadata, including animation, loop count, format, incremental, etc.
|
||||
|
@ -66,8 +67,11 @@
|
|||
@property (nonatomic, assign) BOOL sd_isIncremental;
|
||||
|
||||
/**
|
||||
A bool value indicating whether the image is from thumbnail decoding and may be smaller than the full image data pixel size.
|
||||
A dictionary value contains the decode options when decoded from SDWebImage loading system (say, `SDImageCacheDecodeImageData/SDImageLoaderDecode[Progressive]ImageData`)
|
||||
It may not always available and only image decoding related options will be saved. (including [.decodeScaleFactor, .decodeThumbnailPixelSize, .decodePreserveAspectRatio, .decodeFirstFrameOnly])
|
||||
@note This is used to identify and check the image from downloader when multiple different request (which want different image thumbnail size, image class, etc) share the same URLOperation.
|
||||
@warning This API exist only because of current SDWebImageDownloader bad design which does not callback the context we call it. There will be refactory in future (API break) and you SHOULD NOT rely on this property at all.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL sd_isThumbnail;
|
||||
@property (nonatomic, copy) SDImageCoderOptions *sd_decodeOptions;
|
||||
|
||||
@end
|
||||
|
|
|
@ -186,13 +186,16 @@
|
|||
return value.boolValue;
|
||||
}
|
||||
|
||||
- (void)setSd_isThumbnail:(BOOL)sd_isThumbnail {
|
||||
objc_setAssociatedObject(self, @selector(sd_isThumbnail), @(sd_isThumbnail), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
- (void)setSd_decodeOptions:(SDImageCoderOptions *)sd_decodeOptions {
|
||||
objc_setAssociatedObject(self, @selector(sd_decodeOptions), sd_decodeOptions, OBJC_ASSOCIATION_COPY_NONATOMIC);
|
||||
}
|
||||
|
||||
- (BOOL)sd_isThumbnail {
|
||||
NSNumber *value = objc_getAssociatedObject(self, @selector(sd_isThumbnail));
|
||||
return value.boolValue;
|
||||
- (SDImageCoderOptions *)sd_decodeOptions {
|
||||
SDImageCoderOptions *value = objc_getAssociatedObject(self, @selector(sd_decodeOptions));
|
||||
if ([value isKindOfClass:NSDictionary.class]) {
|
||||
return value;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -18,7 +18,7 @@ void SDImageCopyAssociatedObject(UIImage * _Nullable source, UIImage * _Nullable
|
|||
}
|
||||
// Image Metadata
|
||||
target.sd_isIncremental = source.sd_isIncremental;
|
||||
target.sd_isThumbnail = source.sd_isThumbnail;
|
||||
target.sd_decodeOptions = source.sd_decodeOptions;
|
||||
target.sd_imageLoopCount = source.sd_imageLoopCount;
|
||||
target.sd_imageFormat = source.sd_imageFormat;
|
||||
// Force Decode
|
||||
|
|
Loading…
Reference in New Issue