From 1f3ee8e9d5a7e181db41759c0975b2a5dcb375b1 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 22 Jun 2022 16:53:40 +0800 Subject: [PATCH] Added new API for custom coder better handling the thumbnail size The current WebP/HEIF/AVIF coder both copy paste the same code multiple times and have to fix one by one --- SDWebImage/Core/SDImageCoderHelper.h | 11 +++++++++ SDWebImage/Core/SDImageCoderHelper.m | 37 ++++++++++++++++++++++++++++ Tests/Tests/SDImageCoderTests.m | 24 ++++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/SDWebImage/Core/SDImageCoderHelper.h b/SDWebImage/Core/SDImageCoderHelper.h index 77b9d779..a7be63e4 100644 --- a/SDWebImage/Core/SDImageCoderHelper.h +++ b/SDWebImage/Core/SDImageCoderHelper.h @@ -76,6 +76,7 @@ /** Create a scaled CGImage by the provided CGImage and size. This follows The Create Rule and you are response to call release after usage. It will detect whether the image size matching the scale size, if not, stretch the image to the target size. + @note If you need to keep aspect ratio, you can calculate the scale size by using `scaledSizeWithImageSize` first. @param cgImage The CGImage @param size The scale size in pixel. @@ -83,6 +84,16 @@ */ + (CGImageRef _Nullable)CGImageCreateScaled:(_Nonnull CGImageRef)cgImage size:(CGSize)size CF_RETURNS_RETAINED; +/** Scale the image size based on provided scale size, whether or not to preserve aspect ratio, whether or not to scale up. + @note For example, if you implements thumnail decoding, pass `shouldScaleUp` to NO to avoid the calculated size larger than image size. + + @param imageSize The image size (in pixel or point defined by caller) + @param scaleSize The scale size (in pixel or point defined by caller) + @param preserveAspectRatio Whether or not to preserve aspect ratio + @param shouldScaleUp Whether or not to scale up (or scale down only) + */ ++ (CGSize)scaledSizeWithImageSize:(CGSize)imageSize scaleSize:(CGSize)scaleSize preserveAspectRatio:(BOOL)preserveAspectRatio shouldScaleUp:(BOOL)shouldScaleUp; + /** Return the decoded image by the provided image. This one unlike `CGImageCreateDecoded:`, will not decode the image which contains alpha channel or animated image @param image The image to be decoded diff --git a/SDWebImage/Core/SDImageCoderHelper.m b/SDWebImage/Core/SDImageCoderHelper.m index 1980decc..763a9a49 100644 --- a/SDWebImage/Core/SDImageCoderHelper.m +++ b/SDWebImage/Core/SDImageCoderHelper.m @@ -325,6 +325,43 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over return outputImage; } ++ (CGSize)scaledSizeWithImageSize:(CGSize)imageSize scaleSize:(CGSize)scaleSize preserveAspectRatio:(BOOL)preserveAspectRatio shouldScaleUp:(BOOL)shouldScaleUp { + CGFloat width = imageSize.width; + CGFloat height = imageSize.height; + CGFloat resultWidth; + CGFloat resultHeight; + + if (width <= 0 || height <= 0 || scaleSize.width <= 0 || scaleSize.height <= 0) { + // Protect + resultWidth = width; + resultHeight = height; + } else { + // Scale to fit + if (preserveAspectRatio) { + CGFloat pixelRatio = width / height; + CGFloat scaleRatio = scaleSize.width / scaleSize.height; + if (pixelRatio > scaleRatio) { + resultWidth = scaleSize.width; + resultHeight = ceil(scaleSize.width / pixelRatio); + } else { + resultHeight = scaleSize.height; + resultWidth = ceil(scaleSize.height * pixelRatio); + } + } else { + // Stretch + resultWidth = scaleSize.width; + resultHeight = scaleSize.height; + } + if (!shouldScaleUp) { + // Scale down only + resultWidth = MIN(width, resultWidth); + resultHeight = MIN(height, resultHeight); + } + } + + return CGSizeMake(resultWidth, resultHeight); +} + + (UIImage *)decodedImageWithImage:(UIImage *)image { if (![self shouldDecodeImage:image]) { return image; diff --git a/Tests/Tests/SDImageCoderTests.m b/Tests/Tests/SDImageCoderTests.m index b8e09da7..fa8795ac 100644 --- a/Tests/Tests/SDImageCoderTests.m +++ b/Tests/Tests/SDImageCoderTests.m @@ -366,6 +366,30 @@ expect(encodedImage.size).equal(CGSizeMake(4000, 2629)); } +- (void)test24ThatScaleSizeCalculation { + // preserveAspectRatio true + CGSize size1 = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(100, 200) scaleSize:CGSizeMake(150, 150) preserveAspectRatio:YES shouldScaleUp:NO]; + expect(size1).equal(CGSizeMake(75, 150)); + CGSize size2 = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(100, 200) scaleSize:CGSizeMake(150, 150) preserveAspectRatio:YES shouldScaleUp:YES]; + expect(size2).equal(CGSizeMake(75, 150)); + CGSize size3 = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(100, 200) scaleSize:CGSizeMake(300, 300) preserveAspectRatio:YES shouldScaleUp:NO]; + expect(size3).equal(CGSizeMake(100, 200)); + CGSize size4 = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(100, 200) scaleSize:CGSizeMake(300, 300) preserveAspectRatio:YES shouldScaleUp:YES]; + expect(size4).equal(CGSizeMake(150, 300)); + + // preserveAspectRatio false + CGSize size5 = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(100, 200) scaleSize:CGSizeMake(150, 150) preserveAspectRatio:NO shouldScaleUp:NO]; + expect(size5).equal(CGSizeMake(100, 150)); + CGSize size6 = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(100, 200) scaleSize:CGSizeMake(150, 150) preserveAspectRatio:NO shouldScaleUp:YES]; + expect(size6).equal(CGSizeMake(150, 150)); + + // 0 value + CGSize size7 = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(0, 0) scaleSize:CGSizeMake(999, 999) preserveAspectRatio:NO shouldScaleUp:NO]; + expect(size7).equal(CGSizeMake(0, 0)); + CGSize size8 = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(999, 999) scaleSize:CGSizeMake(0, 0) preserveAspectRatio:NO shouldScaleUp:NO]; + expect(size8).equal(CGSizeMake(999, 999)); +} + #pragma mark - Utils - (void)verifyCoder:(id)coder