Drawing the Flag
Now that we've got the coordinate system arranged to our liking, we can turn to actually drawing the flag. Unlike QuickDraw, where most drawing commands simply replace the pixels in a buffer with new values, Quartz 2D uses a painting algorithm. In order to draw on the context, an application identifies an area of the context that it wants to color, then has Quartz 2D apply virtual paint to that area by either filling it or stroking it.
When working with paint that is opaque, the drawing model is not very different from QuickDraw's drawing model; each painting operation will replace whatever had been drawn to the context beneath it. Unlike QuickDraw, however, Quartz 2D maintains transparency information for its paint alongside the color information. The transparency is carried in an additional color component called the alpha channel.
This formal support makes it easier to achieve transparency effects in Quartz 2D than through QuickDraw. In Quartz 2D, you can actually paint with translucent paint. For this drawing sample, we'll leave transparency out of the equation. All our paints will be opaque. When we specify colors for our paint, however, we'll have to specify a value for the alpha channel as well. The alpha channel value that represents opaque paint is 1.0.
The first thing we will draw is the three rectangular stripes that make up the background. For each stripe, we will select a paint color, outline a region of the context, and then tell Quartz 2D to fill the region we have selected. Just as in QuickDraw, Quartz 2D will allow us to specify colors using the red, green, and blue components of each color. Unlike QuickDraw, however, RGB colors are not our only option. In Quartz 2D you can specify colors in different ways depending on the color space that the colors come from.
Color spaces play a vital role in color management, the science of making sure the color you want your users to see is the color they will see. Some color spaces use red, green, and blue components to identify a color. As an example of an alternative color space, printing code might choose to use the subtractive primitive colors cyan, magenta, yellow, and black to specify colors in a CMYK color space. There are also grayscale color spaces, indexed color spaces, and a number of others as well.
Quartz 2D incorporates a lot of the color management capabilities of ColorSync directly into its drawing model. This offers midlevel and professional high-end graphics applications plenty of flexibility where they need complicated color handling. Other applications, like ours, will satisfy themselves with allowing Quartz 2D to handle common color management tasks behind the scenes. Our sample code specifies its colors in a color space known as Device RGB. Basically this means that we will specify colors using three RGB numbers (plus a fourth for the alpha channel). Because it is a "device" space, Quartz 2D will pass the component values through to the destination device and allow it to interpret the component values as a color.
With the colors in hand, we need to outline our rectangles and tell the system to fill them. You may recall that to specify an area for Quartz 2D to draw, an application constructs a path. A path is a series of line segments or curves that define an area to be filled or a track to be stroked. You can also use a path to define a clipping area for subsequent drawing commands. Our stripes are simple rectangles that we want to fill with solid colors. We could go through the trouble of constructing paths for each rectangle using line segments. Because rectangles are a pretty basic shape, however, Quartz 2D provides a convenience routine that make filling rectangles simple. Here is the drawing code that sends the blue stripe of the flag to the drawing context:
// add the blue vertical stripe CGRect blueRect = flagBounds; blueRect.size.width = (flagBounds.size.width / 3.0); // According to the Texas State Flag Code, the // blue stripe should be Pantone Matching System // color number 281 (dark blue). One web site // translated that to RGB (0, 40, 104). CGContextSetRGBFillColor(inContext, 0, 40.0 / 255.0, 104.0 / 255.0, 1.0); CGContextFillRect(inContext, blueRect);
Listing 2. Drawing the blue stripe
QuickDraw maintains separate colors for the background and foreground. Quartz 2D, on the other hand, doesn't have the concept of background and foreground colors. If you want to draw a background, you simply draw it first and then layer the foreground items on top of it. Quartz 2D does follow the PDF convention of maintaining separate fill and stroke colors as part of its current drawing state.
CGContextSetRGBFillColor is a convenient routine for changing the current fill color using color components in the Device RGB color space.
Note we have to supply a fourth component, 1.0 for the alpha channel, to make our paint opaque. Notice as well that Quartz 2D color components are specified using floating-point numbers in the range 0 to 1.0. We use an (inefficient) division to convert the 0 to 255 color coordinates from a web site to the appropriate range.
In addition to drawing state, the CGContext also maintains a current path. Normally to fill an area of the context, we would use Quartz 2D commands to outline some part of the coordinate system with the current path and then call
CGContextFillPath. Since we're only drawing a rectangle, we can accomplish both tasks with
CGContextFillRect. If you look through the routines available in
CGContext.h, you will find a number of these convenience APIs for handling simple shapes like line segments and rectangles. For example,
CGContextFillRects is useful for filling a large number of rectangles at once, and the routines
CGContextStrokeRectWithWidth both outline rectangles.
Two drawing primitives that QuickDraw programmers will miss are primitives for drawing ovals and rounded rectangles. It isn't difficult for an application to create paths for those shapes, but they aren't quite as easy to produce in Quartz 2D as they are in QuickDraw.
One point may be worth mentioning for the sake of anyone who might be translating PostScript code, or PostScript PICT comments, into Quartz 2D drawing calls. The stroke and fill operations in both PostScript and PDF will consume the current path. PostScript considers the current path to be part of the graphics state. When you want to apply both a stroke and fill to a particular path in PostScript, a common idiom is to construct the path, save the graphics state, issue a fill command that consumes the current path, restore the graphics state and the path along with it, and then stroke the path. Quartz 2D follows the PDF convention that says the current path is not saved and restored as part of the graphics state. In compensation, however, the Quartz 2D graphics state provides separate fill and stroke colors and complements them with a routine that can both fill and stroke the current path in one operation.
The code draws the red and white stripes in pretty much the same way the blue stripe is drawn. Only the rectangles change, so there is little point in examining that code here. This leaves the famous lone star to contend with. For such a simple design element, we'll encounter quite a bit of Quartz 2D knowledge in drawing the star.