From acbdb8c3747feac0ccbd7a2e9cf6461c194860a8 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Tue, 10 Apr 2018 20:44:26 +0800 Subject: [PATCH] Add helper method in coder helper to create decoded CGImage to specify orientation. The existing method just call with Up orientation --- SDWebImage/SDWebImageCoderHelper.h | 11 +++++ SDWebImage/SDWebImageCoderHelper.m | 77 +++++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/SDWebImage/SDWebImageCoderHelper.h b/SDWebImage/SDWebImageCoderHelper.h index f582a400..689bd74d 100644 --- a/SDWebImage/SDWebImageCoderHelper.h +++ b/SDWebImage/SDWebImageCoderHelper.h @@ -61,12 +61,23 @@ /** Create a decoded image by the provided image. This follows The Create Rule and you are response to call release after usage. It will detect whether image contains alpha channel, then create a new bitmap context with the same size of image, and draw it. This can ensure that the image do not need extra decoding after been set to the imageView. + @note This actually call `imageRefCreateDecoded:orientation` with the Up orientation. @param imageRef The CGImage @return A new created decoded image */ + (CGImageRef _Nullable)imageRefCreateDecoded:(_Nonnull CGImageRef)imageRef CF_RETURNS_RETAINED; +/** + Create a decoded image by the provided image. This follows The Create Rule and you are response to call release after usage. + It will detect whether image contains alpha channel, then create a new bitmap context with the same size of image, and draw it. This can ensure that the image do not need extra decoding after been set to the imageView. + + @param imageRef The CGImage + @param orientation The image orientation. + @return A new created decoded image + */ ++ (CGImageRef _Nullable)imageRefCreateDecoded:(_Nonnull CGImageRef)imageRef orientation:(CGImagePropertyOrientation)orientation CF_RETURNS_RETAINED; + /** Return the decoded image by the provided image. This one unlike `imageRefCreateDecoded:`, will not decode the image which contains alpha channel or animated image @param image The image to be decoded diff --git a/SDWebImage/SDWebImageCoderHelper.m b/SDWebImage/SDWebImageCoderHelper.m index 6d22c4dd..fda81231 100644 --- a/SDWebImage/SDWebImageCoderHelper.m +++ b/SDWebImage/SDWebImageCoderHelper.m @@ -248,13 +248,16 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over } + (CGImageRef)imageRefCreateDecoded:(CGImageRef)imageRef { + return [self imageRefCreateDecoded:imageRef orientation:kCGImagePropertyOrientationUp]; +} + ++ (CGImageRef)imageRefCreateDecoded:(CGImageRef)imageRef orientation:(CGImagePropertyOrientation)orientation { if (!imageRef) { return NULL; } size_t width = CGImageGetWidth(imageRef); size_t height = CGImageGetHeight(imageRef); if (width == 0 || height == 0) return NULL; - CGRect rect = CGRectMake(0, 0, width, height); BOOL hasAlpha = [self imageRefContainsAlpha:imageRef]; // iOS prefer BGRA8888 (premultiplied) or BGRX8888 bitmapInfo for screen rendering, which is same as `UIGraphicsBeginImageContext()` or `- [CALayer drawInContext:]` // Through you can use any supported bitmapInfo (see: https://developer.apple.com/library/content/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB ) and let Core Graphics reorder it when you call `CGContextDrawImage` @@ -265,6 +268,25 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over if (!context) { return NULL; } + + // Apply transform + CGAffineTransform transform = SDCGContextTransformFromOrientation(orientation, CGSizeMake(width, height)); + CGRect rect; + switch (orientation) { + case kCGImagePropertyOrientationLeft: + case kCGImagePropertyOrientationLeftMirrored: + case kCGImagePropertyOrientationRight: + case kCGImagePropertyOrientationRightMirrored: { + // These orientation should swap width & height + rect = CGRectMake(0, 0, height, width); + } + break; + default: { + rect = CGRectMake(0, 0, width, height); + } + break; + } + CGContextConcatCTM(context, transform); CGContextDrawImage(context, rect, imageRef); CGImageRef newImageRef = CGBitmapContextCreateImage(context); CGContextRelease(context); @@ -546,7 +568,60 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over return shouldScaleDown; } +#endif +static CGAffineTransform SDCGContextTransformFromOrientation(CGImagePropertyOrientation orientation, CGSize size) { + // Inspiration from @libfeihu + // We need to calculate the proper transformation to make the image upright. + // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored. + CGAffineTransform transform = CGAffineTransformIdentity; + + switch (orientation) { + case kCGImagePropertyOrientationDown: + case kCGImagePropertyOrientationDownMirrored: + transform = CGAffineTransformTranslate(transform, size.width, size.height); + transform = CGAffineTransformRotate(transform, M_PI); + break; + + case kCGImagePropertyOrientationLeft: + case kCGImagePropertyOrientationLeftMirrored: + transform = CGAffineTransformTranslate(transform, size.width, 0); + transform = CGAffineTransformRotate(transform, M_PI_2); + break; + + case kCGImagePropertyOrientationRight: + case kCGImagePropertyOrientationRightMirrored: + transform = CGAffineTransformTranslate(transform, 0, size.height); + transform = CGAffineTransformRotate(transform, -M_PI_2); + break; + case kCGImagePropertyOrientationUp: + case kCGImagePropertyOrientationUpMirrored: + break; + } + + switch (orientation) { + case kCGImagePropertyOrientationUpMirrored: + case kCGImagePropertyOrientationDownMirrored: + transform = CGAffineTransformTranslate(transform, size.width, 0); + transform = CGAffineTransformScale(transform, -1, 1); + break; + + case kCGImagePropertyOrientationLeftMirrored: + case kCGImagePropertyOrientationRightMirrored: + transform = CGAffineTransformTranslate(transform, size.height, 0); + transform = CGAffineTransformScale(transform, -1, 1); + break; + case kCGImagePropertyOrientationUp: + case kCGImagePropertyOrientationDown: + case kCGImagePropertyOrientationLeft: + case kCGImagePropertyOrientationRight: + break; + } + + return transform; +} + +#if SD_UIKIT || SD_WATCH static NSUInteger gcd(NSUInteger a, NSUInteger b) { NSUInteger c; while (a != 0) {