Thursday, December 16, 2010

Offscreen drawing on the iPhone using NSOperations, CGImage, CGLayer

Creating and or modifying images offscreen or in a background thread.

Chiefly for performance. The main thread of an iPhone application is usually fairly busy doing all sorts of things. By rendering complex images in the background, you can do all manor of things. In my case I wanted to know how to render an image larger than the size of the device screen, and then save that image to disk.

For my fellow lovers of NSOperationQueue(aka possibly the most awesome class in Cocoa), this allows you to bundle image processing and generation in to NSOperation subclasses or if you like the new hotness a block, and add it to the queue.

Well with a lot of C. If you have ever overridden UIView's drawRect: then most of what is below should make perfect sense.

First thing is first, as we want to operate on our own thread we have to create everything ourselves. Primarily that means no quick calls to UIGraphicsGetCurrentContext().

The key thing to remember is that the drawing coordinates are inverted. This means that CGPoint(0.0f,0.0f) is actually the bottom left not the top left corner. I'm lead to believe this is a hold over from the Postscript drawing system that originated on the mac, and this is how the big boys do it. So stop whining and code.

Nicely it would appear that the UIImage representation methods automatically invert the image, so you only have to invert your coordinates for CGContext drawing calls.

Exactly How?

This is the process
*Create a CGColorSpace
*Create a CGBitmapContext
*Create a new CGLayer
*Get the the CGLayer's context

Draw into the context as you would normally, using the CGContext methods

*Render that CGLayer into the CGBitmapContext
(if you want an image)
*Create a CGImage from the CGBitmapContext
*Convert the CGImage into a UIImage
*Use one of the UIImageJPEGRepresentation or UIImagePNGRepresentation methods to get an image that can be saved to disk or sent over the wire etc.

NOTE: This code will not work unless you have a image named sample.jpg in your bundle or change the assignment to the backgroundImg variable.

Sample code

Go forth and code.


Anonymous said...

Thanks!, this really helped me.

SSL Certificates said...

Perfect just what I was looking for!