< Previous PageNext Page > Hide TOC

CGLayer Drawing

CGLayer objects (CGLayerRef data type), available in Mac OS X v. 10.4 and later, allow your application to use layers for drawing. CGLayer objects are suited for the following:


Figure 12-1  Repeatedly painting the same butterfly image

Repeatedly painting the same butterfly image

CGLayer objects and transparency layers are parallel to CGPath objects and paths created by CGContext functions. In the case of a CGLayer and a CGPath object, you paint to an abstract destination and can then later draw the complete painting to another destination, such as a display or a PDF. When you paint to a transparency layer or use the CGContext functions that draw paths, you draw directly to the destination represented by a graphics context. There is no intermediate, abstract destination for assembling the painting.

This chapter:

In this section:

How CGLayer Drawing Works
Drawing With a CGLayer
Example: Using Multiple CGLayer objects to Draw a Flag


How CGLayer Drawing Works

A CGLayer, represented by the CGLayerRef data type, is engineered for optimal performance. When possible, Quartz caches a CGLayer object to the video card, which makes drawing the content that’s in a layer much faster than rendering a similar image that’s constructed from a bitmap graphics context. For this reason a CGLayer is typically a better choice for offscreen drawing than a bitmap graphics context is.

All Quartz drawing functions draw to a graphics context. The graphics context provides an abstraction of the destination, freeing you from the details of the destination, such as its resolution. You work in user space, and Quartz performs the necessary transformations to render the drawing correctly to the destination. When you use a CGLayer object for drawing, you also draw to a graphics context. Figure 12-1 illustrates the necessary steps for CGLayer drawing.


Figure 12-2  CGLayer drawing

CGLayer drawing

All CGLayer drawing starts with a graphics context from which you create a CGLayer object using the function CGLayerCreateWithContext. The graphics context used to create a CGLayer object is typically a window graphics context. Quartz creates a CGLayer so that it has all the characteristics of the graphics context—its resolution, colorspace, and graphics state settings. You can provide a size for the layer if you don’t want to use the size of the graphics context. Figure 12-2 shows the graphics context used to create the layer on the left. The gray portion of the box on the right side, labeled CGLayer object, represents the newly created layer.

Before you can draw to the CGLayer, you must obtain the graphics context that’s associated with the CGLayer by calling the function CGLayerGetContext. This graphics context is the same “flavor” as the graphics context used to create the layer. As long as the graphics context used to create the layer is a window graphics context, then the CGLayer graphics context is cached to the GPU if at all possible. The white portion of the box on the right side of Figure 12-2, represents the newly layer graphics context.

You draw to the CGLayer graphics context just as you would draw to any graphics context, passing the layer’s graphic context to the drawing function. Figure 12-2 shows a leaf shape drawn to the layer context.

When you are ready to use the contents of the CGLayer, you can call the functions CGContextDrawLayerInRect or CGContextDrawLayerAtPoint, providing the destination graphics context. Typically you would draw to the same graphics context that you used to create the layer object, but you are not required to. You can draw the layer to any graphics context, keeping in mind that the CGLayer drawing has the characteristics of the graphics context used to create the layer object, which could impose certain constraints (resolution, for example). Figure 12-2 shows the contents of the layer—the leaf—drawn repeatedly to the graphics context used to create the layer object. You can reuse the drawing that’s in a layer as many times as you’d like before releasing the CGLayer object.

Tip:  Use transparency layers when you want to composite parts of a drawing to achieve such effects as shadowing a group of objects. (See “Transparency Layers.”) Use CGLayer objects when you want to draw offscreen or when you need to repeatedly draw the same thing.

Drawing With a CGLayer

You need to perform the following tasks to draw using a CGLayer:

  1. “Create a CGLayer Initialized With an Existing Graphics Context”

  2. “Get a Graphics Context for the CGLayer”

  3. “Draw to the CGLayer Graphics Context”

  4. “Draw the CGLayer to the Destination Graphics Context”

The sections that follow describe each task. See “Example: Using Multiple CGLayer objects to Draw a Flag” for a detailed code example.

Create a CGLayer Initialized With an Existing Graphics Context

The function CGLayerCreateWithContext returns a CGLayer that is initialized with an existing graphics context. The layer inherits all the characteristics of the graphics context, including the color space, size, resolution, and pixel format. Later, when you draw the CGLayer to a destination, Quartz automatically color matches the CGLayer to the destination context.

The function CGLayerCreateWithContext takes three parameters:

Get a Graphics Context for the CGLayer

Quartz always draws to a graphics context. Now that you have a CGLayer, you must create a graphics context associated with the layer. Anything you draw into the layer graphics context is part of the CGLayer.

The function CGLayerGetContext takes a CGLayer as a parameter and returns a graphics context associated with the layer.

Draw to the CGLayer Graphics Context

After you obtain the graphics context associated with a CGLayer, you can perform any drawing you’d like to the layer graphics context. You can open a PDF file or an image file and draw the file contents to the layer. You can use any of the Quartz 2D functions to draw rectangles, lines, and other drawing primitives. Figure 12-3 shows an example of drawing rectangles and lines to a layer.

For example, to draw a filled rectangle to a CGLayer graphics context, you call the function CGContextFillRect, supplying the graphics context you obtained from the function CGLayerGetContext. If the graphics context is named myLayerContext, the function call looks like this:

CGContextFillRect (myLayerContext, myRect)


Figure 12-3  A layer that contains two rectangles and a series of lines

A layer that contains two rectangles and a series of lines

Draw the CGLayer to the Destination Graphics Context

When you are ready to draw the layer to its destination graphics context you can use either of the following functions:

Typically the destination graphics context you supply is a window graphics context and it is the same graphics context you use to create the CGLayer. Figure 12-4 shows the result of repeatedly drawing the CGLayer drawing shown in Figure 12-3. To achieve the patterned effect, you call either of the layer drawing functions repeatedly—CGContextDrawLayerAtPoint or CGContextDrawLayerInRect—changing the offset each time. For example you can call the function CGContextTranslateCTM to change the origin of the coordinate space each time you draw the CGLayer.


Figure 12-4  Drawing a layer repeatedly

Drawing a layer repeatedly

Note:  You are not required to draw a CGLayer to the same graphics context that you use to initialize the CGLayer. However, if you draw the CGLayer to another graphics context, any limitations of the original graphics context are imposed on your drawing.

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.


Figure 12-5  The result of using layers to draw the United States flag

The result of using layers to draw the United States flag

From the perspective of drawing it onscreen, the flag has three parts:

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:

  1. Declares a variable for the horizontal location of the first star.

  2. Declares a variable for the vertical location of the first star.

  3. Declares a variable for the spacing between the red stripes on the flag.

  4. Declares a variable for the horizontal spacing between the stars on the flag.

  5. Declares a variable for the vertical spacing between the stars on the flag.

  6. Declares rectangles that specify where to draw the flag to (bounding box), the stripe layer, and the star field.

  7. Declares an array of points that specify the lines that trace out one star.

  8. Creates a rectangle that is the shape of a single stripe.

  9. Creates a rectangle that is the shape of the star field.

  10. Creates a bounding box that is the same size as the window graphics context passed to the myDrawFlag routine.

  11. Creates a layer that is initialized with the window graphics context passed to the myDrawFlag routine.

  12. Gets the graphics context associated with that layer. You’ll use this layer for the stripe drawing.

  13. Sets the fill color to opaque red for the graphics context associated with the stripe layer.

  14. Fills a rectangle that represents one red stripe.

  15. Creates another layer that is initialized with the window graphics context passed to the myDrawFlag routine.

  16. Gets the graphics context associated with that layer. You’ll use this layer for the star drawing.

  17. Sets the fill color to opaque white for the graphics context associated with the star layer.

  18. Adds the 10 lines defined by the myStarPoints array to the context associated with the star layer.

  19. Fills the path, which consists of the 10 lines you just added.

  20. 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.

  21. Sets up a loop that iterates 7 times, once for each red stripe on the flag.

  22. Draws the stripe layer (which consists of a single red stripe).

  23. Translates the current transformation matrix so that the origin is positioned at the location where the next red stripe must be drawn.

  24. Restores the graphics state to what is was prior to drawing the stripes.

  25. 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.

  26. 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.

  27. Saves the graphics state for the window graphics context because you’ll be transforming the CTM to position the stars properly.

  28. Translates the CTM so that the origin lies in the star field, positioned for the first star (left side) in the first (bottom) row.

  29. 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.

  30. Draws the star layer to the window graphics context. Recall that the star layer contains one white star.

  31. Positions the CTM so that the origin is moved to the right in preparation for drawing the next star.

  32. Positions the CTM so that the origin is moved upward in preparation for drawing the next row of stars.

  33. 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.

  34. 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.

  35. Draws the star layer to the window graphics context.

  36. Positions the CTM so that the origin is moved to the right in preparation for drawing the next star.

  37. Releases the stripe layer.

  38. Releases the star layer.



< Previous PageNext Page > Hide TOC


© 2001, 2007 Apple Inc. All Rights Reserved. (Last updated: 2007-12-11)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.