Context State
In Listing 1-8, the set method, when called on a color instance, specified the color of subsequent fill and stroke operations within the current context. In that listing, the purple color was set first, and then the green color. After each color was specified, it applied to all subsequent drawing operations.
Two related methods specify whether a color is used for just fill (setFill) or just stroke (setStroke) operations. A fill is used to completely color the interior of a shape. A stroke is used to outline that shape. In Figure 1-9, the fill color is green, and the stroke color is purple.
All three of these methods (set, setFill, and setStroke) update a current drawing state, specifying the active fill and stroke colors.
Figure 1-9 The context stroke and fill colors apply to all subsequent drawing operations.
Applying State
Consider the following lines of code, which were used to create the graphic shown in Figure 1-9:
[greenColor setFill]; [purpleColor setStroke]; [bunnyPath fill]; [bunnyPath stroke];
They set fill and stroke colors and then apply them to a Bezier path. The big question is this: Who is the target of these calls, storing the fill and stroke color states, allowing them to be applied by later operations?
The answer is the current context. The object returned by UIGraphicsGetCurrentContext() stores the fill and stroke colors. That context is inferred by each setting and drawing method.
All contexts store graphic state information, which acts as drawing operation parameters. Fill and stroke color are just two types of state saved to a context. As you’re about to discover, contexts can store quite a lot of information. The graphics state affects drawing operations by tweaking the ways each operation is realized.
Pushing and Popping Graphic State
Every context owns a stack of graphic state settings. Each time you create a new context, the stack starts with a fresh state. You can then modify that state and, if needed, push and pop copies of that state onto and off of a graphics state (GState) stack.
This stack is different from the context stack maintained by UIKit. That stack stores drawing destinations, letting you move between contexts by pushing and popping the stack. A drawing destination is like a canvas. When you change the context stack, you choose which canvas to draw to. The state stack is specific to each context. It holds sets of drawing preferences that apply to this context alone, changing how drawing operations apply to each “canvas.” Both approaches use stacks, but they affect different parts of the graphics system.
Each graphics state remembers any changes made to it. For example, if you push a new state onto the stack and adjust the default line width to 10, that context state persists until it’s popped off the stack. After that, the default line width returns to whatever value it was before that state was created.
Listing 1-9 demonstrates the process of managing the graphics state stack. It starts by setting the fill and stroke colors to the same green and purple you saw used in Figure 1-9. It draws a bunny and then “saves” the current state by calling CGContextSaveGState(). This pushes a copy of the state onto the context’s GState stack. Any changes to the context will now apply to that new copy of the graphics state.
If you kept drawing without making any changes to that state, you’d keep creating green bunnies with purple outlines. However, Listing 1-9 updates its colors before drawing. These new colors are orange and blue. And they override any previous color settings for the current state. When the second bunny is drawn, it displays in orange and blue.
Finally, the listing restores the previous graphics state by calling CGContextRestoreGState(). This pops the stack, discarding any changes made to the newer state copy. The final bunny therefore falls back to the original color states, namely purple and green. Figure 1-10 shows the result of the drawing operations detailed in Listing 1-9.
Listing 1-9 Managing State
UIGraphicsBeginImageContext(size); CGContextRef context = UIGraphicsGetCurrentContext(); // Set initial stroke/fill colors [greenColor setFill]; [purpleColor setStroke]; // Draw the bunny [bunnyPath fill]; [bunnyPath stroke]; // Save the state CGContextSaveGState(context); // Change the fill/stroke colors [[UIColor orangeColor] setFill]; [[UIColor blueColor] setStroke]; // Move then draw again [bunnyPath applyTransform: CGAffineTransformMakeTranslation(50, 0)]; [bunnyPath fill]; [bunnyPath stroke]; // Restore the previous state CGContextRestoreGState(context); // Move then draw again [bunnyPath applyTransform: CGAffineTransformMakeTranslation(50, 0)]; [bunnyPath fill]; [bunnyPath stroke]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();
Figure 1-10 The color changes used to draw the second bunny are discarded upon restoring the graphic state to its previous settings.
State Types
A context saves many kinds of state, not just fill and stroke settings. Each of these states expresses a persistent aspect of the current context. Table 1-1 lists customizable state attributes that you can adjust using Core Graphics context calls and provides visual examples that roughly demonstrate the kinds of changes you can make to these settings.
Table 1-1 Context States
Technology |
Explanation |
Color—Color states consist of the fill and stroke settings that specify how items are drawn to the context. |
|
Transformation matrices—These apply geometric transformations to contexts, allowing you to rotate, scale, and translate the canvas you’re drawing to, in order to create sophisticated geometric results. |
|
Clipping—When you clip a context, you create a shape that automatically excludes content. This enables you to build content limited to circles, rectangles, or any other shape you can imagine. |
|
Line parameters—Line states describe how Quartz draws your lines. These states include width (the thickness of the line), dash patterns (the pattern used to draw the line), miter limits (how pointy angles are), join styles (how corners are expressed; styles include miter, round, or bevel), and caps (the ends of lines, drawn as butt, round, and square). |
|
Flatness—This is a factor that determines how accurate each curved path segment can be, specifying the maximum permissible distance between a point on the mathematical curve and the rendered point. The default is 0.6. Larger values produce more jagged curves but they are rendered faster as they require fewer computations. |
|
Antialiasing—This determines whether Quartz mathematically smoothes jagged lines on curves and diagonals by averaging values between pixels. Antialiasing renders more slowly than normal drawing, but its results are visually superior. Quartz defaults to using antialiasing. |
|
Alpha levels—These control the transparency of the material drawn to the context. As the alpha level decreases from 1 (fully opaque) to 0 (fully invisible), drawn material becomes more and more transparent. |
|
Text traits—Text states include font, font size, character spacing, and text drawing modes. Modes specify how the text is drawn (by stroking, filling, etc.). Other details control font smoothing and subpixel positioning. |
|
Blend modes—Blend modes use color and alpha levels to determine how to blend each new layer of color into the material already in the destination. Quartz supplies numerous blend modes. Appendix A explores these modes in exhaustive detail. |