/* * This file is part of the SDWebImage package. * (c) Olivier Poitrey * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ #import "SDImageLoader.h" #import "SDWebImageCacheKeyFilter.h" #import "SDImageCodersManager.h" #import "SDImageCoderHelper.h" #import "SDAnimatedImage.h" #import "UIImage+Metadata.h" #import "SDInternalMacros.h" #import "SDImageCacheDefine.h" #import "objc/runtime.h" SDWebImageContextOption const SDWebImageContextLoaderCachedImage = @"loaderCachedImage"; static void * SDImageLoaderProgressiveCoderKey = &SDImageLoaderProgressiveCoderKey; id SDImageLoaderGetProgressiveCoder(id operation) { NSCParameterAssert(operation); return objc_getAssociatedObject(operation, SDImageLoaderProgressiveCoderKey); } void SDImageLoaderSetProgressiveCoder(id operation, id progressiveCoder) { NSCParameterAssert(operation); objc_setAssociatedObject(operation, SDImageLoaderProgressiveCoderKey, progressiveCoder, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } UIImage * _Nullable SDImageLoaderDecodeImageData(NSData * _Nonnull imageData, NSURL * _Nonnull imageURL, SDWebImageOptions options, SDWebImageContext * _Nullable context) { NSCParameterAssert(imageData); NSCParameterAssert(imageURL); UIImage *image; id cacheKeyFilter = context[SDWebImageContextCacheKeyFilter]; NSString *cacheKey; if (cacheKeyFilter) { cacheKey = [cacheKeyFilter cacheKeyForURL:imageURL]; } else { cacheKey = imageURL.absoluteString; } SDImageCoderOptions *coderOptions = SDGetDecodeOptionsFromContext(context, options, cacheKey); BOOL decodeFirstFrame = SD_OPTIONS_CONTAINS(options, SDWebImageDecodeFirstFrameOnly); CGFloat scale = [coderOptions[SDImageCoderDecodeScaleFactor] doubleValue]; // Grab the image coder id imageCoder = context[SDWebImageContextImageCoder]; if (!imageCoder) { imageCoder = [SDImageCodersManager sharedManager]; } 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 options:coderOptions]; if (image) { // Preload frames if supported if (options & SDWebImagePreloadAllFrames && [image respondsToSelector:@selector(preloadAllFrames)]) { [((id)image) preloadAllFrames]; } } else { // Check image class matching if (options & SDWebImageMatchAnimatedImageClass) { return nil; } } } } if (!image) { image = [imageCoder decodedImageWithData:imageData options:coderOptions]; } if (image) { SDImageForceDecodePolicy policy = SDImageForceDecodePolicyAutomatic; NSNumber *policyValue = context[SDWebImageContextImageForceDecodePolicy]; if (policyValue != nil) { policy = policyValue.unsignedIntegerValue; } // TODO: Deprecated, remove in SD 6.0... #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" if (SD_OPTIONS_CONTAINS(options, SDWebImageAvoidDecodeImage)) { policy = SDImageForceDecodePolicyNever; } #pragma clang diagnostic pop image = [SDImageCoderHelper decodedImageWithImage:image policy:policy]; // assign the decode options, to let manager check whether to re-decode if needed image.sd_decodeOptions = coderOptions; } return image; } UIImage * _Nullable SDImageLoaderDecodeProgressiveImageData(NSData * _Nonnull imageData, NSURL * _Nonnull imageURL, BOOL finished, id _Nonnull operation, SDWebImageOptions options, SDWebImageContext * _Nullable context) { NSCParameterAssert(imageData); NSCParameterAssert(imageURL); NSCParameterAssert(operation); UIImage *image; id cacheKeyFilter = context[SDWebImageContextCacheKeyFilter]; NSString *cacheKey; if (cacheKeyFilter) { cacheKey = [cacheKeyFilter cacheKeyForURL:imageURL]; } else { cacheKey = imageURL.absoluteString; } SDImageCoderOptions *coderOptions = SDGetDecodeOptionsFromContext(context, options, cacheKey); BOOL decodeFirstFrame = SD_OPTIONS_CONTAINS(options, SDWebImageDecodeFirstFrameOnly); CGFloat scale = [coderOptions[SDImageCoderDecodeScaleFactor] doubleValue]; // Grab the progressive image coder id progressiveCoder = SDImageLoaderGetProgressiveCoder(operation); if (!progressiveCoder) { id imageCoder = context[SDWebImageContextImageCoder]; // Check the progressive coder if provided if ([imageCoder respondsToSelector:@selector(initIncrementalWithOptions:)]) { progressiveCoder = [[[imageCoder class] alloc] initIncrementalWithOptions:coderOptions]; } else { // We need to create a new instance for progressive decoding to avoid conflicts for (id coder in [SDImageCodersManager sharedManager].coders.reverseObjectEnumerator) { if ([coder conformsToProtocol:@protocol(SDProgressiveImageCoder)] && [((id)coder) canIncrementalDecodeFromData:imageData]) { progressiveCoder = [[[coder class] alloc] initIncrementalWithOptions:coderOptions]; break; } } } SDImageLoaderSetProgressiveCoder(operation, progressiveCoder); } // If we can't find any progressive coder, disable progressive download if (!progressiveCoder) { return nil; } [progressiveCoder updateIncrementalData:imageData finished:finished]; if (!decodeFirstFrame) { // check whether we should use `SDAnimatedImage` Class animatedImageClass = context[SDWebImageContextAnimatedImageClass]; if ([animatedImageClass isSubclassOfClass:[UIImage class]] && [animatedImageClass conformsToProtocol:@protocol(SDAnimatedImage)] && [progressiveCoder respondsToSelector:@selector(animatedImageFrameAtIndex:)]) { image = [[animatedImageClass alloc] initWithAnimatedCoder:(id)progressiveCoder scale:scale]; if (image) { // Progressive decoding does not preload frames } else { // Check image class matching if (options & SDWebImageMatchAnimatedImageClass) { return nil; } } } } if (!image) { image = [progressiveCoder incrementalDecodedImageWithOptions:coderOptions]; } if (image) { SDImageForceDecodePolicy policy = SDImageForceDecodePolicyAutomatic; NSNumber *policyValue = context[SDWebImageContextImageForceDecodePolicy]; if (policyValue != nil) { policy = policyValue.unsignedIntegerValue; } // TODO: Deprecated, remove in SD 6.0... #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" if (SD_OPTIONS_CONTAINS(options, SDWebImageAvoidDecodeImage)) { policy = SDImageForceDecodePolicyNever; } #pragma clang diagnostic pop image = [SDImageCoderHelper decodedImageWithImage:image policy:policy]; // assign the decode options, to let manager check whether to re-decode if needed image.sd_decodeOptions = coderOptions; // mark the image as progressive (completed one are not mark as progressive) image.sd_isIncremental = !finished; } return image; }