// 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