MPWDrawingContext and adding more and more graphics (for example,
I now do my icons in code), I've discovered a lot about making drawing with code a more pleasant
experience.Blocks
Blocks seem to be a really wonderful match for a graphics context, and most of the changes involve blocks in some way.Bracketing operations such as gsave/grestore now have block versions, so the Objective-C block structure reflects the nesting:
[context ingsave:^(Drawable c ){
[c translate:@[ @130 ,@140]];
[c setFont:[context fontWithName:@"ArialMT" size:345]];
[c setTextPosition:NSMakePoint(0, 0)];
[c show:@"\u2766"];
}];
This is somewhat more compact than the plain code, which for correctness should also have a @try/@finally block wrapped around the basic drawing so exceptions don't mess up the graphics state stack.
[context gsave];
[context translate:@[ @130 ,@140]];
[context setFont:[context fontWithName:@"ArialMT" size:345]];
[context setTextPosition:NSMakePoint(0, 0)];
[context show:@"\u2766"];
[context grestore];
Similar for drawing shadows:
[context withShadowOffset:NSMakeSize(0, -8 * scale) blur:12 * scale color:[context colorGray:0 alpha: 0.75] draw:^(Drawable c ){
[[[c setFillColorGray:0.9 alpha:1.0] ellipseInRect:ellipseRect] fill];
}];
Again, this seems a little clearer than having to explicitly set and unset, makes it harder to miss the end of the bracket when moving code around and remains exception-safe.
[context sethadowOffset:NSMakeSize(0, -8 * scale) blur:12 * scale color:[context colorGray:0 alpha: 0.75]];
[[[context setFillColorGray:0.9 alpha:1.0] ellipseInRect:ellipseRect] fill];
[context clearShadow];
Stored, delayed and repeated drawing
You can create an object for later drawing by sending the-laterWithSize:(NSSize)size content:(DrawingBlock)commands message. For example, here is a simple diamond shape:
NSSize diamondSize=NSMakeSize(16,16);
id diamond = [context laterWithSize:diamondSize
content:^(id context){
id red = [context colorRed:1.0 green:0.0 blue:0.0 alpha:1.0];
[context setFillColor:red];
[[context moveto:diamondSize.width/2 :2]
lineto:diamondSize.width-2 :diamondSize.height/2];
[[context lineto:diamondSize.width/2 :diamondSize.height-2]
lineto:2 :diamondSize.height/2];
[[context closepath] fill];
}];
We can now draw this anywhere we want, and at any scale or orientation, using the -drawImage: message.
[context drawImage:diamond];
You also have layerWitSize:content: and bitmapWithSize:content: messages if you want to specifically use
CGLayer or CGImage instead, but using laterWithSize:content: preserves maximum quality, and it
will automatically switch to a CGLayer when rendering to a PDF context in order to minimize
PDF file size.Patterns
I talked about patterns earlier. What I didn't mention then was that this is just the ability to use a stored set of drawing commands (see previous section) as a color:
[context setColor:diamond];
I am not going to post the comparison to plain CG here, you can read it in the original Apple documentation. I should note that this currently works for colored patterns, not for uncolored patterns, due to the fact that I haven't yet exposed color spaces. The basic process will be very similar.
Polymorphic object arguments
Path construction and graphics state messages with point arguments are now available in a version that takes a single object argument, in addition to the format with anonymous float arguments (moveto:(float)x :(float)y).
- moveto:
- lineto:
- translate:
- scale:
[context moveto:@[ @10, @20]];
Alternatively, any custom object that responds to count and either objectAtIndex:, (float)realAtIndex: or getReals:(float*)buffer length:(int)maxLen can be used. The scale: message can also take a single NSNumber and will treat that as uniform x and y scale.
Linecap parameters
Linecap parameters can now be set using distinct message:- setlinecapRound
- setlinecapButt
- setlinecapSquare
[context setlinecapRound];
vs.
[context setLinecap: kCGContextLinecapRound];
Future
Another reason to be as purely message-based as possible is that it makes bridging to other languages easier, for example for interactive drawing environments: Creating a badge (youtube).
I've also started experimenting with other outputs, for example creating a version of the same badge
composed of CALayer objects using the same drawing commands. Other output should follow, for
example web with SVG or HTML 5 Canvas or direct OpenGL textures.
I also want to finally add image processing operations both stand-alone and as chained drawing contexts, as well as getting more complex text layout options in there.
p.s.: now on hacker news

No comments:
Post a Comment