Graphics and Imaging • 59:06
Quartz 2D is Mac OS X's powerful 2D graphics engine with advanced features such as transparency, anti-aliasing, and support for the PDF imaging model. Learn how to harness the capabilities of Quartz 2D in your applications. This session includes an introduction to the Quartz 2D technology followed by details on upcoming developments in the Quartz 2D API.
Speaker: Derek Clegg
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Good morning, everyone. Welcome to session 207, Quartz 2D In-Depth. And in this session, we're going to talk about our fully modern 2D drawing API that we created when we brought Mac OS X into being. And it's important to realize that we're an interesting position, because Mac OS X actually has two drawing APIs that are typically used by developers. We have, obviously, Quartz 2D, which is our new drawing API. We obviously have Quick Draw, which is our legacy drawing API. And one thing I think that's important to understand is where we're investing our time and effort. And that's behind Quartz 2D.
And as you're going to see in today's presentation, we're adding lots of new features into Quartz 2D to meet the future needs of what application developers, such as yourselves, want. What's important to understand as you sit and listen and take in this presentation, that your take home from WWC this year with regards to 2D graphics is to start thinking about if you have a product that leverages Quick Draw, to come up with a strategy on how you can move your product over to use Quartz 2D.
Because that's really where our focus is on the operating system in terms of 2D graphics. So at this point, I'd like to invite Derek Clegg, a graphics imaging engineer, to the stage to take you through the session. Good morning. Welcome to WWDC Day 4. I hope you're enjoying the conference. My name is Derek Clegg. I work in the Quartz Engineering Technologies Group.
Today I'm going to talk about Quartz, what it is, sort of a rough overview of what it is, a little bit about the Quartz architecture. I'm going to focus most of the time on the Quartz APIs, particularly some of the things we've added for Panther, and along the way I'm going to have a few demos.
So I'm going to briefly cover what Quartz is. Those of you who have been to previous WWDCs have probably seen some of this material. Quartz is our Mac OS X, that is, imaging model, the way we get from the things you want to draw onto the screen. It's been influenced by a couple of very powerful industry standards, such as PostScript and PDF, of course. That's a pretty fundamental piece of Quartz 2D. We also, of course, have adopted parts of QuickDraw and parts of Java 2D. And we really think of it as the next generation imaging model for your next generation applications.
So the key thing to keep in mind, as Travis mentioned, is that here's a graph that we think is very important. On the y-axis is innovation. And as you see, we're really putting a lot of energy into innovating in Quartz 2D. QuickDraw, it's not a flat ramp or a negative ramp or anything like that.
But you can tell that we're really focusing a lot of energy on Quartz 2D. We're really letting QuickDraw, in terms of innovation at any rate-- follow as far along as Quartz 2D is. With Quartz 2D, we're really pushing everything we can there, as you've seen in other talks. We're adding support for OpenGL. We're adding all sorts of interesting things, some of which I'll touch on today.
So the thing, what Quartz 2D is, sometimes it's also called Core Graphics. I'll use both terms interchangeably. Core Graphics is a slightly older name, but the APIs and so on live in the Core Graphics framework. Quartz 2D is a low-level, lightweight 2D rendering library. What that means is it's low-level, it's just C functions, not a lot of complicated C++ goop or anything else, very simple, low-level C functions. It's lightweight, there's not a lot of API that you have to learn to do very complicated things, it's pretty easy to use.
The 2D API is built up front as a resolution and device independent API. So what that means is resolution independent means if you're going to a 72 DPI screen, 96 DPI screen, or a 300 DPI printer, 1400 DPI printer, you use the same APIs, you make the same calls, you do exactly the same thing, but your output comes out beautiful in both cases.
It's device independent, same sort of sense, going to the screen, going to your printer, same API calls, same work you do, but the output comes out appropriately on the two devices. So you can use it for different devices. Of course, we want to have really high-quality fonts, so we use Apple type services to do all our font management, and we want color independence, high-quality, high-fidelity color, and so we use ColorSync for color management.
So some people have asked in the past who uses Quartz 2D. Of course, Carbon, Cocoa, and Java, all our implementations inside Mac OS X rely very heavily on Quartz 2D. And some of them, for example, in the Cocoa case, is pretty much implemented entirely on top of Quartz 2D. So it's actually the underlying framework for everybody.
Many Apple applications not only use it indirectly through their use of Carbon and Cocoa, but often will do direct API calls into the Quartz 2D API. For example, Safari does an awful lot of stuff with Quartz 2D APIs. That's one of the reasons it's so fast and powerful. All our iApps, one way or another, tend to use Quartz 2D, sometimes directly, sometimes indirectly just through Cocoa or Carbon, but they leverage that very heavily, particularly things like iPhoto, iDVD, those types of applications. Keynote, for those of you who saw the talk on Tuesday, uses Quartz 2D.
And of course, third-party applications use Quartz 2D very frequently to do interesting effects or techniques. Mathematica, Microsoft Office, of course, DeltaGraph, and your application can as well. And for those of you who saw Peter's talk on Tuesday, your application might want to consider Quartz 2D before your competitor's application does. Thank you.
So the general thing about Quartz, the whole architecture of Quartz, is Quartz 2D, which is the APIs you talked to, underneath that on a lower level is Quartz Compositor. So it's really sort of two pieces. Quartz Compositor is the part that handles all the window management that sort of says, oh, okay, I want to take these multiple windows and put them on the screen. It's the thing that does all the expose cool stuff, thing that sort of makes your terminal window 20% alpha, for example, and composites under the background so you see things through it and so on.
I'm not going to really focus on that too much. In fact, I'm not going to focus on it at all. There's a session tomorrow, session 211, which we'll go into that in more detail, so you might want to check into that if you're interested in Quartz Compositor, sort of the window management part of the system.
So again, what do we have in Quartz 2D? Well, the things I've already mentioned, device resolution, independence, and so on. Of course, we also have very high quality 2D graphics. All the anti-aliased rendering, both line art and text is anti-aliased at very high quality. All the special effects such as LCD text rendering and so on for your LCD displays. Transparency, of course, is a big part of our feature set.
Off-screen rendering, so you can create a bitmap off-screen, draw into it using exactly the same APIs, get the same beautiful effects, and then composite the result either to your screen or maybe write it out as a PDF file, I mean, as some sort of TIFF file or JPEG file or something like that.
And then we also have support for PDF document. As Peter mentioned on the talk on Tuesday, we really think of PDF as our digital paper format. And to allow you to get the same power that we see present in PDF, we allow you to get the same power that we see present in PDF. You get access to the PDF document, to render it on the screen or to some other destination, and to create it directly. So, let's go over a little bit the types that we have available in the Core Graphics API.
Here's a set of the basic sort of fundamental pieces of the Core Graphics API that we have available for you to work with. Of course, the context, the context is sort of the central switching point. Everything that gets drawn to the screen, to a PDF file, whatever, goes through a context. And when you work with context, you need to use various pieces, various other objects to talk to the context to get things done for you.
So we have support for a path. A path is just a collection of lines and curves. Fonts, of course, are important for text, clearly. Images, as you know, PDF documents are a native type in the Core Graphics API. Colors and color spaces, shading, sort of gradients, you can think of them as radial axial fills of regions. Patterns, repeated drawing operations.
Geometry and affine transforms for convenience to allow you to do arbitrary transformations, rotations, and so on. And data managers, those are the type we use in Core Graphics to get data in and out of the program. So we can get into and out, in from your program and out to your program.
So, as I mentioned earlier, I'm not going to go into a huge amount of depth in some of these various types. The session last year, we covered all of these in detail. It's available on the DVD, so I definitely recommend that you go check that out if you're not that familiar with some of the details, some of the things I talked about today.
So let's look at the CG context. A CG context is sort of, as I said, the switching point. So your application's up here, the purple oval, and it's going to call into the Core Graphics API, either directly just by calling CG context, blah, blah, blah, or indirectly by calling a Cocoa or Carbon or Java function. And the Core Graphics API will then take what call you've made and switch it out to the appropriate destination that you might be drawing to.
So you might have a window context, in which case all your drawing will go to the screen. You might have a PostScript context, in which case all of your drawing will go to a PostScript file, PDF to a PDF document, or bitmap, an off-screen bitmap. So the context is sort of the fundamental piece that we use to push data through the appropriate points so it comes out to the destination that you care about.
Now, in terms of drawing primitives, as I mentioned, there's a number of auxiliary objects that we support that you use to do drawing in a context. Paths is one example. Here are some texts. You can have fancy fonts. You can have rotated text. Images, of course, pretty simple idea there. PDF pages, pages from a PDF file that you can draw.
Just like any other primitive, you can draw it rotated, scaled, however you want. All the content will show up correctly. Patterns. Here you see an oval with a bunch of stars in it, just a repeated set of stars. That's just obviously what a pattern is. Shadings, radial or axial shadings.
This year in Panther we're adding two new drawing primitives. One is shadows. Some of you have seen the cool looking shadows inside Mac OS X in general in the Aqua UI. We've added a primitive inside CG to let you do shadowed, support shadow drawing for any primitive. Whenever you draw anything, if you want to, you can have it shadowed. So here you see on the left a bunch of fancy circles and some sort of logo type design.
And as you can see there's a shadow on the lower left edge that is providing sort of a sense that this thing is on top of the checkered background. An additional thing that we've added as well, and I'm going to go into this a little bit more in detail down the road. Another thing we've added for Panther is transparency layers. And transparency layers is a way to sort of temporarily, draw to another destination and then have the whole entire result composited back with a particular effect.
So in this case I might want to get these nice circles all drawn with a nice transparency. And so I can sort of use a transparency layer to draw the circles off screen and then when I composite it back the entire result will be drawn in with a particular alpha. And I'm going to go into that in a little bit more detail later as well.
So now let's look at the types that I talked about and talk about those a little bit more. Of course, vector geometry is very important. That's what I was talking about with paths. A path can be just a straight line. It can have a dash, as you see on the left. A path can be a collection of curves. It doesn't have to be closed. If you want, it can be closed, like the state of California there, as you see, is a closed path. And in addition, that's a filled path, so paths can be either stroked or filled.
Of course, a path doesn't have to be stroked. It can just be simply filled. As you see on the bottom right, you have a circle with the red circle, which actually consists of two paths, an inside circle and an outside circle. And when you fill it, you just get the interior part filled.
And that brings up an interesting thing, a question about filling. For those of you who know a little bit about graphics, there's two different ways of filling. There's what's called even-odd and winding-number fill. That's the illustration of the star in the lower left corner. And core graphics supports either flavor or fill operation. Okay.
Another thing you can do with paths that's very powerful is use them to clip. You can use them as a vector geometry clip. So here you see on the left an arrow, which we're going to use as just a path that we want to clip to. And when I say clip, what that means is once you set that as a clipping region, everything that's subsequently drawn is clipped through that path and only shows up where that path would be normally filled.
So here you see the arrow, and we set that as a clipping region, and then we draw an image, and what we have is only the part inside the arrow that's drawn. So this can be a very powerful technique for complicated drawing operations, for simple drawing operations. It's very useful to use paths as a clipping region like this.
So in addition to paths, of course, we have text. Text is a very important part of any graphics API because most of the things that you draw tend to be text. We have support for all the font flavors that Mac OS X supports. As you can see, we can use pretty much any font that's available in the system. Text doesn't have to be just sort of laid out vertically or horizontally.
It can be rotated at an arbitrary angle. You have complete freedom over that. Of course, you get all the high-quality anti-aliasing, so when you do rotate it, it looks very beautiful. It doesn't look sort of bitmappy, jaggy type. Text can be stroked or filled, as you can see with the stroked text example where you have a white border on the outside. And of course, you don't have to restrict yourself just to Roman text. You can also have Chinese, Japanese, Korean, any of those flavors.
So the important thing to know about the way the Quartz 2D API works, we're really, like I said before, we're a low-level, lightweight thing. That means we don't work on Unicode strings. Instead, we work on sort of the processed Unicode strings after they go through ATSUI or the Cocoa text rendering system to end up to Glyphs. The Glyph is just an index into a font that tells you the path you use to draw the character. It's a very simple API. So we work at that level. We're not really a Unicode text rendering engine. We're not a layout text rendering engine.
All of that stuff you should use either with Cocoa or ATSUI to get the right result. And in both cases, there's ways to funnel all of your drawing down to Glyphs. And once you have Glyphs, then you could, if you wanted to, you could use the Core Graphics API to draw directly to the screen to a bitmap, whatever.
Of course, they also have, both ATSUI and Cocoa, have a way to draw directly to a context without you having to get involved, which is very powerful. The good thing about Panther, we've improved the text rendering, or the Glyph rendering, I should say, at the Core Graphics level. In some cases, it's up to twice as fast. It was already pretty fast. So this is actually a pretty significant speedup.
And of course, as I mentioned before, we support all the standard flavors, TrueType, Type 1, OpenType, CID, which is for Chinese, Japanese, and Korean. All of those come through ATS, and we use the ATS very heavily to get the glyph data out, and then we go render it, and we put it to the destination in a high-quality way.
Of course, images as well are part of the Core Graphics API. Here you see that images don't have to be necessarily just rectangular blobs of data. They can be rotated. You can have a clipping region associated with it. The image itself can have a clipping region. You can sort of see the ticket has a nice little notch cut out of its edge. So images can be used to do lots of complicated drawing, and you don't have to restrict yourself to just a straight rectangular region of data.
So the, as I mentioned, we have an alpha channel support so you have images that have some transparency, some non-transparency. In Panther we have support for images which have 1 to 32 bits per component. That means that each component of your pixel can have up to 32 bits. So you can have a 32 bit red component, a 32 bit green component, a 32 bit blue component for a total of 96 bits per pixel. So that's very powerful. We think it's going to give us a lot of headroom for a long time.
Color space support, in other words, the way the image data is interpreted, whether it be RGB or CMYK, we support all the standard color spaces. So as I mentioned, red, green, blue, cyan, magenta, yellow, black. Index color spaces such as what you find with GIF images where each pixel is really an index into a color table. And of course, ICC profiles, those are the way of doing color, sorry, device independent color management.
We use ColorSync to allow us to give us access to the ICC profiles and do a lot of work. We also support image mask which range from 1 to 8 bits. An image mask is effectively just a stencil. It's just a way of specifying where you want paint or color to appear on the destination. And so you can think of it as a stencil. And in Panther we have support for 1 to 8 bit image masks.
[Transcript missing]
So in addition to the standard image support that we've had in the past, for this year, for Panther, we're adding new API to QuickTime to give us access to a lot of what QuickTime supports in terms of image formats. So the QuickTime guys have added graciously some API to create a CG image ref directly from any graphics importer that you might happen to have. And this means that any image format that's supported by QuickTime can now be drawn directly into a CG context.
You don't have to sort of go and create a G world and do all the dance that you used to have to do. So that means we now support, through this QuickTime API, JPEG 2000, GIF images, pretty much anything that QuickTime supports is now supported in CG directly drawn into a context.
And the important part about it is that QuickTime, excuse me, the QuickTime API preserves as much of the information of the original source image as it can. So if there was transparency in the original source image, it will preserve that. If there's a color sync profile, it will preserve that. Anything that makes sense to preserve.
Through the Core Graphics API, it will preserve. And the API itself is pretty trivial. It's a very nice, simple API. Graphics import creates CG image. You take a graphics importer and hand it to this API, and out pops a CG image ref. And then you can use that just as you would any other CG image ref to draw to any context, to PDF, to PostScript, whatever you wish.
Another thing that we can do with a context is we can draw PDF documents. We can draw pages of a PDF document into a context. We support importing of PDF 1.0, 1.1, PDF 1.4 flavors of documents. One thing that's very useful is a lot of times people want to create PDF. We also have API to allow you to create PDF from the drawing. A PDF context lets you do PDF document creation.
When we create PDF content, we always will export PDF 1.3, which means that it's more widely supported on older Acrobat readers. If we need to, if there are cases where we have to, we will bump it up to PDF 1.4. For example, if you use transparency, that's only a PDF 1.4 feature. We try to keep the PDF document as widely distributable as possible because we do think of it as something that's very, as we said, the digital paper of Mac OS X.
An important part about our PDF document support is that we have full round tripping of the CG API. What that means is that if you draw anything in Core Graphics and then to a PDF context, what comes out is a PDF document that will view correctly in Preview and in Acrobat exactly as you originally specified the drawing in the context.
We get a full round tripping so you don't lose any information when you draw to a PDF context. It will show up correctly as you want it in Acrobat and Preview. For those of you who worry about this sort of thing, we also have support for prepressed data interchange via PDF X3 if you provide the right keys and additional dictionaries when you create the PDF context.
New this year for Panther, in addition, in the past the API has been pretty monolithic. There's a PDF document and you have to do a bunch of stuff with the PDF document. It's a little bit awkward. So we've added some convenience APIs to do page-based access so you can get information directly. There's now a new type, a CG PDF page ref, which you can work with. So it's a little bit more convenient. You get the standard things such as media box, crop box, and so on from the PDF page. Things like the rotation angle and the page number.
And then what I think is actually even more interesting, we have also decided to unlock the inside of the PDF file. So we now have new API which will give you access directly into the PDF document itself in more of its native form. And the idea, for those of you who aren't familiar with the way PDF is structured, you can sort of think about it as a big tree. And it's a big tree that starts out with the very top with a root node called the catalog.
And this catalog is a dictionary. And all the entries in the dictionary are either more dictionaries or arrays or values of integers and reals and stuff like that. So it's just this big tree structure. It's like a property list. So in order to mirror that structure, we have new API -- or sorry, new types such as like CG PDF dictionary, CG PDF array, and so on, which allow you to sort of model the contents of the PDF file.
We have new API for a PDF document to get its catalog, sort of its root node. And we have new API to get the dictionary directly from a page. A lot of times, you don't necessarily care so much about the entire overarching structure. You just want to know, does this page have a thumbnail or does this page have any annotations and stuff like that? So those are the convenience APIs that we have available.
As you can see, here's a very simple example of how to use this API. In this case, we are thinking, "Oh, we'd like to get the thumbnail for this page." So we declare two types, a dictionary and a stream. The stream is sort of the way PDF represents an arbitrary collection of just data. In this case, it's going to be image data. We get the dictionary for the page that we care about, that we want to get the thumbnail from. And then that's just a dictionary that we can look up values in.
And here we're going to look up the thumbnail. The key, the way the PDF specification defines the thumbnail is via the key "thumb." So here we say, "Does this dictionary have an entry called 'thumb' that's a stream for the page dictionary?" And if it does, then we want to get the data from the stream. And once we get the data, then we may want to go off and draw that to the screen. We may want to pretend like we're previewing and draw the little thumbnail in the drawer on the side.
The format key just says, "Is it JPEG? Is it not JPEG?" Currently in Panther, you have one flavor or the other. We do all of the decryption, decoding, stuff like that for you, except in the case of JPEG, where it doesn't make as much sense. So just a simple demo of this. I'll go over to the So here I have a very simple PDF file. It just has some text, "Hello World" in Times Roman.
And I'll take that to my PDF introspection app. This is actually about a two hour worth of work. Most of it was figuring out what to do in Cocoa to do the right thing. As you can see, what I've done is all I'm doing is just displaying the contents of the PDF file, sort of what it looks like when you actually open it up and look at the real structure inside it. So here you see we're at the top root node. We have the catalog. This is the dictionary for the catalog.
And the root node has the pages. In this case, there's just one page. That's the count. Kids is the PDF way of talking about each page in the document. So we can open it up. Kids is just an array, obviously indexed by zero. And it has a dictionary in it, which is the first page.
So I can bring up my inspector, which I have. And all of this is just with public Panther API. None of this is special or hidden or anything like that. It just allows you complete access to the PDF file. Here you can see that the page has a bunch of entries in it that are related to how the page gets drawn and so on. As you can see, here's the resources.
The resources have some fonts. Here's the font. You can see, oh, look, it's Times Roman. So you say, oh, it's drawing Times Roman. Good. It's doing the right thing. And here's the contents of the actual drawing operations for this PDF file. Basically, it's sort of like set the font to 36 point.
F1 means Times Roman in this case. Position it 1010 and draw Hello World. So it's a pretty trivial PDF file. But this--and this is a pretty trivial app. But we think that this, allowing developers to get access to the contents of the PDF and introspect in interesting ways will be very powerful and provide a lot of interesting application opportunities for you. Okay, let me quit that and go back to the slides.
Okay. So that's the, we're still sort of trundling through the things you can do with a context. Another thing you can work with a context is a color space. The color space type, as I mentioned earlier, tells Core Graphics how to interpret color data. So as I showed you before with that profile, one of the profiles showed you that was an example where the color space, the profile said, oh the first component's red, the second one's green, the third one's blue.
Another one was a profile that said, oh the first component's blue, the second one's green, the third one's red. So color profiles are used in order to provide data and interpretation of the color data. And a better example perhaps is a profile for a camera which might say, oh here's how you tweak things when you draw it so it looks the right way on your monitor.
New for Panther, we're adding two new convenience APIs. One is a way to get the document default color space. A user, if they choose, can set the way they want the document to look. So that's the first one. The second one is a profile that says, oh here's how you tweak things when you draw it so it looks the right way on your monitor. New for Panther, we're adding two new convenience APIs. One is a way to get the document default color space.
A user, if they choose, can set the way they want the document to look. document by default to specify colors if in fact the document doesn't do it itself. There's no profile embedded. This is a convenience function because it turned out in the past. This was actually difficult to get. It's information that's available. It used to be the ColorSync Preferences Panel, now it's in the ColorSync Utility.
It was a little bit more complicated to get that information so now we have a simple way of getting it directly through the CGAPI. We're also adding a new API to allow you to get a display color space. A display color space is our term for a color space which is very fast in drawing to the screen. There's no matching. What that means is there's no special conversions that happen in order to draw something to the screen.
As you specify it, it's drawn to the screen. You get very fast drawing to the screen. It's very appropriate for something like UI or something like that where the precise color that you get on the screen may not be that critical relative to the color. It's very important to get the precise color that you get on the screen. You don't want to have a lot of color in the drawing. You want to have a lot of color in the drawing to the time it might take to do the drawing.
For example, if you had a screen saver or something, you don't really care that much about precise color but you just want it to be fast. The important thing too is that you get a consistent profile any time you go to another output device. When you're printing, for example, you get what's called in ColorSync products the generic profile.
That is just some profile that's been specified which means that from any device that you're printing, you'll always get a consistent result. You won't get different results or crazy results when you're printing to different types of printers and stuff like that. You'll always get a constant look. This is, I think, something that will be very powerful for developers who want to do fast screen drawing.
If they do need to print, if it does happen that they need to, they'll get a constant consistent result on the output side. There's a lot more information, much more than I can go into here in session 206, the ColorSync update. If you're interested in color management and so on, I recommend that you go to that.
Another thing that we're adding this year for Panther is a new primitive type, the CGColorRef. It turns out in the Jaguar APIs, sometimes it was a little bit awkward to set colors. Well, not necessarily awkward, but the API didn't actually match what was going on internally. Internally, we use a CGColorRef, this primitive type, for all of our color management. And so all the API before, you sort of had to specify in terms of components, and we'd end up allocating ColorRef, and we'd do all this little dance.
And the truth is, what we ended up with internally always was CGColorRef. It's just a container that holds both the color space, the way to interpret data, and the components for the color. So you might, for example, have a CGColorRef which is red with this particular profile, and it would be 100 RGB plus the profile that you're interested in.
As I mentioned, it's the fundamental type for colors in Quartz 2D. It's a thing that we use always internally. So anytime you are calling the old APIs, you will end up internally getting the CG color ref. Well, if you can create one yourself and use it yourself and manage it yourself, then as you can imagine, we don't have to do that work internally.
We can just use the color ref that you provide us directly. So it is in fact very fast. It's much faster to use a color ref than it is to sort of go through the original APIs. That said, there are cases when it's not as maybe as powerful. There are some cases where it's more powerful than it is in other cases.
So in particular, if you use a constant color, if you draw a lot of text in red with this particular profile or a lot of text in purple with this other profile, creating a CG color ref that you hang on to, that you reuse over and over and over, will be much faster than setting that every single time. Part of the reason for that is we can do all sorts of optimizations in the back end.
So if you want to, for example, we might cache the color, we might do pre-conversions to a known set of color spaces because we're always going to some particular destination. Who knows, we might upload it to OpenGL. It could be anything in the world. But if you can reuse the color over and over, it will be somewhat faster than if you had to set it and recreate a new thing and then discard it at the end.
If on the other hand, you use lots and lots of different colors, if you're using every color of the rainbow all over the place, then you don't get as big a speed advantage because we can't reuse the color. We're sort of creating one, using it, discarding it, creating one, using it, discarding it, so you don't get quite as much advantage. But there are cases where it would be very powerful.
A typical case would be black text. Almost every piece of text that's ever drawn is in black, the slides here notwithstanding. So creating a black color and reusing that over and over every time you draw a black text makes a lot of sense. Of course, there's some new API to allow you to set the color directly into the context. CGContext.setFillColorWithColor is a little bit awkward names, but the better names were already taken.
Okay, another thing that you can do with a context is specify a shading. Shadings are just a way of doing axial or radial gradients where you provide a function that tells us what colors you want to be drawn across the gradient. So here you see on the left you see a radial shading where you start out with a white bright dot in the sort of offset from the center of the circle and you sort of transition outward smoothly to pink. And on the right you have an axial shading which goes through a number of different colors all provided by the function that you might provide yourself. So we don't really restrict what colors you can use, how you work with it.
We also don't do any restriction in terms of things like, you know, here you can see that the circles don't have to be concentric or anything like that. They can sort of be pretty much two different circles anywhere in space that you want and you'll get a nice transition between them.
For Panther, we've put an awful lot of effort into improving our performance of axial and radial shading drawing. We want to, well, the truth is that we want to have what we call keynote quality gradients. And by keynote quality, I mean satisfy the principal user keynote. And so, you know, that takes a bit of work, it turns out. And so our goal has been for Panther to be keynote quality. And I believe that we've reached that goal in the current release.
So one of the things that we allow you to do is actually specify the level of quality you want because there are cases when you actually just sort of want a fast, as fast as possible shading, not necessarily super high quality because it's just a one-off or you're doing some small little drawing in the corner or who knows what.
The typical way that this works is through dithering. As you might know, if you're doing a gradient, when you zoom it up very large, you can sometimes get banding. And so dithering is an approach to solve this problem. So you can now control the shading and the way it's drawn via the interpolation quality.
Same sort of way you control the quality of images when they're scaled up or scaled down by the interpolation quality. And then also in Panther, we've added the ability to have a transition between opaque and non-opaque colors. In the past, in Jaguar, all the colors were treated as if they were just 100% opaque.
So you can do a nice transition with a transparent color. Now in Panther, we've added the ability to use transparent, non-opaque colors. So you can have a transition from 0%, maybe 0% alpha, so completely transparent, to fully opaque. So you get a nice transition across where you start out with seeing the stuff in the background. And by the time you get to the end, you see nothing from the background.
Another thing that we've added, as I mentioned earlier, is shadows. Shadows are basically applied to any drawing you do. We'll take the result of the drawing and we'll create a shadow from it and we'll write it down essentially underneath the object that you're drawing at the offset you provide with the blur radius you provide. That way you can do shadows to all sorts of things that you want. Anything that's standard CG API, you can shadow.
As you see here on the right, I have some text shadowed in an ordinary way. On the left, it may be hard to see. Maybe you can see it. On the left, the top figure, this little logo that I've created, has a shadow that has a hard edge. On the bottom, it has a softer edge. The blur radius, the amount of blur you get in your shadow is controllable so that you can set that as you want depending on the effect you're trying to get.
As I mentioned, the API that is available allows you to specify an offset, sort of the position relative to the object you're drawing, how far off the shadow appears, the position around as well, and also the blur radiance, of course. And this is part of the graphic state, which means that you can save the graphic state, set up your shadow, do some drawing, and restore the graphic state, and then the shadow's gone. So you can do just very precise shadowing of objects.
Here's a code example of actually how you would use a shadow, how you'd actually set it up. In this case, we're imagining that we've already set up a path that we want to stroke in the context somewhere. Somebody's done that for us, and we want to do a shadow of it. So you can see here we start out by just doing a save G state.
We're going to save the graphic state because we just want this path's shadow, nothing else. We set up the offset. Here it's going to be 5.5 in default user space relative to what we're drawing, so it's going to be sort of shifted. Actually, it's going to be shifted up this way.
So it's as if the light source were down here shooting up. And then we just call CGContextSetShadow with the context, of course, the offset that we've just created, and the blur radius. And we imagine someone has specified the blur radius that they want to use. And then we call CGContextStrokePath and CGContextRestoreGState. So it's as simple as that. It's nothing very fancy or complex, but now you get a shadowed path.
And all of the nice anti-aliasing that happens with regular, everything else will happen. All of the pretty drawings, the interpolation, anything that happens that needs to happen will happen. And you'll just have a shadow underneath the object you were drawing. So this can be very, very powerful. And here's a simple demo I'm going to show of this.
So here's just a bunch of objects that are animating. This is just a simple Cocoa app that just animates the object. You can see all the objects have shadows underneath them. And you get, and the shadows don't really incur a tremendous performance penalty. This is in fact very little of one. This is animating a pretty good clip.
And as you can see here, we have the shadow. You can sort of see that in this case the shadow is sort of drawn. It looks as if it's a 3D effect. Of course it's not. It's just sort of whatever is drawn later will cast a shadow on what was drawn before. So it looks, here you see that it looks as though this is on top of this. But of course it's not really. This was just drawn later.
But you do have the shadow on top of the other objects. As you can see here a little bit where the shadows intersect you get a slightly darker appearance. And as I mentioned you can set the shadow position anywhere you want. You can sort of play around with it and say, "Oh, I like it there. That's really pretty." Or whatever you like.
And as I said before you can set the blur radius so you can get it to be more fuzzy, more diffuse light. Or a sharper light. Whatever you'd like. So it's actually, it's a pretty simple API and then you can just use it for anything that you might want to draw. Okay, so back to the slides.
Okay, so another thing that we've added for Panther, as I mentioned earlier, is transparency layers. And the problem we're trying to solve is this math equation. What do you get when you want to do a logo? Let's say we're going to combine these three circles in a way that we would like to combine them so that they're overlapping each other, but we want to have the results shadowed, okay? How do we do that so that it looks right? As a clear example, here you see on the left, you have our logo, our three circles drawn, but we've tried to shadow it and we get this horrible result where every circle shadows the ones underneath it or ones drawn earlier. It looks awful. What we really want is the one on the right where we have this nice little logo that I've created and the whole thing is shadowed.
What we've added in Panther is what we call a transparency layer. I've been mentioning shadows and you might think, "Well, why isn't it called a shadow layer?" Well, both names are actually sort of misnomers. It's really just a way of, you can sort of think of it as a layer. It's really a way to sort of temporarily say, "Okay, take what I'm going to draw for a while until I tell you to stop and collect it all up in whatever form makes sense.
Then after I say stop, take the result and bring it back to the table." You can draw it back with the effect that you want. Here, what we're sort of doing is we're saying we're going to begin a transparency layer where we're going to draw the three circles all together. Then when we say, "Now we're done," we take the three circles as they're drawn and then take the whole thing and shadow the result.
So, so it's, it, as I've mentioned here, it's good if you want to do a bunch of objects all simultaneously and just applying this, the effect to the entire aggregate, not to sort of each one independently. And it really is just like a layer in a graphics application. You can sort of think of it that way. The important thing that makes it really simple to use is that you can is that the graphic state parameters in your context aren't changed except for the ones that actually affect the final compositing result.
So that means you can sort of just set up your G-state the way you want to, begin a transparency layer, draw some stuff in a transparency layer, and bam, you have the right result. And it's nestable, so you can do transparency layer inside a transparency layer inside a transparency layer, do the whole thing all the way back, and you'll get the right result as well.
So as a simple example here you see again, this is sort of the origin of why we call it the transparency layer. Here on the left is sort of what I would consider the wrong thing. I don't want each circle 50% alpha drawn on top of each other because then I get this sort of, I don't know, it doesn't even look right on the left hand side. What I really want is all the circles drawn essentially simultaneously and then the final result composited to the destination with the alpha value that I've chosen.
The code to do that is really, really simple. Here you see what I start out with is I tell the context what alpha I'm going to be using, and then I begin transparency layer. I just say, "Begin a transparency layer. That's all I have to do." And I presume I have some subroutine that does my little logo, draw circles in rect, which will go off and draw them in the right way, and basically draw one on top of the other on top of the other. And then I just say, "CG context in transparency layer." And what I end up with is all of that drawing that I've done is composited to the destination with the alpha .7, so I get the effect I'm actually looking for. So again, a little demo.
Here's my simple application. So you can see this is wrong. I'm imagining that I'm an artist and I'm going to make my logo and I'm trying to, and then it's, oh, it's just all wrong, everything's shadowed, it's terrible. So what I'm going to do is I'm going to click here.
And now when I do the drawing, it's as if each of these is drawn and it's all drawn sort of simultaneously and just the result is shadowed. So I don't have this bad effect of everything sort of being shadowed one on top of another. As you can see, it works just the same as any other shadow.
It just sort of can move around. And of course only the aggregate is shadowed, not each thing independently. And of course blur radius works the same way. So it actually can be pretty powerful for doing groups of objects and applying effect to the, just to the result, not to the each individual object independently. Okay, back to the slides.
Okay, so that's sort of all of the types, all of the primitive things you can work with with a context to do drawing, do actually relatively complicated drawing. And so now we want to step back a little bit and look at just the context idea in general. So in Jaguar we have, again as I showed you earlier, you can pretty much go to a window, post group, PDF, bitmap context. And the one thing is that the bitmap context is restricted in Jaguar.
You can only have an RGB destination. So no matter what, anytime you're doing drawing, you just get RGB out, which is good in many cases. It's very powerful. But for Panther, we're also going to let you create a CMYK bitmap context so you can, for those of you who are clapping, hooray, so that you can do direct output from your core graphics API to a CMYK bitmap. And of course, obviously, you can still do an RGB bitmap if you want. And this can be very powerful in some printing applications.
And it's very simple, again. You don't have any new API. You just create a CMYK color space. This is a parameter that you pass to the bitmap creation function. And you just create a CMYK color space, pass it to the bitmap context creation function, and now you have a CMYK context. And it will just work exactly the way any other context does.
In addition, so we have this little nice diagram on the bottom, window, PostScript, PDF, bitmap. And for those of you who saw Peter's talk on Tuesday, one of the key things was OpenGL. And you might say, wow, I'd like to be able to draw with the CG context into my OpenGL context. Why can't I? Well, you can in Panther. We have support now for an OpenGL context.
So you take your GL context obj and you pass it to a special creation function and out pops a CG context. And again, it works just like every other context you can do. You can do high quality text drawing. You can do all the shadows and shadings and stuff like that that I showed you earlier, transparency layers. Everything works the same as every other context in the system.
The API is again pretty simple. There's CG, it's a mouthful to say. I'm going to take It's CGGL Context Create, and it takes a CGL context obj. That's the thing that you work with for the OpenGL for those of you who are familiar with it. A size, which is the viewport size, and a color space. The color space says how to convert device independent colors to the actual destination of the GL context.
And what you get back is a CG context ref, just like any other CG context ref, so you can start immediately drawing your high-quality text on your GL context and so on. It has an additional API, CGGL Context Update Viewport Size, in case your viewport size changes, you can call that.
In addition to all of that, we're also adding in Panther, although it's not in the seed that you're going to get or that you have yet. We're adding API to do sort of a scroll rect and copy bits. For those of you who are familiar with QuickDraw, light.
When I say light, what I mean is actually it's really a copy. It doesn't go through any special effects, doesn't do anything crazy or anything like that. It just takes bits here and there. It copies them to here. But this is very powerful for the situations when you need that, when you want to do a scroll yourself or some sort of copy yourself.
You can do a scroll, which means that you're copying from one context back to itself. So if you just wanted to slide some content up. Or you can do a copy. So you take a rectangle of bits from one context and copy them to another context, another destination.
And in addition, we will have API which lets you create a CG image directly from a region in a context. You might do some drawing to the screen, grab the area you're interested in, and create a CG image from that. And maybe you write that as a JPEG or TIFF or something else as you wish.
Okay, so that's the sort of the general thing of context for Panther. The directions we're going, as you can see, we're sort of trying to innovate more, add things that people need. We've got a lot of new APIs for you to work with. We've had the PDF metadata access. You can actually crack open PDF files and start working with them directly. Pixel copying, as I mentioned.
And we've put in a lot of performance and quality improvements and we're going to continue doing that. We view performance as a very critical thing in Panther and certainly at our level. We're pretty much nowadays, everything funnels through us. Performance is key for us. And so we spend a lot of effort making things very fast and very high quality where that is appropriate.
Okay, so that's sort of what's new in Panther for context. So now let's sort of step back even farther and look at what you can do with the Quartz API in general. One of the things that we've added, which we found people have been very interested in, is Python bindings.
So since Quartz is so powerful and rich and has all those nice things you can do with it, it'd be great if you didn't have to actually write a program to do anything. If you could just write a simple script to have things come out, the drawing that you might want to do. And so we've added some Python bindings that let you get access directly to the CG APIs to do anything that you can do with it.
So that's what you can do with a regular ordinary program. As I mentioned here, the fact that it's an object-oriented interpreted language makes it really simple to do quick sort of one-off programs, little tests or examples and so on. And there's lots of ways you could use this. Here I mentioned scripting a PDF manipulation. For example, you might want to take a PDF file and do, say, a 4-up version of it or put a watermark on it, something like that.
Or you might want to take some sort of complicated PDF file or maybe just some complicated drawing of your own and draw directly to a bitmap and then take the result and create a JPEG out of it. So rather than having to go through the whole process of writing a program to do that, you could just use a script language to do that for you.
The, as I say, the APIs that we have, almost all the APIs are wrapped so you can use them just like you would use them in your C program. Most of them are wrapped, not all of them, but we're probably going to end up adding all of them. And then because the CG API is powerful, it's still not everything you need to do, sort of really what you want in terms of some convenience functions.
So, for example, CG doesn't support a lot of the QuickTime image IO, but we've added wraps for that via the new API so that you can get IO from QuickTime easily. And then because, again, as I mentioned earlier, we don't really work at the text level. We work at the glyph level. We've also added some wraps to do layout and rendering of text via Cocoa. So you can do regular text layout, HTML, RTF, anything like that. And so you can sort of get high-quality text layout through the Python bindings.
So here's a code example of how you might, in this case, we're going to draw a red circle in the middle of, well, near the middle of a page and create a PDF file from it. So it's a really very simple example. As you can see, we start out by doing from core graphics import star, which is the Python way of saying bring in everything that has to do with the core graphics. Thank you. And then we create a rectangle. So this looks just like your regular C code that you might write if you're using core graphics. Cgrec make without the semicolon. That's the only part that's missing.
In this case, it's going to be 8.5 by 11, 612 by 792 points. We create a PDF context with a file. So we're going to write to the file called circle.pdf, and we pass in the default page rect. So if we don't specify a page rect, that's going to be the rectangle that's used. And then we do Cgcontext begin page. We start out with the first page.
Set RGB fill color. In this case, it's going to be red, no green, no blue, and fully opaque. Alpha is one. And then we add an arc. The arc is positioned at 300, 300. That's the center. Its radius is 100. And it goes from 0. To 2 pi radians. And we're going to make it counterclockwise. And then we just fill the path.
Now we have a red circle in the PDF file. And when we do end page, we're done. That's it. So now we have done a very simple drawing of a red circle in a PDF file. Of course, this is a simple example, but you can do much more complicated things than this.
Okay, so that's Python bindings. Then we've also found that a lot of people have legacy PostScript files. PDF is a great digital paper format, but there's people who have written theses, that have used tech to produce PostScript, and who knows what all, right? There's all this PostScript files that sort of are still around that you might like to move forward into PDF. Or you might just want to have an application that displays PostScript files if you wanted to accept it.
So we have new API in Panther that will convert PostScript to PDF so that you can call it directly and you can do all the conversions you want from PostScript to PDF. You provide a set of callbacks which are a way for us to tell you things like, oh, I'm finished page one, I'm going to start on page two, or oh, I'm finished the document, or by the way, there's this warning that occurred, or I couldn't find this font, or I'm aborting because you've used an invalid operator, whatever. The callback mechanisms are there so that you get as much information that you might normally think you would get from, say, a PostScript printer.
The one thing, the things to remember are that because it is a PostScript interpreter, it brings in the entire PostScript VM into your process, which is, you know, can be pretty big actually, to be frank. It's not, it's a non-zero cost to your application. And again, because you are bringing up this VM and it's got to figure out about fonts and all that sort of information, it can be a little slow to start up. So that's one thing to keep in mind as well, that those two things are things that you might want to be aware of. But once it's started up, bam, off you go. All the conversion happens pretty fast.
So here's an example, a code example of what you might do. And it turns out the astute among you or the awake perhaps will notice that I'm writing and reading from the same file, which is a bug. But if you imagine that in the first line I'm actually creating a data provider, a way to get data into Core Graphics from your application with some PostScript URL.
In the second line I'm going to create a data consumer, a way for Core Graphics to give information to you with, in this case let's imagine a different URL, a different PDF URL let's suppose. So we have an in and an out and then we're going to create a converter. CGPS converter create creates the converter.
We presume the callbacks have been specified by you so that you want information about warnings or pages being completed and stuff like that. And once we've created the converter then we can just use it to create, to convert PostScript files. And so here you see CGPS converter convert takes the converter and an input file and an output file and returns success if it succeeds and false if it fails.
And what's good about this is that once you've started this up and converted the PostScript file then you can just reuse the converter. It's all ready to go and you can just sort of convert more PostScript files if you have a thousand of them or just have this sort of ready to go so that when someone drags in a PostScript file you can do the conversion automatically. So it has a pretty flexible interface in that respect.
Okay, so to sum up, we have lots of API that you can play with for Panther. Of course, there's all the API we've already had in Jaguar. So for those of you who haven't been using Quartz 2D, I encourage you to experiment with some of this and play around and see what you can do. We're going to keep adding more API where it makes sense, and part of the way that makes sense is for you to tell us what you need and what is missing that you find would be really useful.
A lot of things we've added this year have been based directly on feedback we've gotten last year, so your feedback is very important to us and is very useful for helping us direct where we're innovating to satisfy the needs of you, the developers. And so now I'm going to bring Travis back up to wrap up and give you the roadmap.
So remaining today in the roadmap for the graphics imaging track, our next session is going to be session 2.8, which is Fragment Programming with OpenGL. In the graphics and imaging overview, you saw us do a lot of really incredible real-time pixel operations and hosting those in the GPU. And this is a session that's going to talk about the techniques that you use to do that.
Then we also have session 209, which is OpenGL optimizations. If you are interested in finding out how to use OpenGL to its best on Mac OS X, please attend that session. And then to people doing 2D graphics, obviously a good companion session that you'll want to attend is 210, which is Mac OS X printing update.
Obviously a significant client to the Quartz 2D technology inside Mac OS X is the printing system, because our printing system does use Quartz to do both its PostScript generation and also its rasterization. So there's going to be lots of information relating to Quartz 2D and how it all interacts with our whole printing architecture. So 210 is going to be a great session for you to attend. And then we have 211, which is talking about the parts of Quartz that do not deal with drawing, but deal with the other services.
That the system needs, for example, handling displays and whatnot. And then also we have an interesting session, 212, which is cutting edge OpenGL techniques, which is going to be presented by our hardware partners, ATI. They have their sort of OpenGL heavy hitters who are coming to present and let you know what you can do when you really push the envelope with OpenGL. So that's going to be an exciting session. And then we have a feedback forum at our traditional time. Which is... The feedback is Friday at 5:00 PM.
Obviously you can give us feedback when you see us in the hallway or at the end of this Q&A. But if you have more significant feedback you want to get us, please attend the feedback forum. Because it's a great time to basically let us know what you want to see in Mac OS X in terms of graphics technology in the future. So now what I want to do is give you some contact information.
I'm your best point of contact for any of the graphics technologies in Mac OS X. I can be found at [email protected]. And so please contact me with any questions you have. Regarding the technologies you've seen thus far. Let me bring the Q&A panel up. Actually we do have some references here. But... So we have some additional documentation on the ADC site. And we also have a developer web page that addresses Quartz as a general umbrella technology for our windowing system and our 2D drawing APIs.