208 lines
8.3 KiB
Objective-C
208 lines
8.3 KiB
Objective-C
// UIImage+Resize.m
|
|
// Created by Trevor Harmon on 8/5/09.
|
|
// Free for personal or commercial use, with or without modification.
|
|
// No warranty is expressed or implied.
|
|
|
|
#import "UIImage+Resize.h"
|
|
|
|
// Private helper methods
|
|
@interface UIImage ()
|
|
- (UIImage *)resizedImage:(CGImageRef)imageRef
|
|
size:(CGSize)newSize
|
|
transform:(CGAffineTransform)transform
|
|
drawTransposed:(BOOL)transpose
|
|
interpolationQuality:(CGInterpolationQuality)quality;
|
|
- (CGAffineTransform)transformForOrientation:(CGSize)newSize;
|
|
@end
|
|
|
|
@implementation UIImage (Resize)
|
|
|
|
// Returns a copy of this image that is cropped to the given bounds.
|
|
// The bounds will be adjusted using CGRectIntegral.
|
|
// JPMH-This method no long ignores the image's imageOrientation setting.
|
|
- (UIImage *)croppedImage:(CGRect)bounds {
|
|
CGAffineTransform txTranslate;
|
|
CGAffineTransform txCompound;
|
|
CGRect adjustedBounds;
|
|
BOOL drawTransposed;
|
|
|
|
switch (self.imageOrientation) {
|
|
case UIImageOrientationDown:
|
|
case UIImageOrientationDownMirrored:
|
|
txTranslate = CGAffineTransformMakeTranslation(self.size.width, self.size.height);
|
|
txCompound = CGAffineTransformRotate(txTranslate, M_PI);
|
|
adjustedBounds = CGRectApplyAffineTransform(bounds, txCompound);
|
|
drawTransposed = NO;
|
|
break;
|
|
case UIImageOrientationLeft:
|
|
case UIImageOrientationLeftMirrored:
|
|
txTranslate = CGAffineTransformMakeTranslation(self.size.height, 0.0);
|
|
txCompound = CGAffineTransformRotate(txTranslate, M_PI_2);
|
|
adjustedBounds = CGRectApplyAffineTransform(bounds, txCompound);
|
|
drawTransposed = YES;
|
|
break;
|
|
case UIImageOrientationRight:
|
|
case UIImageOrientationRightMirrored:
|
|
txTranslate = CGAffineTransformMakeTranslation(0.0, self.size.width);
|
|
txCompound = CGAffineTransformRotate(txTranslate, M_PI + M_PI_2);
|
|
adjustedBounds = CGRectApplyAffineTransform(bounds, txCompound);
|
|
drawTransposed = YES;
|
|
break;
|
|
default:
|
|
adjustedBounds = bounds;
|
|
drawTransposed = NO;
|
|
}
|
|
|
|
CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], adjustedBounds);
|
|
UIImage *croppedImage;
|
|
if (CGRectEqualToRect(adjustedBounds, bounds))
|
|
croppedImage = [UIImage imageWithCGImage:imageRef];
|
|
else
|
|
croppedImage = [self resizedImage:imageRef
|
|
size:bounds.size
|
|
transform:[self transformForOrientation:bounds.size]
|
|
drawTransposed:drawTransposed
|
|
interpolationQuality:kCGInterpolationHigh];
|
|
CGImageRelease(imageRef);
|
|
return croppedImage;
|
|
}
|
|
|
|
// Returns a rescaled copy of the image, taking into account its orientation
|
|
// The image will be scaled disproportionately if necessary to fit the bounds specified by the parameter
|
|
- (UIImage *)resizedImage:(CGSize)newSize interpolationQuality:(CGInterpolationQuality)quality {
|
|
BOOL drawTransposed;
|
|
|
|
switch (self.imageOrientation) {
|
|
case UIImageOrientationLeft:
|
|
case UIImageOrientationLeftMirrored:
|
|
case UIImageOrientationRight:
|
|
case UIImageOrientationRightMirrored:
|
|
drawTransposed = YES;
|
|
break;
|
|
|
|
default:
|
|
drawTransposed = NO;
|
|
}
|
|
|
|
return [self resizedImage:newSize
|
|
transform:[self transformForOrientation:newSize]
|
|
drawTransposed:drawTransposed
|
|
interpolationQuality:quality];
|
|
}
|
|
|
|
// Resizes the image according to the given content mode, taking into account the image's orientation
|
|
- (UIImage *)resizedImageWithContentMode:(UIViewContentMode)contentMode
|
|
bounds:(CGSize)bounds
|
|
interpolationQuality:(CGInterpolationQuality)quality {
|
|
CGFloat horizontalRatio = bounds.width / self.size.width;
|
|
CGFloat verticalRatio = bounds.height / self.size.height;
|
|
CGFloat ratio;
|
|
|
|
switch (contentMode) {
|
|
case UIViewContentModeScaleAspectFill:
|
|
ratio = MAX(horizontalRatio, verticalRatio);
|
|
break;
|
|
|
|
case UIViewContentModeScaleAspectFit:
|
|
ratio = MIN(horizontalRatio, verticalRatio);
|
|
break;
|
|
|
|
default:
|
|
[NSException raise:NSInvalidArgumentException format:@"Unsupported content mode: %d", contentMode];
|
|
}
|
|
|
|
CGSize newSize = CGSizeMake(self.size.width * ratio, self.size.height * ratio);
|
|
|
|
return [self resizedImage:newSize interpolationQuality:quality];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Private helper methods
|
|
|
|
// Returns a copy of the image that has been transformed using the given affine transform and scaled to the new size
|
|
// The new image's orientation will be UIImageOrientationUp, regardless of the current image's orientation
|
|
// If the new size is not integral, it will be rounded up
|
|
- (UIImage *)resizedImage:(CGSize)newSize
|
|
transform:(CGAffineTransform)transform
|
|
drawTransposed:(BOOL)transpose
|
|
interpolationQuality:(CGInterpolationQuality)quality {
|
|
CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height));
|
|
CGRect transposedRect = CGRectMake(0, 0, newRect.size.height, newRect.size.width);
|
|
CGImageRef imageRef = self.CGImage;
|
|
|
|
// Build a context that's the same dimensions as the new size
|
|
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
|
|
if((bitmapInfo == kCGImageAlphaLast) || (bitmapInfo == kCGImageAlphaNone))
|
|
bitmapInfo = kCGImageAlphaNoneSkipLast;
|
|
|
|
CGContextRef bitmap = CGBitmapContextCreate(NULL,
|
|
newRect.size.width,
|
|
newRect.size.height,
|
|
CGImageGetBitsPerComponent(imageRef),
|
|
0,
|
|
CGImageGetColorSpace(imageRef),
|
|
bitmapInfo);
|
|
|
|
// Rotate and/or flip the image if required by its orientation
|
|
CGContextConcatCTM(bitmap, transform);
|
|
|
|
// Set the quality level to use when rescaling
|
|
CGContextSetInterpolationQuality(bitmap, quality);
|
|
|
|
// Draw into the context; this scales the image
|
|
CGContextDrawImage(bitmap, transpose ? transposedRect : newRect, imageRef);
|
|
|
|
// Get the resized image from the context and a UIImage
|
|
CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap);
|
|
UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
|
|
|
|
// Clean up
|
|
CGContextRelease(bitmap);
|
|
CGImageRelease(newImageRef);
|
|
|
|
return newImage;
|
|
}
|
|
|
|
// Returns an affine transform that takes into account the image orientation when drawing a scaled image
|
|
- (CGAffineTransform)transformForOrientation:(CGSize)newSize {
|
|
CGAffineTransform transform = CGAffineTransformIdentity;
|
|
|
|
switch (self.imageOrientation) {
|
|
case UIImageOrientationDown: // EXIF = 3
|
|
case UIImageOrientationDownMirrored: // EXIF = 4
|
|
transform = CGAffineTransformTranslate(transform, newSize.width, newSize.height);
|
|
transform = CGAffineTransformRotate(transform, M_PI);
|
|
break;
|
|
|
|
case UIImageOrientationLeft: // EXIF = 6
|
|
case UIImageOrientationLeftMirrored: // EXIF = 5
|
|
transform = CGAffineTransformTranslate(transform, newSize.width, 0);
|
|
transform = CGAffineTransformRotate(transform, M_PI_2);
|
|
break;
|
|
|
|
case UIImageOrientationRight: // EXIF = 8
|
|
case UIImageOrientationRightMirrored: // EXIF = 7
|
|
transform = CGAffineTransformTranslate(transform, 0, newSize.height);
|
|
transform = CGAffineTransformRotate(transform, -M_PI_2);
|
|
break;
|
|
}
|
|
|
|
switch (self.imageOrientation) {
|
|
case UIImageOrientationUpMirrored: // EXIF = 2
|
|
case UIImageOrientationDownMirrored: // EXIF = 4
|
|
transform = CGAffineTransformTranslate(transform, newSize.width, 0);
|
|
transform = CGAffineTransformScale(transform, -1, 1);
|
|
break;
|
|
|
|
case UIImageOrientationLeftMirrored: // EXIF = 5
|
|
case UIImageOrientationRightMirrored: // EXIF = 7
|
|
transform = CGAffineTransformTranslate(transform, newSize.height, 0);
|
|
transform = CGAffineTransformScale(transform, -1, 1);
|
|
break;
|
|
}
|
|
|
|
return transform;
|
|
}
|
|
|
|
@end
|