Dan Wood: The Eponymous Weblog (Archives)

Dan Wood Dan Wood is co-owner of Karelia Software, creating programs for the Macintosh computer. He is the father of two kids, lives in the Bay Area of California USA, and prefers bicycles to cars. This site is his older weblog, which mostly covers geeky topics like Macs and Mac Programming. Go visit the current blog here.

Useful Tidbits and Egotistical Musings from Dan Wood

Categories: Business · Mac OS X · Cocoa Programming · General · All Categories

Fri, 26 Jan 2007

Core Graphics, or: How I learned to stop worrying and love CGImageRef

I'm a big fan of Core Image; I blogged (1, 2) about quite a while ago. And I like NSImage for its simplicity. But only recently have I been introduced to Core Graphics. I wish I had been aware of its coolness much earlier!

This came up recently when I met Dan of Stunt Software recently at Macworld Expo. He has an application that shows thumbnails of images. He mentioned that he had tried Epeg, which Sandvox has been using to quickly generate thumbnails from JPEG images, but found that Core Graphics has a much faster technique: specifically the function CGImageSourceCreateThumbnailAtIndex.

I started to look into this, and came across a blog post by Wil Shipley that mentioned how fast CG was at doing thumbnails. I had actually read this, but didn't pay too much attention back then! I probably avoided CG because it doesn't have a nice Cocoa-like interface to it. My loss!

I bit the bullet and started to convert some of our code, starting with the thumbnailing in the media browser, to use this technique. Wow, what a speedup! And I actually can go further: all I'm doing now is building the thumbnail and then converting the CGImageRef to an NSImage for MUPhotoView to draw. But I can go further if I modify MUPhotoView to natively deal with the CGImageRef instead of NSImage. That will be my next step, but it will be a bit more work.

Here are some snippets of code (Tiger-only). The first is to read from a file and create a thumbnail.

NSURL *url = [NSURL fileURLWithPath:imagePath];
CGImageSourceRef source = CGImageSourceCreateWithURL((CFURLRef)url,NULL);
if (source)
{
  NSDictionary* thumbOpts = [NSDictionary dictionaryWithObjectsAndKeys:
  (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailWithTransform,
  (id)kCFBooleanTrue, (id)kCGImageSourceCreateThumbnailFromImageIfAbsent,
  [NSNumber numberWithInt:MAX_THUMB_SIZE],
        (id)kCGImageSourceThumbnailMaxPixelSize, 
    nil];
  
  CGImageRef theCGImage = CGImageSourceCreateThumbnailAtIndex(
    source, 0, (CFDictionaryRef)thumbOpts);
   ...
    CFRelease(theCGImage);
  }
  CFRelease(source);
}

To convert the CGImage to an NSImage, I used this code. I had to flip the image, in my situation, to make the image draw properly.

NSRect imageRect = NSZeroRect;
CGContextRef imageContext = nil;

imageRect.size.height = CGImageGetHeight(theCGImage);
imageRect.size.width = CGImageGetWidth(theCGImage);

img = [[[NSImage alloc] initWithSize:imageRect.size] autorelease];
[img setFlipped:YES];
[img lockFocus];
imageContext = (CGContextRef)
    [[NSGraphicsContext currentContext] graphicsPort];
CGContextDrawImage(imageContext, *(CGRect*)&imageRect, theCGImage);
[img unlockFocus];