I recently started working on an image viewing program for Mac OS X using Cocoa, and one of the features I decided to add was the ability to rotate images in 90° increments. I did some searching on the internet, and found a few things:
- Some other people who were struggling with the same thing and posted some code snippets.
- Code for a rotation function that supports arbitrary rotation and scaling.
- Apple Documentation on Drawing
Neither of the first two was exactly what I wanted—the first didn’t quite work, while the second was too complicated and, in order to support arbitrary rotation, created an NSImage object that was way too large.
So, after reading the Apple Cocoa Drawing Guide on Using Transforms, I came up with the following code to rotate an NSImage object 90°. Please note that this function ONLY supports a clockwise or counterclockwise 90° rotation. It also returns an object that must be released by the callER. I’m not 100% sure if this is standard Objective-C/Cocoa methodology, as I’m still a bit new to all this stuff.
But, without any further ado, here is the rotation function:
- (NSImage *)rotateIndividualImage: (NSImage *)image clockwise: (BOOL)clockwise
{
NSImage *existingImage = image;
NSSize existingSize;
/**
* Get the size of the original image in its raw bitmap format.
* The bestRepresentationForDevice: nil tells the NSImage to just
* give us the raw image instead of it's wacky DPI-translated version.
*/
existingSize.width = [[existingImage bestRepresentationForDevice: nil] pixelsWide];
existingSize.height = [[existingImage bestRepresentationForDevice: nil] pixelsHigh];
NSSize newSize = NSMakeSize(existingSize.height, existingSize.width);
NSImage *rotatedImage = [[NSImage alloc] initWithSize:newSize];
[rotatedImage lockFocus];
/**
* Apply the following transformations:
*
* - bring the rotation point to the centre of the image instead of
* the default lower, left corner (0,0).
* - rotate it by 90 degrees, either clock or counter clockwise.
* - re-translate the rotated image back down to the lower left corner
* so that it appears in the right place.
*/
NSAffineTransform *rotateTF = [NSAffineTransform transform];
NSPoint centerPoint = NSMakePoint(newSize.width / 2, newSize.height / 2);
[rotateTF translateXBy: centerPoint.x yBy: centerPoint.y];
[rotateTF rotateByDegrees: (clockwise) ? - 90 : 90];
[rotateTF translateXBy: -centerPoint.y yBy: -centerPoint.x];
[rotateTF concat];
/**
* We have to get the image representation to do its drawing directly,
* because otherwise the stupid NSImage DPI thingie bites us in the butt
* again.
*/
NSRect r1 = NSMakeRect(0, 0, newSize.height, newSize.width);
[[existingImage bestRepresentationForDevice: nil] drawInRect: r1];
[rotatedImage unlockFocus];
return rotatedImage;
}
The function works great and correctly rotates the incoming NSImage 90 degrees. However, People working with JPEGs should probably not save these data to disk in place of existing images, as the function works on the decompressed bits and resaving them can lose more image data. To truly solve this problem, you should use a “Lossless JPEG Image Rotation” algorithm.



[existingImage drawAtPoint:NSZeroPoint
should be:
[image drawAtPoint: NSZeroPoint