2 Nov 2008

The good and bad of the internet

One of the big things that has changed since I last programmed is the Internet.

The ability to log on and find some discussion or code fragments that do what you are trying to do,
or to answer the questions you are trying to answer is fantastic.

At the moment I am trying to get the blog writing app to write out resized images - I don't want to
draw them on screen only load them and save them again.

Now this requires me to use the class NSImage - one that is not in the foundation set of classes, but
in the appkit - now as I am building a command line utility when I used NSImage it failed with a
linker failure - something I have not seen before in Xcode.

So after several hours of struggling I posted a question on Mac Forums and when I got up this morning there
was a response that gave me the clue I was looking for ...

macforums

wonderful.

On the other hand there is a real danger that you start picking up code from other people and you don't really deeply know what you are doing -

Here for example I found someone who had the problems I had this morning - failing to resize an NSImage and then getting crashes

Great the answer is good and works for me -

cocoabuilder


But I don't really (deeply) know what he is doing. Sure at some superficial level it is clear he is drawing the old bitmap onto a new bitmap of the
right size but I have not put those lines of code together and read the manual on each of them so I am in danger of kidding myself
I know what I am doing!

Anyway here is my finished code for a command line utility to resize a jpeg image file.

blah


(Link for this specific entry...)

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
TIFFRepresentation]];

"'
===============================

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.


(Link for this specific entry...)