Mac OS • 1:06:15
Discover techniques to ensure that your application gets the most out of the incredible graphics architecture in Mac OS X. This session covers Carbon printing, optimizing and debugging Quartz, as well as tips for intermixing Quartz and QuickDraw graphics.
Speakers: David Gelphman, Ralph Brunner
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. I'm Travis Bruhn, I'm the Imaging Technology Manager for Mac OS X and I want to welcome everyone to session 132, which is Graphics & Imaging Tips and Tricks. What we're going to be talking about in today's session are two things. The first is something we think is a great opportunity for Carbon developers.
And that is to better define for you the interaction between Quartz 2D and QuickDraw. And we think there's a great opportunity for Carbon applications to look at using some of Mac OS X's very powerful graphic technologies to deliver things such as PDF support, anti-aliased graphics, all sorts of interesting possibilities for features for your users.
Secondly, what we'll do is we'll concentrate on the graphics that are available in the Mac OS X. We'll focus on the graphics that are available in the Mac OS X. on performance issues with Quartz in general. Quartz has a piece that we've talked about a little bit during this WWDC called the Quartz Compositor, which essentially abstracts you from the screen. Unlike Mac OS 9 and earlier where you had direct access to the screen, you don't have that intent.
And therefore, there's a little knowledge we need to communicate with you with regards to how to optimize your application in this environment where your pixels are essentially being buffered by the Quartz Compositor. So I'd like to get started because we have a lot of information for you today. So let me introduce David Gelfman, who will continue the presentation from here.
Hi, I'm David Gelphman. Can you hear me? Okay. I'm David Gelphman. I work in the graphics and imaging group, primarily on printing, but that sort of takes me off into some other areas, so I get to do a little bit of work in QuickDraw, a little bit of work in the Quartz 2D system.
So, as I was lying awake in bed last night thinking about this talk and sort of anticipating it, you might say, I thought about the first developer conference that I came to in 1987. And at that conference, Apple had just recently introduced the Mac 2 and was new technology, was color QuickDraw, and Apple had sort of hidden in the background some new stuff they were going to show us. It was something called Multifinder. Does anybody remember that? Oh, yeah. So, yeah. So, to me it's really exciting to be here talking about something besides QuickDraw in the most part, Core Graphics, Quartz 2D as we call it. And here's the agenda for today.
We're going to start talking about how you can use Quartz 2D in your Carbon-based application on Mac OS X. The Cocoa people, for the most part, get it free. It's easy to use it. They have cover routines for a lot of the functions that you might use. But we're going to talk about how you can use it in your Carbon application.
That sort of leads us to talking some about printing on Mac OS X. Specifically, we're going to focus a lot on the Carbon printing graph port on Mac OS X. It's a different kind of graph port than you've seen before on Mac OS. And I think you'll see that we've done some interesting things there. Then my colleague Ralph Bruhner is going to follow up and talk about performance tuning of your application when you use Quartz, or just when you use anything in Carbon and Cocoa.
So just as a way of sort of getting started, as you've hopefully seen during the conference, Quartz has very powerful 2D capabilities. The imaging model of the Quartz 2D system is consistent with the PostScript imaging model, which is a very powerful, mature imaging model. For those of you that have been using PostScript generation yourself, or you've been using PostScript picture comments, we think that you're going to want to move over and start using Quartz 2D instead of generating your own PostScript.
This is going to have some pretty significant improvements for your application. That is, on all the imaging devices, when you go to print, you're going to see really high quality, rather than the QuickDraw drawing that you were doing when you were printing to raster devices in the past. When you're using Quartz 2D, you're going to see the really high quality.
Now this talk is going to assume that you have some familiarity with Quartz 2D. We're not going to talk all about the imaging model. I am going to touch on some things that are relevant specifically for people that are probably coming from a QuickDraw based background. And this is just a reminder, a heads up. Quartz 2D and Quartz the system is a Mac OS X only technology. It's not something that's in CarbonLib. So I'm going to start by giving a little demo. So if we can get the demo machine up over here.
So the first thing I have here is just some really simple drawing that we've done. Some of this is drawn with QuickDraw, some of it is drawn with Quartz 2D. And I've got the little pixie application here, which is just sort of a fat bits equivalent. where we can magnify the screen, we can come over here and look at-- hopefully people can tell which are the lines that are drawn with Quartz 2D and which are the lines that are drawn with Quickdraw.
The Quickdraw line, the ends of the lines have that square pin that hangs down. You can look with the Pixie application, if you look over on the right side here where we can see how jagged that line is. If we move over and we look at the next line over here, of course, we're looking at a line that was rendered by Quartz 2D. If we look at the end of that line, you can see it's a nice butt end cap.
It's anti-alias along the line. As you're far away, you probably can't see the difference in quality. But of course, if you're looking at a screen and you see that anti-alias line, it really looks a lot better, a lot sharper. And then of course, just for fun, I did a little dash line. That's something that's trivial to do using Quartz 2D.
Now this middle portion here, I have a couple of ovals. And I think as you get far away it's a little harder to tell, but I'll look at it here with Pixie and you can see. Now this is actually not an oval that I've drawn using QuickDraw. What I've done here is I've made an oval mask region that I use to clip, or as a clip region, that I use to clip some drawing. I just have a big rectangle that I'm clipping through an oval. Of course you get essentially the same result if you do the oval itself.
But if you look at the edges here, you can see how jagged it is. If we look over here, what I've done is I've set a clip that's the same oval using Quartz 2D. And I've just drawn the same rectangle. And you can see it's nice and anti-aliased along here. And again, on screen you see this jagged behavior with QuickDraw. And of course it looks nice and smooth.
Now one of the reasons I did this as a clip rather than just drawing an oval, when you go to print, if you've done clipping, what we do is we're going to take the, if you've done clipping with QuickDraw, we're going to take that region clip that you've supplied and we're going to turn that into a path, a Quartz 2D path.
And when we do that, well, regions are device dependent. They are resolution specific. So to do that, what we're going to do is we're going to make a little path that's a bunch of jagged lines just like that. And that's what you're going to see. Now if you draw using Quartz 2D, what you're going to see at print time is you're going to see a nice smooth clip. So of course, you know, you wouldn't really draw an oval this way, but this is just to give you an idea of, what kinds of things you can do with Quartz 2D and how that actually translates into your real imaging.
The last thing here, I've got some text here on the left hand side I've drawn text with QuickDraw and on the right hand side I've done text with Quartz 2D. One of the things that especially small point sizes, it's a little hard with Pixie to see, I hope you can see it when you display it. The difference of course with the text with Quartz 2D is anti-alias, nice clean looking text.
One thing that's really nice is the weight of the text as you go through the point sizes with this sort of waterfall text. The weight's nice and consistent, looks real smooth. When you look with QuickDraw you tend to see sort of the chunky gradations as the text increases in point size.
On this page what I have is a couple different things. First of all, I mean I've already shown you we're mixing Quartz 2D drawing with QuickDraw in a window here, but I just got a picture here that I'm drawing with QuickDraw using DrawPicture. And I've also taken some text using Atsui text drawing and we can apply the usual Quartz 2D transformations to the coordinate system and get different effects. I haven't done anything particularly fancy, but it's really trivial to rotate text. If you want to supply other special effects you can do that.
One thing I put on here because I want to emphasize something I think is really important, we'll talk a little bit about it in just a second, about the details of how to do it. But this zebra picture here is a PDF file that I've just drawn on my page here. And the zebra that's rotated I've done the same way. It's just a PDF that I'm drawing. I've rotated the coordinate system before I've drawn it using Quartz 2D.
But I do want to emphasize it's really easy to support PDF in the system. It's just essentially like drawing a picture. Very simple. Very easy to do. And even if you're not thinking of adopting some of the other things that we're doing with Quartz 2D, consider seriously adding PDF support to your application. Now I did a little thing in here for fun. Oops, excuse me.
Which is, I applied a clip now to the drawing right before I actually drew that last zebra. So I drew a text character, but before I actually drew the text character, I set the text rendering mode in Quartz 2D to be a clip. Then after I did that, I drew the zebra. I just drew the PDF. It's really easy to get these kinds of effects. Okay, so let's go back over to the slides.
Can we go back over to the slides please? Thank you. Okay, so I told you how easy PDF import is, I'm going to show you how easy it is. Now of course, this little example is importing from a file. I've assumed that you already have an FSRef that points to a file.
If you don't have an FSRef, but you have an FSSpec, there's an easy way to get an FSRef. From the FSRef you can create a URL, CFURL. This is part of Core Foundation. Once you have a CFURL, you can make a call that's part of Core Graphics, part of Quartz 2D, CGPDF document create with URL.
You get what's called a PDF document from that. That's the doc that I have here. From that we can get the media box, which is essentially the bounding box of the graphic here. Get the bounding box for that. And in this case I'm just going to draw with that bounding box into my context, which is the CTX parameter.
It's a Quartz 2D context. Draw the document. That's it. That's to draw the first page of a document. You can also find out the number of pages in the document and so on. And there's other bounding boxes that you can get besides the media box, but that's it.
Now if you say, "Oh, I don't have my data as a file on disk or URL really," because you can really have URLs here. Of course, if you were in the Quartz 2D discussion you would have heard about data providers where you can have, there's a generalized mechanism so you can provide the data for the PDF document. So this was just the simple one for a file. So again, straightforward use of Quartz 2D drawing, even for a QuickDraw based application.
Okay, so now, how do I get started? I'm really piped about doing this. Let's get to it. Okay, so the first thing, of course, is you need to have what's called a CG context in order to draw into. All of the Quartz drawing routines expect that you're going to draw into CG context.
It's an explicit parameter that you provide to the drawing routines. There's no set port like there is in QuickDraw. You're going to pass in the context to all of the drawing that you do. So the CG context is something like you might think of as a Quartz drawing port instead of a QuickDraw drawing port.
Now, one thing that's really important to realize is that a CG context has a graphic state that's associated with it. QuickDraw graph port does also. It has the usual things, the pen size, the font, the font size, the background pattern, the foreground pattern, you know, all of that stuff. Well, there's similar kinds of things that are part of the graphic state of the CG context, such as there's a separate fill color and a separate stroke color. There's a separate fill color and stroke, excuse me, separate fill color space and stroke color space.
There's a clip. There's a line width. Those kinds of things. All those parameters are on the CG context. And now, when you get one of these things from QuickDraw, or if you're using QuickDraw at the same time that you're using a CG context that you got for a graph port, you should realize that those graphic states are completely independent. There's no sharing of the graphic state. If you go and you set a color on the QuickDraw port, that's not reflected in your CG context. So when you go to use Quartz 2D, you're going to set the parameters in the Quartz 2D context for your drawing.
Okay, so let's start just, I think, some real basics. And like I said, I'm not going to talk a lot about how you use Quartz 2D, but I think these are relevant things for developers coming from a QuickDraw background. The first thing is that the coordinate system is based on the PostScript PDF coordinate systems.
It was a fundamental decision by Apple to follow the PDF imaging model. This is the coordinate system in the PDF imaging model. Standard Cartesian coordinates: X goes to the right, across the screen or across the page, Y goes up, and the origin is in the lower left corner of the window, or of the port, rather, or of the page.
Okay? So this is what it looks like. This is the relationship between the coordinate systems, the QuickDraw coordinate system, the Quartz2D coordinate system, sort of the standard relationship when you get a CG graphics context for a port. The QuickDraw coordinate system, as always, top left corner, Quartz2D, bottom left corner, Y going up. Now the relationship between the origins is initially, at least if you haven't changed the port origin, the relationship between the origins is based on the port rect height. That's the height of the port.
So there's a couple issues if you're used to drawing with QuickDraw. Apps are used to that QuickDraw coordinate system where it's in the top left corner of the port. Event coordinates are returned in the QuickDraw coordinate system. In some apps, when they switch over to using the Quartz 2D imaging system, they sort of want to keep using that top left corner with the coordinate system flipped.
Okay, well there's a couple ideas. One is, you can actually transform your coordinates on the fly yourself if you want to. The relationship between the QuickDraw coordinate system and the Quartz 2D coordinate system I showed you on the picture before, you can calculate the port bounds height, you can compute from a given QuickDraw Y, you can compute a Y that would be suitable for a Quartz 2D coordinate system drawing.
So you can do that yourself. Now, there's some disadvantage of just doing this kind of transformation. And one of them is that, as you start rotating, and scaling the Quartz 2D coordinate system, something that you can't do with QuickDraw, now this kind of transformation gets a little messy because now your coordinate system is transformed. So, just some other ideas.
Here's what I call approach B, which was using Quartz itself to do coordinate transforms for you. Now, when you draw with the Quartz 2D coordinate system, you're drawing in what's called user space, and that's malleable by the application. You can scale it, translate it, rotate, skew it, do whatever.
And Quartz 2D is always doing transformations, and it transforms on user space coordinates that are supplied by your application when you draw, into device space, or into the space of the actual device you're drawing on, be it a printer, be it on screen, whatever. Now, you can piggyback off of those transformations that Quartz 2D is already doing for you, by simply making your own matrix that you can cat to what's called the CTM, the current transformation matrix from user space into device space. You can piggyback off those transformations, and that's what I'm doing here.
So I make up a transform. There's in CTM, there's in the device space, and then I make up a transform. So I make up a transform. So I make up a transform. So I make up a transform. I'm going to use the CGAffineTransform.h. There's a whole bunch of stuff about transforms you can read about. And you can make a translation transform. In this case, I'm translating by the port-bounds height.
That's the thing that connects the QuickDraw origin to the Core graphics, to the Quartz 2D origin. And then, in addition, I'm going to do a further transform on that transform, by scaling it, by flipping it. So I made y minus y. So at that point, if I actually take that transform that I built up, this QD transform that I built up, and I can cap that to the coordinate system that's already part of my context here, represented by my context parameter here, now I have a flipped coordinate system, and I have the origin of my Quartz coordinate system in the top left corner of the port. Okay? Well, it's all great. Start drawing your graphics.
You do your line art. You do your ovals. You do whatever. Well, wait a minute. When I go and I start drawing text using Atsui, I find that I've got a little problem. I've got a little problem here, which is my text is flipped. Because we flipped the coordinate system, and when you draw text with Atsui into a CG context, they're expecting upright coordinate systems, and that's flipped.
OK, well, it's a tips and tricks. So here's maybe something you can consider. You could actually modify the approach a little bit. It's a little bit of a pain. But what you can do is before you draw your text, you unflip the coordinate system. So normally you're going to draw into a flipped coordinate system.
But before you go draw in the text, you're going to unflip it. The only thing you have to do is unflip the coordinate system. At that point, you want to use minus y-coordinates instead of y-coordinates because now you've got the origin in the top left corner and y goes down. So we'll be using minus y-coordinates. But that's what's involved there.
Okay, now let me just emphasize, the only reason that you would need to consider using some of these approaches is if you want the Quartz 2D coordinate system, when you go to draw, to match the QuickDraw coordinate system that you're familiar with. If you're comfortable drawing with an upright coordinate system, go for it. Don't bother with all of this.
One other thing to say is, in CGaffinetransform.h, in the header file, there are some additional transforms that let you take points and transform them through the matrix to get a new point that's transformed. So if you'd rather do your coordinate transformations using CG by transforming the coordinates, not by transforming the coordinate system that's part of the CG context, you can do that yourself also.
Let's talk just for a second about text drawing. Hopefully you've had an opportunity to go to some of the text drawing, Carbon text drawing sessions here at the conference. We discourage use of the low level Quartz API for drawing text. It's really not suitable for international text. International text, I mean if you've seen some of these demos and the kinds of things that people need to be able to do with international text, all of that is layered on top of the Quartz 2D imaging system, but they are really providing the facilities for drawing international text. So, the recommendation is look at and use ATSUI and MLTE for drawing text into a CG context. And they've talked a little bit about how to do that in those sessions.
Just to give you an idea of how straightforward it is, if you're already familiar with using Atsui text drawing, you know that you're going to use a layout for drawing your text. All you need to do is add a tag to that layout. That's what I'm doing here in this sample code.
Adding a tag to the layout, then I actually draw the text the way I otherwise would have. The tag that I'm adding just says that I'm adding a CG context and I pass a pointer to that context. So I'm supplying the context to Atsui. Now all of the coordinate system transformations and so on, the color, all of those things are used from the CG context, assuming for example the color you haven't made as part of your layout. That would override the color that would be in the CG context.
Okay, so I've given you a bit of an idea about some of the issues maybe of drawing with a CG context. How do you get one? Well, there's a routine, create CG context for port. What that does is, you give it a port, it gives you a CG context for that.
You can start drawing into that CG context. There's a couple other helper routines that you may need to use. One of them, I can't say these without looking at them, sorry. Sync CG context origin with port. What that does is, when you call that, it resets the current transformation matrix on the context of the default. That removes any scaling or anything that you might have provided.
It then resets the CG context origin so that it has that relationship between the port origin and the CG context origin. So, for example, if you've done, a set origin in QuickDraw, now, you want to, and you want to tell the CG context that you're using associated with that port that you've done that, that's when you call this routine. Then there's also a clip CG context to region, where you supply a clipping region and that will reset the clip on the CG context to that that you're supplying here.
Okay, so when would you do these things? Window resizing, so you've made your window bigger or smaller, you're going to have to reset the clip. Because the vis region on the window, or the clipping region rather, on the window, it's gotten larger, you want to sync that up, you want to tell the CG context that you've got a bigger clip. And then if you're scrolling or for other reasons you changed the port origin, now you need to synchronize your CG context with the port origin.
Alright, well I'm mostly a printing guy, so I've got to talk about printing. What about printing? So, I've sort of divided up the kinds of applications, these again are Carbon applications. I've divided them up into three categories. The first category is our favorite kind of application on Mac OS X. That's an application that when imaging during printing, the only thing that you're drawing into the printing port, so to speak, or the only thing that you're trying to image, is something, or all the content is drawn using only Quartz 2D.
So, that's the first type of application. So, all the content is imaged with Quartz 2D. And you say, well, you know, maybe I'd like to do that, but I have all this legacy data that's sitting around. I've got these pic files, and I've got to use Draw Picture.
Well, we recognize that that's an issue, and so what we're adding to Mac OS X, coming soon, is a routine that allows you to take a QuickDraw picture and draw that into a CG context. And I have a little demo that's sort of like doing that, and you'll get a chance to see that. Now, we want to hear from you about what other kinds of QuickDraw content, other things that you're doing, other things you're drawing into a graph port that, hey, right now, you don't know how to draw into a CG context.
Because if that's what's preventing you from moving over to Quartz, we want to know what that is, and we want to fix that. And, by the way, you know, you probably can do it. We just need to give you some ideas about how to do it, and maybe we can add some more convenience things in the future. Okay, so that's the first type of application, your application soon to be, only imaging using Quartz 2D when printing.
Alright, second type of application is what I'll call a QuickDraw only app. It's the vast majority of Carbon applications today. It's what you were doing on 9 when you were printing, you were drawing with QuickDraw in some fashion. And then the third type is a type that we want to talk about also, which are applications that are going to mix their drawing. So they draw with both QuickDraw imaging and Quartz 2D imaging at print time.
Okay, and that might be an application, for example, you say, "Well, I don't really want to move over to Quartz 2D, but man, I like this PDF import. I really want to do that." Or, maybe you want to do almost everything with Quartz 2D, but there's still a little bit of QuickDraw stuff that you haven't quite gotten weaned off of.
Okay, so I'm going to talk about printing issues with these three types of applications in that order. Now before I do so, I need to say a little bit, I mean, I know everybody knows about the printing architecture on Mac OS X, and I'm boring everybody. But it just helps me to remind myself how it works.
So printing architecture on Mac OS X, it's really completely different than it is on Mac OS 8 and 9. We've totally rewritten the back end of printing, the front end of printing, it's all totally different. The APIs are similar, very similar for Carbon Apps, but we've really rewritten the underlying printing system. So, a couple things that are important to know about it. The printing system completely leverages Quartz 2D when actually both at imaging time, at spool time, it's built into the printing system.
All the printing that you do generates a PDF spool file as an intermediate representation for the graphics that you draw in your application. It's the same printing port both for raster printing and for post-script printing. You don't have to worry about driver X and driver Y. It's the same graph port, has the same characteristics.
Both of those, regardless of what the back-end printing device is, we're going to spool out a PDF. Now the good thing about this, I think all your customers are going to like, all our customers are going to like, is you're going to get high quality imaging on all printing devices.
Okay? So, again, I'm talking about an application that only does Quartz 2D imaging during printing. Now, these are Carbon applications, and by default the printing system is set up so that when you actually start printing your document, we're expecting to hand you a QuickDraw graph port, because Carbon, QuickDraw, people think of them together, that's the default graphics context that you get at the time that you go to draw, is QuickDraw.
So before you actually start printing, you need to tell the imaging system that you're going to use Core Graphics Quartz 2D. Those are the same thing. So what I have here is we need to create a CFArray that has one element in it, and that one element is a CFString that we've predefined called KPM Graphics Context Core Graphics.
Okay, you're going to draw with Core Graphics Quartz 2D when you're imaging at print time. That's all you're going to do. So before you actually call this routine PM Session Begin Document, which is beginning the printing process, before you do that, you need to set up the system to set up this array, you need to call a routine that tells the printing system that's the format and that's the type of graphics context that you're going to draw with. So you're going to say PM Session Set Document Format Generation with the print session you got when you created your print session.
And then you pass in that you want to do format PDF, which by the way is the default, but you're reinforcing that, and you're going to pass in this graphics context array. Then you release that array, because we hang on to it, you release it. This is a classic way of using Core Foundation.
Okay, so now you've told the printing system that you're going to use Quartz 2D at imaging So, how do you get your graphics context that's associated with the printing system at print time? Well, you ask the printing system. This is just what you would do if you weren't using Core Graphics. It's a little bit different because now you're saying, okay, I want the Core Graphics context from the Get Graphics context, PM Session Get Graphics context. Okay? Now you have a context, a CG context that you can draw into using Quartz 2D. You can't draw any QuickDraw.
You can only draw into that Quartz 2D graphics context. Alright, one thing that's different about the graphics context in the coordinate system that's different than you might be familiar with with Carbon printing, that the origin of the coordinate system is the lower left corner of the sheet. Okay? Now, when you're doing Carbon printing, you're probably familiar with the fact that the origin of the coordinate system is the top left corner of the imageable area of the page. So this is a significant difference. Not only is it the bottom left corner, it's not the bottom left corner of the imageable area, it's the bottom left corner of the sheet. Thank you.
Okay, now that's for your application that's drawing only using Quartz 2D. So what about your application that's drawing using only QuickDraw, or some QuickDraw? Let's start with the only QuickDraw. Now, as I said, the spool file that we generate during printing is this PDF spool file. Now, QuickDraw applications, they don't know how to generate PDF.
So, what we do during the printing system is we translate the QuickDraw imaging that you do into the port into the Core graphics, Quartz 2D drawing, and we're going to generate that spool file for you. Now, how do you actually get your graph port? You do it the same way as you do, you don't need to set call, you don't need to set this up before you start your printing, but after each page, PM session, begin page call, after that, you're going to go and you're going to get your graphics context, which is going to be a QuickDraw port, and you're going to image into that. Now, let's talk about that port, because that port is completely unlike any graph port you've seen before. It's something like a hybrid QuickDraw raster driver printing port and a port that's a little bit different. PostScript-like graph board.
So, I jumped ahead a little bit a second ago. Because we need to have PDF spool file, of course, applications that draw with Quartz 2D drawing can produce that natively by just using Quartz 2D directly. However, during print time, we need to translate any QuickDraw drawing that you do into Quartz 2D drawing and therefore generate PDF from that. This is true both for QuickDraw, pure QuickDraw applications, and also applications that mix both QuickDraw and Quartz 2D. We have to translate the Quartz, the QuickDraw portion into Quartz 2D drawing.
Here's what that looks like. You do your QuickDraw imaging calls. We replace the QuickDraw port bottlenecks with bottlenecks that translate that QuickDraw drawing into Quartz 2D drawing. So that's that second box, that blue box, that's the translator. That turns into Quartz 2D imaging calls and as you know, or hope you know by now, the Quartz 2D imaging system can produce from those drawing calls a PDF file.
Okay, well the process of doing this translation is very similar to doing this kind of thing that we've done with PostScript printing with the LaserWriter driver for years. The Quartz 2D imaging model is essentially the PostScript imaging model. What we've done is we've borrowed techniques that we've used with the LaserWriter 8 driver to do this translation from QuickDraw into the Quartz 2D imaging model. We've tried to get results that are consistent with the LaserWriter 8 driver wherever possible, because that's something that's very familiar to people. We think that's a win for you and for customers.
Alright, so I'm going to talk about not only similarities with the LaserWriter 8 driver, but also differences. And again, let me just say, what we're talking about is the translation on Mac OS X from the QuickDraw drawing that your application does into the printing graph port, into Quartz 2D drawing, into PDF. Okay? So, we use that same general approach like we did for LaserWriter 8.
We're using the same line layout algorithms that we use for LaserWriter 8. Line layout algorithms are used because frequently in QuickDraw, the metrics are different in your QuickDraw fonts and so on than they are in both PostScript for LaserWriter 8 and in PDF. So, if you're using integer font widths, for example, well, we're going to turn those into fractional, usage of fractional widths on fonts.
We do layout in order to adjust the differences. With regard to images, we have the same QuickDraw transfer mode support that we have in LaserWriter 8. For 1-bit images, we support all the same. We support all the transfer modes except the XOR transfer modes, which we can't support.
For deep PIX maps, anything deeper than 1-bit, we support the source copy mode. We don't support any of the other transfer modes. However, coming soon, we're going to have support for the QuickDraw transparent transfer mode. Transparent transfer mode is where the background color in the graph port is used as a transparent color. So, any pixels in your image that have the same color as the background color in the graph port, they're transparent. when you draw your image.
Okay, more similarities to the LaserWriter 8 driver. We do pattern substitution. So if your application draws with patterns, which, by the way, you actually always are because the port has a pattern in there that's using for all your drawing when you do line art, what we do is we map some of the patterns into grayscale colors or grayg levels. I hope this is clear what's happening here in the slide. It's certainly clearer in the front of the room maybe than the back, but on the top I've got several different patterns.
Let's see what that looks like, dot patterns. And what we do is for certain combinations of the dot patterns, we map those into gray levels. So you can see a much smoother appearance in the drawing. And by the way, of course, if there is a real pattern, like there is in this brick pattern, we're going to generate that pattern.
Okay, some other similarities with LaserWriter 8. We also support many of the picture comments that are supported during printing on Mac OS 8.9. So some of them are the text alignment picture comments, text rotation and flipping, polygon smoothing where you can draw with cubic beziers, graphics rotation where you can have different kinds of QuickDraw graphics and before you draw your graphics you can tell the imaging system to rotate. You can't do this on screen, but the printing system knows how to deal with that.
Dash lines, fractional width lines, so something other than the normal pen sizes, but fractions of a pen. And also color sync support. So on Mac OS 8.9 you could tag your data with picture comments that provided color sync profiles. And we respect those and we'll use those color sync profiles to draw the graphics as calibrated color if you use those picture comments. I have some examples of a couple of these things in the little demo I'm going to do.
So, differences from LaserOtter 8. Well, I think the big one that probably many people run into is that PostScript picture comments are not supported when you're printing in this print graph board. There's no way to take these PostScript picture comments and turn them into PDF without having a PostScript interpreter. We don't have a PostScript interpreter in the PDF printing system. So the PostScript picture comments that you would generate are ignored. They're dropped on the floor. We don't do anything with them.
Instead, in this manner, we act something more like a QuickDraw driver, what you might be familiar with on Mac OS 8 and 9, where the portion, if you say PostScript begin and then you draw with some QuickDraw and you draw some PostScript picture comments and then you say PostScript end, a raster driver on Mac OS 8 and 9 uses the QuickDraw drawing at that point.
And that's what we do here on Mac OS X. Because we're not a PostScript driver, we use the QuickDraw representation. So that also means, for example, if you have an EPS preview that you're doing, you don't just want to say PostScript begin and emit a whole bunch of PostScript code and end. You want to draw that preview image also. And we will use that preview image instead of the EPS data.
Okay, well that's one of the big differences. There's also another difference in something we think is better and hopefully going to help you. That is, we've added for the first time to a PostScript type of driver, we've added support for QuickDraw regions. That is, you can have a QuickDraw region that you clip with, that you fill, that you frame. Arbitrary regions are supported. Now, of course, they're device dependent and you might be unhappy with some of the results you get and that's of course one of the reasons why we're pushing you toward using Quartz 2D, but we are supporting it.
So that's a good thing. Another thing is that we've basically fixed a bug, which is that now when you draw with your patterns, QuickDraw patterns respect the port origin. LaserRotter8 driver didn't respect the port origin when doing patterns. We fixed that. So let's say you had your ducts like this, you had your ducts all lined up, and you were drawing with the origin at the corner of the graphic.
With the LaserRotter8 driver, we're going to use the original 0-0 port origin. With the printing system on Mac OS X, we're going to respect the port origin, so you'll get your ducts in the right place. Okay, let's move over to the demo machine. So I'm going to maybe hide Pixie.
So, I have a couple of images that I want to show you. This is a little application that I wrote. It's essentially leveraging this draw picture into CG context routine that I told you was coming soon in Mac OS X. On the left hand side here I have a picture that I found on, actually I don't know where I found this picture, a picture that came to me. And this is drawn using draw picture.
On the right hand side in this window, the same picture is drawn using Core graphics, using Quartz 2D. It's essentially drawn through a port which has its bottlenecks replaced as they would be during printing. So, I hope you can see some of these, some of the features of this. First of all, we do pattern substitution. Let me use the mouse here to maybe point a little bit. Over here, this is a normal QuickDraw pattern that gets substituted with the gray level here.
All the text here, all the text is anti-aliased. All of the graphics are anti-aliased. This is the QuickDraw version. Of course, over here is the Quartz 2D drawing. What else do we have here that might be of interest? Anyway, of course the goal here is, at print time, we want to get the best quality we can. And we think translating this into Quartz 2D is going to give you better quality, even when you're using QuickDraw.
I've got a couple others. Oh, one thing that's sort of fun, I want to mention, one thing I added to this little app is, oh, a little save as PDF because this window on the right hand side here is drawn using Quartz 2D, if I create a PDF context rather than getting the context for the window and I do that same drawing into that PDF context, I get a PDF file. So you can make it really easy to get PDF out from your application. Here's another drawing.
This is a highway map that we got. Again, here's the QuickDraw version of that. There's a couple things that are shown here. You can also see, if I can bring this up here, you can also see pattern substitution going on here. Here's the QuickDraw pattern. Over here is a nice smooth gray scale. The lines of the map here are drawn using polygons, but at print time, the polygon picture comments are being used.
So, we interpret those picture comments and now you get nice smooth lines. Not only anti-alias, but smoothed out because they're drawn as Beziers. All the text is anti-alias, so if you look at the text in here, over here, the text looks a lot better. And, oh, look, here's some rotated text.
[Transcript missing]
About ColorSync, the left hand side I've got a pic that's an image. It's actually using, this particular one's an extreme example. It's using the ColorSync picture comments. And I'm drawing this with DrawPicture. There is a DrawMatch picture that will let the imaging system handle that with QuickDraw, but I'm just using DrawPicture here. And over here on the right, I'm not using Draw Picture, I'm using, or rendering using Quartz 2D and it picks up the ColorSync picture comments and draws in the calibrated color space.
Somebody's clapping good. I like that. One of the things, this is sort of, you know, I'd like to add a few tips and tricks since that's the way we're advertised. One of them I think, You might be interested in this. This is a PDF that we're generating for one of those graphics that I had.
And when you start to look, you know, if you're familiar with PDF, then you might be interested in looking at it. If you're not familiar, you might be interested in learning a little bit about the PDF that we generate. However, for performance reasons and file size reasons, we're generating PDF that has many of the streams in the PDF file compressed. It's using the FLATE encoding. So here's the data that represents the PDF data that's generated by the printing system.
And it's compressed. Well, for developers we thought, okay, maybe it would be helpful to you if you want to look at that data to not have it be compressed. And I wrote a little app here that's a printing preferences app that, you know, talk about a couple of the preferences that are on here.
So by default the printing system is going to compress the PDF data as it's generating the spool file. And you can turn that off with this little application if you uncheck it and save that. Then when you go to print your PDF, if you go to look at the PDF, you'll see it's not compressed any longer. You can actually look at it in clear text.
Just another couple of things about this particular application. If you were in Paul Danbold's talk yesterday, you learned that the printing system can generate something that's called a job copy for the PostScript data that's being sent to the output device. So for debugging purposes, it's not a save to PostScript functionality that a user would use.
But I know we in the printing team and I think a lot of developers appreciate the ability to see what PostScript's being sent to the printer as part of the printing path, whether if you're generating your own PostScript in some situations or you just want to understand what's going on. You can turn this on.
You can choose a location. By default it's in /temp. That location needs to be writable by all just because of the way the printing system's run. You can set a new printing -- a new location for these. The files that get generated are based on the job name. So you really do get distinct files for each print job and you can go and find them and look at them if you need to. This last thing is a job status log -- printing status log, rather.
And this is basically the data that's coming back from the PostScript printer module that gets logged if you turn this feature on and it goes into the temp directory. So this would be a very specialized developer that would need this. You want to see what's coming back from the printer from the PostScript job module -- the PostScript printer module.
But you can also turn that on. Okay. So I uncheck this and I'm going to save that and I'm going to quit this and I'm going to go and I'm going to print. And now this is something I do and I think other people might find this interesting.
Also, which is it's very easy to get the PDF from the printing system. You just say print preview. There's the PDF file. Well, I like to look at that all the time. So what you can do here -- this is just project builder. Open, drag that. There it is. Open it.
There it is. Okay now, remember I checked on don't compress the PDF stream. So here is the PDF stream from that printing. Okay, so for those that are familiar with PDF, we're just drawing an image here, we're setting up a coordinate system, drawing an image, but it's very easy to get that.
Let me just see if there's anything else I want to talk about. I think that's it. Should we go back to slides? Thank you. Okay, so we've talked about what happens when you draw with QuickDraw and print. Let's talk about if you're drawing with QuickDraw and Quartz 2D and doing Quartz 2D imaging during printing.
So, I think the most important thing that a number of developers have run into is that this routine that I talked about, Create CG Context for Port, if the port that you have is the port you got from the printing system, this routine fails. You get an error. You can't get a CG context from that port.
Now the reason is, as I've talked about, the printing system itself that's translating QuickDraw graphics into Core graphics drawing, into Quartz 2D drawing, the printing system has a CG context associated with this translation that it's doing. Now if you start drawing into that yourself, there's some synchronization problems that we have. Because the printing system thinks that it owns that CG context and it can change the color and it can change the clip and it can do all the stuff in it.
If you start going in and doing those things, we can't do our job. So to work properly doing this, we need to have careful state management of QuickDraw and Quartz 2D drawing at print. Now we're working really hard to resolve these issues. Let's talk about what it's going to look like.
Well, we need to enforce some kind of modality into this to separate the drawing that you might do into this CG context from the drawing that the QuickDraw to Quartz 2D translator would do at print time. So, we've added some routines, or we are adding routines to Mac OS X in the future. These are not in Mac OS X version 10.0.x.
The routines are QD_begin_cgcontext and QD_end_cgcontext. Now these routines, unlike the Get Context Report, these routines work both for screen drawing and for printing. So, if you're only doing screen drawing, you can keep using this CG context, Create Context Report, but if you're doing printing, you want to use these new routines.
Now, in order to enforce the fact that we have this modality where you're doing your, you're doing your QuickDraw CG drawing, and then you're doing your QuickDraw drawing, and then you're doing your Core Graphics drawing, and then you're doing your QuickDraw drawing, in order to enforce this, when you actually call these routines on a port, we take the bottlenecks in the port, the imaging bottlenecks in the port, and we make them non-drawing bottlenecks.
Okay? So, both on screen and printing, QuickDraw drawing that goes on in between the QD_begin_cgcontext and QD_end_cgcontext, that QuickDraw drawing will be in the same context. So, that's why it's going to be ignored. One other thing is, when you call CG, excuse me, QD_end_cgcontext, that CG context that you got from the begin routine goes away. You have to get a new one next time you want to do your drawing with Quartz 2D.
Okay, sounds hard, it's not. Here's the way it looks. So, you start, you don't have a context for the, you don't have a context to draw into. You have a port, you call QD_begin_cgcontext. Pass in the port, you pass in the pointer to your context, you get a context back.
So, now you go and you do some drawing using Quartz 2D. So, in this case, I'm just using the CG context draw PDF document, similar to what I did before on drawing a PDF document. So, you're done drawing your PDF document, you're done for now drawing your Quartz 2D content, and you now are going to draw some QuickDraw content of whatever sort.
You call QD_end_cgcontext, you pass in the port, you pass in the pointer to the context, that you had, that you got from the begin routine. Now that value that you get back from this routine, that context is null. You can't draw to that context anymore. If you have another way of hanging on to that, you're going to be really surprised when you try and draw into that.
Okay, so now you do your copy bits or whatever other QuickDraw imaging that you need to do. It's time to do some more Core graphics drawing, some more drawing with Quartz 2D. You go, you get your context again. Now, when you go to get the context again, it's important to know the graphics state, the G state on that context is reset to an initial G state. So it doesn't remember whatever transformations you might have done between the last time you used begin and CG context. So you get it again, you do some more Quartz 2D imaging and so on.
Okay, now we want you to start moving over to this style if you're using the Quartz 2D system. And so in order to facilitate that, we've written some code that you can link into your application. You can start using this style rather than using the Create Context Report. Again, this is this printing issue.
So these routines work both, the real routines that are going to be in the future version of Mac OS X work both on screen and print time. This linkable code does not enable printing, but it lets you move your use of core graphics of Quartz 2D over to this style of mixing both QuickDraw and core graphics.
So again, if you're only drawing with QuickDraw or if you're only drawing with Quartz 2D, this does not apply. So we have some linkable code. It does not enable printing. It doesn't install these bottlenecks that dummy out the QuickDraw drawing in between, but it lets you get going.
Okay, well, tips and tricks. I hope I gave you a little bit of a tip or a trick with the demo, but let's talk about some printing tips. First thing, number one, most important, don't expect that when you say PostScript begin, draw some picture comments, PostScript end, that that's going to generate PostScript.
You need to do something really special. Paul Danbold talked about how to do that in the printing session. So you need to do something really special if you want to generate your own PostScript. By default, the printing system doesn't generate PostScript, won't let you generate PostScript, okay? So in addition, don't expect that you can use PostScript begin and PostScript end to hide quick draw drawing. Lots of applications do this on Mac OS 8 and 9. They say, ah, well, it's the laser writer driver, you know, so it's not going to, you know, pay attention to the quick draw drawing that I'm doing in between PostScript begin and end.
Hey, we recognize that quick draw, and we're going to image it, okay? So the next one here is if you're someone who likes to actually go in and call the bottlenecks, the quick draw bottlenecks directly, make sure you check that graph port. Make sure that those bottlenecks are nil or don't exist already in the graph port before you go off and call standard bits or standard text or whatever. Because if there's port bottlenecks, you want to chain any calling that you make,
[Transcript missing]
Okay, now there's some documentation that we have out there on the Mac OS X printing graph port. It's already been written. It's already been out there for probably, I don't know, maybe even 10 years, but close to it. And that's the documentation that's in Appendix B, using Picture Comments for printing.
Pretty much what it says in there is true, modified by some of the things I've said here. There are some picture comments in there that say, "Oh, these are going away. You don't want to be using these." Hey, these went away. You don't want to be using those.
Okay? Now coming soon we're working on a tech note that's specifically about the Mac OS X printing graph board. It's going to contain the material that I have in this talk expanded. Okay, so let me just give you a summary so far. We hope you see that there's both my talk and obviously talks earlier in the week with far more impressive things in them.
You can really do some cool stuff with Quartz 2D in Carbon on Mac OS X in Carbon. The printing graph port, it's not like a graph port you saw before. It recognizes all these picture comments, but it's not a PostScript graph port. It's not a QuickDraw graph port. It's this sort of hybrid.
And just to emphasize again, applications can mix Quartz 2D and QuickDraw drawing content on screen and at print time the mixing is going to take the form of these new routines that I've described. Okay, that's the summary of what I've given you so far. What about making it all go fast? And to talk about that, my colleague Ralph Bruhner on the graphics team.
Okay. So, I will talk about Quartz performance tuning and in the next 15 minutes you will learn something about how to improve your graphics speed, what to do about memory usage and most importantly how to find the performance problems. First I'd like to say something about how the architecture in Mac OS X works, the graphics architecture.
On Mac OS X there is a process running in the background called the Windows Server. That process is responsible for managing screen real estate. So when an application creates a window, it will go and send a message to the Windows Server and say, "Well, I need a window that size, that spot." And the Windows Server will go and allocate a buffer for that window, map it into shared memory with that application, and then the application can go and draw into these bits. Well, when the application is done drawing, it sends again a message to the Windows Server and says, "Okay, now here are the new bits. I want to put them on screen." And the Windows Server will do that.
The important concept here is that the window has a backing store. So all your content is buffered and that has some implications for how you do graphics. First of all, what it gives that to you is The Windows Server is able to mix the content of your application with the content of any other application. So that enables things like the drop shadows you see, genie effects, scalable dock and things like that.
Now, you have to be aware that whenever you do a manipulation of your window, that is no longer as lightweight as it was under OS 9. Because pretty much everything you do with windows that affects the window geometry is actually a message that goes over to a different process and tells it what to do.
One other implication is that window buffers use a bit of memory. So the last thing you want to do is allocate a lot of windows which are not on screen, but just eat up some memory. So, two tips here. One is primarily for Cocoa applications. It's a bad idea to go allocate a window at a certain size and immediately resize it to a different size.
Because that essentially means the Windows server will allocate some memory, then it will get a request, well, toss that away, I really changed my mind, I want something else. And that is fairly common when you have a default size in your Nib file and then set it to the window size that the user set it last time to. So if you can avoid that, that is a nice performance gain.
Something from the Carbon side of the world: some Carbon applications on 9 use an off-screen G-World to buffer the window content. So on 10, this is counterproductive because you already have a buffer, so you just double the memory size for the window and you do an additional copy. So if you can avoid that, that helps too.
Another thing for Cocoa applications, in the interface builder you have this little flag that says you can make the window one shot. Well, one shot windows have the property that when they go off screen, the buffer is automatically discarded, and when it comes on screen again, the application gets a redraw message to rebuild the content. So, whenever possible, you should use that. Most times it's far cheaper to redraw the content of the window than swapping it back in from the small file.
Okay, let's talk about drawing. Well, the key message about drawing is, in today's world, graphic speed is essentially a function of memory bandwidth. It's the number of pixels you touch. It doesn't really matter anymore what you're actually doing with these pixels. So, the first thing you want to avoid is multiple drawing.
Like, you draw your window with, you fill it with a pinstripe pattern, and then you put a big white rectangle on top of it to have a background for your text. So if you can avoid the pinstripe drawing at the first place, that is a nice performance gain. And the other thing that ties in with the architecture of the Windows Server I mentioned before is avoid unnecessary flushing. So whenever you're done with drawing something, that message gets sent to the Windows Server to tell it, well, put this stuff on screen.
Now, it's a fairly common bug that you flush too often. You have essentially, your drawing is half done, and then somewhere in your code path a flush happens. And what that means is unfinished drawing appears on screen. and then right after that the complete drawing shows up causing flickering. So you want to avoid that, try to avoid that as much as possible.
Now, one nice thing about the Windows Server is all updates you do are synced to the CRT beam, so you really get totally smooth graphics if you play an animation. You get the exact 75 Hz of your monitor or whatever. And that's the reason why dragging the window around on OS X is really smooth. However, this can backfire for you. If you flush too much, that mechanism will actually ensure that the unfinished drawing stays on screen for long enough for the user to notice. So, it might be a good idea to look at that.
So in today's world, OS X.0.x, window buffers live in main memory. But however, it is a good idea not to rely on that. There may be changes coming. To move the window buffers on the graphics card, or maybe they get compressed, or any other performance optimizations we might think of. So, relying on the fact that the window buffer is in main memory today is not such a future-proof strategy. So, what you want to avoid is touching the window bits directly. Whenever possible, use QuickDraw, QuickTime, Quartz 2D to produce your content.
So this here on the slide, essentially getting the port for your window and then the pick space address. That's whenever possible, you should avoid that. Well, there are definitely cases where you have to. So I can imagine if you write an emulator for something, then you probably want to produce your own bits. So, if you do, we'd like to hear from you. We may come up with some APIs to enable that without breaking the performance optimizations we're going to do in the future.
Okay, a word about the velocity engine. On OS X, we have a certain number of functions inside the graphics system that are optimized for the G4. And this gives, in some cases, really significant speedups. In fact, whenever we go and To vectorize one of these routines, we usually get more than 2x out of it. And in some extreme cases, we saw something like 17x speedups.
Well, for your drawing to actually make use of that, there's essentially one hint to give here, which is whenever you produce your own bitmaps, like you create a CG bitmap context or you create a QuickDraw G-World and then hand that to the graphics system, make the row bytes a multiple of 16, which is the vector length on the G4 chip. That will enable more optimizations to kick in.
And as a side effect of that, because there are routines that are significantly faster, do test your software on a G3 as well, not just your G4 developer machine. If you have interactive graphics going on, which is really nice on a G4, it may be that you're actually triggering one of these optimizations and it's dark slow on a G3. So it's worth verifying that.
Okay, now I'm going to do a demo about how to find performance issues on your system. Can I get rid of this? So in your developer folder there is a little application called Quartz Debug. It has this colorful window here. And it enables you to
[Transcript missing]
The first switch here is flash screen updates, which tells the Windows Server whenever it puts something on screen, flash the area briefly in yellow to make it very visible where updates are happening. So for example, if I switch over to simple text here, you see there's some flickering going on, and when I move the window around you see there's certain areas that get updated.
Now, I'm using simple text here because simple text has an interesting performance bug. So, when I type here, You see, it redraws the entire window for every character I type. So this mechanism makes it blatantly visible where your performance problems are and why your key repeat rate is really slow.
So this helps a lot in finding these kinds of problems. Because the window contents are buffered, it's now much harder to see. You no longer see flickering on screen usually if the flush is in the right place. So this is the mechanism to counteract that effect and tell you what's going on.
Another switch here is all the flash drawing, which essentially activates whenever a primitive is drawn, either to QuickDraw or to Quartz 2D, we flash immediately. This is like the immediate mode drawing you see on OS 9. So if you combine that with the flash screen updates, then you actually see every single primitive. And you see there's a lot of flickering going on, the menu gets redrawn, and stuff like that.
So this makes your system really, really slow, as you can see. But it helps you to detect something like duplicate drawing. If an area flashes twice, then you know, well, okay, there is probably something going wrong here. Okay, so if I switch back you see there's the fireworks still going on.
Another thing that Quartz debug allows you to do is look at the window list in the system. These are all the windows the Windows Server sees. There are things like size on screen, size in memory, position and the name of the app that produces that window. For example, the top three here say Quartz Debug App.
There is one window which is 22 pixels high and has the width of the entire screen, which is the menu bar on the top. There's another window down here which is 300 by 200 something, which is this nice little picture here. And there's a third window, interestingly. which is 19 by 19 pixels. Now this is AppKit caching that little checkbox here. So, which is internally implemented as an off-screen window.
Look at your application using the "Show Window List" feature here and find out if there are windows that are off-screen and if they're big, try to get rid of them because you don't really want them to end up in the swap file. Okay, that's it for the demo.
[Transcript missing]
Before we get to the Q&A, I just want to take a quick time to go over the last item on our roadmap at WWDC relating to graphics. And we have at 2 o'clock in room J1 the Graphics Feedback Forum. This is a very important forum for you to come and tell us feedback on Mac OS X's graphics technologies.
It's a good opportunity to query us on to why certain elements of the architecture are implemented differently from what you experience in your Mac OS 9. Additionally, provide your feedback and features and things you would like to see in the system. If you need my contact information, I'm [email protected]. I can be reached. If you have questions about any of the 2D graphics technologies in Mac OS X, feel free to send me emails.