Thursday, November 1, 2012

CGLayer performance and quality

The CoreGraphics CGLayer object seems to keep causing confusion. I hope I can clear that up a bit.

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.

1 comment:

Wang Wenlin said...

Thank you, great explains!