Introduction to Quartz 2D for QuickDraw Programmersby Scott Thompson
Editor's note: Apple is moving from QuickDraw to Quartz for the handling of 2D graphics on Mac OS X. In this article, R. Scott Thompson introduces you to the concepts behind Quartz 2D, and explains how to make the transition from QuickDraw.
The Legacy of QuickDraw
QuickDraw has been a vital part of the Macintosh operating system since its release in 1984. QuickDraw is not only the basis for the easy-to-use interface that redefined the way people work with computers, but over the years it has also offered a wealth of functionality that application developers use to surprise and delight customers.
As the demands on QuickDraw increased, the architecture has grown and evolved. From its modest beginnings, supporting eight basic colors on computers with black-and-white screens, QuickDraw has grown to support 256 colors and then both 16- and 32-bit color graphics. QuickDraw has also complemented hallmark technologies of the Macintosh platform--technologies like QuickTime and ColorSync. QuickDraw has been a valuable asset to the Macintosh operating system for decades.
As quickly as it has changed, however, QuickDraw has not evolved rapidly enough to incorporate many of the features found in more modern graphics libraries. To extend the graphics reach of Mac OS X, Apple has adopted a new strategy for handling 2D graphics: the Core Graphics system.
Quartz 2D is a key component of the Core Graphics architecture of Mac OS X, and is the modern technology in the system whose functionality most closely matches that of QuickDraw. It implements a drawing model very similar to the one found in the Portable Document Format (PDF) from Adobe Systems, and allows an application to issue drawing commands to an extensive range of graphics devices.
Quartz 2D, however, is not the PDF analog to Display PostScript. While the API designers at Apple have used PDF as a guiding hand in implementing Quartz 2D, the actual Quartz API extends the basic drawing model with additional features like integrated color management and support for transparency and compositing when working with devices that support those kinds of features.
Quartz 2D is just one aspect of the entire Core Graphics system. Another popular facet of Core Graphics, for example, is the Quartz Extreme window compositor. The Quartz Extreme window compositor combines the features of Quartz 2D and other graphics libraries on the system, with the power of advanced graphics hardware, to provide an industry-leading compositing window system that offers a tantalizing glimpse into the future potential of Quartz 2D and other parts of the Core Graphics system.
Because QuickDraw does not share the same growth potential found in Quartz 2D, Apple encourages application developers to begin using Quartz 2D in its place. This is a boon to both Apple and application developers as the rich feature set in Quartz has the potential to provide innovation and performance improvements for the foreseeable future.
Along with its rich feature set, Quartz 2D has a similarly rich field of concepts and abstractions that make up its drawing model. Many of those have no clear counterpart in QuickDraw. This presents a learning curve to QuickDraw programmers that want to use Quartz 2D in their applications.
Unfortunately, there is also a bit of an "unlearning" curve for some developers, as many of the tips and tricks that make sense in the QuickDraw environment are either inefficient or have better alternatives under Quartz 2D. This article introduces some of the models and abstractions that make up Quartz 2D. It draws analogs to similar models in QuickDraw, where they exist, with the goal of providing the QuickDraw programmer with a gentle, but practical introduction to the concepts behind the Quartz 2D API.
Introducing the Quartz 2D Drawing Model
Pick up any documentation on QuickDraw, and you're not likely to get far into it before you're presented with a discussion of the QuickDraw coordinate system and how it relates to the pixels on the output device. Pixels play a prominent, fundamental role in the QuickDraw drawing model. Indeed, much of the lore an engineer needs to coax the maximum performance out of QuickDraw centers around the efficient storage and shuffling of pixel-based data.
This leads to a bit of culture shock for many QuickDraw programmers early in their exploration of Quartz 2D, because the treatment of pixels in the Quartz 2D API seems almost an afterthought. Some of the drawing commands that an application issues to Quartz 2D find their way onto devices with pixel buffers very similar to QuickDraw's PixMap.
However, Quartz extends the reach of those same commands to include output media that are not raster-oriented. For example, an application can use the same Quartz 2D drawing commands to render an image to a bitmap display that it uses to incorporate that image into an OpenGL context, place it into a PDF file, or send it to a PostScript printer.
How are Quartz 2D drawings built, then? Well, instead of using pixels, a Quartz 2D application works by covering different areas with virtual paint. An application will use drawing commands to identify an area of the current coordinate system and then explain how it wants Quartz to paint that area. Successive layers of paint are built up, one on top of the other, until the entire drawing is complete.
The power of the drawing model comes from a rich set of primitives for identifying areas of the destination where paint should be applied, and a powerful box of virtual paints to color with. As the application is painting, however, it needs to have some link to the destination device, and this is where the Core Graphics Context (represented by the type
CGContextRef) comes into play. A Core Graphics Context shares a lot of similarities to the QuickDraw
GrafPort, so an exploration involving comparison and contrast may be in order.
The central drawing environment in the QuickDraw API is the Color Graphics Port. An application works with a graphics port through the
CGrafPort structure. A graphics port consists of a finite pixel buffer and the information needed to establish a coordinate system on that buffer. It also stores a number of parameters that affect the drawing commands that are issued to the port. This includes things such as drawing colors, pen patterns, the current clipping region, and similar state information. Through that coordinate system, an application has access the individual pixels of the buffer. This leads to the following characteristics of the QuickDraw coordinate system:
- QuickDraw uses integers to represent its coordinate space.
- The QuickDraw coordinate space is limited by the size of a short integer.
- When it is first created, any drawing that occurs outside of the bounds of a port will be clipped to that port's bounds.
- The coordinate system shares a one-to-one mapping with pixels in the PixMap. To preserve that mapping, the coordinate system has a fixed orientation with respect to the PixMap (it cannot be rotated) and a single coordinate value cannot represent more than one pixel (the coordinate space cannot be scaled).
Like the QuickDraw Graphics Port, a Quartz graphics context defines a coordinate system for drawing and maintains a number of important drawing parameters like the drawing color information, clipping information, and other such things. Unlike its QuickDraw counterpart, however, the Quartz graphics context supports drawing to devices that don't have pixels.
Instead, the coordinate system for a Core Graphics context is set up to allow an application the maximum flexibility in specifying areas that it wants to paint. This frees the application from many considerations about how those areas map onto the output device. This level of abstraction grants a certain freedom to the Quartz coordinate system that the QuickDraw coordinate system doesn't enjoy. Compare these features of the Quartz coordinate system with the QuickDraw features above.
- Quartz coordinates are specified using floating point values.
- In the Quartz coordinate space, an open plane is limited by the value of a single floating point value. In practical use, this means that the Quartz coordinate plane is infinite in extent.
- When it is first created, a Quartz drawing context does not clip its drawing.
- Because the coordinate system is not tied to a pixel buffer, the coordinate system is free to move, rotate, and scale relative to the drawing destination.
The drawing plane defined by the coordinate system of the
CGContext is called user space. One of the challenges to understanding the Quartz 2D imaging model is grasping how drawing commands that are issued in user space will be translated into images on the output device.
When working with QuickDraw coordinates, it is clear that the points (1, 0) and (2, 0) are one pixel apart, since the units of the QuickDraw coordinate axes are pixels. What then are the units of the Core Graphics coordinate axes? For Quartz 2D to maintain its device independence, these units must also be device-independent. For this reason, the unit defined for the coordinate axes in Quartz 2D is the point that is defined to be exactly 1/72 of an inch (or to put it another way, there are 72 points per inch).
Most developers familiar with modern computer systems will recognize the point from its popular usage in specifying the size of text. For example, 64-point text is larger than 18-point text. In fact, the definition of the point unit has a long-standing relationship to typesetting from the time it was done using lead slugs on Gutenberg-style printing presses. A traditional typographic point is slightly more than 1/72 of an inch, but in the imaging model shared by Quartz 2D and PDF, a point is defined to be exactly 1/72 of an inch.
CGContext first connects to a device, a convention of Quartz is that it positions the origin of the coordinate system in the lower left-hand corner of the device and scales the coordinate system so that one unit in user space system is equivalent to one point on the destination device. By way of an example, that means that if the destination is a page in a 300-dpi laser printer, where one dot on the printer is 1/300th of an inch, and a program draws a line that is 72 points long in user space, that same drawing will be represented on the printer as 300 dots. It is possible to change the Quartz context's coordinate system so that one inch of user space no longer equals one inch on the output device, but when the context is first created, the convention is that Quartz sets it up so that 72 points in user space equals one inches worth of device pixels.
There are exceptions to every rule, and with Quartz as we know it today,
CGContexts created on pixel buffers (like the pixel buffer of a window, or a bitmap context for offscreen drawing) are exceptions to the 72-points-equals-one-inch rule. One day Quartz may be able to determine the dot pitch of your monitor and set up its context so that one inch on the monitor matches 72 units of user space. Currently, however, when Quartz creates a graphics context that is attached to a pixel context, the context is set up so that one unit of user space corresponds to one pixel in the output pixel map.
This is an important point to know. What it means is that when Quartz sets up a context for a bitmap destination the units of user space correspond to the units of QuickDraw's coordinate system where one unit in user space represents one pixel on the output device. This relationship can be very handy when moving QuickDraw drawing code to Quartz.
In fact, the HIView system takes advantage of this fact to present HIViews with a coordinate system that very closely matches that of QuickDraw. But a bit of care is prudent because the Quartz coordinate system, unlike the QuickDraw coordinate system, can change in such a way that this relationship no longer holds. The details of transforming the Quartz coordinate system will have to wait for another time. For the moment, however, with the coordinate system in hand, however, let's look a bit more closely about how you use those coordinates to tell Quartz where to paint.
Pages: 1, 2