Quartz 2D Graphics for Mac OS X Developers

The CGContext is responsible for keeping track of a number of variables that affect the way the computer draws graphics. Collectively these variables are known as the context's graphics state. The drawing contexts of most graphics libraries maintain some kind of state information: the current pen color, pixel pattern blending modes, or even the bounds of the drawing surface are all examples of graphics state used in pixel drawing libraries. The information in the CGContext state is very similar.

The CGContext, however, does not store the fields of its graphics state in the same way that other systems store theirs. Most graphics libraries maintain a single set of state variables. If you want to change one of those variables temporarily, you have to store the old value, change the field, draw your graphics, and restore the old value from your saved copy. As you draw your graphics, if you have to change many different state variables, this process can become tedious and error prone.

If you look through the CGContext API, there are not many methods that retrieve the values of fields in the graphics state. Instead of relying on accessor routines, Quartz uses a stack to save and restore the values of most of the fields in a context's graphics state.

The Graphics State Stack

The CGContext maintains a stack. Each frame of the stack contains some of the fields in the graphics state of the context. When an application draws into the context, Quartz 2D retrieves its drawing parameters from the topmost frame. When the application changes the value of one of the parameters, the library only changes that value in the frame at the top of the stack.

The Quartz 2D API includes two routines that manipulate the graphics state stack. The routine CGContextSaveGState asks the computer to create a new stack frame and push it onto the stack. When Quartz creates the stack frame, it duplicates the frame at the top of the stack. The new frame contains the exact same values as the previous one. This new frame is pushed onto the top of the stack.

In the course of drawing, the application is free to modify the variables in the new stack frame to suit its purposes. Only the fields in the topmost stack frame will change while the code issues drawing commands. To retrieve the old values for fields in the graphics state, the application calls CGContextRestoreGState. This routine pops the topmost frame off the stack and discards it. The net effect is that the graphics state that was in force when CGContextSaveGState was called becomes the current graphics state again.

Consider the following snippet of pseudocode which demonstrates this technique:

// Set the stroke color to red CGContextSetRGBStrokeColor(cgContext, 1, 0, 0, 1); // Save the current graphics state CGContextSaveGState(cgContext); // change the current stroke color to blue CGContextSetRGBStrokeColor(cgContext, 0, 1, 0, 1); // ... drawing here would use the blue stroke color // restore the old graphics state settings CGContextRestoreGState(cgContext); // ... drawing here would use the newly restored red stroke color

This code changes the current stroke color to a device RGB red then calls CGContextSaveGState. After doing so, both of the frames at the top of the stack indicate that the current stroke color should be red. Next, the code changes the current stroke color to blue. Now the topmost stack frame indicates that Quartz 2D should draw with blue while the second frame still calls for strokes to be drawn in red. Any drawing that happens at this point would use the blue color at the top of the stack for stroked paths. Finally, the code calls CGContextRestoreGState. This pops the stack frame with the blue color off the stack and discards it. This returns the frame with the red stroke color to the top of the stack. Because drawing commands use the parameters in the topmost stack frame, any strokes drawn after restoring the GState will draw in red.

There are a number of variables that make up the graphics state stack. Subsequent chapters will explore the details of how various fields in the graphics state affect drawing. The remainder of this chapter, provides an overview of the fields of that stack, as well as fields of the context that are not stored as part of the graphics state stack.

The Current Transformation Matrix (CTM)

As discussed in the previous chapter, your application draws its graphics in user space, and Quartz 2D maps those graphics onto the drawing device. The CGContext stores the information necessary for Quartz to properly perform this mapping as part of the graphics state that Quartz stores on the stack.

The computer converts points from user space to device space with a mathematical construct called an affine transformation. We discuss the details of affine transformations in Chapter 5. In understanding the role of that transformation in the graphics state, all you really need to know is that Quartz represents affine transformations using a collection of six floating point numbers in a matrix.

The matrix that stores the current mapping from user space onto the destination device is called, appropriately enough, the Current Transformation Matrix or CTM. In truth, the current matrix is the matrix in the graphics state at the top of the graphics state stack.

The application can change the CTM by calling methods on the CGCon-text. The three most common routines are CGContextTranslateCTM, CGContextScaleCTM, and CGContextRotateCTM. As discussed in Chapter 5, all the affine transformations Quartz 2D supports can be built from these three primitives. The CGContext also has methods for concatenating a new transformation onto the CTM or even replacing the CTM wholesale with a self-made matrix.

Clipping Information

The current clipping settings are also part of the graphics state. The CGContext has methods to change the current clipping area. Your application can change this area by constructing a path or by providing an image mask. If you specify a path, then Quartz will limit drawing to the interior of the path. If you specify an image mask then the computer will map that image onto the destination device and use the pixels of the mask as a screen for subsequent drawing.

In either case, Quartz treats your application's new clip settings a bit differently than it does the other fields of the graphics state. Each new clipping area, be it a path or an image, does not replace the clipping area in the graphics state. Instead, Quartz 2D intersects the new area with the clipping area already established in the context. The new clipping area is set to those areas where the new clip and the old clip intersect.

To put it more simply, if you only use the methods of the context that specify the new area, your application can only shrink the clipping area. The only way to get back to the larger area is to take advantage of the fact that the clipping area is part of the graphics state. If an application wants to temporarily clip out some drawing, it must save the current clipping state by saving the GState on the graphics stack. After changing the clip and drawing, recovering the old clipping area is as simple as popping the graphics state stack back to its original position.

Text Settings

The CGContext maintains a number of variables in its graphics state related to text drawing. The text state includes the usual suspects: the current font, text size, and the current text position. An interesting field seen in the sample code of the previous chapter was the text drawing mode that controls whether the text your application draws is filled, stroked, used to change the clipping area, or even not drawn at all.

Another interesting text-related field is the Text Matrix. The text matrix is an additional transformation that Quartz applies only to text. The font defines each glyph in its own space. The text matrix is the transformation that Quartz uses to map the glyph from its definition space to user space. The current text position, which played a role in the previous chapters sample code, is part of the text matrix.

The graphics state contains a field that controls whether or not Quartz should apply font smoothing to text you draw in your context. See the topic "Antialiasing and Font Smoothing" later in the chapter for more details.

Mac OS X also allows the user to set font smoothing settings through the Appearance system control panel. If the user has chosen to turn off font smoothing in that control panel, Quartz ignores the smoothing settings of your CGContext and will not apply font smoothing at all.

Line Art Settings

There are a large number of settings maintained by the graphics state that come into play when drawing line art. The following table offers a list of settings and a short description of each. More information on the individual settings can be found in the chapter on drawing line art.

Table 4.1. Line Art Graphics State Fields

Category

Graphics State Field

Description

Drawing Color

Stroke Color

The color the computer uses when drawing a stroke along a path.

 

Fill Color

The color the computer uses when filling a path.

 

Stroke Pattern

This field sets the pattern to use when stroking along a path.

 

Fill Pattern

As with the stroke pattern, Quartz allows you to set the current fill field to a pattern as opposed to a color.

 

Pattern Phase

A pattern is a repeating series of pictures. The phase describes the extent to which that pattern is shifted relative to the origin.

Stroke Attributes

Line Width

This is the width of the line used when stroking a path.

 

Line Dash

The line dash is a dotted line pattern used with a stroked path.

 

Line Cap

Quartz can add optional square or round caps to the ends of stroked paths.

 

Miter Limit

When two segments of a stroked path meet, the computer can create a miter join between the two segments. This parameter controls the maximum length that the miter will extend from the center of the stroke. (For more information on miter joins, see Chapter 7, "Line ArtDrawing.").

General Drawing

Flatness

When drawing line art, flatness controls how accurately the shapes drawn will match the underlying curve. For more information on flatness, see Chapter 7.

Image Settings

The only field in the graphics state that has an impact on drawing images is the interpolation quality setting. When Quartz is drawing an image into some graphics contexts, it must sometimes synthesize pixels on the destination by interpolating color values from the original image. This is often the case when drawing the image with a scale or rotation transformation applied to the context.

There are a number of different algorithms that the computer can use to select colors for the interpolated pixels. In general these algorithms make trade-offs between the quality of the resulting image versus the amount of time spent calculating the interpolated pixels. The interpolation quality setting allows your computer a measure of control over that trade-off and gives the context some guidelines to use when selecting an algorithm.

Not every context supports image interpolation. Moreover, since the interpolation quality is a suggestion, the context is free to ignore any setting your application gives this parameter. The actual choice of an interpolation algorithm is left to the context itself, and your application has no direct control over that choice through the generic CGContext API.

General Settings

Also found within the graphics state are a number of drawing parameters that affect all of the objects drawn into a context. There is not a lot to tie these options together other than the fact that they affect every object that is drawn on the context. The following table summarizes these fields.

Table 4.2. General Graphics State Settings

Graphics State Field

Description

Alpha

This alpha combines with the alpha values of the current drawing color and sets the overall alpha of any objects drawn into the context.

Color Rendering Intent

The Rendering Intent is a color management parameter that affects the way colors are mapped from their source color space into the color space of the context.

Shadow settings

Quartz can add a drop shadow to each object as you draw it into a context.

Blending Mode

Quartz supports the blending modes defined by the PDF standard and will apply the blending mode to each object.

Antialiasing

Your application can instruct Quartz as to whether or not it should antialias graphics that are drawn in the context. As with the image interpolation settings, not all contexts may support antialiasing. For those that do, however, your application may enjoy a slight performance improvement when drawing with antialiasing turned off. Antialiasing can also be disabled using a setting that is not part of the graphics state and that overrides this field. See "Antialiasing and Font Smoothing" later in this chapter.

While there are relatively few drawing primitives, it is the fields of the graphics state that really embody the flexibility of the Quartz library. Drawing with Quartz 2D largely comes down to the tasks of selecting the proper settings for the fields of the graphics state and then specifying the primitives that you want to draw. The technique of manipulating the graphics state stack to save and restore values can be an invaluable tool and greatly simplifies management of the fields in the graphics state. While the graphics state stack is an important aspect of the CGContext, the context does contain other information that is not part of the graphics state that is stored on the stack. Having covered the stack's fields, it is time to refocus attention to other parts of the graphics context.

The Current Path

In discussing the drawing model in the previous chapter, the concept of the path was briefly introduced. In essence, a path is a resolution independent tool for describing an area of user space or a trajectory you want to stroke. Each context maintains one special path, known as the current path. The CGContext API contains routines to construct the current path. After building a path, applications can fill or stroke the path to create line art. The drawing methods consume the current path, perform the requested operation, and leave the context ready to accept a new path.

In addition to asking Quartz to draw a path, you also can ask it to combine that path with the current clipping area of the context.

It is important to realize that in Quartz 2D the current path is not part of the graphics state. This is just the opposite of the way PostScript handles its current path. In PostScript the current path is part of the graphics state.

In Quartz 2D, as in PDF files, if the code constructs a path in the context, saves the graphics state, fills the path, and then restores the graphics state, the path will not restore along with the graphics state fields. When translating PostScript code to Quartz 2D, you may have to modify the logic of that code to account for this behavior.

Color Space Settings

The CGContext plays a role in the Color Management that is integral to the Quartz 2D imaging model. Some graphics devices, like display screens and printers, have color spaces of their own. Other devices, like offscreen bitmaps rely on the application that creates them to specify a color space for them. Regardless of the source of the color space information, when Quartz creates a context it associates a destination color space with it. The color matching code works to ensure that the colors in a drawing are reproduced as faithfully as possible in that color space.

When you specify a color that your application would like to use as a stroke or fill color, you can identify the color space that the color comes from as well. Quartz will perform whatever calculations are necessary to map the color from its source color space to the color space of the graphics context on which you are drawing.

When an application is working with a context that is attached to a device, say a printer or a display, the color space of that context is taken from the device itself. For offscreen bitmap contexts, the application specifies a color space for the context. Programs also can retrieve the color space of a bitmap context using the CGBitmapContext opaque data type API.

Категории