That's a good question, and the answer lies in the fact that the example uses pattern images and mask images, rather than CoreGraphics patterns and geometric primitives. Once you explicitly ask for bitmap representations, you will be dealing with pixels and different resolution. The clue is to avoid going to pixels as much and as long as possible. The doughnut shape, for example, can easily be achieved using basic geometry and a little knowledge of the Postscript/PDF fill rules.
Using the standard "nonzero-winding-number" rule, a doughnut effect can be achieved by having the two arcs that are nested inside each other drawn in opposite directions. That's one of the reasons the extra "clockwise" parameter exists.
NSPoint centerPoint = NSMakePoint([view frame].size.width/2, 150); [context arcWithCenter:centerPoint radius:50 startDegrees:0 endDegrees:360 clockwise:YES]; [context arcWithCenter:centerPoint radius:100 startDegrees:0 endDegrees:360 clockwise:NO]; [context fill];
(The code examples here use MPWDrawingContext for convenience, pure CoreGraphics code tends to be two to three times more verbose). The second way to achieve the doughnut would be to just use the even/odd fill rule, in which case the direction doesn't matter. matter.
Patterns can also be specified geometrically, or rather with callbacks to draw the pattern shape. Objective-C Blocks are really a perfect fit for specifying these sorts of callbacks, but were only introduced much later than the CoreGraphics pattern callback API. The following code shows how to specify the diamond pattern via an Objective-C block, courtesy of some glue API provided by MPWDrawingContext.
NSSize patternSize=NSMakeSize(16,16); id diamond = [context laterWithSize:patternSize content:^(idcontext){ id red = [context colorRed:1.0 green:0.0 blue:0.0 alpha:1.0]; [context setFillColor:red]; [[context moveto:patternSize.width/2 :2] lineto:patternSize.width-2 :patternSize.height/2]; [[context lineto:patternSize.width/2 :patternSize.height-2] lineto:2 :patternSize.height/2]; [[context closepath] fill]; }]; [context setFillColor:diamond]; [[context nsrect:[[self view] frame]] fill];
The "laterWithSize:content:" message creates a callback object that not only encapsulates the block, but also implements a -CGColor method so the callback can be used directly as a color in -setFillColor:.
With all the graphics specified using pure geometry, CoreGraphics can now do its thing and automatically handle varying device resolutions, wether it's a retina display or a zoomable interface or even print, all without ever having to deal with the different resolutions in code. Although I haven't tested it, the code should also use less memory, because it doesn't create potentially large temporary bitmaps, and for the cherry on top it's also a fraction of the code. CoreGraphics rules!
Forked project on github.
No comments:
Post a Comment