Example: Using Multiple CGLayer objects to Draw a Flag
This section shows how to use two CGLayer objects to draw the flag shown in Figure 12-5 onscreen. First you’ll see how to reduce the flag to simple drawing primitives, then you’ll look at the code needed to accomplish the drawing.
From the perspective of drawing it onscreen, the flag has three parts:
-
A pattern of red and white stripes. You can reduce the pattern to a single red stripe because, for onscreen drawing, you can assume a white background. You create a single red rectangle, then repeatedly draw the rectangle at various offsets to create the seven red stripes necessary for the U.S. flag. A CGLayer is ideal for repeated drawing. You draw the red rectangle to a CGLayer, then draw the CGLayer onscreen seven times.
-
A blue rectangle. You need the blue rectangle once, so using a CGLayer is of no benefit. When it comes time to draw the blue rectangle, draw it directly onscreen.
-
A pattern of 50 white stars. Like the red stripe, a CGLayer is ideal for drawing the stars. You create a path that outlines a star shape, and then fill the path with white. Draw one star to a CGLayer, then draw the layer 50 times, adjusting the offset each time to get the appropriate spacing.
The code in “Code that uses layers to draw a flag” produces the output shown in Figure 12-5 . A detailed explanation for each numbered line of code appears following the listing. The listing is rather long, so you might want to print the explanation so that you can read it as you look at the code. The myDrawFlag
routine is called from within a Cocoa or Carbon application. The application passes a window graphics context and a rectangle that specifies the size of the view associated with the window graphics context.
Listing 12-1 Code that uses layers to draw a flag
void myDrawFlag (CGContextRef context, CGRect* contextRect) |
{ |
int i, j, |
num_six_star_rows = 5, |
num_five_star_rows = 4; |
float start_x = 5.0,// 1 |
start_y = 108.0,// 2 |
red_stripe_spacing = 34.0,// 3 |
h_spacing = 26.0,// 4 |
v_spacing = 22.0;// 5 |
CGContextRef myLayerContext1, |
myLayerContext2; |
CGLayerRef stripeLayer, |
starLayer; |
CGRect myBoundingBox,// 6 |
stripeRect, |
starField; |
// ***** Setting up the primitives ***** |
const CGPoint myStarPoints[] = {{ 5, 5}, {10, 15},// 7 |
{10, 15}, {15, 5}, |
{15, 5}, {2.5, 11}, |
{2.5, 11}, {16.5, 11}, |
{16.5, 11},{5, 5}}; |
|
stripeRect = CGRectMake (0, 0, 400, 17); // stripe// 8 |
starField = CGRectMake (0, 102, 160, 119); // star field// 9 |
|
myBoundingBox = CGRectMake (0, 0, contextRect->size.width, // 10 |
contextRect->size.height); |
|
// ***** Creating layers and drawing to them ***** |
stripeLayer = CGLayerCreateWithContext (context, // 11 |
stripeRect.size, NULL); |
myLayerContext1 = CGLayerGetContext (stripeLayer);// 12 |
|
CGContextSetRGBFillColor (myLayerContext1, 1, 0 , 0, 1);// 13 |
CGContextFillRect (myLayerContext1, stripeRect);// 14 |
|
starLayer = CGLayerCreateWithContext (context, |
starField.size, NULL);// 15 |
myLayerContext2 = CGLayerGetContext (starLayer);// 16 |
CGContextSetRGBFillColor (myLayerContext2, 1.0, 1.0, 1.0, 1);// 17 |
CGContextAddLines (myLayerContext2, myStarPoints, 10);// 18 |
CGContextFillPath (myLayerContext2); // 19 |
|
// ***** Drawing to the window graphics context ***** |
CGContextSaveGState(context); // 20 |
for (i=0; i< 7; i++) // 21 |
{ |
CGContextDrawLayerAtPoint (context, CGPointZero, stripeLayer);// 22 |
CGContextTranslateCTM (context, 0.0, red_stripe_spacing);// 23 |
} |
CGContextRestoreGState(context);// 24 |
|
CGContextSetRGBFillColor (context, 0, 0, 0.329, 1.0);// 25 |
CGContextFillRect (context, starField);// 26 |
|
CGContextSaveGState (context); // 27 |
CGContextTranslateCTM (context, start_x, start_y); // 28 |
for (j=0; j< num_six_star_rows; j++) // 29 |
{ |
for (i=0; i< 6; i++) |
{ |
CGContextDrawLayerAtPoint (context,CGPointZero, |
starLayer);// 30 |
CGContextTranslateCTM (context, h_spacing, 0);// 31 |
} |
CGContextTranslateCTM (context, (-i*h_spacing), v_spacing); // 32 |
} |
CGContextRestoreGState(context); |
|
CGContextSaveGState(context); |
CGContextTranslateCTM (context, start_x + h_spacing/2, // 33 |
start_y + v_spacing/2); |
for (j=0; j< num_five_star_rows; j++) // 34 |
{ |
for (i=0; i< 5; i++) |
{ |
CGContextDrawLayerAtPoint (context, CGPointZero, |
starLayer);// 35 |
CGContextTranslateCTM (context, h_spacing, 0);// 36 |
} |
CGContextTranslateCTM (context, (-i*h_spacing), v_spacing); |
} |
CGContextRestoreGState(context); |
|
CGLayerRelease(stripeLayer);// 37 |
CGLayerRelease(starLayer); // 38 |
} |
Here’s what the code does:
-
Declares a variable for the horizontal location of the first star.
-
Declares a variable for the vertical location of the first star.
-
Declares a variable for the spacing between the red stripes on the flag.
-
Declares a variable for the horizontal spacing between the stars on the flag.
-
Declares a variable for the vertical spacing between the stars on the flag.
-
Declares rectangles that specify where to draw the flag to (bounding box), the stripe layer, and the star field.
-
Declares an array of points that specify the lines that trace out one star.
-
Creates a rectangle that is the shape of a single stripe.
-
Creates a rectangle that is the shape of the star field.
-
Creates a bounding box that is the same size as the window graphics context passed to the
myDrawFlag
routine. -
Creates a layer that is initialized with the window graphics context passed to the
myDrawFlag
routine. -
Gets the graphics context associated with that layer. You’ll use this layer for the stripe drawing.
-
Sets the fill color to opaque red for the graphics context associated with the stripe layer.
-
Fills a rectangle that represents one red stripe.
-
Creates another layer that is initialized with the window graphics context passed to the
myDrawFlag
routine. -
Gets the graphics context associated with that layer. You’ll use this layer for the star drawing.
-
Sets the fill color to opaque white for the graphics context associated with the star layer.
-
Adds the 10 lines defined by the
myStarPoints
array to the context associated with the star layer. -
Fills the path, which consists of the 10 lines you just added.
-
Saves the graphics state of the windows graphics context. You need to do this, because you’ll draw the same stripe repeatedly, but in different locations.
-
Sets up a loop that iterates 7 times, once for each red stripe on the flag.
-
Draws the stripe layer (which consists of a single red stripe).
-
Translates the current transformation matrix so that the origin is positioned at the location where the next red stripe must be drawn.
-
Restores the graphics state to what is was prior to drawing the stripes.
-
Sets the fill color to the appropriate shade of blue for the star field. Note that this color has an opacity of 1.0. Although all the colors in this example are opaque, they don’t need to be. You can create nice effects with layered drawing by using partially transparent colors. Recall that an alpha value of 0.0 specifies a transparent color.
-
Fills the star field rectangle with blue. You draw this rectangle directly to the window graphics context. Don’t use layers if you are drawing something only once.
-
Saves the graphics state for the window graphics context because you’ll be transforming the CTM to position the stars properly.
-
Translates the CTM so that the origin lies in the star field, positioned for the first star (left side) in the first (bottom) row.
-
This and the next for loop sets up the code to repeatedly draw the star layer so the five odd rows on the flag each contain six stars.
-
Draws the star layer to the window graphics context. Recall that the star layer contains one white star.
-
Positions the CTM so that the origin is moved to the right in preparation for drawing the next star.
-
Positions the CTM so that the origin is moved upward in preparation for drawing the next row of stars.
-
Translates the CTM so that the origin lies in the star field, positioned for the first star (left side) in the second row from the bottom. Note that the even rows are offset with respect to the odd rows.
-
This and the next for loop sets up the code to repeatedly draw the star layer so the four even rows on the flag each contain five stars.
-
Draws the star layer to the window graphics context.
-
Positions the CTM so that the origin is moved to the right in preparation for drawing the next star.
-
Releases the stripe layer.
-
Releases the star layer.