1 Nov 2008

Resizing a jpeg in Cocoa

If your image has an NSBitmapImageRep it's dead simple. Get the image
rep and send it

- (NSData *)representationUsingType:(NSBitmapImageFileType)
storageType properties:(NSDictionary *)properties.

The first argument can be one of (at least) NSBMPFileType,
NSGIFFileType, NSJPEGFileType, NSPNGFileType, or NSTIFFFileType. Then
do what you like with the NSData you end up getting.

If you don't have a bitmap ImageRep, or don't care to go to the
effort to find out if you do, you can make one:

NSBitmapImageRep* bm = [NSBitmapImageRep imageRepWithData:[image


How to Resize an NSImage

NSImage has a method called setSize, which the documentation says "sets the width and height of the image."

It seems like one should be able to simply read the data into the image, set its size, get the TIFF representation, and write that to disk.
Not so. As it turns out, setSize() only specifies how the image is displayed when it's drawn-it does nothing to the underlying data.
The most straightforward way to actually resize the data is to create another NSImage with the new size,
lock focus on it, draw the source image into the new image, and then get the TIFF representation of the new, scaled image.
This may be obvious to some, but it certainly wasn't to me,
and I had trouble finding illuminating examples or explanations through Google,
so I thought I'd post my code here to help anyone else who is similarly befuddled.
If anyone has anything interesting to add on the subject, do let us know in the comments…

NSData *sourceData ... // Get your data from a file, URL, etc.
float resizeWidth = 13.0;
float resizeHeight = 13.0;
NSImage *sourceImage = [[NSImage alloc] initWithData: sourceData];
NSImage *resizedImage = [[NSImage alloc] initWithSize: NSMakeSize(resizeWidth, resizeHeight)];
NSSize originalSize = [sourceImage size];
[resizedImage lockFocus];
[sourceImage drawInRect: NSMakeRect(0, 0, resizeWidth, resizeHeight) fromRect: NSMakeRect(0, 0, originalSize.width, originalSize.height) operation: NSCompositeSourceOver fraction: 1.0];
[resizedImage unlockFocus];
NSData *resizedData = [resizedImage TIFFRepresentation];
[sourceImage release];
[resizedImage release];

You can control the output a bit by throwing a [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolation{None, Low, High}]; in there after [resizeImage lockFocus].
More interpolation means more smoothness, less is closer to resampling. The default here seems to be low interpolation.
For something as small as a favicon you probably want low or none, I'd think, as it's going to look blurry with smoothing.
I did this a while ago in writing a little app to make thumbnail images for photographs. Then I found Pic2Icon..

Hm, it's an aesthetic issue, but I disagree with Ken - high interpolation looks good for small icons too.
In my current project I'm taking usericons, that are usually 64x64 or later, and resizing them down to 16x16 with good results using high interpolation.

Okay, I did a little test out of curiosity. High interpolation certainly looks a lot better than I guessed it would, and no interpolation is a clear loser on my test set.
I've been waffling on whether I prefer low or high interpolation, but right now I'm thinking high.
You can see the images at http://homepage.mac.com/kenferry/temp/FavIconsInterpolationTest.zip
The images are freshly downloaded favicons, resized to 13x13 pixels since that's what Buzz does above.
The favicons tested are those that are in my NetNewsWire cache, but I redownloaded them in case Brent messed with the images before he saved them (it looks like he didn't).
The holes in the grid indicate images that NSImage was unable to load. I don't know what's going on with that.

[NSImage setSize] can scale the image. All one has to do is to call [NSImage setScalesWhenResized: YES] first.]

Thanks for the test. I actually think that the high interpolation ones look pretty darn good, and I think I'll switch to using that method in Cocoalicious.

click here to return to the blog