/* * 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 "SDImageCacheDefine.h" #import "SDImageCodersManager.h" #import "SDImageCoderHelper.h" #import "SDAnimatedImage.h" #import "UIImage+Metadata.h" #import "SDInternalMacros.h" #import 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); // Use cache key to detect scale NSNumber *preserveAspectRatioValue = context[SDWebImageContextImagePreserveAspectRatio]; NSValue *thumbnailSizeValue; BOOL shouldScaleDown = SD_OPTIONS_CONTAINS(options, SDWebImageScaleDownLargeImages); NSNumber *scaleDownLimitBytesValue = context[SDWebImageContextImageScaleDownLimitBytes]; if (scaleDownLimitBytesValue == nil && shouldScaleDown) { // Use the default limit bytes scaleDownLimitBytesValue = @(SDImageCoderHelper.defaultScaleDownLimitBytes); } if (context[SDWebImageContextImageThumbnailPixelSize]) { thumbnailSizeValue = context[SDWebImageContextImageThumbnailPixelSize]; } NSString *typeIdentifierHint = context[SDWebImageContextImageTypeIdentifierHint]; NSString *fileExtensionHint; if (!typeIdentifierHint) { // UTI has high priority fileExtensionHint = cacheKey.pathExtension; // without dot if (fileExtensionHint.length == 0) { // Ignore file extension which is empty fileExtensionHint = nil; } } // First check if user provided decode options SDImageCoderMutableOptions *mutableCoderOptions; if (context[SDWebImageContextImageDecodeOptions] != nil) { mutableCoderOptions = [NSMutableDictionary dictionaryWithDictionary:context[SDWebImageContextImageDecodeOptions]]; } else { mutableCoderOptions = [NSMutableDictionary dictionaryWithCapacity:6]; } // Override individual options mutableCoderOptions[SDImageCoderDecodeFirstFrameOnly] = @(decodeFirstFrame); mutableCoderOptions[SDImageCoderDecodeScaleFactor] = @(scale); mutableCoderOptions[SDImageCoderDecodePreserveAspectRatio] = preserveAspectRatioValue; mutableCoderOptions[SDImageCoderDecodeThumbnailPixelSize] = thumbnailSizeValue; mutableCoderOptions[SDImageCoderDecodeTypeIdentifierHint] = typeIdentifierHint; mutableCoderOptions[SDImageCoderDecodeFileExtensionHint] = fileExtensionHint; mutableCoderOptions[SDImageCoderDecodeScaleDownLimitBytes] = scaleDownLimitBytesValue; return [mutableCoderOptions copy]; } void SDSetDecodeOptionsToContext(SDWebImageMutableContext * _Nonnull mutableContext, SDWebImageOptions * _Nonnull mutableOptions, SDImageCoderOptions * _Nonnull decodeOptions) { if ([decodeOptions[SDImageCoderDecodeFirstFrameOnly] boolValue]) { *mutableOptions |= SDWebImageDecodeFirstFrameOnly; } else { *mutableOptions &= ~SDWebImageDecodeFirstFrameOnly; } mutableContext[SDWebImageContextImageScaleFactor] = decodeOptions[SDImageCoderDecodeScaleFactor]; mutableContext[SDWebImageContextImagePreserveAspectRatio] = decodeOptions[SDImageCoderDecodePreserveAspectRatio]; mutableContext[SDWebImageContextImageThumbnailPixelSize] = decodeOptions[SDImageCoderDecodeThumbnailPixelSize]; mutableContext[SDWebImageContextImageScaleDownLimitBytes] = decodeOptions[SDImageCoderDecodeScaleDownLimitBytes]; NSString *typeIdentifierHint = decodeOptions[SDImageCoderDecodeTypeIdentifierHint]; if (!typeIdentifierHint) { NSString *fileExtensionHint = decodeOptions[SDImageCoderDecodeFileExtensionHint]; if (fileExtensionHint) { typeIdentifierHint = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExtensionHint, kUTTypeImage); // Ignore dynamic UTI if (UTTypeIsDynamic((__bridge CFStringRef)typeIdentifierHint)) { typeIdentifierHint = nil; } } } mutableContext[SDWebImageContextImageTypeIdentifierHint] = typeIdentifierHint; } 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 imageCoder = context[SDWebImageContextImageCoder]; if (!imageCoder) { imageCoder = [SDImageCodersManager sharedManager]; } 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 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; }