From 9b9fc6fc9bbd5bee2a431062ead34176eb34135c Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sun, 12 Jun 2022 18:13:30 +0800 Subject: [PATCH] Support using iOS 15 UIImage new API imageByPreparingForDisplay for faster force decoding This API seems support both async/sync version, SD use sync version here because we already call them on global queue The API mark available on watchOS, but header does not pass that __has_include, a little strange --- SDWebImage/Core/SDImageCoderHelper.m | 79 +++++++++++++++++++--------- 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/SDWebImage/Core/SDImageCoderHelper.m b/SDWebImage/Core/SDImageCoderHelper.m index 1f641212..1980decc 100644 --- a/SDWebImage/Core/SDImageCoderHelper.m +++ b/SDWebImage/Core/SDImageCoderHelper.m @@ -330,6 +330,19 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over return image; } +#if SD_UIKIT + // See: https://developer.apple.com/documentation/uikit/uiimage/3750834-imagebypreparingfordisplay + // Need CGImage-based + if (@available(iOS 15, tvOS 15, *)) { + UIImage *decodedImage = [image imageByPreparingForDisplay]; + if (decodedImage) { + SDImageCopyAssociatedObject(image, decodedImage); + decodedImage.sd_isDecoded = YES; + return decodedImage; + } + } +#endif + CGImageRef imageRef = image.CGImage; if (!imageRef) { return image; @@ -355,36 +368,54 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over return image; } - if (![self shouldScaleDownImage:image limitBytes:bytes]) { - return [self decodedImageWithImage:image]; - } - CGFloat destTotalPixels; CGFloat tileTotalPixels; if (bytes == 0) { - bytes = kDestImageLimitBytes; + bytes = [self defaultScaleDownLimitBytes]; } + bytes = MAX(bytes, kBytesPerPixel); destTotalPixels = bytes / kBytesPerPixel; tileTotalPixels = destTotalPixels / 3; + + CGImageRef sourceImageRef = image.CGImage; + CGSize sourceResolution = CGSizeZero; + sourceResolution.width = CGImageGetWidth(sourceImageRef); + sourceResolution.height = CGImageGetHeight(sourceImageRef); + + if (![self shouldScaleDownImagePixelSize:sourceResolution limitBytes:bytes]) { + return [self decodedImageWithImage:image]; + } + + CGFloat sourceTotalPixels = sourceResolution.width * sourceResolution.height; + // Determine the scale ratio to apply to the input image + // that results in an output image of the defined size. + // see kDestImageSizeMB, and how it relates to destTotalPixels. + CGFloat imageScale = sqrt(destTotalPixels / sourceTotalPixels); + CGSize destResolution = CGSizeZero; + destResolution.width = MAX(1, (int)(sourceResolution.width * imageScale)); + destResolution.height = MAX(1, (int)(sourceResolution.height * imageScale)); + +#if SD_UIKIT + // See: https://developer.apple.com/documentation/uikit/uiimage/3750835-imagebypreparingthumbnailofsize + // Need CGImage-based + if (@available(iOS 15, tvOS 15, *)) { + // Calculate thumbnail point size + CGFloat scale = image.scale ?: 1; + CGSize thumbnailSize = CGSizeMake(destResolution.width / scale, destResolution.height / scale); + UIImage *decodedImage = [image imageByPreparingThumbnailOfSize:thumbnailSize]; + if (decodedImage) { + SDImageCopyAssociatedObject(image, decodedImage); + decodedImage.sd_isDecoded = YES; + return decodedImage; + } + } +#endif + CGContextRef destContext = NULL; // autorelease the bitmap context and all vars to help system to free memory when there are memory warning. // on iOS7, do not forget to call [[SDImageCache sharedImageCache] clearMemory]; @autoreleasepool { - CGImageRef sourceImageRef = image.CGImage; - - CGSize sourceResolution = CGSizeZero; - sourceResolution.width = CGImageGetWidth(sourceImageRef); - sourceResolution.height = CGImageGetHeight(sourceImageRef); - CGFloat sourceTotalPixels = sourceResolution.width * sourceResolution.height; - // Determine the scale ratio to apply to the input image - // that results in an output image of the defined size. - // see kDestImageSizeMB, and how it relates to destTotalPixels. - CGFloat imageScale = sqrt(destTotalPixels / sourceTotalPixels); - CGSize destResolution = CGSizeZero; - destResolution.width = MAX(1, (int)(sourceResolution.width * imageScale)); - destResolution.height = MAX(1, (int)(sourceResolution.height * imageScale)); - // device color space CGColorSpaceRef colorspaceRef = [self colorSpaceGetDeviceRGB]; BOOL hasAlpha = [self CGImageContainsAlpha:sourceImageRef]; @@ -592,14 +623,10 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over return YES; } -+ (BOOL)shouldScaleDownImage:(nonnull UIImage *)image limitBytes:(NSUInteger)bytes { ++ (BOOL)shouldScaleDownImagePixelSize:(CGSize)sourceResolution limitBytes:(NSUInteger)bytes { BOOL shouldScaleDown = YES; - CGImageRef sourceImageRef = image.CGImage; - CGSize sourceResolution = CGSizeZero; - sourceResolution.width = CGImageGetWidth(sourceImageRef); - sourceResolution.height = CGImageGetHeight(sourceImageRef); - float sourceTotalPixels = sourceResolution.width * sourceResolution.height; + CGFloat sourceTotalPixels = sourceResolution.width * sourceResolution.height; if (sourceTotalPixels <= 0) { return NO; } @@ -609,7 +636,7 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over } bytes = MAX(bytes, kBytesPerPixel); destTotalPixels = bytes / kBytesPerPixel; - float imageScale = destTotalPixels / sourceTotalPixels; + CGFloat imageScale = destTotalPixels / sourceTotalPixels; if (imageScale < 1) { shouldScaleDown = YES; } else {