How to Resize an NSImage

I decided to take a break from my normal rock and roll lifestyle (ha!) this evening to get some work done on Cocoalicious, with an eye toward doing a new release sometime next week. Specifically, I’ve been working on integrating and tweaking Eric Blair’s lovely favicon support patch.

In the course of polishing the favicon support, it occurred to me that we could improve performance a lot by pre-sizing the cached version of each favicon on download, instead of always resizing them on display (as it turns out, favicons in the wild come in all kinds of sizes). So I set to work changing the code to do just that, only to run up against a lot of confusion about just how to resize an image using NSImage.

At first glance it seems like the task is fairly straightforward. 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];

13 Responses to “How to Resize an NSImage”

  1. Ken Says:

    Hey Buzz,

    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.. :-)

    By the way, the podcast rocks.

    -Ken

  2. Ken Says:

    Oh, also, Dan Wood did a nice write up of the Core Image version at , and covered conversion between NSImage and CIImage at

  3. Ken Says:

    Once more without the angle brackets:

    Dan Wood’s write ups are at http://weblog.karelia.com/Cocoa/Core_Image,_part_2.html and http://weblog.karelia.com/Cocoa/Core_Image__Practic.html .

    Not useful for Cocoalicious, but who knows what those crafty googlers will be after.

  4. Jens Alfke Says:

    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 64×64 or later, and resizing them down to 16×16 with good results using high interpolation.

  5. Eric Blair Says:

    D’oh. I didn’t even think of resizing the cached version instead of resizing at display time.

  6. Fraser Speirs Says:

    If you think that’s a pain, try doing that on a digital photo without losing the EXIF data :-)

    My only resort was to call out to /usr/bin/sips in FlickrExport. Haven’t yet investigated Core Image, but I’m pessimistic that it will preserve EXIF all the way through.

  7. Ken Says:

    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 13×13 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.

  8. Sven Weidauer Says:

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

  9. Buzz Andersen Says:

    Ken:
    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.

    Sven:
    Are you sure about that? I could have sworn I tried that and it didn’t work.

  10. Ken Says:

    Frasier,

    Check out the ImageIO framework, new in Tiger. It handles _everything_, it totally rocks.

  11. Sven Weidauer Says:

    Buzz:

    I never tried scaling bitmap images like that. It did work for scaling a PDF page and grabbing the TIFF representation of that.

  12. Arathi Says:

    I have an application in which i try to resize the NSImage to new resolution.Sometimes i use the destination image resolution is same that of source.I have to do some other functionality also.My problem is I make a new NSImage with same resolution as that of the source , the clarity of the new NSImage is less.Any way to maintain the quality of the new NSImage as that of the source NSImage.

  13. Victor Says:

    NSImage setSize

    does in fact work…however I have a strange situation where the bitmapImageReps have a wierd out of phase appearance at oddball resize dimensions…like 50% and 80% (I have the resize tied into an NSSlider) I tried BUZZ’s code to no avail

    Got any Ideas?

Leave a Reply