Thursday, November 1, 2012

CGLayer performance and quality

The intent of CGLayer was to have some sort of object for optimized repeated drawing, similar
to how you would call a drawing procedure multiple times, have multiple instances of a view or simply
draw the same PDF or bitmap image multiple times. The difference was supposed to be that the drawing
layer could do secret magic "stuff" to optimize the repeated instance drawings. Not giving away the
specific representation or optimizations performed means that whatever is done can be adapted both
to different contexts and with changing technology.

Currently, that primarily means a texture stored on the graphics card, and that is and was one
of the possible optimizations. However, the reasoning for such an opaque optimized object
goes back further, at least to NeXTStep/Rhapsody: DisplayPostscript was implemented as a server process, and despite
Mach's fast memory-remapping messages, shipping images back and forth between the client
and server was not an optimization, and so you couldn't use (client side) bitmaps for
optimized drawing. Well you could, but it wouldn't be optimized.

With Quartz becoming a client-side drawing library in Mac OS X, some of the reasons for
these distinctions disappeared, but the distinctions (such as NSBitmapImageRep
vs. NSCachedImageRep) remained. I think there were plans for CGLayer
to become a kind of NSCachedImageRep on steroids, for example maintaining
OpenGL display lists, but as far as I could tell, those plans never really panned out:
in my tests drawing a CGLayer always looked the same as drawing a cached
bitmap, and also performed similarly.

In 10.5, most of the accumulated cruft was cleaned up, NSBitmapImageRep
for examples is now only a fairly thin wrapper around
its underlying CGImage, and NSCachedImageRep was deprecated
completely in 10.6, as it no longer has any advantages. With these changes, drawing a CGImage
or a NSBitmapImageRep became as fast as possible, with CGImages
stored on the graphics card. At this point CGLayer was essentially abandoned,
and for drawing to the screen, you can just as easily use a NSBitmapImageRep,
CGImage, UIImage etc.

So is CGLayer now completely useless? Not quite! It turns out that
when drawing to a PDF context, CGLayer will generate a PDF Form
object, which stores the original drawing commands (vectors, text, images). This
is obviously much better than drawing a bitmap, both in terms of quality and in
terms of file size and performance.

So if your plans include printing or generating a PDF, then I would recommend taking
a look at CGLayer for repeated drawing of (vector) content.