CGContextRef
) that encapsulates the information Quartz uses to draw images to an output device, such as a PDF file, a bitmap, or a window on a display. The information inside a graphics context includes graphics drawing parameters and a device-specific representation of the paint on the page. All objects in Quartz are drawn to, or contained by, a graphics context.
These graphics contexts are available to your application:
-
A bitmap graphics context allows you to paint RGB colors, CMYK colors, or grayscale into a bitmap. A bitmap is a rectangular array (or raster) of pixels, each pixel representing a point in an image. Bitmap images are also calledsampled images.
-
A PDF graphics context allows you to create a PDF file. I
-
A window graphics context is a graphics context that you can use to draw into a window. Note that because Quartz 2D is a graphics engine and not a window management system, you use one of the application frameworks to obtain a graphics context for a window.
- A layer context (
CGLayerRef
) is an offscreen drawing destination associated with another graphics context. It is designed for optimal performance when drawing the layer to the graphics context that created it. A layer context can be a much better choice for offscreen drawing than a bitmap graphics context. - When you want to print in Mac OS X, you send your content to a PostScript graphics context that is managed by the printing framework.
Quartz modifies the results of drawing operations according to the parameters in the current graphics state. The graphics state contains parameters that would otherwise be taken as arguments to drawing routines. Routines that draw to a graphics context consult the graphics state to determine how to render their results. For example, when you call a function to set the fill color, you are modifying a value stored in the current graphics state. Other commonly used elements of the current graphics state include the line width, the current position, and the text font size.
The graphics context contains a stack of graphics states. When Quartz creates a graphics context, the stack is empty. When you save the graphics state, Quartz pushes a copy of the current graphics state onto the stack. When you restore the graphics state, Quartz pops the graphics state off the top of the stack. The popped state becomes the current graphics state.
To save the current graphics state, use the function CGContextSaveGState
to push a copy of the current graphics state onto the stack. To restore a previously saved graphics state, use the function CGContextRestoreGState
to replace the current graphics state with the graphics state that’s on top of the stack.
Note that not all aspects of the current drawing environment are elements of the graphics state. For example, the current path is not considered part of the graphics state and is therefore not saved when you call the functionCGContextSaveGState
. The graphics state parameters that are saved when you call this function are listed below:
To draw to the screen in an iOS application, you set up a UIView
object and implement its drawRect:
method to perform drawing. The view’s drawRect:
method is called when the view is visible onscreen and its contents need updating. Before calling your custom drawRect:
method, the view object automatically configures its drawing environment so that your code can start drawing immediately. As part of this configuration, the UIView
object creates a graphics context (a CGContextRef
opaque type) for the current drawing environment. You obtain this graphics context in your drawRect:
method by calling the UIKit function UIGraphicsGetCurrentContext
.
The default coordinate system used throughout UIKit is different from the coordinate system used by Quartz. In UIKit, the origin is in the upper-left corner, with the positive-y value pointing downward. The UIView
object modifies the CTM of the Quartz graphics context to match the UIKit conventions by translating the origin to the upper left corner of the view and inverting the y-axis by multiplying it by -1
.
A bitmap graphics context accepts a pointer to a memory buffer that contains storage space for the bitmap. When you paint into the bitmap graphics context, the buffer is updated. After you release the graphics context, you have a fully updated bitmap in the pixel format you specify.
Note: Bitmap graphics contexts are sometimes used for drawing offscreen. Before you decide to use a bitmap graphics context for this purpose, see “Core Graphics Layer Drawing.” CGLayer objects (CGLayerRef
) are optimized for offscreen drawing because, whenever possible, Quartz caches layers on the video card.
iOS Note: iOS applications should use the function UIGraphicsBeginImageContextWithOptions
instead of using the low-level Quartz functions described here. If your application creates an offscreen bitmap using Quartz, the coordinate system used by bitmap graphics context is the default Quartz coordinate system. In contrast, if your application creates an image context by calling the function UIGraphicsBeginImageContextWithOptions
, UIKit applies the same transformation to the context’s coordinate system as it does to a UIView
object’s graphics context. This allows your application to use the same drawing code for either without having to worry about different coordinate systems. Although your application can manually adjust the coordinate transformation matrix to achieve the correct results, in practice, there is no performance benefit to doing so.
CGBitmapContextCreate
to create a bitmap graphics context.
Bitmap graphics contexts support anti-aliasing, which is the process of artificially correcting the jagged (or aliased) edges you sometimes see in bitmap images when text or shapes are drawn. These jagged edges occur when the resolution of the bitmap is significantly lower than the resolution of your eyes. To make objects appear smooth in the bitmap, Quartz uses different colors for the pixels that surround the outline of the shape. By blending the colors in this way, the shape appears smooth. You can turn anti-aliasing off for a particular bitmap graphics context by calling the function CGContextSetShouldAntialias
. The anti-aliasing setting is part of the graphics state.
You can control whether to allow anti-aliasing for a particular graphics context by using the functionCGContextSetAllowsAntialiasing
. Pass true
to this function to allow anti-aliasing; false
not to allow it. This setting is not part of the graphics state. Quartz performs anti-aliasing when the context and the graphic state settings are set to true
.
Points are x and y coordinates that specify a location in user space. You can call the function CGContextMoveToPoint
to specify a starting position for a new subpath. Quartz keeps track of the current point, which is the last location used for path construction. For example, if you call the function CGContextMoveToPoint
to set a location at (10,10), that moves the current point to (10,10). If you then draw a horizontal line 50 units long, the last point on the line, that is, (60,10), becomes the current point. Lines, arcs, and curves are always drawn starting from the current point.
Most of the time you specify a point by passing to Quartz functions two floating-point values to specify x and y coordinates. Some functions require that you pass a CGPoint
data structure, which holds two floating-point values.
A line is defined by its endpoints. Its starting point is always assumed to be the current point, so when you create a line, you specify only its endpoint. You use the function CGContextAddLineToPoint
to append a single line to a subpath.
You can add a series of connected lines to a path by calling the function CGContextAddLines
. You pass this function an array of points. The first point must be the starting point of the first line; the remaining points are endpoints. Quartz begins a new subpath at the first point and connects a straight line segment to each endpoint.
CGContextAddArc
creates a curved segment from a circle. You specify the center of the circle, the radius, and the radial angle (in radians). You can create a full circle by specifying a radial angle of 2 pi.
The function CGContextAddArcToPoint
is ideal to use when you want to round the corners of a rectangle. Quartz uses the endpoints you supply to create two tangent lines. You also supply the radius of the circle from which Quartz slices the arc. The center point of the arc is the intersection of two radii, each of which is perpendicular to one of the two tangent lines. Each endpoint of the arc is a tangent point on one of the tangent lines.
If the current path already contains a subpath, Quartz appends a straight line segment from the current point to the starting point of the arc. If the current path is empty, Quartz creates a new subpath at the starting point for the arc and does not add the initial straight line segment.
CGContextAddCurveToPoint
to append a cubic Bézier curve from the current point, using control points and an endpoint you specify.
You can append a quadratic Bézier curve from the current point by calling the functionCGContextAddQuadCurveToPoint
, and specifying a control point and an endpoint.
To close the current subpath, your application should call CGContextClosePath
. This function adds a line segment from the current point to the starting point of the subpath and closes the subpath. Lines, arcs, and curves that end at the starting point of a subpath do not actually close the subpath. You must explicitly call CGContextClosePath
to close a subpath.
Some Quartz functions treat a path’s subpaths as if they were closed by your application. Those commands treat each subpath as if your application had called CGContextClosePath
to close it, implicitly adding a line segment to the starting point of the subpath.
After closing a subpath, if your application makes additional calls to add lines, arcs, or curves to the path, Quartz begins a new subpath starting at the starting point of the subpath you just closed.
You can add an ellipse to the current path by calling the function CGContextAddEllipseInRect
. You supply a rectangle that defines the bounds of the ellipse. Quartz approximates the ellipse using a sequence of Bézier curves. The center of the ellipse is the center of the rectangle. If the width and height of the rectangle are equal (that is, a square), the ellipse is circular, with a radius equal to one-half the width (or height) of the rectangle. If the width and height of the rectangle are unequal, they define the major and minor axes of the ellipse.
The ellipse that is added to the path starts with a move-to operation and ends with a close-subpath operation, with all moves oriented in the clockwise direction.
You can add a rectangle to the current path by calling the function CGContextAddRect
. You supply a CGRect
structure that contains the origin of the rectangle and its width and height.
The rectangle that is added to the path starts with a move-to operation and ends with a close-subpath operation, with all moves oriented in the counter-clockwise direction.
You can add many rectangles to the current path by calling the function CGContextAddRects
and supplying an array ofCGRect
structures.
CGContextBeginPath
. Next, you set the starting point for the first shape, or subpath, in the path by calling the function CGContextMoveToPoint
. After you establish the first point, you can add lines, arcs, and curves to the path.
CGPathRef
and CGMutablePathRef
. You can call the function CGPathCreateMutable
to create a mutable CGPath object to which you can add lines, arcs, curves, and rectangles.
CGContextAddPath
. The path stays in the graphics context until Quartz paints it. You can add the path again by calling CGContextAddPath
.
Parameter | Function to set parameter value |
---|---|
Line width | |
Line join | |
Line cap | |
Miter limit | |
Line dash pattern | |
Stroke color space | |
Stroke color | |
Stroke pattern |
| Strokes the current path. |
| Strokes the specified rectangle. |
| Strokes the specified rectangle, using the specified line width. |
| Strokes an ellipse that fits inside the specified rectangle. |
| Strokes a sequence of lines. |
| If you pass the constant |
When you fill the current path, Quartz acts as if each subpath contained in the path were closed. It then uses these closed subpaths and calculates the pixels to fill. There are two ways Quartz can calculate the fill area. Simple paths such as ovals and rectangles have a well-defined area. But if your path is composed of overlapping segments or if the path includes multiple subpaths, such as the concentric circles shown in Figure 3-12, there are two rules you can use to determine the fill area.
The default fill rule is called the nonzero winding number rule. To determine whether a specific point should be painted, start at the point and draw a line beyond the bounds of the drawing. Starting with a count of 0, add 1 to the count every time a path segment crosses the line from left to right, and subtract 1 every time a path segment crosses the line from right to left. If the result is 0, the point is not painted. Otherwise, the point is painted. The direction that the path segments are drawn affects the outcome. Figure 3-12 shows two sets of inner and outer circles that are filled using the nonzero winding number rule. When each circle is drawn in the same direction, both circles are filled. When the circles are drawn in opposite directions, the inner circle is not filled.
You can opt to use the even-odd rule. To determine whether a specific point should be painted, start at the point and draw a line beyond the bounds of the drawing. Count the number of path segments that the line crosses. If the result is odd, the point is painted. If the result is even, the point is not painted. The direction that the path segments are drawn doesn’t affect the outcome. As you can see in Figure 3-12, it doesn’t matter which direction each circle is drawn, the fill will always be as shown.
Figure 3-12 Concentric circles filled using different fill rules Function | Description |
---|---|
| Fills the current path using the even-odd rule. |
| Fills the current path using the nonzero winding number rule. |
| Fills the area that fits inside the specified rectangle. |
| Fills the areas that fits inside the specified rectangles. |
| Fills an ellipse that fits inside the specified rectangle. |
| Fills the current path if you pass |
Blend modes specify how Quartz applies paint over a background. Quartz uses normal blend mode by default, which combines the foreground painting with the background painting using the following formula:
result = (alpha * foreground) + (1 - alpha) * background
“Color and Color Spaces” provides a detailed discussion of the alpha component of a color, which specifies the opacity of a color. For the examples in this section, you can assume a color is completely opaque (alpha value = 1.0). For opaque colors, when you paint using normal blend mode, anything you paint over the background completely obscures the background.
You can set the blend mode to achieve a variety of effects by calling the function CGContextSetBlendMode
, passing the appropriate blend mode constant. Keep in mind that the blend mode is part of the graphics state. If you use the function CGContextSaveGState
prior to changing the blend mode, then calling the functionCGContextRestoreGState
resets the blend mode to normal.
The current clipping area is created from a path that serves as a mask, allowing you to block out the part of the page that you don’t want to paint. For example, if you have a very large bitmap image and want to show only a small portion of it, you could set the clipping area to display only the portion you want to show.
When you paint, Quartz renders paint only within the clipping area. Drawing that occurs inside the closed subpaths of the clipping area is visible; drawing that occurs outside the closed subpaths of the clipping area is not.
When the graphics context is initially created, the clipping area includes all of the paintable area of the context (for example, the media box of a PDF context). You alter the clipping area by setting the current path and then using a clipping function instead of a drawing function. The clipping function intersects the filled area of the current path with the existing clipping area. Thus, you can intersect the clipping area, shrinking the visible area of the picture, but you cannot increase the area of the clipping area.
The clipping area is part of the graphics state. To restore the clipping area to a previous state, you can save the graphics state before you clip, and restore the graphics state after you’re done with clipped drawing.
Setting up a circular clip area
CGContextBeginPath (context); |
CGContextAddArc (context, w/2, h/2, ((w>h) ? h : w)/2, 0, 2*PI, 0); |
CGContextClosePath (context); |
CGContextClip (context); |
Function | Description |
---|---|
Uses the nonzero winding number rule to calculate the intersection of the current path with the current clipping path. | |
Uses the even-odd rule to calculate the intersection of the current path with the current clipping path. | |
Sets the clipping area to the area that intersects both the current clipping path and the specified rectangle. | |
Sets the clipping area to the area that intersects both the current clipping path and region within the specified rectangles. | |
Maps a mask into the specified rectangle and intersects it with the current clipping area of the graphics context. Any subsequent path drawing you perform to the graphics context is clipped. |
1.0
to specify that objects you draw should be fully opaque; set it to 0.0
to specify that newly drawn objects are fully transparent. An alpha value between 0.0
and 1.0
specifies a partially transparent object. You can supply an alpha value as the last color component to all routines that accept colors. You can also set the global alpha value using the CGContextSetAlpha
function. Keep in mind that if you set both, Quartz multiplies the alpha color component by the global alpha value.
CGContextTranslateCTM
to modify the x and y coordinates of each point by a specified amount.
CGContextRotateCTM
to specify the rotation angle, in radians.
CGContextScaleCTM
to specify the x and y scaling factors.
CGContextConcatCTM
to combine the CTM with an affine transform.
The affine transform functions available in Quartz operate on matrices, not on the CTM. You can use these functions to construct a matrix that you later apply to the CTM by calling the function CGContextConcatCTM
. The affine transform functions either operate on, or return, a CGAffineTransform
data structure. You can construct simple or complex affine transforms that are reusable.
The affine transform functions perform the same operations as the CTM functions—translation, rotation, scaling, and concatenation.
Function | Use |
---|---|
| To construct a new translation matrix from x and y values that specify how much to move the origin. |
| To apply a translation operation to an existing affine transform. |
| To construct a new rotation matrix from a value that specifies in radians how much to rotate the coordinate system. |
| To apply a rotation operation to an existing affine transform. |
| To construct a new scaling matrix from x and y values that specify how much to stretch or shrink coordinates. |
| To apply a scaling operation to an existing affine transform. |
Quartz also provides an affine transform function that inverts a matrix, CGAffineTransformInvert
. Inversion is generally used to provide reverse transformation of points within transformed objects. Inversion can be useful when you need to recover a value that has been transformed by a matrix: Invert the matrix, and multiply the value by the inverted matrix, and the result is the original value. You usually don’t need to invert transforms because you can reverse the effects of transforming the CTM by saving and restoring the graphics state.
In some situations you might not want to transform the entire space, but just a point or a size. You operate on aCGPoint
structure by calling the function CGPointApplyAffineTransform
. You operate on a CGSize
structure by calling the function CGSizeApplyAffineTransform
. You can operate on a CGRect
structure by calling the functionCGRectApplyAffineTransform
. This function returns the smallest rectangle that contains the transformed corner points of the rectangle passed to it. If the affine transform that operates on the rectangle performs only scaling and translation operations, the returned rectangle coincides with the rectangle constructed from the four transformed corners.
CGContextSetFillColor
, to set the fill color. Then you call the function CGContextFillRect
to paint the filled rectangle with the color you specify. To paint with a pattern, you first call the functionCGContextSetFillPattern
to set the pattern. Then you call CGContextFillRect
to actually paint the filled rectangle with the pattern you specify. The difference between painting with colors and with patterns is that you must define the pattern. You supply the pattern and color information to the function CGContextSetFillPattern
.
Here’s an example of how Quartz works behind the scenes to paint with a pattern you provide. When you fill or stroke with a pattern, Quartz conceptually performs the following tasks to draw each pattern cell:
-
Saves the graphics state.
-
Translates the current transformation matrix to the origin of the pattern cell.
-
Concatenates the CTM with the pattern matrix.
-
Clips to the bounding rectangle of the pattern cell.
-
Calls your drawing callback to draw the pattern cell.
-
Restores the graphics state.
Quartz takes care of all the tiling for you, repeatedly rendering the pattern cell to the drawing space until the entire space is painted. You can fill or stroke with a pattern. The pattern cell can be of any size you specify. If you want to see the pattern, you should make sure the pattern cell fits in the drawing space. For example, if your pattern cell is 8 units by 10 units, and you use the pattern to stroke a line that has a width of 2 units, the pattern cell will be clipped since it is 10 units wide. In this case, you might not recognize the pattern.
There are two kinds of patterns: Color Patterns and Stencil Patterns.
A shadow is an image painted underneath, and offset from, a graphics object such that the shadow mimics the effect of a light source cast on the graphics object. Text can also be shadowed. Shadows can make an image appear three dimensional or as if it’s floating.
Shadows have three characteristics:
-
An x-offset, which specifies how far in the horizontal direction the shadow is offset from the image.
-
A y-offset, which specifies how far in the vertical direction the shadow is offset from the image.
-
A blur value, which specifies whether the image has a hard edge.
Shadows in Quartz are part of the graphics state. You call the function CGContextSetShadow
, passing a graphics context, offset values, and a blur value. After shadowing is set, any object you draw has a shadow drawn with a black color that has a 1/3 alpha value in the device RGB color space. In other words, the shadow is drawn using RGBA values set to {0, 0, 0, 1.0/3.0}
.
You can draw colored shadows by calling the function CGContextSetShadowWithColor
, passing a graphics context, offset values, a blur value, and a CGColor object. The values to supply for the color depend on the color space you want to draw in.
If you save the graphics state before you call CGContextSetShadow
or CGContextSetShadowWithColor
, you can turn off shadowing by restoring the graphics state. You also disable shadows by setting the shadow color to NULL
.
Follow these steps to paint with shadows:
-
Save the graphics state.
-
Call the function
CGContextSetShadow
, passing the appropriate values. -
Perform all the drawing to which you want to apply shadows.
-
Restore the graphics state.