Drawing With Quartz 2 D
Drawing With Quartz 2 D
Guide
Contents
Introduction 14
Who Should Read This Document? 14
Organization of This Document 14
See Also 15
Overview of Quartz 2D 17
The Page 17
Drawing Destinations: The Graphics Context 18
Quartz 2D Opaque Data Types 20
Graphics States 22
Quartz 2D Coordinate Systems 23
Memory Management: Object Ownership 26
Graphics Contexts 27
Drawing to a View Graphics Context in iOS 27
Creating a Window Graphics Context in Mac OS X 28
Creating a PDF Graphics Context 30
Creating a Bitmap Graphics Context 35
Supported Pixel Formats 39
Anti-Aliasing 40
Obtaining a Graphics Context for Printing 41
Paths 42
Path Creation and Path Painting 42
The Building Blocks 44
Points 44
Lines 44
Arcs 45
Curves 46
Closing a Subpath 48
Ellipses 49
Rectangles 49
Creating a Path 50
Painting a Path 51
Contents
Transforms 76
About Quartz Transformation Functions 77
Modifying the Current Transformation Matrix 77
Creating Affine Transforms 83
Evaluating Affine Transforms 85
Getting the User to Device Space Transform 85
The Math Behind the Matrices 85
Patterns 89
The Anatomy of a Pattern 89
Colored Patterns and Stencil (Uncolored) Patterns 93
Tiling 94
How Patterns Work 94
Painting Colored Patterns 95
Write a Callback Function That Draws a Colored Pattern Cell 95
Set Up the Colored Pattern Color Space 97
Set Up the Anatomy of the Colored Pattern 97
Specify the Colored Pattern as a Fill or Stroke Pattern 99
Draw With the Colored Pattern 99
A Complete Colored Pattern Painting Function 99
Painting Stencil Patterns 101
Write a Callback Function That Draws a Stencil Pattern Cell 102
Set Up the Stencil Pattern Color Space 103
Contents
Shadows 107
How Shadows Work 108
Shadow Drawing Conventions Vary Based on the Context 108
Painting with Shadows 108
Gradients 112
Axial and Radial Gradient Examples 112
A Comparison of CGShading and CGGradient Objects 115
Extending Color Beyond the End of a Gradient 116
Using a CGGradient Object 118
Using a CGShading Object 121
Painting an Axial Gradient Using a CGShading Object 123
Painting a Radial Gradient Using a CGShading Object 130
See Also 135
Contents
Contents
Text 212
Glossary 213
Document Revision History 216
Swift 13
Overview of Quartz 2D 17
Figure 1-1
Figure 1-2
Figure 1-3
Figure 1-4
Figure 1-5
Table 1-1
Graphics Contexts 27
Figure 2-1
Figure 2-2
Figure 2-3
Figure 2-4
Table 2-1
Listing 2-1
Listing 2-2
Listing 2-3
Listing 2-4
Listing 2-5
Listing 2-6
Paths 42
Figure 3-1
Figure 3-2
Figure 3-3
Figure 3-4
Figure 3-5
Figure 3-6
Figure 3-7
Figure 3-8
Figure 3-9
Figure 3-10
Figure 3-11
Figure 3-12
Figure 3-13
Figure 3-14
Figure 3-15
Figure 3-16
Figure 3-17
Figure 3-18
Figure 3-19
Figure 3-20
Figure 3-21
Figure 3-22
Figure 3-23
Figure 3-24
Figure 3-25
Figure 3-26
Figure 3-27
Figure 3-28
Figure 3-29
Figure 3-30
Table 3-1
Table 3-2
Table 3-3
Table 3-4
Table 3-5
Table 3-6
Listing 3-1
Transforms 76
Figure 5-1
Figure 5-2
Figure 5-3
Figure 5-4
Figure 5-5
Figure 5-6
Figure 5-7
Figure 5-8
Table 5-1
A scaled image 80
An image that is translated and rotated 81
An image that is translated, scaled, and then rotated 82
An image that is rotated, scaled, and then translated 83
Affine transform functions for translation, rotation, and scaling 84
Patterns 89
Figure 6-1
Figure 6-2
Figure 6-3
Figure 6-4
Figure 6-5
Figure 6-6
Figure 6-7
Figure 6-8
Figure 6-9
Figure 6-10
Listing 6-1
Listing 6-2
Listing 6-3
Listing 6-4
Listing 6-5
Listing 6-6
Listing 6-7
Listing 6-8
Shadows 107
Figure 7-1
Figure 7-2
Figure 7-3
Listing 7-1
A shadow 107
A shadow with no blur and another with a soft edge 107
A colored shadow and a gray shadow 109
A function that sets up shadows 109
Gradients 112
Figure 8-1
Figure 8-2
Figure 8-3
Figure 8-4
Figure 8-5
Figure 8-6
Figure 8-7
Figure 8-8
Figure 8-9
Figure 8-10
Figure 8-11
Figure 8-12
Table 8-1
Listing 8-1
Listing 8-2
Listing 8-3
Listing 8-4
Listing 8-5
Listing 8-6
Listing 8-7
Listing 8-8
Listing 8-9
Listing 8-10
Listing 8-11
Listing 8-12
Listing 8-13
Listing 8-14
Listing 8-15
10
Figure 11-7
Figure 11-8
Figure 11-9
Figure 11-10
Figure 11-11
Figure 11-12
Figure 11-13
Figure 11-14
Figure 11-15
Figure 11-16
Figure 11-17
Figure 11-18
Figure 11-19
Figure 11-20
Figure 11-21
Figure 11-22
Figure 11-23
Figure 11-24
Figure 11-25
Figure 11-26
Figure 11-27
Figure 11-28
Figure 11-29
Figure 11-30
Figure 11-31
Figure 11-32
Figure 11-33
Table 11-1
Listing 11-1
Listing 11-2
Listing 11-3
Listing 11-4
Listing 11-5
The image that results from applying the image mask to the original image 157
The image that results from masking the original image with an image 158
Chroma key masking 158
The original image 159
An image with light to midrange brown colors masked out 160
A image after masking colors from dark brown to black 161
An image drawn after masking a range of colors and setting a fill color 162
A masking image 163
An image drawn to a context after clipping the content with an image mask 163
An image drawn to a context after clipping the content with an image 164
Background drawing (left) and foreground image (right) 165
Drawing an image over a background using normal blend mode 166
Drawing an image over a background using multiply blend mode 166
Drawing an image over a background using screen blend mode 167
Drawing an image over a background using overlay blend mode 168
Drawing an image over a background using darken blend mode 169
Drawing an image over a background using lighten blend mode 169
Drawing an image over a background using color dodge blend mode 170
Drawing an image over a background using color burn blend mode 171
Drawing an image over a background using soft light blend mode 172
Drawing an image over a background using hard light blend mode 173
Drawing an image over a background using difference blend mode 173
Drawing an image over a background using exclusion blend mode 174
Drawing an image over a background using hue blend mode 175
Drawing an image over a background using saturation blend mode 176
Drawing an image over a background using color blend mode 177
Drawing an image over a background using luminosity blend mode 178
Functions for creating images 151
Code that creates a subimage and draws it enlarged 153
The prototype for the function CGImageMaskCreate 155
Masking light to mid-range brown colors in an image 160
Masking shades of brown to black 160
Masking a range of colors and setting a fill color and 161
11
Figure 12-5
Listing 12-1
The result of using layers to draw the United States flag 184
Code that uses layers to draw a flag 185
12
SwiftObjective-C
13
Introduction
Quartz 2D is an advanced, two-dimensional drawing engine available for iOS application development and to
all Mac OS X application environments outside of the kernel. Quartz 2D provides low-level, lightweight 2D
rendering with unmatched output fidelity regardless of display or printing device. Quartz 2D is resolution- and
device-independent; you dont need to think about the final destination when you use the Quartz 2D application
programming interface (API) for drawing.
The Quartz 2D API is easy to use and provides access to powerful features such as transparency layers, path-based
drawing, offscreen rendering, advanced color management, anti-aliased rendering, and PDF document creation,
display, and parsing.
The Quartz 2D API is part of the Core Graphics framework, so you may see Quartz referred to as Core Graphics
or, simply, CG.
Draw graphics
Overview of Quartz 2D (page 17) describes the page, drawing destinations, Quartz opaque data types,
graphics states, coordinates, and memory management, and it takes a look at how Quartz works under
the hood.
Graphics Contexts (page 27) describes the kinds of drawing destinations and provides step-by-step
instructions for creating all flavors of graphics contexts.
14
Introduction
See Also
Paths (page 42) discusses the basic elements that make up paths, shows how to create and paint them,
shows how to set up a clipping area, and explains how blend modes affect painting.
Color and Color Spaces (page 68) discusses color values and using alpha values for transparency, and it
describes how to create a color space, set colors, create color objects, and set rendering intent.
Transforms (page 76) describes the current transformation matrix and explains how to modify it, shows
how to set up affine transforms, shows how to convert between user and device space, and provides
background information on the mathematical operations that Quartz performs.
Patterns (page 89) defines what a pattern and its parts are, tells how Quartz renders them, and shows
how to create colored and stenciled patterns.
Shadows (page 107) describes what shadows are, explains how they work, and shows how to paint with
them.
Gradients (page 112) discusses axial and radial gradients and shows how to create and use CGShading and
CGGradient objects.
Transparency Layers (page 136) gives examples of what transparency layers look like, discusses how they
work, and provides step-by-step instructions for implementing them.
Data Management in Quartz 2D (page 140) discusses how to move data into and out of Quartz.
Bitmap Images and Image Masks (page 146) describes what makes up a bitmap image definition and shows
how to use a bitmap image as a Quartz drawing primitive. It also describes masking techniques you can
use on images and shows the various effects you can achieve by using blend modes when drawing images.
Core Graphics Layer Drawing (page 179) describes how to create and use drawing layers to achieve
high-performance patterned drawing or to draw offscreen.
PDF Document Creation, Viewing, and Transforming (page 190) shows how to open and view PDF
documents, apply transforms to them, create a PDF file, access PDF metadata, add links, and add security
features (such as password protection).
PDF Document Parsing (page 200) describes how to use CGPDFScanner and CGPDFContentStream objects
to parse and inspect PDF documents.
PostScript Conversion (page 208) gives an overview of the functions you can use in Mac OS X to convert
a PostScript file to a PDF document. These functions are not available in iOS.
Text (page 212) describes Quartz 2D low-level support for text and glyphs, and alternatives that provide
higher-level and Unicode text support. It also discusses how to copy font variations.
See Also
These items are essential reading for anyone using Quartz 2D:
15
Introduction
See Also
Quartz 2D Reference Collection , organized by header file, provides a complete reference for the Quartz 2D
application programming interface.
Color Management Overview is a brief introduction to the principles of color perception, color spaces, and
color management systems.
Mailing lists. Join the quartz-dev mailing list to discuss problems using Quartz 2D.
Programming With Quartz: 2D and PDF Graphics in Mac OS X provides in-depth information on using
Quartz. This book is current through Mac OS X v10.4 and was written prior to the introduction of iOS. The
book includes examples that show how to support earlier versions of Mac OS X as well as how to use the
features introduced in v10.4. The sample code associated with this book is available from the publisher.
16
Overview of Quartz 2D
SwiftObjective-C
Quartz 2D is a two-dimensional drawing engine accessible in the iOS environment and from all Mac OS X
application environments outside of the kernel. You can use the Quartz 2D application programming interface
(API) to gain access to features such as path-based drawing, painting with transparency, shading, drawing
shadows, transparency layers, color management, anti-aliased rendering, PDF document generation, and PDF
metadata access. Whenever possible, Quartz 2D leverages the power of the graphics hardware.
In Mac OS X, Quartz 2D can work with all other graphics and imaging technologiesCore Image, Core Video,
OpenGL, and QuickTime. Its possible to create an image in Quartz from a QuickTime graphics importer, using
the QuickTime function GraphicsImportCreateCGImage. See QuickTime Framework Reference for details.
Moving Data Between Quartz 2D and Core Image in Mac OS X (page 144) describes how you can provide images
to Core Image, which is a framework that supports image processing.
Similarly, in iOS, Quartz 2D works with all available graphics and animation technologies, such as Core Animation,
OpenGL ES, and the UIKit classes.
The Page
Quartz 2D uses the painters model for its imaging. In the painters model, each successive drawing operation
applies a layer of paint to an output canvas, often called a page. The paint on the page can be modified by
overlaying more paint through additional drawing operations. An object drawn on the page cannot be modified
except by overlaying more paint. This model allows you to construct extremely sophisticated images from a
small number of powerful primitives.
17
Overview of Quartz 2D
Drawing Destinations: The Graphics Context
Figure 1-1 shows how the painters model works. To get the image in the top part of the figure, the shape on
the left was drawn first followed by the solid shape. The solid shape overlays the first shape, obscuring all but
the perimeter of the first shape. The shapes are drawn in the opposite order in the bottom of the figure, with
the solid shape drawn first. As you can see, in the painters model the drawing order is important.
Figure 1-1
Result
Drawing order
Result
The page may be a real sheet of paper (if the output device is a printer); it may be a virtual sheet of paper (if
the output device is a PDF file); it may even be a bitmap image. The exact nature of the page depends on the
particular graphics context you use.
18
Overview of Quartz 2D
Drawing Destinations: The Graphics Context
You can think of a graphics context as a drawing destination, as shown in Figure 1-2. When you draw with
Quartz, all device-specific characteristics are contained within the specific type of graphics context you use. In
other words, you can draw the same image to a different device simply by providing a different graphics
context to the same sequence of Quartz drawing routines. You do not need to perform any device-specific
calculations; Quartz does it for you.
Figure 1-2
Printer
Layer
Drawing
Bitmap
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 called sampled images . See Creating a Bitmap Graphics Context (page 35).
A PDF graphics context allows you to create a PDF file. In a PDF file, your drawing is preserved as a sequence
of commands. There are some significant differences between PDF files and bitmaps:
PDF files, unlike bitmaps, may contain more than one page.
When you draw a page from a PDF file on a different device, the resulting image is optimized for the
display characteristics of that device.
PDF files are resolution independent by naturethe size at which they are drawn can be increased
or decreased infinitely without sacrificing image detail. The user-perceived quality of a bitmap image
is tied to the resolution at which the bitmap is intended to be viewed.
19
Overview of Quartz 2D
Quartz 2D Opaque Data Types
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. See Creating a Window Graphics Context in Mac
OS X (page 28) for details.
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. See Core
Graphics Layer Drawing (page 179).
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. See Obtaining a Graphics Context for Printing (page 41) for more information.
You can rotate and display a PDF page by creating a PDF page object, applying a rotation operation to
the graphics context, and asking Quartz 2D to draw the page to a graphics context.
You can draw a pattern by creating a pattern object, defining the shape that makes up the pattern, and
setting up Quartz 2D to use the pattern as paint when it draws to a graphics context.
20
Overview of Quartz 2D
Quartz 2D Opaque Data Types
You can fill an area with an axial or radial shading by creating a shading object, providing a function that
determines the color at each point in the shading, and then asking Quartz 2D to use the shading as a fill
color.
Figure 1-3
PDF pages
Patterns
Shading
CGPathRef, used for vector graphics to create paths that you fill or stroke. See Paths (page 42).
CGImageRef, used to represent bitmap images and bitmap image masks based on sample data that you
CGLayerRef, used to represent a drawing layer that can be used for repeated drawing (such as for
backgrounds or patterns) and for offscreen drawing. See Core Graphics Layer Drawing (page 179)
CGShadingRef and CGGradientRef, used to paint gradients. See Gradients (page 112).
CGFunctionRef, used to define callback functions that take an arbitrary number of floating-point
arguments. You use this data type when you create gradients for a shading. See Gradients (page 112).
CGColorRef and CGColorSpaceRef, used to inform Quartz how to interpret color. See Color and Color
CGImageSourceRef and CGImageDestinationRef, which you use to move data into and out of Quartz.
See Data Management in Quartz 2D (page 140) and Image I/O Programming Guide .
CGPDFScannerRef and CGPDFContentStreamRef, which parse PDF metadata. See PDF Document
21
Overview of Quartz 2D
Graphics States
CGPSConverterRef, used to convert PostScript to PDF. It is not available in iOS. See PostScript
Graphics States
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 thats 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
function CGContextSaveGState. The graphics state parameters that are saved when you call this function
are listed in Table 1-1.
Table 1-1
Parameters
Clipping area
Anti-aliasing setting
22
Overview of Quartz 2D
Quartz 2D Coordinate Systems
Parameters
Rendering intent
Blend mode
x-axis
(0,0)
Because different devices have different underlying imaging capabilities, the locations and sizes of graphics
must be defined in a device-independent manner. For example, a screen display device might be capable of
displaying no more than 96 pixels per inch, while a printer might be capable of displaying 300 pixels per inch.
If you define the coordinate system at the device level (in this example, either 96 pixels or 300 pixels), objects
drawn in that space cannot be reproduced on other devices without visible distortion. They will appear too
large or too small.
Quartz accomplishes device independence with a separate coordinate systemuser spacemapping it to
the coordinate system of the output devicedevice spaceusing the current transformation matrix, or CTM.
A matrix is a mathematical construct used to efficiently describe a set of related equations. The current
23
Overview of Quartz 2D
Quartz 2D Coordinate Systems
transformation matrix is a particular type of matrix called an affine transform, which maps points from one
coordinate space to another by applying translation, rotation, and scaling operations (calculations that move,
rotate, and resize a coordinate system).
The current transformation matrix has a secondary purpose: It allows you to transform how objects are drawn.
For example, to draw a box rotated by 45 degrees, you rotate the coordinate system of the page (the CTM)
before you draw the box. Quartz draws to the output device using the rotated coordinate system.
A point in user space is represented by a coordinate pair (x ,y ), where x represents the location along the
horizontal axis (left and right) and y represents the vertical axis (up and down). The origin of the user coordinate
space is the point (0,0). The origin is located at the lower-left corner of the page, as shown in Figure 1-4 (page
23). In the default coordinate system for Quartz, the x-axis increases as it moves from the left toward the right
of the page. The y-axis increases in value as it moves from the bottom toward the top of the page.
Some technologies set up their graphics contexts using a different default coordinate system than the one
used by Quartz. Relative to Quartz, such a coordinate system is a modified coordinate system and must be
compensated for when performing some Quartz drawing operations. The most common modified coordinate
system places the origin in the upper-left corner of the context and changes the y-axis to point towards the
bottom of the page. A few places where you might see this specific coordinate system used are the following:
In Mac OS X, a subclass of NSView that overrides its isFlipped method to return YES.
The reason UIKit returns Quartz drawing contexts with modified coordinate systems is that UIKit uses a different
default coordinate convention; it applies the transform to Quartz contexts it creates so that they match its
conventions. If your application wants to use the same drawing routines to draw to both a UIView object and
a PDF graphics context (which is created by Quartz and uses the default coordinate system), you need to apply
a transform so that the PDF graphics context receives the same modified coordinate system. To do this, apply
a transform that translates the origin to the upper-left corner of the PDF context and scales the y-coordinate
by -1.
Using a scaling transform to negate the y-coordinate alters some conventions in Quartz drawing. For example,
if you call CGContextDrawImage to draw an image into the context, the image is modified by the transform
when it is drawn into the destination. Similarly, path drawing routines accept parameters that specify whether
an arc is drawn in a clockwise or counterclockwise direction in the default coordinate system. If a coordinate
24
Overview of Quartz 2D
Quartz 2D Coordinate Systems
system is modified, the result is also modified, as if the image were reflected in a mirror. In Figure 1-5, passing
the same parameters into Quartz results in a clockwise arc in the default coordinate system and a
counterclockwise arc after the y-coordinate is negated by the transform.
Figure 1-5
(0,0)
(0,0)
Default Coordinates
Modified Coordinates
flipped_coordinates.eps
Cocoa Drawing
Apple Computer, Inc.
February 9, 2006
It is up to your application to adjust any Quartz calls it makes to a context that has a transform applied to it.
For example, if you want an image or PDF to draw correctly into a graphics context, your application may need
to temporarily adjust the CTM of the graphics context. In iOS, if you use a UIImage object to wrap a CGImage
object you create, you do not need to modify the CTM. The UIImage object automatically compensates for
the modified coordinate system applied by UIKit.
25
Overview of Quartz 2D
Memory Management: Object Ownership
Important: The above discussion is essential to understand if you plan to write applications that directly
target Quartz on iOS, but it is not sufficient. On iOS 3.2 and later, when UIKit creates a drawing context for
your application, it also makes additional changes to the context to match the default UIKIt conventions.
In particular, patterns and shadows, which are not affected by the CTM, are adjusted separately so that
their conventions match UIKits coordinate system. In this case, there is no equivalent mechanism to the
CTM that your application can use to change a context created by Quartz to match the behavior for a
context provided by UIKit; your application must recognize the what kind of context it is drawing into and
adjust its behavior to match the expectations of the context.
If you create or copy an object, you own it, and therefore you must release it. That is, in general, if you
obtain an object from a function with the words Create or Copy in its name, you must release the
object when youre done with it. Otherwise, a memory leak results.
If you obtain an object from a function that does not contain the words Create or Copy in its name,
you do not own a reference to the object, and you must not release it. The object will be released by its
owner at some point in the future.
If you do not own an object and you need to keep it around, you must retain it and release it when youre
done with it. You use the Quartz 2D functions specific to an object to retain and release that object. For
example, if you receive a reference to a CGColorspace object, you use the functions CGColorSpaceRetain
and CGColorSpaceRelease to retain and release the object as needed. You can also use the Core
Foundation functions CFRetain and CFRelease, but you must be careful not to pass NULL to these
functions.
26
Graphics Contexts
A graphics context represents a drawing destination. It contains drawing parameters and all device-specific
information that the drawing system needs to perform any subsequent drawing commands. A graphics context
defines basic drawing attributes such as the colors to use when drawing, the clipping area, line width and style
information, font information, compositing options, and several others.
You can obtain a graphics context by using Quartz context creation functions or by using higher-level functions
provided by one of the Mac OS X frameworks or the UIKit framework in iOS. Quartz provides functions for
various flavors of Quartz graphics contexts including bitmap and PDF, which you can use to create custom
content.
This chapter shows you how to create a graphics context for a variety of drawing destinations. A graphics
context is represented in your code by the data type CGContextRef, which is an opaque data type. After you
obtain a graphics context, you can use Quartz 2D functions to draw to the context, perform operations (such
as translations) on the context, and change graphics state parameters, such as line width and fill color.
27
Graphics Contexts
Creating a Window Graphics Context in Mac OS X
The method currentContext returns the NSGraphicsContext instance of the current thread. The method
graphicsPort returns the low-level, platform-specific graphics context represented by the receiver, which
is a Quartz graphics context. (Dont get confused by the method names; they are historical.) For more information
see NSGraphicsContext Class Reference .
After you obtain the graphics context, you can call any of the Quartz 2D drawing functions in your Cocoa
application. You can also mix Quartz 2D calls with Cocoa drawing calls. You can see an example of Quartz 2D
drawing to a Cocoa view by looking at Figure 2-1. The drawing consists of two overlapping rectangles, an
opaque red one and a partially transparent blue one. Youll learn more about transparency in Color and Color
Spaces (page 68). The ability to control how much you can see through colors is one of the hallmark features
of Quartz 2D.
Figure 2-1
28
Graphics Contexts
Creating a Window Graphics Context in Mac OS X
To create the drawing in Figure 2-1, you first create a Cocoa application Xcode project. In Interface Builder,
drag a Custom View to the window and subclass it. Then write an implementation for the subclassed view,
similar to what Listing 2-1 shows. For this example, the subclassed view is named MyQuartzView. The
drawRect: method for the view contains all the Quartz drawing code. A detailed explanation for each numbered
line of code appears following the listing.
Note: The drawRect: method of the NSView class is invoked automatically each time the view
needs to be drawn. To find out more about overriding the drawRect: method, see NSView Class
Reference .
Listing 2-1
@implementation MyQuartzView
- (id)initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame:frameRect];
return self;
}
- (void)drawRect:(NSRect)rect
{
CGContextRef myContext = [[NSGraphicsContext
// 1
currentContext] graphicsPort];
// ********** Your drawing code here **********
CGContextSetRGBFillColor (myContext, 1, 0, 0, 1);
// 3
// 4
// 5
// 6
@end
// 2
29
Graphics Contexts
Creating a PDF Graphics Context
2.
This is where you insert your drawing code. The four lines of code that follow are examples of using Quartz
2D functions.
3.
Sets a red fill color thats fully opaque. For information on colors and alpha (which sets opacity), see Color
and Color Spaces (page 68).
4.
Fills a rectangle whose origin is (0,0) and whose width is 200 and height is 100. For information on
drawing rectangles, see Paths (page 42).
5.
6.
Fills a rectangle whose origin is (0,0) and whose width is 100 and height is 200.
The Quartz 2D API provides two functions that create a PDF graphics context:
30
Graphics Contexts
Creating a PDF Graphics Context
CGPDFContextCreateWithURL, which you use when you want to specify the location for the PDF output
as a Core Foundation URL. Listing 2-2 (page 31) shows how to use this function to create a PDF graphics
context.
CGPDFContextCreate, which you use when you want the PDF output sent to a data consumer. (For
more information see Data Management in Quartz 2D (page 140).) Listing 2-3 (page 32) shows how to use
this function to create a PDF graphics context.
A detailed explanation for each numbered line of code follows each listing.
iOS Note: A PDF graphics context in iOS uses the default coordinate system provided by Quartz,
without applying a transform to match the UIKit coordinate system. If your application plans on
sharing drawing code between your PDF graphics context and the graphics context provided by
UIView object, your application should modify the CTM of the PDF graphics context to modify the
coordinate system. See Quartz 2D Coordinate Systems (page 23).
Listing 2-2
// 1
path,
kCFURLPOSIXPathStyle,
false);
if (url != NULL) {
myOutContext = CGPDFContextCreateWithURL (url,
// 2
inMediaBox,
NULL);
CFRelease(url);
// 3
}
return myOutContext;
// 4
31
Graphics Contexts
Creating a PDF Graphics Context
1.
Calls the Core Foundation function to create a CFURL object from the CFString object supplied to the
MyPDFContextCreate function. You pass NULL as the first parameter to use the default allocator. You
also need to specify a path style, which for this example is a POSIX-style pathname.
2.
Calls the Quartz 2D function to create a PDF graphics context using the PDF location just created (as a
CFURL object) and a rectangle that specifies the bounds of the PDF. The rectangle (CGRect) was passed
to the MyPDFContextCreate function and is the default page media bounding box for the PDF.
3.
4.
Returns the PDF graphics context. The caller must release the graphics context when it is no longer needed.
Listing 2-3
myOutContext = NULL;
CFURLRef
url;
CGDataConsumerRef
dataConsumer;
// 1
path,
kCFURLPOSIXPathStyle,
false);
if (url != NULL)
{
dataConsumer = CGDataConsumerCreateWithURL (url);
// 2
if (dataConsumer != NULL)
{
myOutContext = CGPDFContextCreate (dataConsumer,
// 3
inMediaBox,
NULL);
CGDataConsumerRelease (dataConsumer);
// 4
}
CFRelease(url);
// 5
32
Graphics Contexts
Creating a PDF Graphics Context
return myOutContext;
// 6
Calls the Core Foundation function to create a CFURL object from the CFString object supplied to the
MyPDFContextCreate function. You pass NULL as the first parameter to use the default allocator. You
also need to specify a path style, which for this example is a POSIX-style pathname.
2.
Creates a Quartz data consumer object using the CFURL object. If you dont want to use a CFURL object
(for example, you want to place the PDF data in a location that cant be specified by a CFURL object), you
can instead create a data consumer from a set of callback functions that you implement in your application.
For more information, see Data Management in Quartz 2D (page 140).
3.
Calls the Quartz 2D function to create a PDF graphics context passing as parameters the data consumer
and the rectangle (of type CGRect) that was passed to the MyPDFContextCreate function. This rectangle
is the default page media bounding box for the PDF.
4.
5.
6.
Returns the PDF graphics context. The caller must release the graphics context when it is no longer needed.
Listing 2-4 shows how to call the MyPDFContextCreate routine and draw to it. A detailed explanation for
each numbered line of code appears following the listing.
Listing 2-4
CGRect mediaBox;
// 1
// 2
// 3
CFStringRef myKeys[1];
// 4
CFTypeRef myValues[1];
myKeys[0] = kCGPDFContextMediaBox;
myValues[0] = (CFTypeRef) CFDataCreate(NULL,(const UInt8 *)&mediaBox, sizeof
(CGRect));
CFDictionaryRef pageDictionary = CFDictionaryCreate(NULL, (const void **)
myKeys,
(const void **) myValues,
1,
33
Graphics Contexts
Creating a PDF Graphics Context
&kCFTypeDictionaryKeyCallBacks,
&
kCFTypeDictionaryValueCallBacks);
CGPDFContextBeginPage(myPDFContext, &pageDictionary);
// ********** Your drawing code here **********
// 5
// 6
// 7
CFRelease(pageDictionary);
// 8
CFRelease(myValues[0]);
CGContextRelease(myPDFContext);
Declares a variable for the rectangle that you use to define the PDF media box.
2.
Sets the origin of the media box to (0,0) and the width and height to variables supplied by the application.
3.
Calls the function MyPDFContextCreate (See Listing 2-3 (page 32)) to obtain a PDF graphics context,
supplying a media box and a pathname. The macro CFSTR converts a string to a CFStringRef data type.
4.
Sets up a dictionary with the page options. In this example, only the media box is specified. You dont
have to pass the same rectangle you used to set up the PDF graphics context. The media box you add
here supersedes the rectangle you pass to set up the PDF graphics context.
5.
Signals the start of a page. This function is used for page-oriented graphics, which is what PDF drawing
is.
6.
Calls Quartz 2D drawing functions. You replace this and the following four lines of code with the drawing
code appropriate for your application.
7.
8.
Releases the dictionary and the PDF graphics context when they are no longer needed.
You can write any content to a PDF thats appropriate for your applicationimages, text, path drawingand
you can add links and encryption. For more information see PDF Document Creation, Viewing, and
Transforming (page 190).
34
Graphics Contexts
Creating a Bitmap Graphics Context
You use the function CGBitmapContextCreate to create a bitmap graphics context. This function takes the
following parameters:
data. Supply a pointer to the destination in memory where you want the drawing rendered. The size of
bitsPerComponent. Specify the number of bits to use for each component of a pixel in memory. For
example, for a 32-bit pixel format and an RGB color space, you would specify a value of 8 bits per
component. See Supported Pixel Formats (page 39).
bytesPerRow. Specify the number of bytes of memory to use per row of the bitmap.
Tip: When you create a bitmap graphics context, youll get the best performance if you make
sure the data and bytesPerRow are 16-byte aligned.
35
Graphics Contexts
Creating a Bitmap Graphics Context
colorspace. The color space to use for the bitmap context. You can provide a Gray, RGB, CMYK, or NULL
color space when you create a bitmap graphics context. For detailed information on color spaces and
color management principles, see Color Management Overview . For information on creating and using
color spaces in Quartz, see Color and Color Spaces (page 68). For information about supported color
spaces, see Color Spaces and Bitmap Layout (page 149) in the Bitmap Images and Image Masks (page 146)
chapter.
bitmapInfo. Bitmap layout information, expressed as a CGBitmapInfo constant, that specifies whether
the bitmap should contain an alpha component, the relative location of the alpha component (if there is
one) in a pixel, whether the alpha component is premultiplied, and whether the color components are
integer or floating-point values. For detailed information on what these constants are, when each is used,
and Quartz-supported pixel formats for bitmap graphics contexts and images, see Color Spaces and Bitmap
Layout (page 149) in the Bitmap Images and Image Masks (page 146) chapter.
Listing 2-5 shows how to create a bitmap graphics context. When you draw into the resulting bitmap graphics
context, Quartz records your drawing as bitmap data in the specified block of memory. A detailed explanation
for each numbered line of code follows the listing.
Listing 2-5
context = NULL;
CGColorSpaceRef colorSpace;
void *
bitmapData;
int
bitmapByteCount;
int
bitmapBytesPerRow;
bitmapBytesPerRow
= (pixelsWide * 4);
bitmapByteCount
= (bitmapBytesPerRow * pixelsHigh);
// 1
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
// 2
// 3
if (bitmapData == NULL)
{
fprintf (stderr, "Memory not allocated!");
return NULL;
36
Graphics Contexts
Creating a Bitmap Graphics Context
}
context = CGBitmapContextCreate (bitmapData,
// 4
pixelsWide,
pixelsHigh,
8,
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast);
if (context== NULL)
{
free (bitmapData);
// 5
// 6
return context;
// 7
Declares a variable to represent the number of bytes per row. Each pixel in the bitmap in this example is
represented by 4 bytes; 8 bits each of red, green, blue, and alpha.
2.
Creates a generic RGB color space. You can also create a CMYK color space. See Color and Color Spaces (page
68) for more information and for a discussion of generic color spaces versus device dependent ones.
3.
Calls the calloc function to create and clear a block of memory in which to store the bitmap data. This
example creates a 32-bit RGBA bitmap (that is, an array with 32 bits per pixel, each pixel containing 8 bits
each of red, green, blue, and alpha information). Each pixel in the bitmap occupies 4 bytes of memory. In
Mac OS X 10.6 and iOS 4, this step can be omittedif you pass NULL as bitmap data, Quartz automatically
allocates space for the bitmap.
4.
Creates a bitmap graphics context, supplying the bitmap data, the width and height of the bitmap, the
number of bits per component, the bytes per row, the color space, and a constant that specifies whether
the bitmap should contain an alpha channel and its relative location in a pixel. The constant
kCGImageAlphaPremultipliedLast indicates that the alpha component is stored in the last byte of
each pixel and that the color components have already been multiplied by this alpha value. See The Alpha
Value (page 69) for more information on premultiplied alpha.
37
Graphics Contexts
Creating a Bitmap Graphics Context
5.
If the context isnt created for some reason, frees the memory allocated for the bitmap data.
6.
7.
Returns the bitmap graphics context. The caller must release the graphics context when it is no longer
needed.
Listing 2-6 shows code that calls MyCreateBitmapContext to create a bitmap graphics context, uses the
bitmap graphics context to create a CGImage object, then draws the resulting image to a window graphics
context. Figure 2-3 (page 39) shows the image drawn to the window. A detailed explanation for each numbered
line of code follows the listing.
Listing 2-6
CGRect myBoundingBox;
// 1
// 2
// 3
// 4
// 5
// 6
// 7
CGContextRelease (myBitmapContext);
// 8
if (bitmapData) free(bitmapData);
// 9
CGImageRelease(myImage);
// 10
Declares a variable to store the origin and dimensions of the bounding box into which Quartz will draw
an image created from the bitmap graphics context.
2.
Sets the origin of the bounding box to (0,0) and the width and height to variables previously declared,
but whose declaration are not shown in this code.
3.
Calls the application-supplied function MyCreateBitmapContext (see Listing 2-5 (page 36)) to create
a bitmap context that is 400 pixels wide and 300 pixels high. You can create a bitmap graphics context
using any dimensions that are appropriate for your application.
38
Graphics Contexts
Creating a Bitmap Graphics Context
4.
Calls Quartz 2D functions to draw into the bitmap graphics context. You would replace this and the next
four lines of code with drawing code appropriate for your application.
5.
6.
Draws the image into the location in the window graphics context that is specified by the bounding box.
The bounding box specifies the location and dimensions in user space in which to draw the image.
This example does not show the creation of the window graphics context. See Creating a Window Graphics
Context in Mac OS X (page 28) for information on how to create one.
7.
Gets the bitmap data associated with the bitmap graphics context.
8.
9.
Figure 2-3
An image created from a bitmap graphics context and drawn to a window graphics context
39
Graphics Contexts
Creating a Bitmap Graphics Context
Table 2-1
CS
Availability
Null
Mac OS X, iOS
Gray
8 bpp, 8 bpc,kCGImageAlphaNone
Mac OS X, iOS
Gray
8 bpp, 8 bpc,kCGImageAlphaOnly
Mac OS X, iOS
Gray
Mac OS X
Gray
Mac OS X
RGB
Mac OS X, iOS
RGB
Mac OS X, iOS
RGB
Mac OS X, iOS
RGB
Mac OS X, iOS
RGB
Mac OS X, iOS
RGB
Mac OS X
RGB
Mac OS X
RGB
Mac OS X
Components
RGB
Mac OS X
CMYK
Mac OS X
CMYK
Mac OS X
CMYK
Mac OS X
Anti-Aliasing
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.
40
Graphics Contexts
Obtaining a Graphics Context for Printing
By blending the colors in this way, the shape appears smooth. You can see the effect of using anti-aliasing in
Figure 2-4. 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 function
CGContextSetAllowsAntialiasing. 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.
Figure 2-4
41
Paths
A path defines one or more shapes, or subpaths. A subpath can consist of straight lines, curves, or both. It can
be open or closed. A subpath can be a simple shape, such as a line, circle, rectangle, or star, or a more complex
shape such as the silhouette of a mountain range or an abstract doodle. Figure 3-1 shows some of the paths
you can create. The straight line (at the upper left of the figure) is dashed; lines can also be solid. The squiggly
path (in the middle top) is made up of several curves and is an open path. The concentric circles are filled, but
not stroked. The State of California is a closed path, made up of many curves and lines, and the path is both
stroked and filled. The stars illustrate two options for filling paths, which youll read about later in this chapter.
Figure 3-1
In this chapter, youll learn about the building blocks that make up paths, how to stroke and paint paths, and
the parameters that affect the appearance of paths.
42
Paths
Path Creation and Path Painting
Figure 3-2 shows a path that has been painted and that contains two subpaths. The subpath on the left is a
rectangle, and the subpath on the right is an abstract shape made up of straight lines and curves. Each subpath
is filled and its outline stroked.
Figure 3-2
Figure 3-3 shows multiple paths drawn independently. Each path contains a randomly generated curve, some
of which are filled and others stroked. Drawing is constrained to a circular area by a clipping area.
Figure 3-3
43
Paths
The Building Blocks
Points
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.
Lines
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.
44
Paths
The Building Blocks
Arcs
Arcs are circle segments. Quartz provides two functions that create arcs. The function 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. Figure 3-4 shows multiple paths drawn
independently. Each path contains a randomly generated circle; some are filled and others are stroked.
Figure 3-4
45
Paths
The Building Blocks
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, as shown in Figure 3-5. The red portion of the circle is whats actually drawn.
Figure 3-5
Tangent
point 1
Tangent
line 2
Radius
Tangent
point 2
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.
Curves
Quadratic and cubic Bzier curves are algebraic curves that can specify any number of interesting curvilinear
shapes. Points on these curves are calculated by applying a polynomial formula to starting and ending points,
and one or more control points. Shapes defined in this way are the basis for vector graphics. A formula is much
more compact to store than an array of bits and has the advantage that the curve can be re-created at any
resolution.
46
Paths
The Building Blocks
Figure 3-6 shows a variety of curves created by drawing multiple paths independently. Each path contains a
randomly generated curve; some are filled and others are stroked.
Figure 3-6
The polynomial formulas that give to rise to quadratic and cubic Bzier curves, and the details on how to
generate the curves from the formulas, are discussed in many mathematics texts and online sources that
describe computer graphics. These details are not discussed here.
You use the function CGContextAddCurveToPoint to append a cubic Bzier curve from the current point,
using control points and an endpoint you specify. Figure 3-7 shows the cubic Bzier curve that results from
the current point, control points, and endpoint shown in the figure. The placement of the two control points
determines the geometry of the curve. If the control points are both above the starting and ending points, the
curve arches upward. If the control points are both below the starting and ending points, the curve arches
downward.
Figure 3-7
Point 1
Current point
Point 3
Control point
Point 4
Endpoint
47
Paths
The Building Blocks
You can append a quadratic Bzier curve from the current point by calling the function
CGContextAddQuadCurveToPoint, and specifying a control point and an endpoint. Figure 3-8 shows two
curves that result from using the same endpoints but different control points. The control point determines
the direction that the curve arches. Its not possible to create as many interesting shapes with a quadratic
Bzier curve as you can with a cubic one because quadratic curves use only one control point. For example,
its not possible to create a crossover using a single control point.
Figure 3-8
A Current point
A Current point
C Endpoint
C Endpoint
Closing a Subpath
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 paths 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.
48
Paths
The Building Blocks
Ellipses
An ellipse is essentially a squashed circle. You create one by defining two focus points and then plotting all
the points that lie at a distance such that adding the distance from any point on the ellipse to one focus to
the distance from that same point to the other focus point is always the same value. Figure 3-9 shows multiple
paths drawn independently. Each path contains a randomly generated ellipse; some are filled and others are
stroked.
Figure 3-9
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 Bzier
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.
Rectangles
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.
49
Paths
Creating a Path
You can add many rectangles to the current path by calling the function CGContextAddRects and supplying
an array of CGRect structures. Figure 3-10 shows multiple paths drawn independently. Each path contains a
randomly generated rectangle; some are filled and others are stroked.
Figure 3-10
Creating a Path
When you want to construct a path in a graphics context, you signal Quartz by calling the function
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, keeping in mind the following:
Lines, arcs, and curves are drawn starting at the current point. An empty path has no current point; you
must call CGContextMoveToPoint to set the starting point for the first subpath or call a convenience
function that implicitly does this for you.
When you want to close the current subpath within a path, call the function CGContextClosePath to
connect a segment to the starting point of the subpath. Subsequent path calls begin a new subpath, even
if you do not explicitly set a new starting point.
When you draw arcs, Quartz draws a line between the current point and the starting point of the arc.
Quartz routines that add ellipses and rectangles add a new closed subpath to the path.
You must call a painting function to fill or stroke the path because creating a path does not draw the path.
See Painting a Path (page 51) for detailed information.
50
Paths
Painting a Path
After you paint a path, it is flushed from the graphics context. You might not want to lose your path so easily,
especially if it depicts a complex scene you want to use over and over again. For that reason, Quartz provides
two data types for creating reusable pathsCGPathRef and CGMutablePathRef. You can call the function
CGPathCreateMutable to create a mutable CGPath object to which you can add lines, arcs, curves, and
rectangles. Quartz provides a set of CGPath functions that parallel the functions discussed in The Building
Blocks (page 44). The path functions operate on a CGPath object instead of a graphics context. These functions
are:
See Quartz 2D Reference Collection for a complete list of the path functions.
When you want to append the path to a graphics context, you call the function CGContextAddPath. The
path stays in the graphics context until Quartz paints it. You can add the path again by calling
CGContextAddPath.
Note: You can replace the path in a graphics context with the stroked version of the path by calling
the function CGContextReplacePathWithStrokedPath.
Painting a Path
You can paint the current path by stroking or filling or both. Stroking paints a line that straddles the path. Filling
paints the area contained within the path. Quartz has functions that let you stroke a path, fill a path, or both
stroke and fill a path. The characteristics of the stroked line (width, color, and so forth), the fill color, and the
method Quartz uses to calculate the fill area are all part of the graphics state (see Graphics States (page 22)).
51
Paths
Painting a Path
Parameter
Line width
CGContextSetLineWidth
Line join
CGContextSetLineJoin
Line cap
CGContextSetLineCap
Miter limit
CGContextSetMiterLimit
CGContextSetLineDash
CGContextSetStrokeColorSpace
Stroke color
CGContextSetStrokeColorCGContextSetStrokeColorWithColor
Stroke pattern
CGContextSetStrokePattern
The line width is the total width of the line, expressed in units of the user space. The line straddles the path,
with half of the total width on either side.
The line join specifies how Quartz draws the junction between connected line segments. Quartz supports the
line join styles described in Table 3-2. The default style is miter join.
Table 3-2
Style
Description
Miter join
Quartz extends the outer edges of the strokes for the two segments
until they meet at an angle, as in a picture frame. If the segments
meet at too sharp an angle, a bevel join is used instead. A segment
is too sharp if the length of the miter divided by the line width is
greater than the miter limit.
Round join
52
Paths
Painting a Path
Style
Appearance
Bevel join
Description
Quartz finishes the two segments with butt caps. The resulting
notch beyond the ends of the segments is filled with a triangle.
The line cap specifies the method used by CGContextStrokePath to draw the endpoint of the line. Quartz
supports the line cap styles described in Table 3-3. The default style is butt cap.
Table 3-3
Style
Appearance
Description
Butt cap
Round cap
Projecting
square cap
A closed subpath treats the starting point as a junction between connected line segments; the starting point
is rendered using the selected line-join method. In contrast, if you close the path by adding a line segment
that connects to the starting point, both ends of the path are drawn using the selected line-cap method.
A line dash pattern allows you to draw a segmented line along the stroked path. You control the size and
placement of dash segments along the line by specifying the dash array and the dash phase as parameters to
CGContextSetLineDash:
void CGContextSetLineDash (
CGContextRef ctx,
CGFloat phase,
const CGFloat lengths[],
size_t count
);
53
Paths
Painting a Path
The elements of the lengths parameter specify the widths of the dashes, alternating between the painted
and unpainted segments of the line. The phase parameter specifies the starting point of the dash pattern.
Figure 3-11 shows some line dash patterns.
Figure 3-11
The stroke color space determines how the stroke color values are interpreted by Quartz. You can also specify
a Quartz color (CGColorRef data type) that encapsulates both color and color space. For more information
on setting color space and color, see Color and Color Spaces (page 68).
Function
Description
CGContextStrokePath
CGContextStrokeRect
CGContextStrokeRectWithWidth
CGContextStrokeEllipseInRect
CGContextStrokeLineSegments
CGContextDrawPath
54
Paths
Painting a Path
CGContextBeginPath (context);
for (k = 0; k < count; k += 2) {
CGContextMoveToPoint(context, s[k].x, s[k].y);
CGContextAddLineToPoint(context, s[k+1].x, s[k+1].y);
}
CGContextStrokePath(context);
When you call CGContextStrokeLineSegments, you specify the line segments as an array of points, organized
as pairs. Each pair consists of the starting point of a line segment followed by the ending point of a line segment.
For example, the first point in the array specifies the starting position of the first line, the second point specifies
the ending position of the first line, the third point specifies the starting position of the second line, and so
forth.
Filling a Path
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 (page 56) 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.
55
Paths
Painting a Path
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 doesnt affect the outcome. As you can see in Figure 3-12, it doesnt matter which
direction each circle is drawn, the fill will always be as shown.
Figure 3-12
Even-odd
Winding-number
Quartz provides the functions shown in Table 3-5 for filling the current path. Some are convenience functions
for stroking rectangles or ellipses.
Table 3-5
Function
Description
CGContextEOFillPath
CGContextFillPath
Fills the current path using the nonzero winding number rule.
CGContextFillRect
CGContextFillRects
CGContextFillEllipseInRect
CGContextDrawPath
56
Paths
Painting a Path
Color and Color Spaces (page 68) 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 function
CGContextRestoreGState resets the blend mode to normal.
The rest of this section show the results of painting the rectangles shown in Figure 3-13 over the rectangles
shown in Figure 3-14. In each case (Figure 3-15 through Figure 3-30), the background rectangles are painted
using normal blend mode. Then the blend mode is changed by calling the function CGContextSetBlendMode
with the appropriate constant. Finally, the foreground rectangles are painted.
Figure 3-13
Figure 3-14
57
Paths
Painting a Path
Note: You can also use blend modes to composite two images or to composite an image over any
content thats already drawn to the graphics context. Using Blend Modes with Images (page 164)
provides information on how to use blend modes to composite images and shows the results of
applying blend modes to two images.
58
Paths
Painting a Path
59
Paths
Painting a Path
60
Paths
Painting a Path
61
Paths
Painting a Path
pure black or pure white produce darker or lighter areas, but do not result in pure black or white. The overall
effect is similar to what youd achieve by shining a diffuse spotlight on the foreground image. Use this to add
highlights to a scene. Figure 3-23 shows the result of painting Figure 3-13 (page 57) over Figure 3-14 (page
57) using soft light blend mode. To use this blend mode, call the function CGContextSetBlendMode with
the constant kCGBlendModeSoftLight.
Figure 3-23 Rectangles painted using soft light blend mode
62
Paths
Painting a Path
63
Paths
Painting a Path
64
Paths
Painting a Path
65
Paths
Clipping to a Path
Clipping to a Path
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 dont 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 youre done with clipped drawing.
Listing 3-1 shows a code fragment that sets up a clipping area in the shape of a circle. This code causes drawing
to be clipped, similar to whats shown in Figure 3-3 (page 43). (For another example, see Clip the Context (page
126) in the chapter Gradients (page 112).)
Listing 3-1
CGContextBeginPath (context);
CGContextAddArc (context, w/2, h/2, ((w>h) ? h : w)/2, 0, 2*PI, 0);
CGContextClosePath (context);
CGContextClip (context);
Table 3-6
Function
Description
CGContextClip
CGContextEOClip
Uses the even-odd rule to calculate the intersection of the current path
with the current clipping path.
CGContextClipToRect
Sets the clipping area to the area that intersects both the current clipping
path and the specified rectangle.
66
Paths
Clipping to a Path
Function
Description
CGContextClipToRects
Sets the clipping area to the area that intersects both the current clipping
path and region within the specified rectangles.
CGContextClipToMask
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. (See Masking an Image by
Clipping the Context (page 162).)
67
Devices (displays, printers, scanners, cameras) dont treat color the same way; each has its own range of colors
that the device can produce faithfully. A color produced on one device might not be able to be produced on
another device.
To work with color effectively and to understand the Quartz 2D functions for using color spaces and color, you
should be familiar with the terminology discussed in Color Management Overview . That document discusses
color perception, color values, device-independent and device color spaces, the color-matching problem,
rendering intent, color management modules, and ColorSync.
In this chapter, youll learn how Quartz represents color and color spaces, and what the alpha component is.
This chapter also discusses how to:
Values
Color space
Components
HSB
0, 0, 1
RGB
1, 1, 0, 0
CMYK
1, 0, 0
BGR
68
If you provide the wrong color space, you can get quite dramatic differences, as shown in Figure 4-1. Although
the green color is interpreted the same in BGR and RGB color spaces, the red and blue values are flipped.
Figure 4-1
Color spaces can have different numbers of components. Three of the color spaces in the table have three
components, while the CMYK color space has four. Value ranges are relative to that color space. For most color
spaces, color values in Quartz range from 0.0 to 1.0, with 1.0 meaning full intensity. For example, the color blue
at full intensity, specified in the RGB color space in Quartz, has the values (0, 0, 1.0). In Quartz, color also has
an alpha value that specifies the transparency of a color. The color values in Table 4-1 dont show an alpha
value.
0.75
0.5
0.1
69
0.0
You can make both the objects on the page and the page itself transparent by setting the alpha value globally
in the graphics context before painting. Figure 4-3 compares a global alpha setting of 0.5 with the default
value of 1.0.
Figure 4-3
In the normal blend mode (which is the default for the graphics state) Quartz performs alpha blending by
combining the components of the source color with the components of the destination color using the formula:
destination = (alpha * source) + (1 - alpha) * destination
where source is one component of the new paint color and destination is one component of the background
color. This formula is executed for each newly painted shape or image.
For object transparency, set the alpha value to 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.
To allow the page itself to be fully transparent, you can explicitly clear the alpha channel of the graphics context
using the CGContextClearRect function, as long as the graphics context is a window or bitmap graphics
context. You might want to do this when creating a transparency mask for an icon, for example, or to make
the background of a window transparent.
70
L*a*b* is a nonlinear transformation of the Munsell color notation system (a system that specifies colors
by hue, value, and saturationor chroma values). This color space matches perceived color difference
with quantitative distance in color space. The L* component represents the lightness value, the a*
component represents values from green to red, and the b* component represents values from blue to
yellow. This color space is designed to mimic how the human brain decodes color. Use the function
CGColorSpaceCreateLab.
ICC is a color space from an ICC color profile, as defined by the International Color Consortium. ICC profiles
define the gamut of colors supported by a device along with other device characteristics so that this
information can be used to accurately transform the color space of one device to the color space of another.
The manufacturer of the device typically provides an ICC profile. Some color monitors and printers contain
embedded ICC profile information, as do some bitmap formats such as TIFF. Use the function
CGColorSpaceCreateICCBased.
Calibrated RGB is a device-independent RGB color space that represents colors relative to a reference white
point that is based on the whitest light that can be generated by the output device. Use the function
CGColorSpaceCreateCalibratedRGB.
71
Calibrated gray is a device-independent grayscale color space that represents colors relative to a reference
white point that is based on the whitest light that can be generated by the output device. Use the function
CGColorSpaceCreateCalibratedGray.
kCGColorSpaceGenericGray, which specifies generic gray, a monochromatic color space that permits
the specification of a single value ranging from absolute black (value 0.0) to absolute white (value 1.0).
kCGColorSpaceGenericRGB, which specifies generic RGB, a three-component color space (red, green,
and blue) that models the way an individual pixel is composed on a color monitor. Each component of
the RGB color space ranges in value from 0.0 (zero intensity) to 1.0 (full intensity).
magenta, yellow, and black) that models the way ink builds up during printing. Each component of the
CMYK color space ranges in value from 0.0 (does not absorb the color) to 1.0 (fully absorbs the color).
72
RGB
73
Table 4-2
Color-setting functions
Function
CGContextSetRGBStrokeColor
CGContextSetRGBFillColor
CGContextSetCMYKStrokeColor
CGContextSetCMYKFillColor
CGContextSetGrayStrokeColor
CGContextSetGrayFillColor
CGContextSetStrokeColorWithColor
CGContextSetFillColorWithColor
CGContextSetStrokeColor
CGContextSetFillColor
You specify the fill and stroke colors as values located within the fill and stroke color spaces. For example, a
fully saturated red color in the RGB color space is specified as an array of four numbers: (1.0, 0.0, 0.0, 1.0). The
first three numbers specify full red intensity and no green or blue intensity. The fourth number is the alpha
value, which is used to specify the opacity of the color.
If you reuse colors in your application, the most efficient way to set fill and stroke colors is to create a CGColor
object, which you then pass as a parameter to the functions CGContextSetFillColorWithColor and
CGContextSetStrokeColorWithColor. You can keep the CGColor object around as long as you need it.
You can improve your applications performance by using CGColor objects directly.
You create a CGColor object by calling the function CGColorCreate, passing a CGColorspace object and an
array of floating-point values that specify the intensity values for the color. The last component in the array
specifies the alpha value.
74
to the closest possible match inside the gamut of the output device. This can produce a clipping effect,
where two different color values in the gamut of the graphics context are mapped to the same color value
in the output devices gamut. This is the best choice when the colors used in the graphics are within the
gamut of both the source and the destination, as is often the case with logos or when spot colors are used.
those within the gamut) to account for the difference between the white point of the graphics context
and the white point of the output device.
the gamut of the graphics context to fit inside the gamut of the output device. Perceptual intent is good
for photographs and other complex, detailed images.
kCGRenderingIntentSaturation. Preserves the relative saturation value of the colors when converting
into the gamut of the output device. The result is an image with bright, saturated colors. Saturation intent
is good for reproducing images with low detail, such as presentation charts and graphs.
75
Transforms
The Quartz 2D drawing model defines two completely separate coordinate spaces: user space, which represents
the document page, and device space, which represents the native resolution of a device. User space coordinates
are floating-point numbers that are unrelated to the resolution of pixels in device space. When you want to
print or display your document, Quartz maps user space coordinates to device space coordinates. Therefore,
you never have to rewrite your application or write additional code to adjust the output from your application
for optimum display on different devices.
You can modify the default user space by operating on the current transformation matrix, or CTM. After you
create a graphics context, the CTM is the identity matrix. You can use Quartz transformation functions to modify
the CTM and, as a result, modify drawing in user space.
This chapter:
76
Transforms
About Quartz Transformation Functions
120
100
120
100
80
80
60
60
40
40
20
20
0
The Quartz 2D API provides five functions that allow you to obtain and modify the CTM. You can rotate, translate,
and scale the CTM, and you can concatenate an affine transformation matrix with the CTM. See Modifying the
Current Transformation Matrix (page 77).
Quartz also allows you to create affine transforms that dont operate on user space until you decide to apply
the transform to the CTM. You use another set of functions to create affine transforms, which can then be
concatenated with the CTM. See Creating Affine Transforms (page 83).
You can use either set of functions without understanding anything about matrix math. However if you want
to understand what Quartz does when you call one of the transform functions, read The Math Behind the
Matrices (page 85).
77
Transforms
Modifying the Current Transformation Matrix
Figure 5-2
Translation moves the origin of the coordinate space by the amount you specify for the x and y axes. You call
the function CGContextTranslateCTM to modify the x and y coordinates of each point by a specified amount.
Figure 5-3 shows an image translated by 100 units in the x-axis and 50 units in the y-axis, using the following
line of code:
CGContextTranslateCTM (myContext, 100, 50);
Figure 5-3
A translated image
100
50
78
Transforms
Modifying the Current Transformation Matrix
Rotation moves the coordinate space by the angle you specify. You call the function CGContextRotateCTM
to specify the rotation angle, in radians. Figure 5-4 shows an image rotated by 45 degrees about the origin,
which is the lower left of the window, using the following line of code:
CGContextRotateCTM (myContext, radians(45.));
The image is clipped because the rotation moved part of the image to a location outside the context. You
need to specify the rotation angle in radians.
Its useful to write a radians routine if you plan to perform many rotations.
#include <math.h>
static inline double radians (double degrees) {return degrees * M_PI/180;}
Figure 5-4
A rotated image
45
Scaling changes the scale of the coordinate space by the x and y factors you specify, effectively stretching or
shrinking the image. The magnitude of the x and y factors governs whether the new coordinates are larger or
smaller than the original. In addition, by making the x factor negative, you can flip the coordinates along the
x-axis; similarly, you can flip coordinates horizontally, along the y-axis, by making the y factor negative. You
call the function CGContextScaleCTM to specify the x and y scaling factors. Figure 5-5 shows an image whose
x values are scaled by .5 and whose y values are scaled by .75, using the following line of code:
79
Transforms
Modifying the Current Transformation Matrix
Figure 5-5
A scaled image
.5
.75
Concatenation combines two matrices by multiplying them together. You can concatenate several matrices
to form a single matrix that contains the cumulative effects of the matrices. You call the function
CGContextConcatCTM to combine the CTM with an affine transform. Affine transforms, and the functions
that create them, are discussed in Creating Affine Transforms (page 83).
Another way to achieve a cumulative effect is to perform two or more transformations without restoring the
graphics state between transformation calls. Figure 5-6 shows an image that results from translating an image
and then rotating it, using the following lines of code:
CGContextTranslateCTM (myContext, w,h);
80
Transforms
Modifying the Current Transformation Matrix
Figure 5-6
Figure 5-7 shows an image that is translated, scaled, and rotated, using the following lines of code:
CGContextTranslateCTM (myContext, w/4, 0);
CGContextScaleCTM (myContext, .25,
.5);
81
Transforms
Modifying the Current Transformation Matrix
Figure 5-7
Original image
Translate
Scale
Rotate
The order in which you perform multiple transformations matters; you get different results if you reverse the
order. Reverse the order of transformations used to create Figure 5-7 and you get the results shown in Figure
5-8, which is produced with this code:
CGContextRotateCTM (myContext, radians ( 22.));
CGContextScaleCTM (myContext, .25,
.5);
82
Transforms
Creating Affine Transforms
Figure 5-8
Rotate
Translate
Original image
Scale
83
Transforms
Creating Affine Transforms
Table 5-1
Function
Use
CGAffineTransformMakeTranslation
CGAffineTransformTranslate
CGAffineTransformMakeRotation
CGAffineTransformRotate
CGAffineTransformMakeScale
CGAffineTransformScale
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 dont 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 a CGPoint 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 function CGRectApplyAffineTransform. 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.
You can create a new affine transform by calling the function CGAffineTransformMake, but unlike the other
functions that make new affine transforms, this one requires you to supply matrix entries. To effectively use
this function, you need to have an understanding of matrix math. See The Math Behind the Matrices (page
85).
84
Transforms
Evaluating Affine Transforms
85
Transforms
The Math Behind the Matrices
The six critical values of a 3 x 3 transformation matrix a , b , c , d , tx and ty are shown in the following
matrix:
Note: The rightmost column of the matrix always contains the constant values 0, 0, 1. Mathematically,
this third column is required to allow concatenation, which is explained later in this section. It appears
in this section for the sake of mathematical correctness only.
Given the 3 x 3 transformation matrix described above, Quartz uses this equation to transform a point (x , y )
into a resultant point (x, y ):
The result is in a different coordinate system, the one transformed by the variable values in the transformation
matrix. The following equations are the definition of the previous matrix transform:
The following matrix is the identity matrix. It performs no translation, scaling, or rotation. Multiplying this
matrix by the input coordinates always returns the input coordinates.
Using the formulas discussed earlier, you can see that this matrix would generate a new point (x , y ) that is
the same as the old point (x , y ):
86
Transforms
The Math Behind the Matrices
These are the resulting equations that Quartz uses to apply the translation:
These are the resulting equations that Quartz uses to scale the coordinates:
This matrix describes a rotation operation, rotating the point (x , y ) counterclockwise by an angle a:
These are the resulting equations that Quartz uses to apply the rotation:
87
Transforms
The Math Behind the Matrices
These are the resulting equations that Quartz uses to apply the transform:
Note that the order in which you concatenate matrices is importantmatrix multiplication is not commutative.
That is, the result of multiplying matrix A by matrix B does not necessarily equal the result of multiplying matrix
B by matrix A.
As previously mentioned, concatenation is the reason the affine transformation matrix contains a third column
with the constant values 0, 0, 1. To multiply one matrix against another matrix, the number of columns of one
matrix must match the number of rows of the other. This means that a 2 x 3 matrix cannot be multiplied against
a 2 x 3 matrix. Thus we need the extra column containing the constant values.
An inversion operation produces original coordinates from transformed ones. Given the coordinates (x , y ),
which have been transformed by a given matrix A to new coordinates (x , y ), transforming the coordinates
(x , y ) by the inverse of matrix A produces the original coordinates (x , y ). When a matrix is multiplied by its
inverse, the result is the identity matrix.
88
Patterns
A pattern is a sequence of drawing operations that is repeatedly painted to a graphics context. You can use
patterns in the same way as you use colors. When you paint using a pattern, Quartz divides the page into a
set of pattern cells, with each cell the size of the pattern image, and draws each cell using a callback you
provide. Figure 6-1 shows a pattern drawn to a window graphics context.
Figure 6-1
A pattern cell
89
Patterns
The Anatomy of a Pattern
The size of this particular pattern cell includes the area of the four colored rectangles and space above and to
the right of the rectangles, as shown in Figure 6-3. The black rectangle surrounding each pattern cell in the
figure is not part of the cell; its drawn to indicate the bounds of the cell. When you create a pattern cell, you
define the bounds of the cell and draw within the bounds.
Figure 6-3
Pattern cells with black rectangles drawn to show the bounds of each cell
You can specify how far apart Quartz draws the start of each pattern cell from the next in the horizontal and
vertical directions. The pattern cells in Figure 6-3 are drawn so that the start of one pattern cell is exactly a
pattern width apart from the next pattern cell, resulting in each pattern cell abutting on the next. The pattern
cells in Figure 6-4 have space added in both directions, horizontal and vertical. You can specify different spacing
values for each direction. If you make the spacing less than the width or height of a pattern cell, the pattern
cells overlap.
Figure 6-4
90
Patterns
The Anatomy of a Pattern
When you draw a pattern cell, Quartz uses pattern space as the coordinate system. Pattern space is an abstract
space that maps to the default user space by the transformation matrix you specify when you create the
patternthe pattern matrix.
91
Patterns
The Anatomy of a Pattern
Note: Pattern space is separate from user space. The untransformed pattern space maps to the base
(untransformed) user space, regardless of the state of the current transformation matrix. When you
apply a transformation to pattern space, Quartz applies the transform only to pattern space.
The default conventions for a patterns coordinate systems are those of the underlying graphics
context. By default, Quartz uses a coordinate system where a positive x value represents a
displacement to the right and a positive y value represents an upward displacement. However, a
graphics context created by UIKit uses a different convention, where positive y values indicate a
downward displacement. While this convention is normally applied to the graphics context by
concatenating a transformation onto the coordinate system, in this case, Quartz also modifies the
default conventions of pattern space to match.
If you dont want Quartz to transform the pattern cell, you can specify the identity matrix. However, you can
achieve interesting effects by supplying a transformation matrix. Figure 6-5 shows the effect of scaling the
pattern cell shown in Figure 6-2. Figure 6-6 demonstrates rotating the pattern cell. Translating the pattern cell
is a bit more subtle. Figure 6-7 shows the origin of the pattern, with the pattern cell translated in both directions,
horizontal and vertical, so that the pattern no longer abuts the window as it does in Figure 6-1 (page 89).
Figure 6-5
Figure 6-6
92
Patterns
Colored Patterns and Stencil (Uncolored) Patterns
Figure 6-7
Other patterns are defined solely on their shape and, for that reason, can be thought of as stencil patterns,
uncolored patterns, or even as an image mask. The red and black stars shown in Figure 6-9 are each renditions
of the same pattern cell. The cell itself consists of one shapea filled star. When the pattern cell was defined,
no color was associated with it. The color is specified as part of the pattern drawing process, not as part of the
pattern cell creation.
Figure 6-9
93
Patterns
Tiling
Tiling
Tiling is the process of rendering pattern cells to a portion of a page. When Quartz renders a pattern to a device,
Quartz may need to adjust the pattern to fit the device space. That is, the pattern cell as defined in user space
might not fit perfectly when rendered to the device because of differences between user space units and
device pixels.
Quartz has three tiling options it can use to adjust patterns when necessary. Quartz can preserve:
The pattern, at the expense of adjusting the spacing between pattern cells slightly, but by no more than
one device pixel. This is referred to as no distortion.
Spacing between cells, at the expense of distorting the pattern cell slightly, but by no more than one
device pixel. This is referred to as constant spacing with minimal distortion.
Spacing between cells (as for the minimal distortion option) at the expense of distorting the pattern cell
as much as needed to get fast tiling. This is referred to as constant spacing.
2.
Translates the current transformation matrix to the origin of the pattern cell.
3.
4.
5.
6.
94
Patterns
Painting Colored Patterns
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.
Write a Callback Function That Draws a Colored Pattern Cell (page 95)
2.
3.
4.
5.
These are the same steps you use to paint a stencil pattern. The difference between the two is how you set up
color information. You can see how all the steps fit together in A Complete Colored Pattern Painting
Function (page 99).
You can name your callback whatever you like. The one in Listing 6-1 is named MyDrawColoredPattern.
The callback takes two parameters:
95
Patterns
Painting Colored Patterns
info, a generic pointer to private data associated with the pattern. This parameter is optional; you can
pass NULL. The data passed to your callback is the same data you supply later, when you create the pattern.
context, the graphics context for drawing the pattern cell.
The pattern cell drawn by the code in Listing 6-1 is arbitrary. Your code draws whatever is appropriate for the
pattern you create. These details about the code are important:
The pattern size is declared. You need to keep the pattern size in mind as you write your drawing code.
Here, the size is declared as a global. The drawing function doesnt specifically refer to the size, except in
a comment. Later, you specify the pattern size to Quartz 2D. See Set Up the Anatomy of the Colored
Pattern (page 97).
The drawing function follows the prototype defined by the CGPatternDrawPatternCallback callback
type definition.
The drawing performed in the code sets colors, which makes this a colored pattern.
Listing 6-1
#define H_PATTERN_SIZE 16
#define V_PATTERN_SIZE 18
CGRect
96
Patterns
Painting Colored Patterns
CGColorSpaceRef patternSpace;
// 1
// 2
CGColorSpaceRelease (patternSpace);
// 3
Creates a pattern color space appropriate for a colored pattern by calling the function
CGColorSpaceCreatePattern, passing NULL as the base color space.
2.
Sets the fill color space to the pattern color space. If you are stroking your pattern, call
CGContextSetStrokeColorSpace.
3.
CGPatternRef CGPatternCreate (
void *info,
CGRect bounds,
CGAffineTransform matrix,
CGFloat xStep,
CGFloat yStep,
CGPatternTiling tiling,
97
Patterns
Painting Colored Patterns
bool isColored,
const CGPatternCallbacks *callbacks );
The info parameter is a pointer to data you want to pass to your drawing callback. This is the same pointer
discussed in Write a Callback Function That Draws a Colored Pattern Cell (page 95).
You specify the size of the pattern cell in the bounds parameter. The matrix parameter is where you specify
the pattern matrix, which maps the pattern coordinate system to the default coordinate system of the graphics
context. Use the identity matrix if you want to draw the pattern using the same coordinate system as the
graphics context. The xStep and yStep parameters specify the horizontal and vertical spacing between cells
in the pattern coordinate system. See The Anatomy of a Pattern (page 89) to review information on bounds,
pattern matrix, and spacing.
The tiling parameter can be one of three values:
kCGPatternTilingNoDistortion
kCGPatternTilingConstantSpacingMinimalDistortion
kCGPatternTilingConstantSpacing
98
Patterns
Painting Colored Patterns
You set the version field to 0. The drawPattern field is a pointer to your drawing callback. The releaseInfo
field is a pointer to a callback thats invoked when the CGPattern object is released, to release storage for the
info parameter you passed to your drawing callback. If you didnt pass any data in this parameter, you set
this field to NULL.
Although colored patterns supply their own color, you must pass a single alpha value to inform Quartz of the
overall opacity of the pattern when its drawn. Alpha can vary from 1 (completely opaque) to 0 (completely
transparent). These lines of code show an example of how to set opacity for a colored pattern used to fill.
CGFloat alpha = 1;
99
Patterns
Painting Colored Patterns
{
CGPatternRef
pattern;
// 1
CGColorSpaceRef patternSpace;
// 2
CGFloat
alpha = 1,
// 3
width, height;
// 4
// 5
static const
&MyDrawPattern,
NULL};
CGContextSaveGState (myContext);
patternSpace = CGColorSpaceCreatePattern (NULL);
// 6
// 7
CGColorSpaceRelease (patternSpace);
// 8
// 9
// 10
// 11
H_PATTERN_SIZE,
// 12
V_PATTERN_SIZE,
// 13
kCGPatternTilingConstantSpacing,
// 14
true,
// 15
&callbacks);
// 16
// 17
CGPatternRelease (pattern);
// 18
// 19
CGContextRestoreGState (myContext);
}
2.
3.
Declares a variable for alpha and sets it to 1, which specifies the opacity of the pattern as completely
opaque.
100
Patterns
Painting Stencil Patterns
4.
Declares variable to hold the height and width of the window. In this example, the pattern is painted over
the area of a window.
5.
Declares and fills a callbacks structure, passing 0 as the version and a pointer to a drawing callback function.
This example does not provide a release info callback, so that field is set to NULL.
6.
Creates a pattern color space object, setting the patterns base color space to NULL. When you paint a
colored pattern, the pattern supplies its own color in the drawing callback, which is why you set the color
space to NULL.
7.
Sets the fill color space to the pattern color space object you just created.
8.
9.
Passes NULL because the pattern does not need any additional information passed to the drawing callback.
10. Passes a CGRect object that specifies the bounds of the pattern cell.
11. Passes a CGAffineTransform matrix that specifies how to translate the pattern space to the default user
space of the context in which the pattern is used. This example passes the identity matrix.
12. Passes the horizontal pattern size as the horizontal displacement between the start of each cell. In this
callback function.
17. Sets the fill pattern, passing the context, the CGPattern object you just created, and a pointer to the alpha
Quartz fills the rectangle using the pattern you just set up.
Write a Callback Function That Draws a Stencil Pattern Cell (page 102)
2.
3.
101
Patterns
Painting Stencil Patterns
4.
5.
These are actually the same steps you use to paint a colored pattern. The difference between the two is how
you set up color information. You can see how all the steps fit together in A Complete Stencil Pattern Painting
Function (page 104).
Take a look at the code in Listing 6-5, which draws the pattern cell shown in Figure 6-10. Notice that the code
simply creates a path and fills the path. The code does not set color.
Listing 6-5
#define PSIZE 16
r = 0.8 * PSIZE / 2;
theta = 2 * M_PI * (2.0 / 5.0); // 144 degrees
CGContextMoveToPoint(myContext, 0, r);
102
Patterns
Painting Stencil Patterns
CGPatternRef pattern;
CGColorSpaceRef baseSpace;
CGColorSpaceRef patternSpace;
// 1
// 2
// 3
CGColorSpaceRelease(patternSpace);
// 4
CGColorSpaceRelease(baseSpace);
// 5
This function creates a generic RGB space. Generic color spaces leave color matching to the system. For
more information, see Creating Generic Color Spaces (page 72).
2.
Creates a pattern color space. The color space you supply specifies how colors are represented for the
pattern. Later, when you set colors for the pattern, you must set them using the pattern color space. For
this example, you will need to specify color using RGB values.
3.
Sets the color space to use when filling a pattern. You can set a stroke color space by calling the function
CGContextSetStrokeColorSpace.
4.
5.
103
Patterns
Painting Stencil Patterns
A stencil pattern does not supply a color in the drawing callback, so you must pass a color to the fill or stroke
functions to inform Quartz what color to use. Listing 6-7 shows an example of how to set color for a stencil
pattern. The values in the color array are interpreted by Quartz in the color space you set up earlier. Because
this example uses device RGB, the color array contains values for red, green, and blue components. The fourth
value specifies the opacity of the color.
Listing 6-7
104
Patterns
Painting Stencil Patterns
Listing 6-8
#define PSIZE 16
// 1
// 2
// 3
// 4
// 5
CGColorSpaceRelease (patternSpace);
CGColorSpaceRelease (baseSpace);
pattern = CGPatternCreate(NULL, CGRectMake(0, 0, PSIZE, PSIZE),
// 6
// 7
CGPatternRelease (pattern);
// 8
// 9
Declares an array to hold a color value and sets the value (which will be in RGB color space) to opaque
green.
2.
Declares and fills a callbacks structure, passing 0 as the version and a pointer to a drawing callback function.
This example does not provide a release info callback, so that field is set to NULL.
3.
Creates an RGB device color space. If the pattern is drawn to a display, you need to supply this type of
color space.
4.
Creates a pattern color space object from the RGB device color space.
5.
Sets the fill color space to the pattern color space object you just created.
105
Patterns
Painting Stencil Patterns
6.
Creates a pattern object. Note that the second to last parameterthe isColored parameteris false.
Stencil patterns do not supply color, so you must pass false for this parameter. All other parameters are
similar to those passed for the colored pattern example. See A Complete Colored Pattern Painting
Function (page 99).
7.
Sets the fill pattern, passing the color array declared previously.
8.
9.
Fills a rectangle. Quartz fills the rectangle using the pattern you just set up.
106
Shadows
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, as shown in Figure 7-1. Text can also be shadowed.
Shadows can make an image appear three dimensional or as if its floating.
Figure 7-1
A shadow
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, as seen in the left side of Figure 7-2, or
a diffuse edge, as seen in the right side of the figure.
This chapter describes how shadows work and shows how to use the Quartz 2D API to create them.
Figure 7-2
107
Shadows
How Shadows Work
A positive x offset indicates the shadows is to the right of the graphics object.
In Mac OS X, a positive y offset indicates upward displacement. This matches the default coordinate system
for Quartz 2D.
In iOS, if your application uses Quartz 2D APIs to create a PDF or bitmap context, a positive y offset indicates
upwards displacement.
In iOS, if the graphics context was created by UIKit, such as a graphics context created by a UIView object
or a context created by calling the UIGraphicsBeginImageContextWithOptions function, then a
positive y offset indicates a downward displacement. This matches the drawing conventions of the UIKit
coordinate system.
2.
108
Shadows
Painting with Shadows
3.
4.
2.
Create a CGColorSpace object to ensure that Quartz interprets the shadow color values correctly.
3.
Create a CGColor object that specifies the shadow color you want to use.
4.
5.
6.
The two rectangles in Figure 7-3 are drawn with shadowsone with a colored shadow.
Figure 7-3
The function in Listing 7-1 shows how to set up shadows to draw the rectangles shown in Figure 7-3. A detailed
explanation for each numbered line of code appears following the listing.
Listing 7-1
// 1
20);
// 2
// 3
// 4
CGColorSpaceRef myColorSpace;
// 5
109
Shadows
Painting with Shadows
CGContextSaveGState(myContext);
// 6
// 7
// 8
// 9
// 10
// 11
// 12
CGColorRelease (myColor);
// 13
CGColorSpaceRelease (myColorSpace);
// 14
CGContextRestoreGState(myContext);
// 15
Takes three parametersa graphics context and a width and height to use when constructing the
rectangles.
2.
Declares and creates a CGSize object that contains offset values for the shadow. These values specify a
shadow offset 15 units to the left of the object and 20 units above the object.
3.
Declares an array of color values. This example uses RGBA, but these values wont take on any meaning
until they are passed to Quartz along with a color space, which is necessary for Quartz to interpret the
values correctly.
4.
5.
6.
Saves the current graphics state so that you can restore it later.
7.
Sets a shadow to have the previously declared offset values and a blur value of 5, which indicates a soft
shadow edge. The shadow will appear gray, having an RGBA value of {0, 0, 0, 1/3}.
110
Shadows
Painting with Shadows
8.
The next two lines of code draw the rectangle on the right side of Figure 7-3 (page 109). You replace these
lines with your own drawing code.
9.
Creates a device RGB color space. You need to supply a color space when you create a CGColor object.
10. Creates a CGColor object, supplying the device RGB color space and the RGBA values declared previously.
This object specifies the shadow color, which in this case is red with an alpha value of 0.6.
11. Sets up a color shadow, supplying the red color you just created. The shadow uses the offset created
111
Gradients
Quartz provides two opaque data types for creating gradientsCGShadingRef and CGGradientRef. You
can use either of these to create axial or radial gradients. A gradient is a fill that varies from one color to another.
An axial gradient (also called a linear gradient ) varies along an axis between two defined end points. All points
that lie on a line perpendicular to the axis have the same color value.
A radial gradient is a fill that varies radially along an axis between two defined ends, which typically are both
circles. Points share the same color value if they lie on the circumference of a circle whose center point falls
on the axis. The radius of the circular sections of the gradient are defined by the radii of the end circles; the
radius of each intermediate circle varies linearly from one end to the other.
This chapter provides examples of the sorts of linear and radial gradients you can create with Quartz, compares
the two approaches you can take to painting gradients, and then shows how to use each opaque data type
to create a gradient.
112
Gradients
Axial and Radial Gradient Examples
Quartz also lets you specify colors and locations along an axis to create more complex axial gradients, as shown
in Figure 8-2. The color at the starting point is a shade of red and the color at the ending point is a shade of
violet. However, there are also five locations on the axis whose color is set to orange, yellow, green, blue, and
indigo, respectively. You can think of the result as six sequential linear gradients along the same axis. Although
the axis used here is the same as that used in Figure 8-1 (45 degree angle), it doesnt have to be. The angle of
the axis is defined by the starting and ending point that you provide.
Figure 8-2
Figure 8-3 shows a radial gradient that varies between a small, bright red circle and a larger black one.
Figure 8-3
113
Gradients
Axial and Radial Gradient Examples
With Quartz, you are not restricted to creating gradients based on color changes; you can vary only the alpha,
or you can vary the alpha along with the other color components. Figure 8-4 shows a gradient whose red,
green, and blue components remain constant as the alpha value varies from 1.0 to 0.1.
Note: If you vary a gradient using alpha, you will not be able to capture that gradient when drawing
to a PDF content. Because of this, such a gradient cant be printed. If you need to draw a gradient
to a PDF, use an alpha of 1.0.
Figure 8-4
You can position the circles in a radial gradient to create a variety of shapes. If one circle is partially or fully
outside the other, Quartz creates a conical surface for circles that have unequal circumferences, and a cylindrical
surface for circles that have equal circumferences. A common use of a radial gradient is to create a shaded
sphere, as shown in Figure 8-5. In this case, a single point (a circle with a radius of 0) lies within a larger circle.
Figure 8-5
114
Gradients
A Comparison of CGShading and CGGradient Objects
You can create more complex effects by nesting several radial gradients similar to the shape shown in Figure
8-6. The toroidal portion of the shape is created using concentric circles.
Figure 8-6
115
Gradients
Extending Color Beyond the End of a Gradient
When you create a CGGradient object, you simply set up a color space, locations, and a color for each location.
When you draw to a context using a gradient object, you specify whether Quartz should draw an axial or radial
gradient. At drawing time, you specify starting and ending points or radii, depending on whether you draw
an axial or radial gradient, in contrast to CGShading objects, whose geometry is defined at creation time, not
at drawing time.
Table 8-1 summarizes the differences between the two opaque data types.
Table 8-1
CGGradient
CGShading
116
Gradients
Extending Color Beyond the End of a Gradient
Figure 8-7 shows an axial gradient that extends at both the starting and ending locations. The line in the figure
shows the axis of the gradient. As you can see, the fill colors correspond to the colors at the starting and ending
points.
Figure 8-7
Figure 8-8 compares a radial gradient that does not use the extension options with one that uses extension
options for both the starting and ending locations. Quartz takes the starting and ending color values and uses
those solid colors to extend the surface as shown. The figure shows the starting and ending circles, and the
axis of the gradient.
Figure 8-8
117
Gradients
Using a CGGradient Object
Create a CGGradient object, supplying a color space, an array of two or more color components, an array
of two or more locations, and the number of items in each of the two arrays.
2.
3.
A location is a CGFloat value in the range of 0.0 to 1.0, inclusive, that specifies the normalized distance along
the axis of the gradient. A value of 0.0 specifies the starting point of the axis, while 1.0 specifies the ending
point of the axis. Other values specify a proportion of the distance, such as 0.25 for one-fourth of the distance
from the starting point and 0.5 for the halfway point on the axis. At a minimum, Quartz uses two locations. If
you pass NULL for the locations array, Quartz uses 0 for the first location and 1 for the second.
The number of color components per color depends on the color space. For onscreen drawing, youll use an
RGB color space. Because Quartz draws with alpha, each onscreen color has four componentsred, green,
blue, and alpha. So, for onscreen drawing, the number of elements in the color component array that you
provide must contain four times the number of locations. Quartz RGBA color components can vary in value
from 0.0 to 1.0, inclusive.
Listing 8-1 is a code fragment that creates a CGGradient object. After declaring the necessary variables, the
code sets the locations and the requisite number of color components (for this example, 2 X 4 = 8). It creates
a generic RGB color space. (In iOS, where generic RGB color spaces are not available, your code should call
CGColorSpaceCreateDeviceRGB instead.) Then, it passes the necessary parameters to the function
CGGradientCreateWithColorComponents. You can also use the function CGGradientCreateWithColors
which is convenient if your application sets up CGColor objects.
Listing 8-1
CGGradientRef myGradient;
CGColorSpaceRef myColorspace;
118
Gradients
Using a CGGradient Object
size_t num_locations = 2;
CGFloat locations[2] = { 0.0, 1.0 };
CGFloat components[8] = { 1.0, 0.5, 0.4, 1.0,
// Start color
myColorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
myGradient = CGGradientCreateWithColorComponents (myColorspace, components,
locations, num_locations);
After you create a CGGradient object, you can use it to paint an axial or linear gradient. Listing 8-2 is a code
fragment that declares and sets the starting and ending points for a linear gradient and then paints the gradient.
Figure 8-1 (page 112) shows the result. The code does not show how to obtain the CGContext object
(myContext).
Listing 8-2
Listing 8-3 is a code fragment that uses the CGGradient object created in Listing 8-1 (page 118) to paint the
radial gradient shown in Figure 8-9 (page 120). This example illustrates the result of extending the area of the
gradient by filling it with a solid color.
Listing 8-3
119
Gradients
Using a CGGradient Object
Figure 8-9
The radial gradient shown in Figure 8-4 (page 114) was created using the variables shown in Listing 8-4.
Listing 8-4
120
Gradients
Using a CGShading Object
Listing 8-5 shows the variables used to create the gray gradient shown in Figure 8-10, which has three locations.
Listing 8-5
size_t num_locations = 3;
CGFloat locations[3] = { 0.0, 0.5, 1.0};
CGFloat components[12] = {
Figure 8-10
A CGColorSpace object that describe the color space for Quartz to use when it interprets the color
component values your callback supplies.
121
Gradients
Using a CGShading Object
Starting and ending points. For axial gradients, these are the starting and ending coordinates (in user
space) of the axis. For radial gradients, these are the coordinates of the center of the starting and ending
circles.
Starting and ending radii (only for a radial gradient) for the circles used to define the gradient area.
A CGFunction object, which you obtain by calling the function CGFunctionCreate, discussed later in
this section. This callback routine must return a color to draw at a particular point.
Boolean values that specify whether to fill the area beyond the starting or ending points with a solid color.
The CGFunction object you supply to the CGShading creation functions contains a callbacks structure and all
the information Quartz needs to implement your callback. Perhaps the trickiest part of setting up a CGShading
object is creating the CGFunction object. When you call the function CGFunctionCreate, you supply the
following:
The number of input values to your callback. Quartz requires that your callback takes one input value.
An array of floating-point values. Quartz supplies your callback with only one element in this array. An
input value can range from 0, for the color at the start of the gradient, to 1, for the color at the end of the
gradient.
The number of output values provided by your callback. For each input value, your callback must supply
a value for each color component and an alpha value to designate opacity. The color component values
are interpreted by Quartz in the color space you create and supply to the CGShading creation function.
For example, if you are using an RGB color space, you supply the value 4 as the number of output values
(R, G, B, and A).
An array of floating-point values that specify each of the color components and an alpha value.
A callbacks data structure that contains the version of the structure (set this field to 0), your callback for
generating color component values, and an optional callback to release the data supplied to your callback
in the info parameter. If you were to name your callback myCalculateShadingValues, it would look
like this:
void myCalculateShadingValues (void *info, const CGFloat *in, CGFloat *out)
After you create the CGShading object, you can set up additional clipping if you need to do so. Then, call the
function CGContextDrawShading to paint the clipping area of the context with the gradient. When you call
this function, Quartz invokes your callback to obtain color values that span the range from the starting point
to the ending point.
When you no longer need the CGShading object, you release it by calling the function CGShadingRelease.
122
Gradients
Using a CGShading Object
Painting an Axial Gradient Using a CGShading Object (page 123) and Painting a Radial Gradient Using a
CGShading Object (page 130) provide step-by-step instructions on writing code that uses a CGShading object
to draw a gradient.
To paint the axial gradient shown in the figure, follow the steps explained in these sections:
1.
2.
3.
4.
5.
void *info. This is NULL or a pointer to data you pass to the CGShading creation function.
const CGFloat *in. Quartz passes the in array to your callback. The values in the array must be in the
input value range defined for your CGFunction object. For this example, the input range is 0 to 1; see
Listing 8-7 (page 125).
123
Gradients
Using a CGShading Object
CGFloat *out. Your callback passes the out array to Quartz. It contains one element for each color
component in the color space, and an alpha value. Output values should be in the output value range
defined for your CGFunction object. For this example, the output range is 0 to 1; see Listing 8-7 (page 125).
For more information on these parameters, see CGFunctionEvaluateCallback.
Listing 8-6 shows a function that computes color component values by multiplying the values defined in a
constant array by the input value. Because the input value ranges from 0 through 1, the output values range
from black (for RGB, the values 0, 0, 0), through (1, 0, .5) which is a purple hue. Note that the last component
is always set to 1, so that the colors are always fully opaque.
Listing 8-6
components = (size_t)info;
v = *in;
for (k = 0; k < components -1; k++)
*out++ = c[k] * v;
*out++ = 1;
}
After you write your callback to compute color values, you package it as part of a CGFunction object. Its the
CGFunction object you supply to Quartz when you create a CGShading object. Listing 8-7 shows a function
that creates a CGFunction object that contains the callback from Listing 8-6. A detailed explanation for each
numbered line of code appears following the listing.
124
Gradients
Using a CGShading Object
Listing 8-7
// 1
{
size_t numComponents;
static const CGFloat input_value_range [2] = { 0, 1 };
static const CGFloat output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 };
static const CGFunctionCallbacks callbacks = { 0,
// 2
&myCalculateShadingValues,
NULL };
// 3
// 4
1,
// 5
input_value_range,
// 6
numComponents,
// 7
output_value_ranges,
// 8
&callbacks);
// 9
2.
Declares a callbacks structure and fills it with the version of the structure (0), a pointer to your color
component calculation callback, and NULL for the optional release function.
3.
Calculates the number of color components in the color space and increments the value by 1 to account
for the alpha value.
4.
Passes a pointer to the numComponents value. This value is used by the callback
myCalculateShadingValues to determine the number of components to compute.
5.
6.
Provides an array that specifies the valid intervals for the input. This array contains 0 and 1.
7.
Passes the number of output values, which is the number of color components plus alpha.
8.
Provides an array that specifies the valid intervals for each output value. This array specifies, for each
component, the intervals 0 and 1. Because there are four components, there are eight elements in this
array.
125
Gradients
Using a CGShading Object
9.
CGFunctionRef myFunctionObject;
CGShadingRef myShading;
startPoint = CGPointMake(0,0.5);
endPoint = CGPointMake(1,0.5);
colorspace = CGColorSpaceCreateDeviceRGB();
myFunctionObject = myGetFunction (colorspace);
126
Gradients
Using a CGShading Object
Listing 8-9
CGContextBeginPath (myContext);
CGContextAddArc (myContext, .5, .5, .3, 0,
my_convert_to_radians (180), 0);
CGContextClosePath (myContext);
CGContextClip (myContext);
Release Objects
You call the function CGShadingRelease when you no longer need the CGShading object. You also need to
release the CGColorSpace object and the CGFunction object as shown in Listing 8-10.
Listing 8-10 Releasing objects
CGShadingRelease (myShading);
CGColorSpaceRelease (colorspace);
CGFunctionRelease (myFunctionObject);
startPoint,
endPoint;
CGAffineTransform myTransform;
127
// 1
Gradients
Using a CGShading Object
startPoint = CGPointMake(0,0.5);
// 2
endPoint = CGPointMake(1,0.5);
// 3
colorspace = CGColorSpaceCreateDeviceRGB();
// 4
myShadingFunction = myGetFunction(colorspace);
// 5
// 6
startPoint, endPoint,
myShadingFunction,
false, false);
// 7
// 8
CGContextSaveGState (myContext);
// 9
// 10
CGContextBeginPath (myContext);
// 11
// 12
CGColorSpaceRelease (colorspace);
// 13
CGShadingRelease (shading);
CGFunctionRelease (myShadingFunction);
128
Gradients
Using a CGShading Object
CGContextRestoreGState (myContext);
// 14
2.
Assigns a value to the starting point. The routine calculates values based on a user space that varies from
0 to 1. Youll scale the space later for the window that Quartz draws into. You can think of this coordinate
location as x at the far left side and y at 50% from the bottom.
3.
Assigns a value to the ending point. You can think of this coordinate location as x at the far right side and
y at 50% from the bottom. As you can see, the axis for the gradient is a horizontal line.
4.
Creates a color space for device RGB because this routine draws to the display.
5.
Creates a CGFunction object by calling the routine shown in Listing 8-7 (page 125) and passing the color
space you just created.
6.
Creates a CGShading object for an axial gradient. The last two parameters are false, to signal that Quartz
should not fill the area beyond the starting and ending points.
7.
Sets up an affine transform that is scaled to the height and width of the window used for drawing. Note
that the height is not necessarily equal to the width. In this example, because the two arent equal, the
end result is elliptical rather than circular.
8.
Concatenates the transform you just set up with the graphics context passed to the routine.
9.
Saves the graphics state to enable you to restore this state later.
10. Sets up a clipping area. This line and the next two lines clip the context to a rectangle that is filled with
white. The effect is that the gradient is drawn to a window with a white background.
11. Creates a path. This line and the next three lines set up an arc that is half a circle and adds it to the graphics
context as a clipping area. The effect is that the gradient is drawn to an area that is half a circle. However,
the circle will be transformed by the height and width of the window (see step 8), resulting in a final effect
of a gradient drawn to a half ellipse. As the window is resized by the user, the clipping area is resized.
12. Paints the gradient to the graphics context, transforming and clipping the gradient as described previously.
13. Releases objects. This line and the next two lines release all the objects you created.
14. Restores the graphics state to the state that existed before you set up the filled background and clipped
to half a circle. The restored state is still transformed by the width and height of the window.
129
Gradients
Using a CGShading Object
To paint a radial gradient, follow the steps explained in the following sections:
1.
2.
3.
4.
130
Gradients
Using a CGShading Object
from the colors shown in Figure 8-11 (page 123). Despite the differences in color output, the code in Listing
8-12 is similar to Listing 8-6 (page 124) in that each function follows the same prototype. Each function takes
one input value and calculates N values, one for each color component of the color space plus an alpha value.
Listing 8-12 Computing color component values
static void
{
size_t k, components;
double frequency[4] = { 55, 220, 110, 0 };
components = (size_t)info;
for (k = 0; k < components - 1; k++)
*out++ = (1 + sin(*in * frequency[k]))/2;
*out++ = 1; // alpha
}
Recall that after you write a color computation function, you need to create a CGFunction object, as described
for axial values in Set Up a CGFunction Object to Compute Color Values (page 123).
startPoint = CGPointMake(0.25,0.3);
startRadius = .1;
endPoint = CGPointMake(.7,0.7);
endRadius = .25;
colorspace = CGColorSpaceCreateDeviceRGB();
myShadingFunction = myGetFunction (colorspace);
131
Gradients
Using a CGShading Object
CGShadingCreateRadial (colorspace,
startPoint,
startRadius,
endPoint,
endRadius,
myShadingFunction,
false,
false);
Notice that you use the same function to paint a gradient regardless of whether the gradient is axial or radial.
Release Objects
You call the function CGShadingRelease when you no longer need the CGShading object. You also need to
release the CGColorSpace object and the CGFunction object as shown in Listing 8-14.
Listing 8-14 Code that releases objects
CGShadingRelease (myShading);
CGColorSpaceRelease (colorspace);
CGFunctionRelease (myFunctionObject);
132
// 1
Gradients
Using a CGShading Object
CGRect bounds);
{
CGPoint startPoint,
endPoint;
CGFloat startRadius,
endRadius;
CGAffineTransform myTransform;
CGFloat width = bounds.size.width;
CGFloat height = bounds.size.height;
startPoint = CGPointMake(0.25,0.3);
// 2
startRadius = .1;
// 3
endPoint = CGPointMake(.7,0.7);
// 4
endRadius = .25;
// 5
colorspace = CGColorSpaceCreateDeviceRGB();
// 6
// 7
// 8
startPoint, startRadius,
endPoint, endRadius,
myShadingFunction,
false, false);
// 9
// 10
CGContextSaveGState (myContext);
// 11
// 12
// 13
CGColorSpaceRelease (colorspace);
// 14
CGShadingRelease (shading);
133
Gradients
Using a CGShading Object
CGFunctionRelease (myShadingFunction);
CGContextRestoreGState (myContext);
// 15
2.
Assigns a value to the center of the starting circle. The routine calculates values based on a user space
that varies from 0 to 1. Youll scale the space later for the window Quartz draws into. You can think of this
coordinate location as x at 25% from the left and y at 30% from the bottom.
3.
Assigns the radius of the starting circle. You can think of this as 10% of the width of user space.
4.
Assigns a value to the center of the ending circle. You can think of this coordinate location as x at 70%
from the left and y at 70% from the bottom.
5.
Assigns the radius of the ending circle. You can think of this as 25% of the width of user space. The ending
circle will be larger than the starting circle. The conical shape will be oriented from left to right, tipped
upwards.
6.
Creates a color space for device RGB because this routine draws to the display.
7.
Creates a CGFunctionObject by calling the routine shown in Listing 8-7 (page 125) and passing the color
space you just created. However, recall that youll use the color calculation function shown in Listing
8-12 (page 131).
8.
Creates a CGShading object for a radial gradient. The last two parameters are false, to signal that Quartz
should not fill the area beyond the starting and ending points of the gradient.
9.
Sets up an affine transform that is scaled to the height and width of the window used for drawing. Note
that the height is not necessarily equal to the width. In fact, the transformation will change whenever the
user resizes the window.
10. Concatenates the transform you just set up with the graphics context passed to the routine.
11. Saves the graphics state to enable you to restore this state later.
12. Sets up a clipping area. This line and the next two lines clip the context to a rectangle that is filled with
white. The effect is that the gradient is drawn to a window with a white background.
13. Paints the gradient to the graphics context transforming the gradient as described previously.
14. Releases object. This line and the next two lines release all the objects you created.
15. Restores the graphics state to the state that existed before you set up the filled background. The restored
134
Gradients
See Also
See Also
CGFunction Reference describes the functions needed to calculate gradient colors for a CGShading object.
CGContext Reference describes the functions that draw to a context with CGGradient and CGShading
objects.
135
Transparency Layers
A transparency layer consists of two or more objects that are combined to yield a composite graphic. The
resulting composite is treated as a single object. Transparency layers are useful when you want to apply an
effect to a group of objects, such as the shadow applied to the three circles in Figure 9-1.
Figure 9-1
136
Transparency Layers
How Transparency Layers Work
If you apply a shadow to the three circles in Figure 9-1 without first rendering them to a transparency layer,
you get the result shown in Figure 9-2.
Figure 9-2
137
Transparency Layers
Painting to a Transparency Layer
2.
3.
The three rectangles in Figure 9-3 are painted to a transparency layer. Quartz renders the shadow as if the
rectangles are a single unit.
Figure 9-3
The function in Listing 9-1 shows how to use a transparency layer to generate the rectangles in Figure 9-3. A
detailed explanation for each numbered line of code follows the listing.
Listing 9-1
// 1
CGFloat wd,
CGFloat ht)
{
CGSize
// 2
// 3
// 4
// 5
138
Transparency Layers
Painting to a Transparency Layer
// 6
Takes three parametersa graphics context and a width and height to use when constructing the
rectangles.
2.
Sets up a CGSize data structure that contains the x and y offset values for the shadow. This shadow is
offset by 10 units in the horizontal direction and 20 units in the vertical direction.
3.
Sets the shadow, specifying a value of 10 as the blur value. (A value of 0 specifies a hard edge shadow
with no blur.)
4.
Signals the start of the transparency layer. From this point on, drawing occurs to this layer.
5.
The next six lines of code set fill colors and fill the three rectangles shown in Figure 9-3 (page 138). You
replace these lines with your own drawing code.
6.
Signals the end of the transparency layer and signals that Quartz should composite the result into the
context.
139
Managing data is a task every graphics application needs to perform. For Quartz, data management refers to
supplying data to or receiving data from Quartz 2D routines. Some Quartz 2D routines move data into Quartz,
such as those that get image or PDF data from a file or another part of your application. Other routines accept
Quartz data, such as those that write image or PDF data to a file or provide the data to another part of your
application.
Quartz provides a variety of functions for managing data. By reading this chapter, you should be able to
determine which functions are best for your application.
Note: The preferred way to read and write image data is to use the Image I/O framework, which is
available in iOS 4 and Mac OS X 10.4 and later. See Image I/O Programming Guide for more information
on the CGImageSourceRef and CGImageDestinationRef opaque data types. Image sources and
destinations not only offer access to image data, but also provide better support for accessing image
metadata.
URL. Data whose location can be specified as a URL can act as a supplier or receiver of data. You pass a
URL to a Quartz function using the Core Foundation data type CFURLRef.
CFData. The Core Foundation data types CFDataRef and CFMutableDataRef are data objects that let
simple allocated buffers take on the behavior of Core Foundation objects. CFData is toll-free bridged
with its Cocoa Foundation counterpart, the NSData class; if you are using Quartz 2D with the Cocoa
framework, you can pass an NSData object to any Quartz function that takes a CFData object.
Raw data. You can provide a pointer to data of any type along with a set of callbacks that take care of
basic memory management for the data.
The data itself, whether represented by a URL, a CFData object, or a data buffer, can be image data or PDF
data. Image data can use any type of file format. Quartz understands most of the common image file formats.
Some of the Quartz data management functions work specifically with image data, a few work only with PDF
data, while others are more generic and can be used either for PDF or image data.
140
URL, CFData, and raw data sources and destinations refer to data outside the realm of Mac OS X or iOS graphics
technologies, as shown in Figure 10-1. Other graphics technologies in Mac OS X or iOS often provide their own
routines to communicate with Quartz. For example, a Mac OS X application can send Quartz images to Core
Image and use it to alter the image with sophisticated effects.
Figure 10-1
URL
Quartz 2D
Raw data
CFData
Raw data
Core Image
The function CGPDFDocumentCreateWithURL is a convenience function that creates a PDF document from
the file located at the specified URL.
141
Data providers are an older mechanism with more limited functionality. They can be used to obtain image or
PDF data.
You can supply a data provider to:
The function CGImageSourceUpdateDataProvider to update an existing image source with new data.
For more information on images, see Bitmap Images and Image Masks (page 146).
Table 10-1
Function
CGImageSourceCreateWithDataProvider
CGImageSourceCreateWithData
CGImageSourceCreateWithURL
CGPDFDocumentCreateWithURL
CGDataProviderCreateSequential
CGDataProviderCreateDirectAccess
CGDataProviderCreateWithData
CGDataProviderCreateWithURL
CGDataProviderCreateWithCFData
142
The function CGPDFContextCreateWithURL is a convenience function that writes PDF data to the location
specified by a URL.
Data consumers are an older mechanism with more limited functionality. They are used to write image or PDF
data. You can supply a data consumer to:
The PDF context creation function CGPDFContextCreate. This function returns a graphics context that
records your drawing as a sequence of PDF drawing commands that are passed to the data consumer
object.
Note: For the best performance when working with raw image data, use the vImage framework.
You can import image data to vImage from a CGImageRef reference with the
vImageBuffer_InitWithCGImage function. For details, see Accelerate Release Notes .
For more information on images, see Bitmap Images and Image Masks (page 146).
143
Table 10-2
Function
CGImageDestinationCreateWithDataConsumer
CGImageDestinationCreateWithData
CGImageDestinationCreateWithURL
CGPDFContextCreateWithURL
CGDataConsumerCreateWithURL
CGDataConsumerCreateWithCFData
CGDataConsumerCreate
imageWithCGImage:
imageWithCGImage:options:
imageWithCGLayer:
144
imageWithCGLayer:options:
The following Core Image methods return a Quartz image from a Core Image image. You can use them to
move a processed image back into Quartz 2D:
createCGImage:fromRect:
createCGLayerWithSize:info:
For a complete description of Core Image methods, see Core Image Reference Collection .
145
Bitmap images and image masks are like any drawing primitive in Quartz. Both images and image masks in
Quartz are represented by the CGImageRef data type. As youll see later in this chapter, there are a variety of
functions that you can use to create an image. Some of them require a data provider or an image source to
supply bitmap data. Other functions create an image from an existing image either by copying the image or
by applying an operation to the image. No matter how you create a bitmap image in Quartz, you can draw
the image to any flavor of graphics context. Keep in mind that a bitmap image is an array of bits at a specific
resolution. If you draw a bitmap image to a resolution-independent graphics context (such as a PDF graphics
context) the bitmap is limited by the resolution at which you created it.
There is one way to create a Quartz image maskby calling the function CGImageMaskCreate. Youll see
how to create one in Creating an Image Mask (page 154). Applying an image mask is not the only way to mask
drawing. The sections Masking an Image with Color (page 158), Masking an Image with an Image Mask (page
155), and Masking an Image by Clipping the Context (page 162) discuss all the masking methods available in
Quartz.
146
Bitmap images
Each sample in a bitmap contains one or more color components in a specified color space, plus one additional
component that specifies the alpha value to indicate transparency. Each component can be from 1 to as many
as 32 bits. In Mac OS X, Quartz also provides support for floating-point components. The supported formats
in Mac OS X and iOS are described in Pixel formats supported for bitmap graphics contexts (page
40). ColorSync provides color space support for bitmap images.
Quartz also supports image masks. An image mask is a bitmap that specifies an area to paint, but not the color.
In effect, an image mask acts as a stencil to specify where to place color on the page. Quartz uses the current
fill color to paint an image mask. An image mask can have a depth of 1 to 8 bits.
147
This section describes the information associated with a bitmap image. When you create and work with Quartz
images (which use the CGImageRef data type), youll see that some Quartz image-creation functions require
you to specify all this information, while other functions require a subset of this information. What you provide
depends on the encoding used for the bitmap data, and whether the bitmap represents an image or an image
mask.
Note: For the best performance when working with raw image data, use the vImage framework.
You can import image data to vImage from a CGImageRef reference with the
vImageBuffer_InitWithCGImage function. For details, see Accelerate Release Notes .
Quartz uses the following information when it creates a bitmap image (CGImageRef):
A bitmap data source, which can be a Quartz data provider or a Quartz image source. Data Management
in Quartz 2D (page 140) describes both and discusses the functions that provide a source of bitmap data.
An interpolation setting, which is a Boolean value that specifies whether Quartz should apply an
interpolation algorithm when resizing the image.
A rendering intent that specifies how to map colors that are located within the destination color space of
a graphics context. This information is not needed for image masks. See Setting Rendering Intent (page
75) for more information.
The pixel format, which includes bits per component, bits per pixel, and bytes per row (Pixel Format (page
149)).
For images, color spaces and bitmap layout (Color Spaces and Bitmap Layout (page 149)) information to
describe the location of alpha and whether the bitmap uses floating-point values. Image masks dont
require this information.
Decode Array
A decode array maps the image color values to other color values, which is useful for such tasks as desaturating
an image or inverting the colors. The array contains a pair of numbers for each color component. When Quartz
renders the image, it applies a linear transform to map the original component value to a relative number
within the designated range appropriate for the destination color space. For example, the decode array for an
image in the RGB color space contains six entries, one pair for each red, green, and blue color component.
148
Pixel Format
The pixel format consists of the following information:
Bits per component, which is the number of bits in each individual color component in a pixel. For an
image mask, this value is the number of significant masking bits in a source pixel. For example, if the
source image is an 8-bit mask, specify 8 bits per component.
Bits per pixel, which is the total number of bits in a source pixel. This value must be at least the number
of bits per component times the number of components per pixel.
Bytes per row. The number of bytes per horizontal row in the image.
Whether a bitmap contains an alpha channel. Quartz supports RGB, CMYK, and gray color spaces. It also
supports alpha, or transparency, although alpha information is not available in all bitmap image formats.
When it is available, the alpha component can be located in either the most significant bits of a pixel or
the least significant bits.
For bitmaps that have an alpha component, whether the color components are already multiplied by the
alpha value. Premultiplied alpha describes a source color whose components are already multiplied by an
alpha value. Premultiplying speeds up the rendering of an image by eliminating an extra multiplication
operation per color component. For example, in an RGB color space, rendering an image with premultiplied
alpha eliminates three multiplication operations (red times alpha, green times alpha, and blue times alpha)
for each pixel in the image.
When you create an image using the function CGImageCreate, you supply a bitmapInfo parameter, of type
CGImageBitmapInfo, to specify bitmap layout information. The following constants specify the location of
the alpha component and whether the color components are premultiplied:
kCGImageAlphaLastthe alpha component is stored in the least significant bits of each pixel, for
example, RGBA.
kCGImageAlphaFirstthe alpha component is stored in the most significant bits of each pixel, for
example, ARGB.
each pixel, and the color components have already been multiplied by this alpha value.
each pixel, and the color components have already been multiplied by this alpha value.
149
than the space required for the number of color components in the color space, the least significant bits
are ignored.
than the space required for the number of color components in the color space, the most significant bits
are ignored.
kCGImageAlphaNoneequivalent to kCGImageAlphaNoneSkipLast.
You use the constant kCGBitmapFloatComponents to indicate a bitmap format that uses floating-point
values. For floating-point formats, you logically OR this constant with the appropriate constant from the previous
list. For example, for a 128 bits per pixel floating-point format that uses premultiplied alpha, with the alpha
located in the least significant bits of each pixel, you supply the following information to Quartz:
kCGImageAlphaPremultipliedLast|kCGBitmapFloatComponents
Figure 11-2 visually depicts how pixels are represented in CMYK and RGB color spaces that use 16- or 32-bit
integer formats. The 32-bit integer pixel formats use 8 bits per component. The 16-bit integer format uses 5
bits per component. Quartz 2D also supports 128-bit floating-point pixel formats that use 32 bits per component.
The 128-bit formats are not shown in the figure.
Figure 11-2
32-bit and 16-bit pixel formats for CMYK and RGB color spaces in Quartz 2D
32 bits per pixel CMYK, kCGImageAlphaNone
C
C M M M M M M M M Y
B A A A A A A A A
G G G G G G G G
G G G G G G G G
G G G G G G G G
G G G G G G G G
G G
G G G B
150
Creating Images
Table 11-1 (page 151) lists the functions that Quartz provides to create CGImage objects. The choice of image
creation function depends on the source of the image data. The most flexible function is CGImageCreate. It
creates an image from any kind of bitmap data. However, its the most complex function to use because you
must specify all bitmap information. To use this function, you need to be familiar with the topics discussed in
Bitmap Image Information (page 147).
If you want to create a CGImage object from an image file that uses a standard image format such as PNG or
JPEG, the easiest solution is to call the function CGImageSourceCreateWithURL to create an image source
and then call the function CGImageSourceCreateImageAtIndex to create an image from the image data
at a specific index in the image source. If the original image file contains only one image, then provide 0 as
the index. If the image file format supports files that contain multiple images, you need to supply the index
to the appropriate image, keeping in mind that the index values start at 0.
If youve drawn content to a bitmap graphics context and want to capture that drawing to a CGImage object,
call the function CGBitmapContextCreateImage.
Several functions are utilities that operate on existing images, either to make a copy, create a thumbnail, or
create an image from a portion of a larger one. Regardless of how you create a CGImage object, you use the
function CGContextDrawImage to draw the image to a graphics context. Keep in mind that CGImage objects
are immutable. When you no longer need a CGImage object, release it by calling the function CGImageRelease.
Table 11-1
Function
Description
CGImageCreate
A flexible function for creating an image. You must specify all the
bitmap information that is discussed in Bitmap Image
Information (page 147).
CGImageSourceCreateImageAtIndex
CGImageSourceCreateThumbnailAtIndex
CGBitmapContextCreateImage
CGImageCreateWithImageInRect
151
Function
Description
CGImageCreateCopy
CGImageCreateCopyWithColorSpace
Data Management in Quartz 2D (page 140) discusses how to read and write image data.
CGImage Reference , CGImageSource Reference , and CGBitmapContext Reference for further information
on the functions listed in Table 11-1 and their parameters.
ABC
DEF
GHI
The image returned by the function CGImageCreateWithImageInRect retains a reference to the original
image, which means you can release the original image after calling this function.
Figure 11-4 (page 153) shows another example of extracting a portion of an image to create another image. In
this case, the roosters head is extracted from the larger image, and then drawn to a rectangle thats larger
than the subimage, effectively zooming in on the image.
152
Listing 11-1 (page 153) shows code that creates and then draws the subimage. The rectangle that the function
CGContextDrawImage draws the roosters head to has dimensions that are twice the dimensions of the
extracted subimage. The listing is a code fragment. Youd need to declare the appropriate variables, create
the rooster image, and dispose of the rooster image and the rooster head subimage. Because the code is a
fragment, it does not show how to create the graphics context that the image is drawn to. You can use any
flavor of graphics context that youd like. For examples of how to create a graphics context, see Graphics
Contexts (page 27).
Figure 11-4
The CGImage object returned by the function is created by a copy operation. Therefore any subsequent changes
you make to the bitmap graphics context do not affect the contents of the returned CGImage object. In some
cases the copy operation actually follows copy-on-write semantics, so that the actual physical copy of the bits
153
occurs only if the underlying data in the bitmap graphics context is modified. You may want to use the resulting
image and release it before you perform additional drawing into the bitmap graphics context so that you can
avoid the actual physical copy of the data.
For an example that shows how to create a bitmap graphics context, seeCreating a Bitmap Graphics
Context (page 35).
(2 bits_per_component ) -1
For example, a 4-bit mask has values that range from 0 to 1 in increments of 1/15 . Component values that
are 0 or 1 represent the extremescompletely block paint and completely allow paint. Values between 0 and
1 allow partial painting using the formula 1 MaskSampleValue. For example, if the sample value of an
8-bit mask scales to 0.7, color is painted as if it had an alpha value of (1 0.7), which is 0.3.
The function CGImageMaskCreate creates a Quartz image mask from bitmap image information that you
supply and that is discussed in Bitmap Image Information (page 147). The information you supply to create an
image mask is the same as what you supply to create an image, except that you do not supply color space
information, a bitmap information constant, or a rendering intent, as you can see by looking at the function
prototype in Listing 11-2.
154
Masking Images
Masking techniques can produce many interesting effects by controlling which parts of an image are painted.
You can:
Apply an image mask to an image. You can also use an image as a mask to achieve an effect thats opposite
from applying an image mask.
Use color to mask parts of an image, which includes the technique referred to as chroma key masking.
Clip a graphics context to an image or image mask, which effectively masks an image (or any kind of
drawing) when Quartz draws the content to the clipped context.
The image you want to apply the mask to. This image cant be an image mask or have a masking color
(see Masking an Image with Color (page 158)) associated with it.
An image mask created by calling the function CGImageMaskCreate. Its possible to provide an image
instead of an image mask, but that gives a much different result. See Masking an Image with an Image (page
157).
Source samples of an image mask act as an inverse alpha value. An image mask sample value (S):
155
Greater than 0 and less 1 allows painting the corresponding image sample with an alpha value of (1
S).
Figure 11-5 shows an image created with one of the Quartz image-creation functions and Figure 11-6 shows
an image mask created with the function CGImageMaskCreate. Figure 11-7 (page 157) shows the image that
results from calling the function CGImageCreateWithMask to apply the image mask to the image.
Figure 11-5
Figure 11-6
An image mask
156
Note that the areas in the original image that correspond to the black areas of the mask show through in the
resulting image (Figure 11-7). The areas that correspond to the white areas of the mask arent painted. The
areas that correspond to the gray areas in the mask are painted using an intermediate alpha value thats equal
to 1 minus the image mask sample value.
Figure 11-7
The image that results from applying the image mask to the original image
Greater than 0 and less 1 allows painting the corresponding image sample with an alpha value of S.
Figure 11-8 (page 158) shows the image that results from calling the function CGImageCreateWithMask to
apply the image shown in Figure 11-6 (page 156) to the image shown in Figure 11-5 (page 156). In this case,
assume that the image shown in Figure 11-6 is created using one of the Quartz image-creation functions, such
as CGImageCreate. Compare Figure 11-8 with Figure 11-7 (page 157) to see how the same sample values,
when used as image samples instead of image mask samples, achieve the opposite effect.
157
The areas in the original image that correspond to the black areas of the image arent painted in the resulting
image (Figure 11-8). The areas that correspond to the white areas are painted. The areas that correspond to
the gray areas in the mask are painted using an intermediate alpha value thats equal to the masking image
sample value.
Figure 11-8
The image that results from masking the original image with an image
An image that is not an image mask and that is not the result of applying an image mask or masking color
to another image.
An array of color components that specify a color or a range of colors for the function to mask in the image.
Figure 11-9
158
The number of elements in the color component array must be equal to twice the number of color components
in the color space of the image. For each color component in the color space, supply a minimum value and a
maximum value that specifies the range of colors to mask. To mask only one color, set the minimum value
equal to the maximum value. The values in the color component array are supplied in the following order:
{min[1], max[1], ... min[N], max[N]}, where N is the number of components.
If the image uses integer pixel components, each value in the color component array must be in the range [0
.. 2^bitsPerComponent - 1] . If the image uses floating-point pixel components, each value can be any
floating-point number that is a valid color component.
An image sample is not painted if its color values fall in the range:
{c[1], ... c[N]}
Listing 11-3 shows a code fragment that sets up a color components array and supplies the array to the function
CGImageCreateWithMaskingColors to achieve the result shown in Figure 11-11.
159
Figure
11-11
Listing 11-4 shows another code fragment that operates on the image shown in Figure 11-10 (page 159) to get
the results shown in Figure 11-12. This example masks a darker range of colors.
Listing 11-4 Masking shades of brown to black
CGImageRef myMaskedImage;
const CGFloat myMaskingColors[6] = { 0, 124, 0, 68, 0, 0 };
myColorMaskedImage = CGImageCreateWithMaskingColors (image,
myMaskingColors);
160
Figure
11-12
You can mask colors in an image as well as set a fill color to achieve the effect shown in Figure 11-13 in which
the masked areas are replaced with the fill color. Listing 11-5 shows the code fragment that generates the
image shown in Figure 11-13.
Listing 11-5 Masking a range of colors and setting a fill color and
CGImageRef myMaskedImage;
const CGFloat myMaskingColors[6] = { 0, 124, 0, 68, 0, 0 };
myColorMaskedImage = CGImageCreateWithMaskingColors (image,
myMaskingColors);
CGContextSetRGBFillColor (myContext, 0.6373,0.6373, 0, 1);
CGContextFillRect(context, rect);
161
Figure
11-13
An image drawn after masking a range of colors and setting a fill color
An image mask created by calling the function CGImageMaskCreate. You can supply an image instead
of an image mask to achieve an effect opposite of what you get by supplying an image mask. The image
must be created with a Quartz image creation function, but it cannot be the result of applying a mask or
masking color to another image.
The resulting clipped area depends on whether you provide an image mask or an image to the function
CGContextClipToMask. If you supply an image mask, you get results similar to those described in Masking
an Image with an Image Mask (page 155), except that the graphics context is clipped. If you supply an image,
the graphics context is clipped similar to whats described in Masking an Image with an Image (page 157).
Take a look at Figure 11-14. Assume it is an image mask created by calling the function CGImageMaskCreate
and then the mask is supplied as a parameter to the function CGContextClipToMask. The resulting context
allows painting to the black areas, does not allow painting to the white areas, and allows painting to the gray
162
area with an alpha value of 1S, where S is the sample value of the image masks. If you draw an image to the
clipped context using the function CGContextDrawImage, youll get a result similar to that shown in Figure
11-15.
Figure
11-14
A masking image
Figure
11-15
An image drawn to a context after clipping the content with an image mask
163
When the masking image is treated as an image, you get the opposite result, as shown in Figure 11-16.
Figure
11-16
2.
Set the blend mode by calling the function CGContextSetBlendMode with one of the blend mode
constants. (The blend modes are based upon those defined in the PDF Reference, Fourth Edition , Version
1.5, Adobe Systems, Inc.)
3.
Draw the image you want to composite over the background by calling the function CGContextDrawImage.
This code fragment composites one image over a background using the darken blend mode:
CGContextSetBlendMode (myContext, kCGBlendModeDarken);
CGContextDrawImage (myContext, myRect, myImage2);
164
The rest of this section uses each of the blend modes available in Quartz to draw the image shown on the right
side of Figure 11-17 over the background that consists of the painted rectangles shown on the left side of the
figure. In all cases, the rectangles are first drawn to the graphics context. Then, a blend mode is set by calling
the function CGContextSetBlendMode, passing the appropriate blend mode constant. Finally, the image of
the jumper is drawn to the graphics context.
Figure
11-17
165
Figure 11-18 shows the result of using normal blend mode to paint the image shown in Figure 11-17 (page
165) over the rectangles shown in the same figure. In this example, the image is drawn using an alpha value
of 1.0, so the background is completely obscured by the image.
Figure
11-18
166
167
You specify overlay blend mode by passing the constant kCGBlendModeOverlay to the function
CGContextSetBlendMode. Figure 11-21 shows the result of using overlay blend mode to paint the image
shown in Figure 11-17 (page 165) over the rectangles shown in the same figure.
Figure
11-21
168
You specify darken blend mode by passing the constant kCGBlendModeDarken to the function
CGContextSetBlendMode. Figure 11-22 shows the result of using darken blend mode to paint the image
shown in Figure 11-17 (page 165) over the rectangles shown in the same figure.
Figure
11-22
169
170
You specify color burn blend mode by passing the constant kCGBlendModeColorBurn to the function
CGContextSetBlendMode. Figure 11-25 shows the result of using color burn blend mode to paint the image
shown in Figure 11-17 (page 165) over the rectangles shown in the same figure.
Figure
11-25
171
You specify soft light blend mode by passing the constant kCGBlendModeSoftLight to the function
CGContextSetBlendMode. Figure 11-26 shows the result of using soft light blend mode to paint the image
shown in Figure 11-17 (page 165) over the rectangles shown in the same figure.
Figure
11-26
172
You specify hard light blend mode by passing the constant kCGBlendModeHardLight to the function
CGContextSetBlendMode. Figure 11-27 shows the result of using hard light blend mode to paint the image
shown in Figure 11-17 (page 165) over the rectangles shown in the same figure.
Figure
11-27
173
174
175
176
177
You specify luminosity blend mode by passing the constant kCGBlendModeLuminosity to the function
CGContextSetBlendMode. Figure 11-33 shows the result of using luminosity blend mode to paint the image
shown in Figure 11-17 (page 165) over the rectangles shown in the same figure.
Figure
11-33
178
CGLayer objects (CGLayerRef data type) allow your application to use layers for drawing.
Layers are suited for the following:
High-quality offscreen rendering of drawing that you plan to reuse. For example, you might be building
a scene and plan to reuse the same background. Draw the background scene to a layer and then draw
the layer whenever you need it. One added benefit is that you dont need to know color space or
device-dependent information to draw to a layer.
Repeated drawing. For example, you might want to create a pattern that consists of the same item drawn
over and over. Draw the item to a layer and then repeatedly draw the layer, as shown in Figure 12-1. Any
Quartz object that you draw repeatedlyincluding CGPath, CGShading, and CGPDFPage objectsbenefits
from improved performance if you draw it to a CGLayer. Note that a layer is not just for onscreen drawing;
you can use it for graphics contexts that arent screen-oriented, such as a PDF graphics context.
Buffering. Although you can use layers for this purpose, you shouldnt need to because the Quartz
Compositor makes buffering on your part unnecessary. If you must draw to a buffer, use a layer instead
of a bitmap graphics context.
Figure 12-1
CGLayer objects and transparency layers are parallel to CGPath objects and paths created by CGContext
functions. In the case of a CGLayer or 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.
179
Layer drawing
LayerCreate
CGLayer Object
LayerGetContext
DrawLayer
Layer context
Graphics context
All layer 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 layer so that it has all the characteristics of the graphics contextits resolution,
color space, and graphics state settings. You can provide a size for the layer if you dont want to use the size
of the graphics context. In Figure 12-2, the left side shows the graphics context used to create the layer. The
gray portion of the box on the right side, labeled CGLayer object, represents the newly created layer.
180
Before you can draw to the layer, you must obtain the graphics context thats associated with the layer 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 created layer graphics context.
You draw to the layers graphics context just as you would draw to any graphics context, passing the layers
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 layer, you can call the functions CGContextDrawLayerInRect
or CGContextDrawLayerAtPoint, to draw the layer into a 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 layer drawing has the characteristics of the graphics
context used to create the layer object, which could impose certain constraints (performance or resolution,
for example). For example, a layer associated with the screen may be cached in video hardware. If the destination
context is a printing or PDF context, it may need to be fetched from the graphics hardware to memory, resulting
in poor performance.
Figure 12-2 (page 180) shows the contents of the layerthe leafdrawn repeatedly to the graphics context
used to create the layer object. You can reuse the drawing thats in a layer as many times as youd 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 (page 136).) Use CGLayer objects when
you want to draw offscreen or when you need to repeatedly draw the same thing.
Create a CGLayer Object Initialized with an Existing Graphics Context (page 182)
2.
3.
4.
See Example: Using Multiple CGLayer Objects to Draw a Flag (page 184) for a detailed code example.
181
The graphics context to create the layer from. Typically you pass a window graphics context so that you
can later draw the layer onscreen.
The size of the layer relative to the graphics context. The layer can be the same size as the graphics context
or smaller. If you need to retrieve the layer size later, you can call the function CGLayerGetSize.
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)
182
CGContextDrawLayerAtPoint, which draws the layer to a graphics context at the point specified.
Typically the destination graphics context you supply is a window graphics context and it is the same graphics
context you use to create the layer. Figure 12-4 shows the result of repeatedly drawing the layer drawing
shown in Figure 12-3 (page 182). To achieve the patterned effect, you call either of the layer drawing functions
repeatedlyCGContextDrawLayerAtPoint or CGContextDrawLayerInRectchanging 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 layer.
Figure 12-4
183
Note: You are not required to draw a layer to the same graphics context that you use to initialize
the layer. However, if you draw the layer to another graphics context, any limitations of the original
graphics context are imposed on your 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 layer is ideal
for repeated drawing. You draw the red rectangle to a layer, then draw the layer onscreen seven times.
A blue rectangle. You need the blue rectangle once, so using a layer 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 layer 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 layer, then draw the layer 50
times, adjusting the offset each time to get the appropriate spacing.
184
The code in Figure 12-2 (page 180) 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 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.
i, j,
num_six_star_rows = 5,
num_five_star_rows = 4;
CGFloat
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},
{10, 15},
{15, 5},
{15, 5},
{2.5, 11},
185
// 7
stripeRect
starField
// 8
// 9
// 10
contextRect->size.height);
// 11
stripeRect.size, NULL);
myLayerContext1 = CGLayerGetContext (stripeLayer);
// 12
// 13
// 14
// 15
// 16
// 17
// 18
CGContextFillPath (myLayerContext2);
// 19
// 20
// 21
i++)
{
CGContextDrawLayerAtPoint (context, CGPointZero, stripeLayer);
// 22
// 23
}
CGContextRestoreGState(context);
// 24
// 25
// 26
CGContextSaveGState (context);
// 27
186
// 28
// 29
j++)
{
for (i=0; i< 6;
i++)
{
CGContextDrawLayerAtPoint (context,CGPointZero,
starLayer);
CGContextTranslateCTM (context, h_spacing, 0);
// 30
// 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
// 36
}
CGContextTranslateCTM (context, (-i*h_spacing), v_spacing);
// 37
}
CGContextRestoreGState(context);
CGLayerRelease(stripeLayer);
// 38
CGLayerRelease(starLayer);
// 39
2.
187
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.
9.
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. Youll 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. Youll 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 youll draw the
1.0. Although all the colors in this example are opaque, they dont 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.
188
26. Fills the star field rectangle with blue. You draw this rectangle directly to the window graphics context.
(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
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
stars.
38. Releases the stripe layer.
39. Releases the star layer.
189
PDF documents store resolution-independent vector graphics, text, and images as a series of commands written
in a compact programming language. A PDF document can contain multiple pages of images and text. PDF
is useful for creating cross-platform, read-only documents and for drawing resolution-independent graphics.
Quartz creates, for all applications, high-fidelity PDF documents that preserve the drawing operations of the
application, as shown in Figure 13-1. The resulting PDF may be optimized for a specific use (such as a particular
printer, or for the web) by other parts of the system, or by third-party products. PDF documents generated by
Quartz view correctly in Preview and Acrobat.
Figure 13-1
Web optimized
Print optimized
Prepress optimized
Quartz not only uses PDF as its digital paper but also includes as part of its API a number of functions that
you can use to display and generate PDF files and to accomplish a number of other PDF-related tasks.
For detailed information about PDF, including the PDF language and syntax, see PDF Reference , Fourth Edition,
Version 1.5.
190
A PDF document
Listing 13-1 shows how to create a CGPDFDocument object and obtain the number of pages in the document.
A detailed explanation for each numbered line of code appears following the listing.
Listing 13-1 Creating a CGPDFDocument object from a PDF file
CGPDFDocumentRef MyGetPDFDocumentRef (const char *filename)
{
CFStringRef path;
CFURLRef url;
CGPDFDocumentRef document;
size_t count;
191
kCFStringEncodingUTF8);
url = CFURLCreateWithFileSystemPath (NULL, path,
// 1
kCFURLPOSIXPathStyle, 0);
CFRelease (path);
document = CGPDFDocumentCreateWithURL (url);
// 2
CFRelease(url);
count = CGPDFDocumentGetNumberOfPages (document);
// 3
if (count == 0) {
printf("`%s' needs at least one page!", filename);
return NULL;
}
return document;
}
Calls the Core Foundation function to create a CFURL object from a CFString object that represents the
filename of the PDF file to display.
2.
3.
Gets the number of pages in the PDF so that the next statement in the code can ensure that the document
has at least one page.
You can see how to draw a PDF page to a graphics context by looking at the code in Listing 13-2. A detailed
explanation for each numbered line of code appears following the listing.
Listing 13-2 Drawing a PDF page
void MyDisplayPDFPage (CGContextRef myContext,
size_t pageNumber,
const char *filename)
{
CGPDFDocumentRef document;
CGPDFPageRef page;
// 1
// 2
192
// 3
CGPDFDocumentRelease (document);
// 4
Calls your function (see Listing 13-1 (page 191)) to create a CGPDFDocument object from a filename you
supply.
2.
Gets the page for the specified page number from the PDF document.
3.
Draws the specified page from the PDF file by calling the function CGContextDrawPDFPage. You need
to supply a graphics context and the page to draw.
4.
Intersects the rectangle associated with the type of PDF box you specify in the box parameter (media,
crop, bleed, trim, or art) and the /MediaBox entry of the specified PDF page. The intersection results in
an effective rectangle.
Rotates the effective rectangle by the amount specified by the /Rotate entry for the PDF page.
Centers the resulting rectangle on rectangle you supply in the rect parameter.
193
If the value of the rotate parameter you supply is nonzero and a multiple of 90, the function rotates the
effective rectangle by the number of degrees you supply. Positive values rotate the rectangle to the right;
negative values rotate the rectangle to the left. Note that you pass degrees, not radians. Keep in mind
that the /Rotate entry for the PDF page contains a rotation as well, and the rotate parameter you
supply is combined with the /Rotate entry.
Scales the effective rectangle, if necessary, so that it coincides with the edges of the rectangle you supply.
If you specify to preserve the aspect ratio by passing true in the preserveAspectRatio parameter,
then the final rectangle coincides with the edges of the more restrictive dimension of the rectangle you
supply in the rect parameter.
You can use this function, for example, if you are writing a PDF viewing application similar to that shown in
Figure 13-3 (page 194). If you were to provide a Rotate Left/Rotate Right feature, you could call
CGPDFPageGetDrawingTransform to compute the appropriate transform for the current window size and
rotation setting.
Figure 13-3
Listing 13-3 shows a function that creates an affine transform for a PDF page using the parameters passed to
the function, applies the transform, and then draws the PDF page. A detailed explanation for each numbered
line of code appears following the listing.
194
// 1
preserveAspectRato);
CGContextSaveGState (context);
// 2
// 3
// 4
// 5
CGContextRestoreGState (context);
// 6
2.
3.
4.
Clips the graphics context to the rectangle specified by the box parameter. The function
CGPDFPageGetBoxRect obtains the page bounding box (media, crop, bleed, trim, and art boxes) associated
with the constant you supplykCGPDFMediaBox, kCGPDFCropBox, kCGPDFBleedBox, kCGPDFTrimBox,
or kCGPDFArtBox.
5.
6.
195
// 1
{
CGContextRef pdfContext;
CFStringRef path;
CFURLRef url;
CFDataRef boxData = NULL;
CFMutableDictionaryRef myDictionary = NULL;
CFMutableDictionaryRef pageDictionary = NULL;
// 2
kCFStringEncodingUTF8);
url = CFURLCreateWithFileSystemPath (NULL, path,
// 3
kCFURLPOSIXPathStyle, 0);
CFRelease (path);
myDictionary = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
// 4
196
// 5
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
// 6
// 7
myDrawContent (pdfContext);
// 8
CGPDFContextEndPage (pdfContext);
// 9
CGContextRelease (pdfContext);
// 10
CFRelease(pageDictionary);
// 11
CFRelease(boxData);
}
Takes as parameters a rectangle that specifies the size of the PDF page and a string that specifies the
filename.
2.
3.
4.
Creates an empty CFDictionary object to hold metadata. The next two lines add a title and creator. You
can add as many key-value pairs as youd like using the function CFDictionarySetValue. For more
information on creating dictionaries, see CFDictionary Reference .
5.
A pointer to a rectangle that defines the default size and location of the PDF page. The origin of the
rectangle is typically (0, 0). Quartz uses this rectangle as the default bounds of the page media box.
If you pass NULL, Quartz uses a default page size of 8.5 by 11 inches (612 by 792 points).
A CFDictionary object that contains PDF metadata. Pass NULL if you dont have metadata to add.
You can use the CFDictionary object to specify output intent optionsintent subtype, condition,
condition identifier, registry name, destination output profile, and a human-readable text string that
contains additional information or comments about the intended target device or production condition.
For more information about output intent options, see CGPDFContext Reference .
6.
Creates a CFDictionary object to hold the page boxes for the PDF page. This example sets the media box.
197
7.
Signals the start of a page. When you use a graphics context that supports multiple pages (such as PDF),
you call the function CGPDFContextBeginPage together with CGPDFContextEndPage to delineate the
page boundaries in the output. Each page must be bracketed by calls to CGPDFContextBeginPage and
CGPDFContextEndPage. Quartz ignores all drawing operations performed outside a page boundary in
a page-based context.
8.
Calls an application-defined function to draw content to the PDF context. You supply your drawing routine
here.
9.
Adding Links
You can add links and anchors to PDF context you create. Quartz provides three functions, each of which takes
a PDF graphics context as a parameter, along with information about the links:
CGPDFContextSetURLForRect lets you specify a URL to open when the user clicks a rectangle in the
CGPDFContextSetDestinationForRect lets you set a destination to jump to when the user clicks a
rectangle in the current PDF page. You must supply a destination name.
CGPDFContextAddDestinationAtPoint lets you set a destination to jump to when the user clicks a
point in the current PDF page. You must supply a destination name.
kCGPDFContextOwnerPassword, to define the owner password of the PDF document. If this key is
specified, the document is encrypted using the value as the owner password; otherwise, the document is
not encrypted. The value of this key must be a CFString object that can be represented in ASCII encoding.
Only the first 32 bytes are used for the password. There is no default value for this key. If the value of this
key cannot be represented in ASCII, the document is not created and the creation function returns NULL.
Quartz uses 40-bit encryption.
198
kCGPDFContextUserPassword, to define the user password of the PDF document. If the document is
encrypted, then the value of this key is the user password for the document. If not specified, the user
password is the empty string. The value of this key must be a CFString object that can be represented in
ASCII encoding; only the first 32 bytes are used for the password. If the value of this key cannot be
represented in ASCII, the document is not created and the creation function returns NULL.
with the user password. The value of this key must be a CFBoolean object. The default value of this key is
kCFBooleanTrue.
with the user password. The value of this key must be a CFBoolean object. The default value of this key is
kCFBooleanTrue.
Listing 14-4 (page 205) (in the next chapter) shows code that checks PDF document to see if its locked and if
it is, attempts to open the document with a password.
199
Quartz provides functions that let you inspect the PDF document structure and the content stream. Inspecting
the document structure lets you read the entries in the document catalog and the contents associated with
each entry. By recursively traversing the catalog, you can inspect the entire document.
A PDF content stream is just what its name suggestsa sequential stream of data such as 'BT 12 /F71 Tf
(draw this text) Tj . . . ' where PDF operators and their descriptors are mixed with the actual PDF
content. Inspecting the content stream requires that you access it sequentially.
This chapter shows how to examine the structure of a PDF document and parse the contents of a PDF document.
200
Figure 14-1 shows some of the metadata describing the two imagesthe text and the image of the roosterthat
make up the PDF file displayed in Figure 13-2 (page 191).
Figure 14-1
You can obtain much more useful information by accessing PDF metadata. The items in Figure 14-1 are just a
sample. For example, you can check to see if a PDF has thumbnail images (shown in Figure 14-2 (page 202))
using the code shown in Listing 14-1.
Listing 14-1 Getting a thumbnail view of a PDF
CGPDFDictionaryRef d;
CGPDFStreamRef stream; // represents a sequence of bytes
d = CGPDFPageGetDictionary(page);
// check for thumbnail data
if (CGPDFDictionaryGetStream (d, Thumb, &stream)){
// get the data if it exists
data = CGPDFStreamCopyData (stream, &format);
201
Quartz performs all the decryption and decoding of the data stream for you.
Figure 14-2
Thumbnail images
Quartz provides a number of functions that you can use to obtain individual values for items in the PDF
metadata. You use the function CGPDFObjectGetValue, passing a CGPDFObjectRef, a PDF object type
(kCGPDFObjectTypeBoolean, kCGPDFObjectTypeInteger, and so forth), and storage for the value. On
return, the storage is filled with the value.
There are numerous other functions you can use to traverse the hierarchy of a PDF file to access the various
nodes and their children. For example, the CGPDFArray functions (CGPDFArrayGetBoolean,
CGPDFArrayGetDictionary, CGPDFArrayGetInteger, and so forth) let you access arrays of values to
retrieve values of specific types. You can find out more about how to use these functions by reading the PDF
specification.
202
You use a CGPDFScanner object (CGPDFScannerRef data type) to parse a PDF content stream. The
CGPDFScanner object invokes callbacks for any operator in the stream for which you have registered a callback.
You perform the tasks described in the following sections to parse a content stream:
1.
Write Callbacks for Operators (page 203). You need to write callbacks only for the operators you want to
handle.
2.
3.
4.
When its appropriate to do so, you need to make sure the you release the scanner, content stream, and operator
table.
The following sections show how to parse a content stream to find marked-content operators (see Table 14-1).
Marked content operators represent only some of the PDF operators used in PDF content. When you write
your own code, youd look for the PDF operators appropriate for your application.
Table 14-1
Marked content operators represent some of the PDF operators that you can parse
Operator
Description
MP
DP
A marked point that has a tag and a property list or object associated with it.
BMC
Signals the start of a marked-content sequence (begin marked content) and is paired with
the EMC marker that signals the end of the sequence. Has a tag associated with it.
BDC
Signals the start of a marked-content sequence and is paired with the EMC marker that
signals the end of the sequence. Has a tag and a property list or object associated with it.
EMC
Signals the end of a marked-content sequence (end marked content) that begins with a
BMC or a BDC marker. This operator does not have a tag associated with it.
203
Quartz has an assortment of CGPDFScannerPop functions for retrieving objects, Boolean values, names,
numbers, strings, arrays, dictionaries, and streams. Each function returns a Boolean value to indicate whether
the item was retrieved successfully.
Listing 14-2 A callback for the MP operator
static void
op_MP (CGPDFScannerRef s, void *info)
{
const char *name;
if (!CGPDFScannerPopName(s, &name))
return;
myTable = CGPDFOperatorTableCreate();
204
// 1
if (myDocument == NULL) {
// 2
// 3
205
// 4
return EXIT_FAILURE;
}
}
if (CGPDFDocumentGetNumberOfPages(myDocument) == 0) {
// 5
CGPDFDocumentRelease(myDocument);
return EXIT_FAILURE;
}
2.
Checks to make sure that a CGPDFDocument object is created. If not, the code exits because it makes no
sense to continue without a document.
3.
Checks whether the document is encrypted. If the document is encrypted, the code attempts to open is
using a blank password. If that fails, the code asks the user for a password and attempts to unlock the
document with the password.
4.
Checks whether the document is unlocked. If its not, the code exits.
5.
Checks to make sure the document has at least one page. Otherwise, the code exits.
// 1
// 2
// 3
206
// 4
CGPDFScannerScan (myScanner);
// 5
CGPDFPageRelease (myPage);
// 6
CGPDFScannerRelease (myScanner);
// 7
CGPDFContentStreamRelease (myContentStream);
// 8
}
CGPDFOperatorTableRelease(myTable);
// 9
Gets the number of pages in the document that you previously opened. See Open the PDF Document (page
205).
2.
3.
4.
Creates a scanner for the content stream. You must pass the content stream and the operator table that
you previously created and set with callbacks. See Create and Set Up the Operator Table (page 204). You
can also pass any data that your callbacks need.
5.
Parses the content stream associated with the scanner. Quartz invokes your callback each time it encounters
one of the operators for which you provided a callback.
6.
7.
8.
9.
Releases the operator table after scanning all the pages in the PDF.
207
PostScript Conversion
The Preview application automatically converts PostScript files to PDF. The Quartz 2D API provides functions
you can use to perform PostScript conversion in your application. The Quartz 2D PostScript conversions functions
are not available in iOS.
Follow these steps to convert a PostScript document to a PDF document:
1.
Write callbacks. Quartz communicates the status of per page processes through callbacks.
2.
3.
4.
Create a data provider object for the PostScript file you want to convert.
5.
Create a data consumer object for the PDF that results from the conversion.
6.
Writing Callbacks
Callbacks provide a way for Quartz to inform your application of the status of the conversion. If your application
has a user interface, you can use the status information to provide feedback to the user, as shown in Figure
15-1.
Figure 15-1
208
PostScript Conversion
Filling In a Callbacks Structure
You can provide callbacks to inform your application that Quartz 2D is:
Sending a message about the process (CGPSConverterMessageCallback). There are several kinds of
messages that can be sent during a conversion process. The most likely are font substitution messages,
and any messages that the PostScript code itself generates. Any PostScript messages written to stdout
are routed through this callbacktypically these are debugging or status messages. In addition, there
can be error messages if the document is malformed.
Quartz 2D passes your callback a generic pointer to data you supply and a CFString object that contains
a message about the conversion.
Deallocating the PostScript converter object (CGPSConverterReleaseInfoCallback). You can use this
callback to deallocate the generic pointer if youve provided data and to perform any additional
postprocessing tasks. Quartz 2D passes your callback a generic pointer to data you supply.
See the CGPSConverter Reference for the prototype each callback follows.
209
PostScript Conversion
Creating a PostScript Converter Object
CGPSConverterBeginPageCallback beginPage;
CGPSConverterEndPageCallback endPage;
CGPSConverterProgressCallback noteProgress;
CGPSConverterMessageCallback noteMessage;
CGPSConverterReleaseInfoCallback releaseInfo;
};
A pointer to generic data that you want passed to your callbacks. You can supply NULL if you dont need
to provide any data.
Important: Although the CGPSConverterConvert function is thread safe (it uses locks to prevent more
than one conversion at a time in the same process), it is not thread safe with respect to the Resource
Manager. If your application uses the Resource Manager on a separate thread, you should either use locks
to prevent CGPSConverterConvert from executing during your usage of the Resource Manager or you
should perform your conversions using the PostScript converter in a separate process.
210
PostScript Conversion
Performing the Conversion
211
Text
This chapter previously described the basic text support provided by Quartz. However, the low-level support
provided by Quartz has been deprecated and superceded by Core Text, an advanced low-level technology for
laying out text and handing fonts. Core Text is designed for high performance and ease of use and allows you
to draw Unicode text directly to a graphics context. If you are writing an application that needs precise control
over how text is displayed, see Core Text Programming Guide .
If you are developing a text application for iOS, look first at Text Programming Guide for iOS , which describes
text support in iOS. In particular, UIKit provides classes that implement common tasks, making it easy to add
text to your application:
If you are developing a text application for Mac OS X, look first at Cocoa Text Architecture Guide , which describes
the Cocoa text system. Cocoa provides full Unicode support, text input and editing, precise text layout and
typesetting, font management, and many other advanced text-handling capabilities.
212
Glossary
213
Glossary
214
Glossary
215
Date
Notes
2014-09-17
Added info about using vImage to work with raw pixel data.
2013-12-16
2012-09-19
2010-11-19
2010-06-25
2009-05-18
2008-06-04
2007-12-11
2007-07-02
216
Date
Notes
Updated the introduction with recent, relevant related documentation
and added a description of the revised Gradients (page 112) chapter.
Revised Quartz 2D Opaque Data Types (page 20) to include
CGGradientRef and provided links to information on
CGImageSourceRef and CGImageDestinationRef opaque data types
which are part of the Image I/O framework.
Updated Table 2-1 (page 40) with additional pixel formats.
2007-01-08
2006-10-03
2006-07-24
2006-06-28
217
Date
Notes
Revised information in Python Bindings for Quartz 2D.
Provided hyperlinks to the functions and methods discussed in Data
Management in Quartz 2D (page 140).
Corrected a typographical error in Listing 2-2 (page 31).
2006-02-07
2006-01-10
2005-11-09
2005-07-07
2005-06-04
2005-04-29
218
Date
Notes
Revised the section Using Blend Modes with Images (page 164) and
replaced the figures with better examples of drawing an image using
different blend modes.
Added information about Core Image and Core Video in the opening
paragraphs of Overview of Quartz 2D (page 17).
Introduced the notion of CGLayer objects in the section Drawing
Destinations: The Graphics Context (page 18).
Added the new Tiger opaque objects to Quartz 2D Opaque Data
Types (page 20).
Added blend mode to Graphics States (page 22). Added information
about using blend modes to Paths (page 42) and Bitmap Images and
Image Masks (page 146).
Revised Graphics Contexts (page 27) to show how to use HIView. Also
added new figures to many sections and provided information on HIView
coordinates as compared to Quartz coordinates.
Added Table 2-1 (page 40) to show the supported color spaces and pixel
formats.
Replaced Figure 2-4 (page 41) to show an enlargement of aliased and
antialiasing drawing and text.
Added Ellipses (page 49) and revised discussions on Painting a Path (page
51) and Clipping to a Path (page 66) to reflect new Tiger content.
Changed clipping region to clipping area throughout the entire book.
Revised information on Creating Color Spaces (page 71) to reflect Tiger
content.
Added Evaluating Affine Transforms (page 85) and Getting the User to
Device Space Transform (page 85).
Revised the chapter formerly titled Data Providers and Data Consumers
to contain information on image sources and image destinations, and
how to move data between Quartz 2D and Core Image. Retitled the
chapter Data Management in Quartz 2D (page 140) to reflect the revised
content.
219
Date
Notes
Renamed the Bitmap Image chapter to Bitmap Images and Image
Masks (page 146) and substantially revised the content to reflect
information about image sources, the new image creation functions,
image masking function, and using blend modes to composite images.
Added the chapter Core Graphics Layer Drawing (page 179).
Added the chapter PDF Document Parsing (page 200), which contains
some material from the old PDF Document chapter along with new
material on scanners and content streams.
Added Copying Font Variations (page ?) and PostScript Fonts (page ?)
to the Text chapter.
2004-06-28
2001-07-01
First version.
220
Apple Inc.
Copyright 2001, 2014 Apple Inc.
All rights reserved.
No part of this publication may be reproduced,
stored in a retrieval system, or transmitted, in any
form or by any means, mechanical, electronic,
photocopying, recording, or otherwise, without
prior written permission of Apple Inc., with the
following exceptions: Any person is hereby
authorized to store documentation on a single
computer or device for personal use only and to
print copies of documentation for personal use
provided that the documentation contains
Apples copyright notice.
No licenses, express or implied, are granted with
respect to any of the technology described in this
document. Apple retains all intellectual property
rights associated with the technology described
in this document. This document is intended to
assist application developers to develop
applications only for Apple-branded products.
Apple Inc.
1 Infinite Loop
Cupertino, CA 95014
408-996-1010
Apple, the Apple logo, Cocoa, ColorSync, eMac,
Mac, Mac OS, Objective-C, OS X, Pages, Quartz,
QuickTime, Spaces, Tiger, and Xcode are
trademarks of Apple Inc., registered in the U.S.
and other countries.
Adobe, Acrobat, and PostScript are trademarks
or registered trademarks of Adobe Systems
Incorporated in the U.S. and/or other countries.
Adobe, Acrobat, and PostScript are trademarks
or registered trademarks of Adobe Systems
Incorporated in the U.S. and/or other countries.
IOS is a trademark or registered trademark of
Cisco in the U.S. and other countries and is used
under license.
OpenGL is a registered trademark of Silicon
Graphics, Inc.
APPLE MAKES NO WARRANTY OR REPRESENTATION,
EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS
DOCUMENT, ITS QUALITY, ACCURACY,
MERCHANTABILITY, OR FITNESS FOR A PARTICULAR
PURPOSE. AS A RESULT, THIS DOCUMENT IS PROVIDED
AS IS, AND YOU, THE READER, ARE ASSUMING THE
ENTIRE RISK AS TO ITS QUALITY AND ACCURACY.
IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT,
INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES RESULTING FROM ANY DEFECT, ERROR OR
INACCURACY IN THIS DOCUMENT, even if advised of
the possibility of such damages.
Some jurisdictions do not allow the exclusion of
implied warranties or liability, so the above exclusion
may not apply to you.