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.
Note:
Before
you call this or any routine that uses CGLayer objects, you must check
to make sure that the system is running Mac OS X v10.4 or later and has
a graphics card that supports using CGLayer objects.
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.