Tuesday, June 12, 2012

A Pleasant Objective-C Drawing Context

In a recent post, Peter Hosey muses On the API design of CGBitmapContextCreate and apparently finds it somewhat lacking. Apart from agreeing violently, I'd extend that not just to the rest of CoreGraphics, but really to the state of drawing APIs in OSX and iOS in general.

On OSX, we have the Cocoa APIs, which are at least somewhat OO, but on the other hand don't have a real graphics context, only the stunted NSGraphcisContext which doesn't actually allow you to do, you know, graphics. Instead we have a whole bunch of "use-once" objects that are typically instantiated, told to draw themselves to an implicit global drawing context and then discarded. This seems exactly backwards, I would expect an explicit graphics context that I can use to draw using a consolidated API.

That consolidated API is only available in the form of the CGContextRef and its associated functions, but alas that's a C API. So extremely long names, but without named parameters or useful polymorphism. Despite the fact that a graphics context is a quintessential example of OO, only Apple can create subclasses of a CGContext, and even then in only a sorta-kinda sort of way: CGBitmapContextCreate returns a CGContextRef that silently knows how to do things that other CGContextRefs do not. Same for CGPDFContextCreate.

On iOS, CoreGraphics is really your only choice, and double so if you want code that works on both iOS and OSX, but that means having to put up with the API, constantly converting between UI/NS objects and CoreGraphics and then there's the whole text mess.

Having been a great fan of algorithmic drawing every since my exposure to DisplayPostscript on the NeXT Cube, I found all of this sufficiently unsatisfactory that I decided to work on a solution, the first step of which is a lightweight drawing context that provides a reasonable Objective-C drawing API on top of CoreGraphics and works the same on OSX and iOS.

The result is on github: MPWDrawingContext, embedded in a version of Mark Gallagher's excellent IconApp AppKit drawing example. The same code is also used in the iOS target (a variant of Marcus Crafter's version of IconApp). I've also started using the context in a bunch of my own projects (that was the purpose after all) and so far it's made my graphics coding much more pleasant.

Another advantage, at least for me, is that bridging to scripting languages is automatic due to Objective-C's runtime information, whereas C functions have to have to be bridged separately and maintained, which is burdensome and doesn't necessarily get done.

At least one of Peter's problem with CGBitmapContextCreate is also solved: creating a bitmap context is as easy as

    [MPWCGDrawingContext rgbBitmapContext:NSMakeSize(595,842)]
Last not least: although it wasn't an explicit goal, there is also a pleasant reduction in code bulk.
LinesCharactersLines %Characters %
AppKit146659367.12%76.69%
CGContext134736773.13%68.63%
MPWDrawingContext985056--