Graphics and Imaging • 1:08:47
Quartz is the high-performance 2D graphics system at the heart of the Aqua user experience. See how to directly access the Quartz 2D APIs to handle even the most advanced vector or bitmap drawing needs. Learn the fundamentals of the drawing model and how to use advanced rendering features to create high-quality 2D graphics. A key session for all application developers interested in creating more compelling 2D graphics in their application.
Speaker: Derek Clegg
Unlisted on Apple Developer site
Transcript
This transcript has potential transcription errors. We are working on an improved version.
My name is Derek Clegg. I'm an engineer in the graphics and imaging group and I'm going to talk to you about Quartz 2D, how you can use it in your application. Give you some introduction to some of the APIs that are available and then move onto some interesting technology directions that you can go. So we've going to cover today a little bit about what Quartz is, sort of the general ideas, some of the fundamental concepts in Quartz 2D.
The basics of what you can use Quartz 2D to do in your application. A lot of you might be familiar with the Cocoa level. We're going to drop down into the, sort of the C APIs underlying. Touch on some of the advance capabilities that are available and then give you some tips for getting started and where you can go after this session.
So Quartz is sort of a big umbrella that a lot of you may be familiar with from Tiger. Includes the windowing system, the basic stuff that manages your windows for you, all of the 2D drawing of course, which I'm going to focus on today, display services, Image I/O, excuse me, Core Image, Core Video, Quartz Composer is part of Quartz and PDF kit. So it's a big package of things. It's not just simply the Quartz 2D API.
And in Leopard we've extended it, made it even better by adding Core Animation which is a very cool, very neat frame work. There's a talk later today on Core Animation. Image Kit also very cool. So Quartz is a pretty big package of things. But I'm going to focus today specifically on Quartz 2D. It's the part of Quartz that is the 2D API underlying all of the 2D drawing in the system.
Now some of the features that are key to Quartz 2D are one: device independence. What this means is that when you write your code using Quartz 2D you don't hav-e to worry about what device its going to end up being used on. For example, you might be writing code that's going to end up in a PDF file or draw some stuff on the screen of course or to bit map contents.
All of that is independent of your code. We handle that on our side, you write your code once and because we do the right thing with device independence, you don't have think about where the device, where it's going to end up. Another part of, component to that, because different devices, different output media have different resolutions Quarts 2D is resolution independent. So you don't have to worry about whether you're going to a 92 DPI screen or a 600 DPI printer.
The resolution is something you don't have to think about. You use the same code, ends up in the same looking good way on each output resolution. Of course, high performance, fundamental part of Quartz 2D if we don't have high performance, we don't have much. So it's very high performance. We spend a lot of effort making sure that it stays at very, very high performance for your applications.
An important part of Quartz 2D that you see all the time and you may not even be aware of it consciously, is the anti-aliased graphics and text. That anti-aliasing is a technique that keeps the edges smooth, keeps them from looking blocky, and, and ugly. So the smoothing of all the graphics and text is a fundamental part of Quartz 2D. - You don't need to do anything to get that. It's just built in.
Alpha transparency is also an important part of Quartz 2D. Alpha is another word for the opacity, the transparency of an object, how much transparent something is when you draw it. So alpha transparency is a fundamental part of Quartz 2D from the beginning which gives us really high quality and graphics, very interesting effects, like the new desktop as you've seen. So that's a fundamental part of Quartz 2D that we have built in.
And of course we need to be able to draw this screen, but a lot of people want to print, they want to send their PDF files to their friends and neighbors, presumably. So built into Quartz 2D is PDF support for output. So again, you don't have to do too much to get your content written to a PDF file. We do all the work for you behind the scenes. And that's been part of Quartz 2D from the beg-inning.
And it's a gateway. That once you sort of understand the Quartz 2D APIs, you can leap from there to other technologies. You can use it partly with other things of wholly, completely write your app on the level of Quartz 2D. So it's a nice sort of front door to a lot of interesting technologies that are part of Mac OS X.
So where is it in the system? For historical reasons, it's also referred to as core graphics and Image I/O and I may in the course of this talk refer to Quartz 2D by either of those names. Those are historical. It is called Quartz 2D from my point of view. So when you want to look for the header files, you'll want to look in these two frameworks.
This architectural diagram, this sort of gives you a flavor of how things fit together. All the green parts are part of the graphics and media technologies. So Quartz 2D and core image and core video are all pretty much peers. Build on top of those are core animation, PF kit, Image kit, all layered on top of Quarts Open GL in the graphics hardware.
Quartz text, I'm not going to talk about today, but I wanted to mention it. It's a very nice framework in Leopard that's integrated very tightly to Quartz 2D, lets you do- lots of things that you wanted to do with text, but at a Quartz 2D friendly way. So it's a really powerful new framework, and definitely worth checking out.
So how would you use Quartz 2D in your world and whatever apps you're writing? Of course in Cocoa, a lot of people already are using the Cocoa drawing classes. And so those are perfectly fine for many applications. But there are times when you want to drop down a level, maybe you are worried about performance or maybe there's some things in Quartz 2D that isn't quite expressed in Cocoa the way you want to. You just want some finer grain control. So it's appropriate in certain circumstances to drop down into the Quartz 2D C APIs if you, even if you're using Cocoa.
Of course in Carbon, if you're a Carbon developer, Quartz 2D is the drawing API you use. Because it is a C framework, it's appropriate for both C and C++ developers. Its very light weight. So it's definitely something you might want to use, that you will be using in Carbon development.
Some people don't realize that Quartz 2D is not just a window system and its not just sort of you know the stuff that appears in the screen. You can use Quartz 2D at the tool and script level and sort of down the command line to build up either complex or simplified, simple scripts, maybe dynamic web page generation, things like that. So you don't really need to have a windows system to use Quartz 2D. You can work at the lower level if you want to.
And in Leopard we have bridge support which lets you do more scriptable tasks in languages that you may prefer to work in but still call the Quartz 2D API. So you can use Ruby or Python if you're familiar with those to do interesting things using Quartz 2D without actually having to sort of drop into writing the C program and all that kind of stuff. And there's some samples in this directory that I mentioned, are worth check out if that's something you're interested in.
So today I'm going to focus on a simple example, sort of give you a sense of what it would be like to use Quartz 2Ds APIs in your app if you were for example writing something like Pages. So Pages, complicated app, does lots of things, but basically it builds up graphics content.
I'm going to walk you through some things Pages does to give, and show you the APIs that correspond to that, to give you a flavor of how in your application you might use some similar type of work. And also sort of get you, give you a sense of what Quartz 2D, what the Quartz 2D API is capabl-e of.
So we're going to do a really simple thing. We're going to try building up a flyer using just the Quartz 2D APIs alone. Of course in Pages this would be really simple You just pull those things together. We're going to build this up using Quartz 2D API, sort of a programmatic flyer generator if you will. But first we need to go over some fundamental ideas that are part of the Quartz 2D model just to get you familiar with what's behind the scenes and what you need to understand to start using Quartz 2D.
The first important concept is that we build everything on top of these opaque data types, abstract certain ideas in the graphics architectural system. And, and a very simple sort of object like style. So of course we are C APIs, so we're not, there's not C++ or Objective-C or anything like that. But the model, the concept, the way we design the architecture is similar in flavor.
So you will work with the sort of, excuse me, opaque data types in a similar fashion that you might work with objects in Objective-C. And because of that we have, follow sort of similar models. Of course we function based rather than method based. But the ideas are the same. We have functions which create these objects. We have sort of the equivalent of accessors but you know CG image get width for example, get height, for example, things like that.
Functions which act on objects, CG contents draw image for example. And so on. And so we, we try to follow this model of, of sort of an object like system to give you a flavor of, its not really object oriented exactly but it's the same, same basic model. Makes it much simpler to work with.
And as a consequence, you need to know a little bit about memory management, so as usual with frameworks that follow the core foundation model, which we do, if you create an object, you own it. And once you have created it, when you're through with it, you must release it, because you're the owner, you must release it.
You can use a standard core foundation function CF release. For historical reasons, all of the different object types have a corresponding release method such as CG image release in this case. But the key thing is that once you create something, you have to release it. If you don't release it, it'll leak and that's bad.
On the other hand, if you don't actually own it, if you didn't create it, you don't release it. If you just get it, the model is sort of you either create or you get. If you just get it, you don't release it. But sometimes you need to keep things around even though you didn't create it directly. So in that case, again following the corporate foundation model, you retain it. CF retain is the function you use. Again for historical reasons there specific functions for each of the object types.
So that's sort of the basics of memory management. It's exactly like core foundation, those of you who are familiar with that will be familiar with this. It's also very similar to the standard Objective-C model.
( Period of silence )
Another concept that's important to understand at the beginning of Quartz 2D is how we work with the coordinates system. Of course we're a graphic system. So you're going to be drawing things into some coordinates system. And the question is how do you sort of, how do we model the coordinate system for you to draw.
The key is that we have a flexible model where by you change the coordinates system. You don't change your objects. This is sort of again in keeping with the idea of object oriented programming. The model that we use as our coordinate system changes in order to change the orientation or scale factor in sort of an object, not the object itself. And there are functions CG conduct scale, CTM, rotate, translate, that modify the coordinate system, CTM in this case stands for coordinate transformation matrix. And that lets you do really powerful effects without having to sort of incur the costs of creating new objects or doing the work yourself.
So for example, imagine we have our coordinate system, the standard one that you get for you when you start up using Quartz 2D and here we have an image that's drawn at certain coordinates and they think, oh but that's nice but we really want to scale it down, we want to shrink it. Now in some graphic systems, you would have to create a new image, scale it down yourself and then draw the result. Using Quartz 2D, instead the model is you modify the coordinates system. You shrink the coordinates system and then you draw your same image.
And on our side, we do the hard work of scaling the image down, getting to the right location, anti aliasing or sampling so that it looks beautiful and you get sort of the nice result without doing too much work on your part. Similarly to rotate, rather than you doing anything directly to the image, instead you rotate the coordinate system. Then you draw your image and we'll do the work to draw it rotated.
And there's another little tricky thing you can do for flipping. It turns out that corresponds to a translate over and then a scale by negative one to flip. And you know I'm going through this pretty fast, so I don't expect you to necessarily you know understand all the mathematics behind all this, but this is something that as you learn about Quartz 2D and as you work with it, the coordinate system transformations will become more familiar to you and they're also something that's pretty important to understand how they play into the whole game. And the key thing is that you modify the coordinate system to change your objects, not the objects themselves.
So as an example of that, a specific one, here we image let's suppose somebody you know I want you to draw two rectangles, a blue one and a green one. The blue one is at 0, 0 width and height is 100 and 100. And the green one is twice as big at a different location.
You could, of course, and a lot of systems assume you would, create two different rectangles and draw them in two points. The nice, and you know I realize rectangles are trivial, but image this is a PDF page or something more complex. The nice thing in Quartz 2D is that what you do instead is you move your coordinate system over to a new location, the location you want to draw your rectangle.
You scale up by a factor of 2 and then you draw the same rectangle. Because our coordinate system is changed, we're now, we now draw a larger rectangle at a new location. So that's a key idea in the Quartz 2D model, that you work with a coordinate system, not the object.
( Period of silence )
So those are sort of some high level abstraction ideas. Let's talk about the actual primitives that you have available to you in Quartz 2D to draw. Quartz paths, pretty fundamental, very important. We have dotted lines, you have open curves, you have something complex, a complex path like the state of California there. The little circular rings are actually two paths, two circles with the interior fill, filled.
The two stars show different draw angles. A number of ways to work with paths in Quartz 2D. - Images, of course through the Image I/O framework, we have full support for JPEG, PNG to pretty much any image format that's available in the world almost. And as you saw with a little animation we also support progressive JPEG so you sort of can pull in data from some external source and display it incrementally.
PDF support, been built in from the beginning, part of Quartz 2D, very fundame-ntal. Any PDF file that you have available we can draw the pages from, display it. You can use those in your application as either like little thumbnails or display as a whole page. Could be really useful for built in artwork that you want to be scalable rather than having everything sort of converted to an image.
You can start out with a PDF and then the things that are naturally scalable, such as text and line art will remain so and things that are you know maybe more device depend, resolution dependent such as images will scale up nicely. But you'll keep the, sort of preserved in the resolution in an independent way.
So it's really useful and we starting to use more and more PDF throughout the system in terms of just our artwork and our UI. Text is another part of Quartz 2D. - As I mentioned Core Text is the right way to go about using Quartz 2D in text. It's a really powerful framework and you can do lots of these cool special effects.
Excuse me. So now those are some of the objects that you have to work with. And the part that, that's important about the Quartz 2D basic drawing model is what's called the painters model, or the painters algorithm. And this is just the way we express content in Quartz 2D. It's very simple. The idea is that just like with a painter, as you put content down, one on top of another, only the things that are not obscured are going to be visible.
So image we start out with a flight background and we draw a gradient on top of it. Pretty simple. Now we draw our image on top of that. We draw out little logo on top of that. We draw our PDF file on top of that. So you see we're sort of building up back to the front.
And that back to front build of content is the way Quartz 2D works. And we do a lot of tricks and handle things very efficiently where we can just sort of make that as fast as possible. But that's sort of the general idea of you started from the back and you sort of move up to build your content. And those of you who are used to Cocoa programming, programming and so on will understand this exactly the same model.
Another piece as I mentioned earlier, that's important about Quartz 2D to understand is alpha transparency, the amount of, of transparency an object has. This is built into the Quartz model. In this case, we have an alpha for every color. So a color has not just the red, green, blue components but also the amount of transparency, how transparent it is. So here we have our little cookie and we're going to draw a blue rectangle with Alpha one. That is fully opaque.
Well, not surprisingly, the cookie is you know obscured by our blue rectangle. Now we take the same cookie, we're going to turn down the alpha on the blue rectangle so the alpha is now point 5, that is its 50 percent transparent. When we draw it of course, the cookie shows up with the little blue on top of it. You can still see through. And then alpha zero, that is fully transparent, no opacity at all, we're going to draw our blue rectangle, oh there it is. It turns out you can't really see it because of course its transparent.
And that's alpha zero. So that, the idea of alpha as part of the color is a fundamental part of Quartz 2D. In addition though, there's also alpha as part of the objects window drawn. So in the same way we can sort of change the coordinates transformation matrix or change the coordinate system, we can also change the alpha when we composite. So that things become more or less transparent depending upon what the global, we call it global alpha is.
So here we have our white rectangle and we're going to draw a cookie and the cookie is, has when we, when we draw it we set the global alpha to one that is everything that's drawn is fully opaque, relative to the, to the object itself. So the cookie shows up fully opaque. Now we do the same thing again, but we're going to turn the global alpha down to point 5, that is 50 percent transparent.
And now the cookie is sort of fading out. So we decrease the alpha, we decrease the transparency and now the cookie is less visible. And then again, guess what we're going to do, we're going to draw the cookie again, global alpha is zero. As you see, or rather as you don't see, the cookie doesn't show up because it's fully transparent.
So this is in the same way that we, it sort of follows the same mode. We don't change the object to get this effect. We don't sort of choose our image and then have to add an alpha mask and do all sorts of things. Instead we just change the global alpha and that changes the drawing when we draw it.
Another part that's important to Quartz 2D is our built in color management that lets us preserve the fidelity of colors, depending upon, independent of the output device. So our input device, so you might have a high quality camera that takes pictures and you get it printed on some special printer and you want the thing you saw when you took the picture to look exactly the same as the thing that gets printed on the output.
And the key part is that with built in color management, that's part of Quartz 2D, we can pres-erve the colors across, across input to output device so that we don't have sort of colors shifting and getting different results. As you can see here, we have something that's not color managed. Things are a little, color is a little off. When it is color managed, the colors stay true.
( Period of silence )
So that's sort of the concepts behind the various objects. A very important object that we have as well is the, is something that abstracts the drawing destination. As I said earlier, we're device independent. What that means is that we, when we go to different output devices such as here is illustrated you know like a window or maybe a PDF file, your printer, bit map, we want to be able to have something that represents that drawing destination that's sort of an abstraction of that idea without necessarily tying this to specific behavior of any one of those destinations.
So the way we express that in the Quartz 2D API is with the CG context ref. The core, CG, core graphics context ref. It's an abstraction of the destination. So when you write your code and use, use a Quartz 2D APIs, you don't need to think about where you're going, where the, where the result is going to end up. You just write the same code and then we'll do the work on the back end on our side to translate what you write to either say for example PDF file or maybe a bit map or maybe something on screen.
So as, as I mentioned here, there's a number of different flavors of the context. And the great thing is you don't have to think about it too much. You, once it's created for you, or once you're created it yourself, you just use the same API and you get the same results on all the different destinations.
So how do you get one of these things to work with it directly if you're wanting to say use Quartz 2D API yourself? If you're using Cocoa, there's the standard model of course is that you have an NS view. And you have a draw rect method. And in the draw rect method you do your drawing. So that's the natural place to get the core graphics context to work with.
So in your draw rect you do this little sequence of operations and you get a contents of, the, the names are a little confusing for historical reasons. Its, it's a little baroque but basically you start out by getting a NS graphic context, which is not the same thing as a core, a CG context. From that NS Graphics context, which is a Cocoa concept you get the so-called graphics port. Now that is your CG context ref.
And from that point you can tell do all your Quartz 2D drawing. So you sort of follow the same little sequence and you get your context every time. On the other hand, if you're using Carbon, you'll typically have an event handler on the HI view and your, your drawing in the event handler, you use this nice little piece of code, just copy it and it will give you back a context. So that's an important way to get the, that's a way to get both the context in from Carbon and Cocoa.
So we, the context abstracts the drawing destination and you can think about you know you don't have to think about where you're ending up. PDF or on screen or whatever. And inside the context is also a set of state that is maintained for you when you're drawing. So some graphics models require you to set the state say in the call to draw a line, that's when you have to well draw this line but also make sure it's red and make sure it's this thick and make sure it's dotted and so on.
So the Quartz model is a little different in that there's a persistent state that's part of the context that hangs around and is maintained for you so you don't have to always be changing or setting the state every time you do a drawing operation. You can set it once and then draw a number of things. Some of the things that are part of the state, the stroke color for a line, fill color, the interior fill.
And of course the coordinates, coordinate transformation matrix that I mentioned before. How the coordinate system looks when you draw to it. Things like the line width that's on parameters that are part of the stroking operation, global alpha I mentioned earlier, shadows, blend modes a whole bunch of things are part of the graphics state. So all of those are maintained for you in the context itself. So that's, that's a nice collection of things for, for you to work with.
the problem though is that sometimes you want to change the state and then get back to where it was before without necessarily sort of having to save all the, these parameters yourself and remember what they were and so on. And the model that we use in core graphics is what's known as a graphics stack. So we take our graphics state and we have a stack of states and you can preserve the state and restore the state in order to sort of temporarily modify the context state and then get it back to what it was before.
So in our case, here we have an example maybe of the simple state you can imagine, just a stroke color and fill color and of course there's a whole bunch of parameters other than that. But let's just look at these two. And we imagine that we want to change the fill color, not really worrying about what it was and then fill something and then we're going to return back to the state we were before.
So the idea that, the API that you use is CG context, save G state. That's basically is going to save the graphic state for you and push a copy of it on the graphics stack that you can modify freely and then we're going to restore it back when we are done.
So we've made a copy of the graphic state and push it on the stack. And now we're going to change the fill color. Let's say we just change it to green. That's nice, change it to green. And notice that we're just changing the current state, not the stuff that's saved on the stack.
We do our, you know whatever we want to draw in green color. And then we're done and we want to return back to the state we had before. So you kind of restore G state, we'll take the current state and pop it off and it restores back to the saved state.
So we go back to the previous state. So this is actually a really powerful model in that you can just change the parameters you care about and return back to where you were before without having to sort of keep track of everything yourself. We do a lot of the work on our side to keep track of things for you.
This is something that those of you who use Cocoa have had happen for you implicitly for since the beginning of Cocoa. In your draw rect method, right before the draw rect method is called, Cocoa with call this save G state to save the graphic state. In your draw rect method, you can change things all you want as you know. And it doesn't affect any other drawing in the system. And you may have wondered, well why is that? Well because they save the G state right before they call your draw rect method.
When you return to your draw rect method, they restore the G state. So any changes you made are basically isolated only to your draw rect method. So that's a really powerful simple model that makes things a lot simpler for you when you actually do, it's rather than having to do state management.
( Period of silence )
So this is all fine. We've gotten a lot of ideas down, but we haven't done anything. So we need, lets start now just to actually start drawing. And we do the simplest thing we can do which is just filling and stroking a rectangle. It's about, you can't do that on a graphic system, you might as well throw it out.
So to do this again, we start out, we have this idea of this abstraction of the drawing destination, the context. And we have a little structure called the CG rect that represents a rectangle, it's just four point values. And there are some convenience functions that let you fill and stroke a rectangle very simply. And as a bonus there's also something that lets you fill and stroke an ellipse. That's pretty easy to do either the erect or the ellipse case. We'll just talk about the rectangle for the moment.
And I'll just walk you through some code that takes a context and fills a rectangle with a green, a blue color and then strokes its outline with the green color. This is a pretty simple function that gives you a flavor of how Quartz 2D API works. So we first create our rectangle, this CG rect may call to create this structure. The first two parameters are the origin, 10, 10. The last two parameters are the width and the height.
And once we've created a rectangle we're going to fill it with blue. And let's imagine, I'll talk about this in a moment that there's some magic function called my get blue color that returns a blue color. So we're going to set the fill color and the context to blue.
And then we're going to fill a rectangle. So that's pretty much all there is to it. What we have at that point suddenly is a rectangle appears at the right location with the right color and we're very happy. Now we're going to stroke the rectangle with an outline of green.
So in the same way we imagined as a function my get green color, and that's going to return a green color that we set as a stroke color. And now we're going to stroke the rectangle. In this case we're going to stroke it, but we're also going to pass in the width. The, the width of the line that we want to stroke it with and we call this function, we get a rectangle that's stroked with a green, green outline.
The part that's important to note about Quartz 2D model is its- stroking is on both sides of the path. So when I say I want to stroke width to be 10, the total width is 10, but I really get five on one side, five on the other side. So the model of having the stroke appear on both sides is pretty fundamental and it's important to note that this is the way we do things.
Now I'm going to do the same thing but I'm just going to change the order of the operations. And this is going to illustrate a couple of ideas. One is the idea of saving the graphic state and if there is a graphic state that keeps state for you, there's also the idea of translating or moving the coordinate system and then sort of showing you also a bit of the idea of painters model, how the order of operations is fundamental.
So I begin by saving the graphic state. That's going to save everything so we don't change, so we can preserve our, our state and return back. Now I'm going to translate the, the CTM. That means we're going to translate the coordinate system over by 200. So we've just moved our origin over by 200 in the X direction.
And now we're going to do the same operations the other way around. We're first going to stroke the rectangle, same way, with the green color. Now notice that because we have a graphic state stack, we don't reset the stroke color. The stroke color hasn't changed. It's still green. So we don't set the color to green again. We just know its going to be green.
So now we stroke the rectangle. We get a green stroked rectangle. And now we're going to fill the rectangle. And its color is going to be blue. Why? Because the part of the graphics state and we haven't the fill color so it's the same as what we set it before. So it's going to be blue.
And now you see we draw the blue rectangle on top of the green stroke. And you can see how the stroke is on the inside and on the outside because the line is a little thinner. And also because the painters model, we're on top of the, the thing we've drawn before. So we have a blue rectangle occurring on top. So it's visually going to be different.
And then finally we're going to restore the graphic state. And notice that we're not undoing our change to the coordinate transformation matrix. That is going to automatically happen since its part of the graphics state. It's going to automatically undo in the sense because we're restoring the graphic state to what it was before. So we know have the origin moved back. And you can go on to draw as many rectangles as you'd like.
( Period of silence )
It's much more interesting to start drawing an image and how would go about doing that in Quartz 2D? Because we need to get our flyer because our boss is demanding the flyer soon so we better get it going.
So there's a number of functions that work with images that workhorses that are very basic that you'll want to use most of the time are 3D functions t=hat let you create an image from a file and then draw it. There's an image source, create with URL. An image source is an abstraction of the data on disk. It's not actually an abstraction of the idea of an image. It's the abstraction of the data on disk.
Once you have an image source, you can create an image. So a CG image is the abstraction of just the date, of the image itself, the image's data. You can create an image from an image source and then once you have image and you have a context, you can draw the image into the context.
So here's a bit of code that illustrates those ideas. Again, imagine that we have a context that's been created for us or that we're in a draw rect routine. And someone's handed us a URL that probably references an image file on disk or maybe someplace else in the system.
And we're going to create an image and draw it from that URL. So the first thing we do is create an image source from the URL. And you'll notice I don't do any error checking or anything like that. That's because otherwise you'd be really bored with all the code on the screen You'll normally need to check all that. This is very optimistic programming where we believe that everything succeeds all the time. So there is some error checking I've left out. In this case, we create an image source. And that is going to represent the file on disk.
Next you create an image from the image source. It turns out that although most image file formats only contain a single image. Some can contain multiple images so that zero after the image source in this call is the index of the image in the image source. And of course this function is to get the number of images and so on.
In this case we're going to get the first one, the zero item. The standard way and once we have the image, we, we release the image source. Because we created it, we must release it. So we release the image source. And now we want to draw the image into our context.
So the CG context image draw takes a rectangle and when you specify that rectangle what Quartz will do is scale your image into that rectangle. In our case, we want to draw an image in a certain location, but we want it to be as big as the image itself is. We don't want to sort of shrink down or anything. We want to make sure it's the same size as the image.
So we use a sort of pseudo accessor functions to access these, these functions that return but within the height of an image. So that when we create a rectangle, it'll be at the origin 200, 100, but its width and height will be exactly the, the rectangles width and height. I mean sorry, the widgets width and height.
Once we've created our rectangle, we just call CG context draw image. And that will show it up at the right location, the right way and then since we created the image, earlier we had the CG source create image and index, we must release it to make sure that we don't leak memory.
So those are the steps that, that you go through to draw an image. I mean it's actually pretty simple. There's not a lot of work going on. And you know error checking and making sure it's in the right location is sometimes its a little bit more code. But the basic idea is exactly this. This is how you use Quartz 2D APIs to draw images.
( Period of silence )
So now we've drawn a path and we've sort of implicitly talked about there's these magic functions that return colors, green color, blue color, how do, how do those actually work. So to draw with color in Quartz 2D, you first need to understand what we think a color is.
So for Quartz, a color is a color space, which I'll talk about in a second. The components themselves that specify the color value, so for example, if its an RGB color, red, green, blue color, the components would specify the amount of red, the amount of green, the amount of blue.
And for us also a color always includes the alpha value, the amount of transparency, whether it's opaque or transparent. So the colors are those the components, the alpha and the color space. So what's a color space? At the biggest level a color space just specifies what model you're working in. Whether you're using RGB for example or CMIK or LAB or device N or index. There's a whole bunch of different styles. A lot of the time you're going to be working with RGB.
You're going to be drawing stuff on screen. You're familiar with an RGB color space. So the biggest level, biggest picture it just that it says how many components are there in this color? Three? Four? One? But in addition, color spaces can contain information that lets you have high fidelity color management. So as we saw before in the previous slide, you know the built in color management uses the color space to make sure that colors are represented accurately from device input to device output.
That said, a lot of the time for most application developers, there's a, there's a special color space we have in Mac OS X, the generic, that's a name. It doesn't really mean generic. It's the name. Which works just fine. Its lets you do high fidelity color on output, on input and it's just sufficient. So we're going to focus on just the generic color space today.
One, in Leopard, its now a lot simpler to work with colors in that space. We have a function that creates a RGB color and a CMIK color and a gray color and so on. In the, in the color space, in the generic color space directly from color parameters. So you pass in the red, green, blue values along with your alpha of course and you get out a CG color ref. The CG color ref is the abstraction of a color that encapsulates all these pieces.
In addition, there's a function, a new function that lets you return, that returns a constant, so called constant color. You pass in a name, you get a color out that it's sort of the same across the whole system. And those of you who have used Cocoa will be familiar with this from the NS color function or methods, you know there's an NS color, red color, you know the green color and so on. So this is the same basic idea. And this is new in Leopard.
Once you have a color, we've seen this already, you can change the color in the context by calling either of these two functions. There's a couple other functions you can work with as well. CG context of fill color with color, so you pass in a CG Color that you've created to change the fill color or the stroke color.
So this is a, this is almost too simple of a function to illustrate. What was that my get blue color that we saw earlier? Well the idea behind that function is that it returns a blue color every time it's called and because it's a get function, we don't have to release it. Its just going to return the same one.
So as usual with that type of model, we're going to have a static variable that's going to hold on to the color for us and after checking to make sure we need to initialize it, we do a very simple operation, CG Color create generic RGB. That creates the CG color that represents blue.
The components are red, green, blue, alpha. And as I said before, the, the amount of each component is a number between zero and one, so in this case we have no zero red, no green, one full 100 percent blue and for the alpha value again, 100 percent. That means fully opaque. So this is an opaque blue color.
And then we just return it as, once we've created it. So this is a pretty, this, this type of, of a little piece of code is pretty good for colors that you use over and over and over. For colors that are one offs, if you're going to create them and then use them and then get rid of them, you might not have something like this. But this is really good if you have a set of colors that you use all the time.
So once you have a color, you know you want to actually color things. So what can you color? Well a path is a standard thing. As we saw before, rectangle, we've both filled and stroked it. But paths are much more complex in, in Quartz 2D. - Paths in Quartz 2D consist of pretty simple pieces.
They're made up of a line segment or, or sets of line segments, besia (sp?) or cubic, sorry besia (sp?), cubic or quadratic curves. So a path sort of is built up over these fundamental pieces and once you've created a path you can do a number of things to it. So paths are open in the sense that they have a different start point and an end point. That's a possibility. They can be closed, which means that they have a different, sorry, the same start and the same end point.
And a path can consist of more than just a single section of you know lines and arches. It can actually consist of multiple sub paths that are disjoint. In this case, in the right hand illustration, really that's actually four sub paths that comprise a single path. The two little line segments and the two circles all are sub paths that are part of the single big path itself.
So excuse me, so paths in, in Quartz 2D can be actually very complex and very rich. You can do an awful lot of stuff with that. And then what's nice too is we have some convenience functions that let you do simple things like just add a rectangle. We sort of saw the rectangle functions before, our ovals, arcs, so you don't have to think about how do I get a circle from a set of besia (sp?) cubic curves. We do that for you.
The way you work with a path is there are two ways to work with paths. The context itself, the, that draw gestation abstraction contains a path within it implicitly. And so you can work directly with the context to build up a path. And once you've built up the path in the context, you can then stroke is or fill it, do some other operations to it. So that's one model for working with paths. That's a, that's a simple model where you just need to sort of you know build up a rectangle or a couple circles and just stroke or fill them.
Sometimes though you actually want to create a path yourself that you hang onto and then that you use later. And we have CG path functions, the CG path object is sort of the abstraction of all this various pieces of a path. And those let you work and build up a path directly that you can hang onto and reuse.
Those of you who have used Cocoa know about the NS besia (sp?) path probably. NS besia (sp?) path is essentially a cover for these functions, so you pretty much have the same functionality of both levels. They're not really doing much other than just calling into the CG functions directly to hang onto a path. Once you have a path, you can add it to the context to, to be drawn or stroked or filled just like you would if you use the implicit one.
So we're going to go through a, a pretty simple example of returning a path from the rectangle. This path is going to represent a rectangle. So it's a bit overkill, but it gives you the flavor of the way Quartz 2D APIs work. You begin by creating a mutable path. This is the core foundation model of mutability verses immutability. So you create a mutable path that you're going to modify.
You begin first by moving in the path to a particular point. In this case, the origin of the rectangle. And then we add a line to the right hand corner of the rectangle. Add the next line to the upper right hand corner of the rectangle. Add the next line to the upper left hand corner of the rectangle and now rather than adding another line back to the origin, which would be okay but it doesn't actually create a closed path, it just creates an open path that happens to start into the same point.
We're going to go ahead and close the sub path and implicitly add the last line back to the origin. So that's our rectangular point. I mean sorry, excuse me our rectangular path. So that's, that's a flavor of how you build up paths. Of course rectangles are simple and, and most applications you're going to do something more complex. And then we return it. So this creates the rectangular path from this path.
But now we want to actually do something with that rectangle. And we're going to talk a little bit about how you might create this, this fancy little logo for your flyer. And if somebody came to you and said, oh, draw this logo for me please. I, this is, the artist said this is what I want and I'm supposed to do it.
Might think oh my goodness, this is a lot of stuff I have to worry about. But I'm going to show you how you do this same little bit of artwork with some very simple code just leverage again the idea of path reuse, alpha transparency values, coordinate transformations so that you can end up with this little logo on the bottom right.
So we're going to start out first by imagining we have our context of course. And someone is, we specified the rectangle we want to use, so we're going to begin by creating a path. That was as we saw before to represent that rectangle. Of course that could be more complex than just a simple rectangular path. It could be any shape.
And we're going to start out by saying we're going to set the context color to be blue, because we want to draw a blue rectangle. And the first thing we're going to do is set the alpha value. In this case, as you read the code, we're going to set it to one, we're going to make it fully opaque. So we're going to draw a fully opaque blue rectangle, color and the context is blue and global alpha is blue. And we begin our path and we add the rectangular path to the context.
And then we fill it. This is not surprising, we get a blue rectangle. You could do this more simply of course with other code. But this idea is, is you know the path is could be more complex and, and more, more you know not just a simple rectangle.
And then we rotate our coordinate system. So we're going to now draw this same thing again but within a rotated coordinate system. In this case the rectangle is going to be draw rotated. Even though the rectangle itself is not changed, the coordinate system changed. So the rectangle now will be rotated. So now we go through our loop again. We decrease the global alpha value and set it in the context. So now we're going to draw everything with a slightly more transparent value.
The same basic operation. We begin the path, add the path, the rectangle to the context and then fill it and now we have the blue rectangle but in the rotated coordinate system, little bit more transparent. So now you have the, the second piece of the logo. And now we just do the same thing over and over. Again we rotate the coordinate system. We draw our rectangle with the global alpha decreased even further. Rotate the coordinate system, draw the rectangle, rotate the coordinate system, draw the rectangle.
And so on. So you can see we get a relatively complex effect with actually not very much code. And, and we're pulling in the whole idea of being able to change the coordinate transformation matrix so that we get this effect of rotating the system and also reusing the path over and over so we're not creating a new object each time so our memory use is better. Excuse me.
And we just change the global alpha rather than setting a new color each time. We can use the global alpha to decrease the, the opacity. And so this gives you a really complex effect with not very much work. And then of course as usual, we have created the path, so we must release it.
( Period of silence )
So now we have a number of pieces of our flyer We have image, we have our little funny logo and we had a gradient in the background that we want to include. And this gradient is pretty subtle, so I'm going to use a, a much of bolder gradient to illustrate the way you work with gradients in Quartz 2D APIs. This is new in Leopard and it's based on feedback from developers who said that the existing method of what's called shadings was too complicated.
So we have a new thing in Leopard, lets you work directly with gradients, much, much simpler, much more developer friendly. So the idea behind a gradient is that it's just an abstraction of an array of colors on a line, range from zero to one. So here we have an example where our gradient is three colors.
The first color starts at zero, the last color ends at one. So the first color is red, last color is blue. And we have another color in the middle at point 5. Of course you can put those colors anywhere you want. And as many as you'd like in the gradient. So sort of get a, the effects you're looking for.
And once you set those colors in this line from zero to one, when you use the gradient, you specify a start point and an end point. And the current transformation, the matrix of the coordinates system and the clip region determine how the extent of the gradient is drawn sort of basically the orientation of the gradient. So the gradient this follows a lot of our same model.
You create an object and then you change the system, the context parameters to change the way the object is drawn. So in this case we have an example on the lower left corner of the gradient with an origin sort of in the middle of this rectangle and an end point at the top of the rectangle and then the clip region is probably the whole rectangle. So we just see it inside there.
We might set the same state up in the middle case and maybe change the start point in there to point to the corners, or maybe we rotate our coordinate system 45 degrees so that we get this other effect for the middle case. And then the far right case we've done the same type of thing, we've changed our start and end point, but we've also changed our clip region to be an ellipse. So you get this gradient and this ellipse without actually doing a lot of work. And I'm going to walk you through some of the code necessary to do these effects.
Another thing that's important about gradients is there's the linear version which is just showed you but we also have radial gradients that fill a circular region. And the area that you clip to can be any complex shape you want. So here we've clipped to this letter B. It could be more complex than that to get the nice gradient effect. The functions you use to work with gradients, there's one main one.
The workhorse function, CG gradient create with colors that lets you specify an array of colors and a set of points. So putting those colors along the line. There's a, a more primitive function that could be useful for certain developers where you specify the components explicitly without creating the colors directly.
And once you've created a gradient, you can draw it using CG context draw linear gradient to draw a linear one or the radial version for a radial one. And you specify start end point, some options and you have your nice gradient. So here's an example of drawing gradients using Quartz 2D APIs for Leopard. We begin by imaging that we have a rectangle that we want to fill with a gradient. So we create this rectangle. Its origin is zero, zero. It's width and height is 100, 100.
We're going to create a, just a sea array of colors. We're going to to start with the color, the first color is going to be blue. It's going to transition to green, then turn to red just as I showed you before. And in order to use the CG gradient API, we need to create a CF array from the standard colors. This is just a core foundation code that you probably are familiar with. You create a CF array passing in the set of colors and the number of colors.
And you call CG gradient, create with color. So you pass in this array of colors. And the, I mentioned before that you should pass in the locations where the colors go on this line from zero to one. As a convenience, if you just pass in null, what we do is we distribute those points uniformly along the line. So in this case, the first color is going to go blue, the second color green is going to go at point 5.
Or the first color will be blue at zero, the second color will be green at point 5 and the last color will be red at one. So we do that work for you if you just need to have a uniform gradient. This is particularly useful if you're just doing a gradient with two colors where you know it's going to be zero to one. And as usual, we've create this array so we must release it.
Now I said before, the way gradients work is they fill the current clip region. And I'll talk about clipping in a second. The idea is just, this is the area where, where the drawing is going to go, so we're going to clip to the rectangle that we want the gradient to fill.
And then we draw our gradient. In this case it's linear. And we specify the start point where the zero value of the gradient maps to. In this case, the point is 50 0, that is 50 in the X direction, zero in the Y direction. So the bottom middle of the, of the rectangle.
And we specify an end point, CG point make 50 100 that is 50 in the X direction in the middle, 100 at the top of the rectangle. So the middle top of the rectangle. And we draw it and that's all you have to do to get the gradient.
And of course we've created it so we have to release it. So this is much simpler than the code you would have to write in Tiger to do the same effect. And we've really taken your feedback to heart around some of these things. There's a class in Cocoa as well and this gradient that works similarly. Again, built right on top of the CG code. In case you want to use that.
so we built up a gradient, we've built our image, we've got our fancy logo, the last piece is to put this little Italian Velo Tours logo thingy on top of our flyer. So that we imagine is going to come in from a PDF file. Someone has created that for us. And we want to draw it in our context.
So how do you do that by using Quartz 2D? So I mentioned before with images there's an image source which is the abstraction of the image data on disk. Similarly with PDF there's a CG PDF document which is the abstraction of the PDF file on disk. And the document sort of is, is a way you, you talk about and ask questions about the document data.
From the document you can get a page in the PDF file, CG PDF page ref is the abstraction of a page. For historical reasons, pages start at one, not at zero. Just the way it is. So you, you sort of work with doc, PDF documents in a similar way you work with images. Just a slightly different type. So as you might image, CG PDF document create with URL, is going to create a PDF document CG PDF document ref from a URL you specify.
Once you have the document, you can ask things like the number of pages, number of other properties, lots of properties you can ask about. And once you have the page you want, you can call CG PDF document get page to return a page. And then you can draw that page in the context.
So this is almost too simple of an example. But this is walking you through this exact idea of how would you create a or rather how would you draw a page from a PDF file onto your context destination? Here we imagine again as with URL probably a file URL that points to a PDF file in disk, we note some page number we want to draw, maybe it's the first page, probably in this case. And we want to just draw another context. So we, first we create a PDF document with our URL.
Again I've left out all the error checking code. We get the page that we want to draw from the document. And then we draw it. That's it. Everything else sort of happens behind the scenes in court. So the page, the PDF page we've drawn for you at that point and we created the document so you'll have to release it, but notice that we didn't create the page. CG PDF document is not going to get page. It's the core foundation model again, the get functions don't need a release. You don't release the page once you get it from the document. You just release the document itself.
( Period of silence )
So that's great. Now we've pretty much built up our little flyer. And now let's image you say well, I'd really like to spice it up a little bit. Maybe I want to put that image in an ellipse. Boy that's going to be hard. Not really. Clipping in the Quartz 2D API is the way you can constrain your drawing to certain regions.
When you clip to a region, anything that's outside that region won't be touched. And anything inside that region is only touched when you draw to it. So you, you can actually get very sophisticated and complex effects by using clipping in Quartz 2D. The nice thing about using a path such as an ellipse to clip is that that's implicitly resolution independent. The paths are always resolution independent.
So you can, you get high quality results if you clip to a path whether you're going to a, your screen or to some 1200 specialized DPI printer. The other point about clipping that's an important aspect in Quartz 2D model is that we intersect not replace. So when you clip, your new clipping area is the intersection of the old one and the old you've specified. So if they are disjoint and you clip, you will get no, no drawing because the clipping area is a null set.
So the intersection, that's different from some other graphics models. The intersection is an important thing. You might think oh my goodness, if I keep clipping, I'm going to shrink down to nothing and then I can never get back. The nice thing is the clipping area is part of the graphic state, so as I talked about before, in order to preserve the current clipping area, you would save the graphic state, do your clipping, do your drawing and then restore the graphic state to what it was before. So you kind of restore G state. So you can get back to where you were. So this is a pretty powerful part of Quartz 2D that lets you do lots of sophisticate effects.
In order to clip to a path, we have a simple convenience function. CG context is clipped direct, it will clip to a rectangle or you can create a path in the context as I showed before and then CG context clip will clip your drawing to the path you set in the context.
So let's go through a simple example of clipping an image to an ellipse. So its, we're going to do the same image drawing we did before, but now we're going to draw it so it only shows up in this ellipse. So the first part of the code, same as we saw earlier. We image we can create an ellipse some how and we have a rectangle we want to draw into. So how do we actually draw the image in the ellipse. We'll first we need to clip, we need to create a clip region which is the elliptical area.
So we begin our path in the context. We add an ellipse in the right, basically we're going to fill that rectangle that we specify with, with the ellipse. And we call CG Context Clip to set the current clipping area to the interior of the ellipse. Now everything that we draw will only show up inside the ellipse. So we're going to draw our image, only the part that's inside the ellipse will show up. And again we you know passing the image rect using the width and height zone to get the right model.
So this is actually, this gives you some really powerful effects without doing too much coding and what's interesting is with all the special effects, like in Keno and everything, they all sort of basically come down to using this type of code, the clipping area code and so on to get this affect. And then as usual we created our image so we have to release it. Don't want any leaks.
So that's one type of clipping where we clip to the path. Another type of clipping in Quartz 2D lets you clip to an image itself. You can use an image, any, any gray scale image as a mask to put, to specify where to put paint. And because it's not necessarily one bit image or you can use an A bit image or any other bit depth.
You can, the mask effectively acts like a transparency value that will fade in or out your drawing so that you get more or less paint if you will put on the destination. And that can also give you a really powerful and really interesting effects, by using the mask itself directly. And it's very simple to use that. You don't have to do a lot of work.
I'll just show you a simple example of clipping to a mask. On the left hand side we have this teapot, lets image that's our mask that someone has given us. And we want to put color through it so that we get the effect on the right hand side with the colored rectangles.
So what we're going to do is draw a set of colored rectangles but first we're going to clip the context to the masked image so that only paint will show up where the image is non zero. And the amount of paint, the amount of color that shows up will be a fraction based on the intensity of the masked image.
Excuse me. We would use the function CG context clip to mask to do this where we pass in a rectangle just as before when we're drawing an image. But also an image that acts as our mask. In this case a teapot. So now when we, once we set the, the teapot image as our mask, when we draw each colored rectangle, it's only going to show up where the teapot is non zero, where the image data itself is non zero and the amount of color that appears is a function of the, the color values in the mask itself.
So you can sort of see this is, this, I mean this is in some ways a, an artificial example. But this gives you some really powerful and sophisticated effects using just a very little bit of code. In this case, CG context clip to mask. And so you can do really interesting things without doing very much work.
( Period of silence )
So lets, at this point we've built up our flyer, we're very happy with it. We want to send it to a friend. And we don't want to send our program, because we're you know we're imaging we're writing this whole piece of code that the only purpose is to create this flyer. So we don't want to send our program. Instead we want to send a PDF file.
How would you get your con, the drawing that you've been doing on the screen to a PDF file? Well we, we want to export it to PDF and there's a very easy way to do that in Quartz 2D. To create the PDF content, we create a special type of context, it's not, it's again, you know the context are all abstract. But it is, it is a specific contents that, that knows how to draw to a PDF file.
Once you've created the PDF context or it's been created for you maybe as part of the print system, you can then use it to export it to your friend, you can mail a PDF file off to a friend or something. Or you can use it as a very high resolution clipboard format. It's, it's a really useful piece that you could add to your application to get high quality output.
So how would you create a PDF context that specifically is going to write to a PDF file, directly yourself? There's a number of functions that are used to create a PDF context. In particular CF PDF context works great with URL, we imagine as usual we have an output URL we'll write to. And we have a default media box, how big the PDF file will be.
Because we need to know on the Quartz 2D side, information about the PDF file, we do have some functions that are specific to a PDF context. In this case, CG PDF context begin page, CG PDF context end page. Both of those functions let you tell us when the page begins and ends. That is a little different from other context. Most other context don't need to know that. You know for PDF we do.
One thing that's new in Leopard that its important to know about particularly if you're working with garbage collection or you implicitly will be because you're say a plug in or something that gets loaded. And it's the PDF context close function. So this is sort of like finalize in standard garbage collection lingo.
It, it tells us on the Quartz 2D side that you-'re finished with the context and we can write out all the trailing data and stuff like that that's special to PDF files. And we'll do it as soon as you call that function. In Tiger that was implicit in that when the context was released, we would do that.
But the problem with that is if you're in the garbage collection environment, you may be releasing next year. You never know. So you, we need to explicitly let you tell us when you're finished with the PDF file, when you're finished writing to the PDF output destination so that we can write all the necessary meta data at the end of the file. And rather than relying on the sort of fragile release mechanism.
So here's a simple example of writing PDF content to a file. The first that we need to do is create a default media rect to do a default page size for our PDF file. We want it to be, because this is what works in the US, 8 and a half by 11 inches. PDF is interesting in that its default coordinate set and the coordinate set it works in is points.
And at 72 points per inch. So we need to convert our 8 and a half by 11 inches to points and that's why we do this 8 point 5 times 72, 11 times 72 to convert that to points. So to get the points for the, for the default page size.
And now we create our PDF context from the URL that's passed in. That's going to be our destination. And we pass in a media rect that specifies the default page size. So we don't do anything else. Everything else, every page will be 8 and a half by 11.
We need to as I mentioned before begin the page. You need to tell Quartz 2D that you're starting your page. And then you do your drawing the regular way. You don't do anything special about that. That's just the same type of content drawing that I showed you before. All of that's, pretty much goes through unchanged.
So that you get the output into the PDF file. And then as usual or rather in this case, you need to tell us you're done with the content. So that the page can be ended, so that we can end the page. And maybe you're going to do another page, so you can do the same thing again and so on. So you draw pages one after another, begin page, draw your content, end page.
And then as I mentioned in Leopard, make sure you call CG PDF context close to tell us now I'm done, write out everything that you need to write to the file, flush all the data to the, to the output destination. And then because we created the content, context we have to release it.
So that's sort of how you might build up a flyer, you know from start to PDF output just using Quartz 2D APIs. Of course I've just barely touched on what's available. There's a lot more that you can do in Quartz 2D. Let me just give you a flavor of some of that.
There's what we call a CG pattern which lets you do replicated drawing. Patterns work like colors, so you can both use, use them both as fill and stroke colors as you, if you will to fill regions. We can use them like a wallpaper where you just replicate the pattern over, over an area.
Shadows, those of you who have used Cocoa probably know about the NS shadow class and of course that's just a straight cover on top of the CG shadow itself. A shadow could be as I show here, it could be either soft edged, hard edged. It can be different orientations, you have different colored shadows for some reason, you might want that.
And you, any, anything that you could can draw in Quartz 2D you can shadow. So here I show some more complex paths that have a nice shadow. You can see the shadow is drawn appropriately in the interior parts and so on. So that, that shadows are very powerful and gives you a lot of cool effects.
Shadings, I mentioned those earlier in passing when I talked about gradients. It's a more complex but also more powerful way to do gradients. Gives you a little bit more control over the way the gradient appears. So that's worth checking out if you need more than what the gradient functions offer.
Transparency layers are a way to draw a group of content and then have an effect applied to them all as a group. Here you can see on the left we have, maybe this is a logo we want to draw consisting of two rectangles with a stroked rectangle. And when we draw them with a shadow, if we draw them one after another we get the shadow sort of falling on top of the other objects, which is not what we want.
On the right, what we've done is used a transparency layer where we draw the content and its treated all as a single set of drawing operations so that then when the shadow is applied, its applied as though that were all just one block of objects. You can see the shadow shows up correctly on the right hand side.
There's an idea of CG layer which lets you do high RSV rendering of drawn content. In this case we imagine we have a layer that we've drawn on little object into. And we can reuse that layer to get the, the same object drawn in multiple times. And that's also part of Quartz 2D. - So that's, that's just sort of a flavor of where you can go.
Of course there's even more beyond this. The thing that you might want to know is like well what do I do now? How do I actually get started? Because this is, you know this talk is all good, but really you have to write code. How do you do that? The thing I would recommend is get this book. This is an excellent, fantastic book. Talks about everything I've talked about plus a lot more.