Quartz 2D Graphics for Mac OS X Developers

Mac OS X includes a remarkable number of different application environments almost all in which Quartz 2D, in one form or another, is available. Clearly Quartz 2D plays an important role in applications with graphical interfaces, like the ones created with the Carbon and Cocoa application APIs. However, Quartz 2D also can be used from command line applications, CGI scripts, and even directly in the HTML of a web page or Dashboard Widget.

While the routines that invoke Quartz 2D will be different in each application environment, the Quartz 2D imaging model should remain familiar. This book is not able to describe each routine of each application interface in detail. For that information, refer to Apple's reference documentation. Instead the focus is on the general information about the different environments in this chapter and then the drawing model is highlighted throughout the rest of the book.

The Quartz 2D C API

Quartz 2D is one part of a larger graphics environment known as Core Graphics. In addition to Quartz 2D, Core Graphics interacts with the window system and hardware to integrate graphics created by the many different graphics libraries on the system. It is responsible for managing the display environment and working with the window server and Quartz compositor to create the image presented on the screens. While Core Graphics encompasses more than Quartz 2D, it's not unusual for people to use the two terms interchangeably when discussing Quartz 2D in context.

The Core Graphics API is an object oriented API. To allow developers to use that API in as many environments as possible, however, the system implements it as a set of C routines. The API itself draws upon the object-oriented concepts of encapsulation, inheritance, and polymorphism. Because C doesn't directly support these concepts, Core Graphics uses opaque data types, in the place of classes, to support encapsulation and inheritance. To provide polymorphism, Core Graphics applies a naming convention to its routines.

Core Graphics draws its design from the Core Foundation API. Developers familiar with Core Foundation can apply much of that knowledge to the Core Graphics APIs as well. In fact, programs can treat Core Graphics objects as Core Foundation objects. For example, Core Graphics objects can be stored in Core Foundation containers.

The Core Graphics APIs manage the life cycle of objects with the Core Foundation ownership model. We present the basics of the naming conventions and the object ownership model here. Apple's documentation on the Core Foundation Design Concepts present the same information in much greater detail (although in the context of Core Foundation and not Core Graphics). You can find it on the Web at the URL:

http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFDesignConcepts/index.html

Naming Conventions

The first thing you will notice about the API elements in Core Graphics is the fact that they include the prefix CG. For example two Core Graphics routines are CGContextBeginPage and CGImageGetWidth. The simple exception to this rule is constants that also follow an older Macintosh convention. The name of constants begin with a lower case k. The constant kCGImageAlphaNone, for example, is one that describes the way images store alpha channels.

After the prefix, the name includes an opaque data type that relates to that API element. CGContextBeginPage is a method of a "Context" object. CGImageGetWidth and kCGImageAlphaNone both have something to do with "Image" objects.

The remainder of the name relates to the specific task that element is for. Object methods are usually verb phrases such as BeginPage or GetWidth. Constants simply have descriptive names. Many times the different parts of the name narrow down the use of the constant from general ideas to more specific ones. For example, in kCGImageAlphaNone, the general concept related to the constant is "Alpha" (the alpha channel), and this particular constant represents an image with no alpha channel (alpha channel of none).

Core Graphics Objects

A Core Graphics object is an instance of a particular opaque data type. This is completely analogous to an instance of a class in other object-oriented environments. Routines that create objects return a value known as an object reference. These data objects are easy to recognize because their type name ends with the suffix Ref. For example, one of the routines that create an image in Quartz 2D is CGImageCreate. The data type this routine returns is a CGImageRef.

It's important to realize that an object reference is not a pointer. Object references might not obey the semantics as pointers. This may have consequences in the way you handle references inside of your application. For example, if your application includes debugging code that validates pointers, you cannot use that code to validate a reference. The Core Graphics API does use NULL to represent an invalid or un-initialized reference. You may compare object references against NULL just as you would a pointer.

Unfortunately, the naming convention applied to object references does not apply to every API on the system. If you are new to Macintosh programming and you are using the Carbon API, one particular case worth noting is the file reference data type, FSRef. Based on its name, you might conclude that an FSRef variable is an object reference. In this case, the value of the FSRef is an opaque data structure, but it is not a pointer-sized value. You cannot compare an FSRef against NULL, and FSRefs do not implement the Core Foundation ownership model.

Core Graphics uses the Core Foundation reference counting system to manage the life cycle of objects. Each object contains an integer valued reference count. When the object is created, its reference count has the value 1. The API includes routines for incrementing and decrementing the reference count. Incrementing the reference count is called retaining the object. Decrementing the reference count is called releasing the object. When the object's reference count becomes 0, the system destroys the object and releases any resources it owns.

To retain an object you can use the Core Foundation routine CFRetain or a type-specific routine. To release a CGContext object, for example you could use the type-specific call CGContextRetain. Similarly, to release an object you can use the Core Foundation routine CFRelease or a type-specific routine such as CGContextRelease. The end result of CFRetain and CGContextRetain are the same; the only difference is that CFRetain will generate an error if you pass it NULL. CGContextRetain will not.

The pseudocode in Listing 1.1 presents an example of reference counting in Core Graphics. Listing 1.1 is not valid codefor example some of the parameters to CGImageCreate are not given. The listing represents code that manages the existence of two objects, a CGDataProvider and a CGImage.

Listing 1.1. Object Ownership Pseudocode

// Create a new data provider from a URL CGDataProviderRef myDataProvider = CGDataProviderCreateWithURL(myURL); // Create an image with the contents of the URL CGImageRef myImage = CGImageCreate(..., myDataProvider, ...); // The data provider has served our purposes so release it. CGDataProviderRelease(myDataProvider); myDataProvider = NULL; // Call some code that does something interesting with the image. DrawMyImage(myImage); // We're done with the image now so we can release it CGImageRelease(myImage); myImage = NULL;

The code begins by creating an instance of the opaque type CGDataProvider. This newly created object begins with a reference count of 1. The CGImageCreate routine builds a CGImage as the second object.

The code uses the CGDataProvider as one of the parameters to the CGImageCreate routine. When the image has been created, the program has no use for the CGDataProvider. Listing 1.1 asks Core Graphics to release a reference to the object by calling the CGDataProviderRelease routine.

After you release the object, Quartz 2D may or may not dispose of the CGDataProvider. The code calls CGDataProviderRelease to tell the system that it has finished using the object. However, the CGImageCreate routine may have retained the object for its own uses. From your perspective, however, you no longer have a valid reference to the object, so you would set the reference variable to NULL.

The image also has an initial reference count of 1. The code calls DrawMyImage, which presumably draws the image on a graphics context. This is the only thing the code does with the image. The call to CGImageRelease releases the image and destroys it. If the image has retained the CGDataProvider, it will release that as well.

When you receive an object from a Core Graphics routine, you may or may not be responsible for releasing the object. The key to making the decision is the names of method. If a method's name includes Create (as in CGBitmapContextCreate) or Copy then the routine will return an object that you must release when you have finished using it. If the name includes the term Get, as in CGImageGetColorSpace, then the object returned by the routine belongs to the caller. If you want to maintain a reference to the object, you should retain it. If you do not retain it, however, do not release it.

Other Object-Oriented Features

Object references can participate in polymorphic methods. The abstract data type in Quartz 2D have inheritance hierarchies. For example, Quartz 2D includes an opaque data type called a CGContext. A CGContext is an abstract interface to a particular graphics device. There are several opaque data types that inherit behaviors from the CGContext type. The CGContext for drawing to a bitmap might be an instance of the CGBitmapContext data type, while the context for drawing to a PDF file would be a CGPDFContext. Both of these context types inherit their context behavior from the CGContext base type.

Inside application code, the polymorphic data type CGContextRef can store an object reference to any type of graphics context. Any routine that accepts a CGContextRef as a parameter should accept a bitmap context or a PDF context as well.

That's not to say that every polymorphic method applies equally well to all of the opaque types. For example, PDF files have pages while bitmap contexts do not. Quartz 2D has a method called CGContextBeginPage. This routine accepts a CGContextRef as a parameter and tries to create a new page on that context. Starting a new page in a PDF makes sense but creating a new page in a bitmap context does not. Nevertheless, CGContextBeginPage is a polymorphic routine, and you can pass a bitmap context to it. The system simply will won't do anything in response.

Accessing Quartz 2D from Cocoa

Cocoa and the AppKit include a number of Objective-C classes that are implemented using the Quartz 2D API. For example, NSBezierPath is a class that allows you to draw Quartz 2D line art. An important fact to note about the AppKit's drawing classes is that they are not a one-to-one reflection of the opaque data types found in the Core Graphics API. The Cocoa classes offer the same graphic quality as the "raw" Core Graphics APIs, but the system presents a more direct embodiment of the graphics model than the C APIs.

Cocoa does not yet provide Objective-C interfaces for every feature in Quartz 2D. However, because Objective-C applications can call C routines, they can make direct calls to Quartz 2D's C interface. This allows Cocoa applications to take full advantage of the Quartz 2D system, but it may require stepping away from the graphics support that the Cocoa classes provide by default.

Alternative APIs

In addition to the APIs provided by the traditional graphical application frameworks, Quartz 2D also has bindings to other, more unusual environments. In particular, Quartz 2D is available to shell scripts, command line tools, and CGI scripts through a set of Python bindings. HTML developers can use Quartz 2D in Safari and in Dashboard widgets, using the canvas tag. For the sake of completeness, we briefly explore those interfaces here.

The Python Interface

Many developers are surprised to learn that Quartz 2D has bindings for the Python scripting language. A scripting interface opens the power of Quartz 2D to applications that don't have user interfaces. For example, a server side CGI script, written in Python, could use Quartz 2D to generate an up-to-the-minute weather map.

Working with Quartz 2D through Python is straightforward. Python has its own object model that Apple has integrated with the Quartz 2D object model. The Python runtime environment has a garbage collector. That means that when working with Python, your script does not have to keep track of object references. The Python interface to Quartz 2D takes advantage of this memory scheme. Quartz 2D Python scripts don't have to retain and release objects explicitly.

The methods that you call on Core Graphics objects in Python take their names from the routines in the C API that invoke the same behavior. Because Python has a class hierarchy, the Python methods do not have the CG prefix, and they do not explicitly name the class they work with. Following language guidelines, the initial letter of a method name is not capitalized.

This scheme sounds a bit more complicated than it really is. For example, the Core Graphics method CGContextAddLineToPoint is reflected in the Python interface as the method addLineToPoint called on an instance of the Python CGContext class.

When working with the Quartz 2D API through a C-like language, you have the advantage of being able to call any other API on the system. That means if you need to call QuickTime to import an image, you are free to do so. Unfortunately, Python interfaces do not exist for all that functionality on Mac OS X. The Python interface for Quartz 2D, therefore, includes a number of handy utility routines that help your scripts create graphics. For example, the Python interface includes the routine CGImageImport, which you can use to create a CGImage from a file on disk.

While there is plenty of reference documentation for the Quartz 2D routines themselves, those parts of the Python interface that aren't found in the C interface are not documented as well as they could be. There are quite a few example programs that Mac OS X includes with the developer tools. These files are found at the path

/Developer/Examples/Quartz/Python

That folder also contains a file called API-SUMMARY that holds a wealth of information about the Python interface to Quartz 2D and different ways you can use it in your own scripts.

The HTML Canvas Tag

Beginning with Mac OS X 10.4 Tiger, developers also can encode Quartz 2D graphics into HTML content that is part of a Dashboard Widget or an HTML page displayed in Safari. To get Quartz 2D graphics directly into HTML content, there are two important steps. The first is to identify some area of your HTML in which you would like to draw Quartz graphics. To do this, the HTML code uses the canvas tag. After the drawing area has been identified, the HTML document can draw into that canvas using JavaScript to talk to it.

The JavaScript routines that put content into an HTML canvas follow naming conventions that are very similar to those found in the Python interface. The code will typically begin by asking the canvas for a context object. It can then call methods on that context to create graphics.

Apple has provided some tutorials about working with the canvas in Safari and in Dashboard widgets. You can reach one such tutorial by following the URL:

http://developer.apple.com/documentation/AppleApplications/Conceptual/SafariJSProgTopics/index.html

Категории