i’m so full of ideas

Archive for January, 2010

iPhone: UIImage rotation and scaling

January 31, 2010 8:03 pm

Hello to my three remaining blog subscribers! Long time no see!

For my first post back after my long hiatus, I’m going to revisit the single most popular entry I’ve written to date, which was about UIImage rotation. I’ve used that code a great deal since I first wrote it. I’ve modified it several times. It’s pretty near perfect at this point.

The original code I modified for that earlier post was for dealing with photos taken with the iPhone camera. I stripped out a lot of that stuff, because I was only interested in rotating images that were embedded in the program. And then, wouldn’t you know it, I got a contract job that required me to deal with iPhone camera images as well. So I had to revisit the subject.

The original code from blog.logichigh.com had the rotation and scaling all lumped together in one chaotic function. I split it out into separate rotation and scaling methods, so they can be used independently. It also makes for easier code maintenance.

The code contains [UIImage rotate:], which works the same as the last time around, but it has been streamlined a bit internally. It will rotate any UIImage to any orientation, with or without mirroring. Then we have two scaling methods, the simplest being [UIImage scaleWithMaxSize:]. You provide it with a float value, which is the largest width and/or height that you want the output image to have. If the input image is already smaller than that, it won’t be scaled.

Finally, the all-singing, all-dancing method for massaging photos from the iPhone camera: [UIImage rotateAndScaleFromCameraWithMaxSize:]. It examines the EXIF data in the image and, if needed, rotates it to the proper orientation. Then it scales the image to the maximum width and/or height supplied to the method.

This code has been thoroughly tested in several real-world iPhone projects. There are no known bugs. If you find one, I’d love to hear about it, so I can fix it.

Here’s the header file, WBImage.h:

// WBImage.h -- extra UIImage methods // by allen brunson  march 29 2009 #ifndef WBIMAGE_H #define WBIMAGE_H #import <UIKit/UIKit.h> @interface UIImage (WBImage) // rotate UIImage to any angle -(UIImage*)rotate:(UIImageOrientation)orient; // rotate and scale image from iphone camera -(UIImage*)rotateAndScaleFromCameraWithMaxSize:(CGFloat)maxSize; // scale this image to a given maximum width and height -(UIImage*)scaleWithMaxSize:(CGFloat)maxSize; -(UIImage*)scaleWithMaxSize:(CGFloat)maxSize  quality:(CGInterpolationQuality)quality; @end #endif  // WBIMAGE_H

And here’s the implementation file, WBImage.mm:

// WBImage.mm -- extra UIImage methods // by allen brunson  march 29 2009 #include "WBImage.h" static inline CGFloat degreesToRadians(CGFloat degrees) {     return M_PI * (degrees / 180.0); } static inline CGSize swapWidthAndHeight(CGSize size) {     CGFloat  swap = size.width;     size.width  = size.height;     size.height = swap;     return size; } @implementation UIImage (WBImage) // rotate an image to any 90-degree orientation, with or without mirroring. // original code by kevin lohman, heavily modified by yours truly. // http://blog.logichigh.com/2008/06/05/uiimage-fix/ -(UIImage*)rotate:(UIImageOrientation)orient {     CGRect             bnds = CGRectZero;     UIImage*           copy = nil;     CGContextRef       ctxt = nil;     CGRect             rect = CGRectZero;     CGAffineTransform  tran = CGAffineTransformIdentity;     bnds.size = self.size;     rect.size = self.size;     switch (orient)     {         case UIImageOrientationUp:         return self;         case UIImageOrientationUpMirrored:         tran = CGAffineTransformMakeTranslation(rect.size.width, 0.0);         tran = CGAffineTransformScale(tran, -1.0, 1.0);         break;         case UIImageOrientationDown:         tran = CGAffineTransformMakeTranslation(rect.size.width,          rect.size.height);         tran = CGAffineTransformRotate(tran, degreesToRadians(180.0));         break;         case UIImageOrientationDownMirrored:         tran = CGAffineTransformMakeTranslation(0.0, rect.size.height);         tran = CGAffineTransformScale(tran, 1.0, -1.0);         break;         case UIImageOrientationLeft:         bnds.size = swapWidthAndHeight(bnds.size);         tran = CGAffineTransformMakeTranslation(0.0, rect.size.width);         tran = CGAffineTransformRotate(tran, degreesToRadians(-90.0));         break;         case UIImageOrientationLeftMirrored:         bnds.size = swapWidthAndHeight(bnds.size);         tran = CGAffineTransformMakeTranslation(rect.size.height,          rect.size.width);         tran = CGAffineTransformScale(tran, -1.0, 1.0);         tran = CGAffineTransformRotate(tran, degreesToRadians(-90.0));         break;         case UIImageOrientationRight:         bnds.size = swapWidthAndHeight(bnds.size);         tran = CGAffineTransformMakeTranslation(rect.size.height, 0.0);         tran = CGAffineTransformRotate(tran, degreesToRadians(90.0));         break;         case UIImageOrientationRightMirrored:         bnds.size = swapWidthAndHeight(bnds.size);         tran = CGAffineTransformMakeScale(-1.0, 1.0);         tran = CGAffineTransformRotate(tran, degreesToRadians(90.0));         break;         default:         // orientation value supplied is invalid         assert(false);         return nil;     }     UIGraphicsBeginImageContext(bnds.size);     ctxt = UIGraphicsGetCurrentContext();     switch (orient)     {         case UIImageOrientationLeft:         case UIImageOrientationLeftMirrored:         case UIImageOrientationRight:         case UIImageOrientationRightMirrored:         CGContextScaleCTM(ctxt, -1.0, 1.0);         CGContextTranslateCTM(ctxt, -rect.size.height, 0.0);         break;         default:         CGContextScaleCTM(ctxt, 1.0, -1.0);         CGContextTranslateCTM(ctxt, 0.0, -rect.size.height);         break;     }     CGContextConcatCTM(ctxt, tran);     CGContextDrawImage(ctxt, rect, self.CGImage);     copy = UIGraphicsGetImageFromCurrentImageContext();     UIGraphicsEndImageContext();     return copy; } -(UIImage*)rotateAndScaleFromCameraWithMaxSize:(CGFloat)maxSize {     UIImage*  imag = self;     imag = [imag rotate:imag.imageOrientation];     imag = [imag scaleWithMaxSize:maxSize];     return imag; } -(UIImage*)scaleWithMaxSize:(CGFloat)maxSize {     return [self scaleWithMaxSize:maxSize quality:kCGInterpolationHigh]; } -(UIImage*)scaleWithMaxSize:(CGFloat)maxSize  quality:(CGInterpolationQuality)quality {     CGRect        bnds = CGRectZero;     UIImage*      copy = nil;     CGContextRef  ctxt = nil;     CGRect        orig = CGRectZero;     CGFloat       rtio = 0.0;     CGFloat       scal = 1.0;     bnds.size = self.size;     orig.size = self.size;     rtio = orig.size.width / orig.size.height;     if ((orig.size.width <= maxSize) && (orig.size.height <= maxSize))     {         return self;     }     if (rtio > 1.0)     {         bnds.size.width  = maxSize;         bnds.size.height = maxSize / rtio;     }     else     {         bnds.size.width  = maxSize * rtio;         bnds.size.height = maxSize;     }     UIGraphicsBeginImageContext(bnds.size);     ctxt = UIGraphicsGetCurrentContext();     scal = bnds.size.width / orig.size.width;     CGContextSetInterpolationQuality(ctxt, quality);     CGContextScaleCTM(ctxt, scal, -scal);     CGContextTranslateCTM(ctxt, 0.0, -orig.size.height);     CGContextDrawImage(ctxt, orig, self.CGImage);     copy = UIGraphicsGetImageFromCurrentImageContext();     UIGraphicsEndImageContext();     return copy; } @end