WWDC2003 Session 413
Transcript
Kind: captions Language: en well here's my mic on can you all hear me ok I want to thank Jason for introducing me I know he just had a jog down here from the very long queue a session in the cocoa you I techniques part so I'm here to talk about cocoa 2d drawing techniques that's me John Randolph a senior engineer in a marketing department which is kind of fun and what I'm going to cover in this talk is how the cocoa classes that reflect the courts drawing model are used in 2d rendering so I'll talk a little bit about images pats and text how we manipulate the coordinate space in cocoa generally how to draw using the cocoa frameworks I'll talk a little bit about more sophisticated drawing techniques than I did last year particularly combining vector and image drawing and then my colleague Troy will come up and discuss the changes to the cocoa to the drawing API for panther so there are a couple of additions to existing classes and some new api's and maybe you'll get a demo or two into this so where does this all fit in before we get to gooeys we just have the bsd front of the bsd api and we have Darwin and then we've added all of these amazing graphics technologies like quartz 2d opengl and quicktime for time-based media java can use them carbon can use them cocoa can use them and we use these frameworks talking to the graphics layers to implement aqua in your applications and today we're going to talk specifically about cocoa and quartz and how you use them to comply with jungle NZ's desires so and you all comply right good so quartz 2d gives us a couple of very powerful features we get a window management a very nice complete vector drawing API a lot of image handling code compositing of those images and that's actually pervasive in our OS and I believe that we are the the only vendor that offers that and we've got a lot of powerful text rendering capabilities also in the courts 2d layer the courts 2d compositor what call court services now is the windows server that takes care of blending those windows together on the screen we have hardware accelerated window compositing we have transparency of windows we have windows mapped into open GL texture memory and you've seen that in other sessions and besides that we have the PostScript PDF style vector based drawing model which you all should be familiar with from your days with the apple laserwriter Oh some what 20 years ago now and this is based on past that can be filled or stroked they are resolution independent and on Mac OS 10 you get that anti-aliasing for free the coordinate system that we get from quartz 2d and that we that we use in Cocoa views when we're drawing is based on a floating point system the the origin is in the lower left corner just like it is in math I want to make the point that on today's displays the whole number of values land exactly between screen pixels and sometimes you might adjust your drawing to make sure that lines that you draw exactly take one pixel I'm going to tell you not to rely on this because you're not always drawing to the screen sometimes you're going to be drawing for printing context you're going to be drawing in much higher resolution context like when you're imaging for a printer or when you're imaging for a high-priced typesetter so when you're doing these drawing techniques please check to see whether you actually need to do them more features of quartz 2d we can scale and rotate images we can map them to what other color depth is available on the display where they're being drawn we've got color managed rendering of images which you might have just seen across the hall in the QuickTime session we've got jpeg decoding and all of this has been optimized to use the velocity engine on the geforce if its present and of course we've got people working very hard on making that go as fast as it possibly can on the g5 so more about quartz 2d features we've got a very rich set of compositing operators there are 14 different modes the original porter duff paper on the subject described 12 modes and we invented two more hour compositing takes into account both the source and the destination alpha values again this is all optimized to use whatever Hardware you have in the Machine and I'm going to point out a couple of examples here on any machines that Scott's to knock off 10 developer tools installed you can go to developer examples app kit composite lab and there's an app there to let you try out all the compositing modes and see what their effects are and then there's a simpler app on the DTF sample code website actually two of them tinted image and cropped image with both which both show you things that you can do by compositing and courts 2d also gives us text rendering as I described before I'm not going to get much into text because it's a pretty big subject I urge you to go to the cocoa text text session I will just mention that the anti-aliasing and the LCD text and so forth is also velocity engine optimized now let me just grab a little drink here so in the cocoa framework we do window management through members of through instances of the NS window class we do vector drawing mostly with the NS veggie a pic NS busy a path class images and compositing you're done with the NS image rep and in its image class text is done with NS text and a string and ass attributed string and a lot of other classes that I won't be going into here so when you're going to start rendering the B images or the vector art that makes your app unique you need to know where you're going to draw now if you're down at the at the courts level you'd be drawing into a CG window but if you're a Coco coder it's a little simpler than that you have an NS window object that manages the CG windows it will get your events it will talk to the windows server and tell it when something has changed it needs to be updated and it has a drawing context so that has things like what image interpolation is in effect what the current line drawing width is what the current drawn color is and so forth most of the drawing in your apps is going to happen in NS view instances a drawing destination in cocoa basically is anything that can react to unlock folk with it to lock focus and unlock focus and NS image is also a place to draw that you'll use if you want to build something in an off-screen buffer the way you might have done in the G world in earlier versions of Mac oz so let me mention NS view here I like to think of NS view as a rectangle of responsibility within a window it owns some portion of the window and any events that happen in that area are going to come to this NS view object things like keystrokes things like Mouse events and so forth things like drags into that part of the window NS views got its own coordinate space not necessarily its own drawing context views live in a hierarchy so every view has a super view and any number of sub views most of the drawing and NS views are going to happen within the context of the draw rect method and some changes to the way and as view objects behave are coming up in Panther and Troy will fill you in on that so let me go over something here that seems to be a point of confusion for a number of new Koko developers there are two rectangles that are very significant for NS views there are the frame and the bounds rectangles the bounds determines your coordinate space it tells you how big you are and where you're your coordinate space origin is as far as your point of view is concerned determines the scaling and the rotation of your coordinate space you can change it around to to suit your drawing with methods like set bound size and rotate by angle and so forth so let's say we've got a view that's going to draw this this nice little farmhouse here and the balance of this obviously are the borders of of the image now if I go and put that in a scrollview well it's still this big but it's only going to draw in this area that's outlined by the solid solid rectangle even though it thinks it's still as big as as the outer rectangle the frame is what's actually going to enclose the area that gets drawn the bounce is the largest area that you might ever draw let me give a little bit of advice for writing NS views something that's very tempting is when there's a change in your model in your code you're going to want to draw immediately and I'm going to ask you for performance reasons to not do that calling lock focus and unlock focus is usually not necessary now there are times when it is necessary like if you have a live animation going or like we do in the kit where we have that default button pulsing so if you must draw right away you can go ahead and use lock focus and unlock focus but most of the time in your apps it's not going to be necessary and all you'll need to do is call set needs display or set needs display in wrecked and then the view updating machinery can take it from there and the main piece of advice you want to give you here is let the framework do the driving it really is a benefit for performance to do that wherever you can ok so whenever you're going to do the kind of drawing that you used to do with PostScript code you use in the nsbe a path class it's got the familiar path construction operators such as move to in line to and relative line to and curve to and so forth it also has convenience methods such as veggie a path with oval and wrecked or bezzie a path well there are convenience methods that will give you a rectangle and I happen to like to add methods to be a path to give me other kinds of shapes that I might use frequently in an app you can use a path as an object that will add to your to your clipping boundaries and you can make it the current clipping path with set clip or add it to the current clipping path with add clip and it's been a path can also give you character glyph outlines you can ask for these from NS bezzie a path after you've gotten a glyph from NS and one thing that I've seen happen very often in fact I've made this mistake myself and a lot of code is that people will often create paths and then just throw them away and this isn't actually necessary since the NS bezzie a path object is a container for these paths building commands you can just empty it and reuse it and that's a little more memory efficient bezzie a path of course does bezzie a cubic splines thus the name of the class and it's got the other PostScript marking features that you might expect like setting the line caps setting the flatness of curves when you render them and so forth so most of the things that you would look for in postscript or in PDF when you're drawing in a cocoa view you probably want to look to the NS bezzie a class and its budget class and let me also mention there are a lot of really simple and convenient drawing functions in the app kit if you just need to fill up a rectangle with the color or fill it with a color using a compositing operation whenever you think that they're probably should be a function to do what you're about to do take the time to look for it because it probably is there I'm going to mention NS image now I often hear the question how do I get to this particular pixel of my NS image instance and the answer is you don't because NS images don't have pixels not all NS images are just lists of values over space and the color space a lot of NS images for example PDF for resolution independent so some NS image reps have pixels not all do and whenever you're going to try to get the value of the color at any particular location you're going to have to actually render that image first and then try to read the value back so the point of i want to emphasize here is that some NS r image in NS image reps have pixels some don't but NS images the containers for NS image reps themselves do not have pixels now NS image is also something you can use as a destination for off-screen drawing and in my demo I'll show you a little example of how I did that in one case something to remember when you're drawing into an NS image it's not an NS view so the NS view methods for manipulating the coordinate space are not there the courts 2d that courts City functions for manipulating accordance that coordinate space are still there and you can still fit up call all of those functions and whenever you've got focus locked on a view you have a you have an active courts drawing context and all of the functions that directly manipulate that context are still valid now another thing that's kind of handy here is that you can use the NS affine transform class to describe any linear transformation of coordinates and NS affine transform can be applied in any drawing context so whether you're drawing in an image or drawing interview the NS a-- find transform can still be applied a couple more things about an image in in jaguar we gained the ability to draw NS images progressively so if you're loading something expensively over the net you can get notifications that tell you I know enough about it to tell you how big it is I know enough to render a band of pixels I know enough to actually render the entire image and every time you get this notification you can just draw the image and it will draw as much of it as you have at that point we've also got features added in Panther for multi-frame images so if you have an NS PDF image rep that you've loaded from a PDF file you can set the current page that it will draw when it's told to draw by calling the set current page method and if you're going to go through something like a PNG or gif file that's got multiple frames you actually can set them well you go through set property with value to get to the individual frames and set which frame you're looking at the other thing that we would have in a jiff image for example is the current frame duration which you can query so if you're going to animate a jiff in Jaguar this is how you get at it basically as a dictionary of attributes of the image rep we'll talk a bit about the compositing Venice images the the one I tend to use most often is composited point frame from wrecked operation fraction and you could also use draw in wrecked I will point out that the composite to point and the draw in wrecked functions do not behave the same way one is more expensive than the other draw in wrecked will honor the fact that you've rotated the coordinate space if you've done so composite to point doesn't bother it's the fast path so if you don't need to actually fit that image into a rectangle that's different from its original size then that's the one you would you would prefer if you want to just composite a solid color you can call in a fill wreck using operation which apparently a number of people never never found while they were reading through all of the app kit functions that I just suggested everyone read up on and let me just give my little demonstration of combining vector and raster drawing so this is a sample that is just sitting out there at the dts website right now you can go to the BTS sample code page and go to Coco and pick this project up called cropped image and what it will do is it will take a path draw it into an image and then use that image as a mask to composite another image so if I want to for example just select the mandrels eyes here what's happening here is that that path that I showed you before is being composited against the mandrel image and this is the result now i can turn edge smoothing on or off here that's a that's a graphics context function but the point I'm trying to get to here is that this kind of thing combining the vector with the image drawing is a way to get some fairly sophisticated effects and it's not a terribly expensive thing to do let me just show you the code for that so I have a path here that i got from those Mouse events and what i do to make that cropped image is I make a new image that is the same size as the the image that I'm going to be cutting down with the path i lock focus on that new and on that new image fill it up with with a solid color and then I take the existing image composite with the source in operator and the result is that basically that mask punches out the area that I've selected let me show you one other thing that I were able to do by compositing actually I wanted to show you the running app not the project here ok here's another copy of the traditional mascot of image processing code and what I've done here is I've taken this original image and basically composited against solid colors using the plus darker method to extract each of the components in turn and then back here i reassemble them using the plus lighter method so I can just turn these on and off and see the effects of various color components being being composited back or not so let's go back to the slides please okay i will mention as i always do in my demos the things that i did wrong in that demo first of all I was drawing way too much and if you go and get that code you'll see where I made the mistakes basically every time I was drawing the the cropped image I was redrawing the entire area covered by the image view also I'm pretty sloppy with the bezzie a pass I'm actually creating them and throwing them away every time there's a mouse event I can get away with that on g4 but probably not on like a bond i blue imac i also didn't bother with scaling the path but you can see that when you go get the sample yourself and try resizing those image views will just briefly mention text it's a pretty big subject and Doug Davidson is going to be talking about that in session for 27 which will be the last session in the big room upstairs on Friday there's a lot of power in the cocoa text system and if you really want to dig into into the meat of that Doug can fill you in on how to do that I'll just mention that if you just want to draw a label or something you can just send messages to win a string or an ass attributed string to have them render in your view if you're going to do text that has maybe a couple of lines tutor maybe you want to edit the text in your in your custom views and a cell objects are kind of handy for this because they know how to deal with that for more information on the text please catch catch Doug session okay a couple more apka classes that are involved in 2d rendering there's the affine transform which as I said will contain any combination rotation scaling and shearing and you can apply it to the current graphics context there's the NS graphics context method which is where you store current drawing attributes such as what your current drawing color is what your current transformation matrix is and what level of image interpolation quality you may have one there's the end of screen class which you use to get information about just what displays are available on your your particular system that you're running on and Troy Stevens is going to come up now and talk about the details of what's changed in in its view and how you may want to alter your drawing when you're getting ready to deploy your apps for Panther so that's right thank you John thanks very much for that hello everyone my name is Troy Stevens I'm a software engineer in the cocoa frameworks group at Apple and among other things there I work on the NS view class and as you as you may know is the base class for NS control as well as for pretty much every other kind of object in a cocoa application that has some notion of where it lives in a window that has the ability to receive and process Mouse events keyboard events and that can draw itself so there are a couple of topics relating to NSU that I want to address with you today first of all we'll touch on a technique actually a couple of techniques for hiding views when you want them to temporarily disappear from your user interface so we'll talk briefly about that and then for the meat of today's talk we're going to talk about some techniques that you can use both on shipping versions of Mac OS 10 on Jaguar and earlier and then starting on Panther some new features that we've added to help you to actively optimize your view drawing to draw as little as possible so that you can really have your application screen even if you're only running on say a g4 so first off let's dive right into hiding views what do you do if you want to temporarily hide a view in a Coco application well first of all why would you want to do this why might you want to hide a view well for example you may have some control that is only applicable when your application is in some particular state when say some particular kind of object in your document model is selected or when some other state is set in some other control as an example let's take a look at the view options panel in finder which you may be familiar with we have down at the bottom of the panel here we have a color well that is only present when it is applicable when it is appropriate for the user to be able to set a color now as an alternative to hiding a view to making it completely disappear one thing you can usually do with the control is to disable it and that in fact is what the finder team has done here with the keep arranged by pop-up button that is directly above the area where the color well Liz in general we recommend that you disable rather than hiding views if you have a control disable it that way it's still there in your user interface it appears grayed out it changes to a grayed out appearance it no longer response to user input but it gives your users of visual cue that yes there is some other option here that may become available to me if I get the application into a different state if I select some different object on the other hand hiding of you may really be what you're after and also if your view of a more general type of NS view that is not specifically a control remember they set enabled mechanism in cocos defined at the NS control level you may have something like a scroll view that is not a kind of control you may want to hide that so supposing you want to do that how would you go about it well we have a time-honored technique that you can use in all shipping versions of Mac OS 10 you simply take the view and remove it from its parent you have some other objects such as your controller object retain it first so that it doesn't go away on you and then you remove the view from its super view and then when you want to show the view again you simply reinsert it back in its parent and it's back on the screen this is a nice simple technique it works quite well there are a few details you may have to be concerned with however for one thing you don't get auto sizing behavior you know those wonderful little springs that you're able to set an ID that once you get the hang of how they work they enable you to define very easily an automatic layout behavior for when your view superb user resized how it be positioned and resize so when you take the view out of its parent it no longer gets auto sizing behavior you may have to worry about figuring out later when you show it again where does it go and it's super view what size should it be one thing you can do to very easily get around this is to take some sort of a proxy view and replace it substitute it for your hidden you you take the hidden view out and you put say an instance of NS view which doesn't draw anything but fault that'll do fine you take an instance of NS you set it up with the same spring settings in code give it the same origin and same frame size as your view and as the super view is resized as the user drags the corner of the window to resize things your proxy view will get the sex d auto sizing then when you want to hide show your hidden view once again you simply take the size and position of the proxy view swap them on to the octave hidden view reinsert it in super view so you've solved that problem another thing you may need to be concerned about however is your key loop interface builder an able to you to explicitly define a tabbing order for your controls when users hit tab or shift tab on the keyboard they may want to be able to navigate among your controls without using the mouse without clicking in them if you've defined an explicit key loop you take the view out of its parent well it's lost its position in the key loop so when you go to show it again you may need to figure out where does it go in the queue loop you'll have to to reinsert it explicitly where it belongs in the queue loop also of the view if you're using tooltips if you have any cursor wrecked or tracking rectangles that you've defined on those on that view you may also need to manage them when you hide and show the view now it's pretty rare that you'll have a given view that you want to hide for which all these things apply you may not be using all of these features so in general the remove from super view technique works quite well and usually don't have to worry about all this complexity however you may be wondering at this point well this is a little strange for for Coco a little atypical usually Coco make simple things simple right so can't we make this a little simpler can't we have a simpler way where we can just send a message to a view and ask it to hide itself let app kit take care of all of it something may be sort of like this well by popular demand this is a new API in Panther as alleles are mentioned in the cocoa update talk set hidden you'll notice the method signature is the same as the set enabled call that's available on NS control you use the same you send the same message to show of you again as you send to high that you simply very the bull parameter so too high to view you send it asset hidden message with a parameter of yes if you immediately disappears from the users perspective it's gone in addition any sub views that that view may have in any sub views that they may in turn have will also disappear those views are implicitly hidden by virtue of being contained within that view from your perspective as a developer however that view is still very much there if you look in the are in the Bews parents subviews list you will still see it there as a result it gets auto sizing behavior when its parent view is resized it also stays in the key loop you don't have to worry about it it's ignored when the user is tabbing through control so you don't go off into the weeds as OE said you don't have to worry about that and also app get automatically takes care of any tooltips cursor wrecks or tracking Rex you may have those are temporarily disabled while the view is hit and pretty much everything that you would expect to have to worry about app kit will automatically take care of for you in addition if you have a view such as an OpenGL view or an NS movie view that renders to a hardware surface that surface will be ordered out and automatically ordered back in as appropriate so on jaguar and earlier you can use the remove from super view technique we've seen some ways that you can deal with some of the details that you may have to worry about when you're doing that so that works fine i'm jaguar and earlier on Panther going forward pardon me we have the set hidden API that takes care of all of this stuff for you it works with any ns view class hiding views is dead simple in Panther we've given you a way to set a new kind of state on an object the concept of being hidden so it's only natural that we should provide some getter access or methods and we have to in this case the reason for this is the fact that i mentioned earlier that hiding a view is in effect hiding the entire view hierarchy that may be rooted at that view an ordinary control doesn't have sub views but you may have a box or some other type of view that has sub views when you hide that view set hidden I want to clarify is not a recursive operation when you send set heading to a view it's not like set hidden is then recursively sent to all its sub views we don't do it that way but rather we implicitly take that knowledge that that some super view of a view is hidden and we apply that as state to the view so you can think of is hidden main access / method as the atomic counterpart to set hidden the value that it returns reflects the state of the object you are asking and no other by contrast we also have this method is hidden or had hidden ancestor this answers the question that you probably more commonly we want to be asking about of you I don't care if it's hidden because somebody asked it specifically to hide or if it has some other view higher up in the view hierarchy that has been asked to hide I just want to know if it's hidden from my window and so is hidden or has hidden ancestor is the message to send when you want to do that we try to be concise when we choose method names but above all else we also like to be clear so is hidden or housing Manchester answers that question this is in some sense sort of a convenience method you could implement this yourself in terms of is hidden quite easily by walking up to view hierarchy but it's such a common question to want to ask we've implemented this for you and in fact that state is cached so it's a very inexpensive constant time operation to ask you if it's been hidden if it has a hidden ancestor whatever so that's you hiding in Panther that was view hiding in Panther now I'd like to move on to talking about optimizing view drawing John showed us some fantastic things that you can do with these spectacular drawing capabilities of quartz in cocoa there are some all of these things all of these drawing operations have an associated costs and whenever possible you of course want to avoid doing work that you don't have to do you want to avoid sending instructions to the courts graphics pipeline that don't really have to be processed maybe that drawing is going to be clipped out and if you can figure that out at a much higher level then your applications can run much more quickly and be more responsive when user drag things around in your view and so on so look at some techniques that you can use to optimize view drawing on all versions of Mac OS 10 and then we'll also look at some of the improvements we've been working on for Panther to help automatically improve performance for you whenever possible we like to try to make optimizations that require no work on your part that enable your applications to automatically inherit the benefit with full compatibility and without having to do any additional using this new API and so on but we also do provide new API that will see that can help you to more tightly constrain the drawing you're doing so that you're sending less down to the courts graphics pipeline and your applications can respond faster when the users dragging objects around and so on so first some things you can do on all versions of OS 10 the first thing I want to encourage you to do here today is to be lazy and you can go back until your manager that I said that I mean this in the most positive sense of the word is developers we all know that laziness can be a virtue aversion to doing work on your applications part anyhow can help you to avoid having having the system having the CPU having a graphic system do work that is unnecessary or redundant in particular and John touched on this earlier you may have noticed that n su has some display methods there is display display if needed display and wrecked display if needed in wrecked what these methods do is effectively demand that a view immediately display some portion of itself in other words this is a synchronous call by the time the message send returns to you that view has been drawn but instead instead of invoking those display methods directly although they are there for you to use when appropriate when that's really what you want we recommend that you mark areas of the view as needing display you can use set needs display in wrecked preferably rectangles are the primitive for invalidating parts of views when some state in the view or its object model changes and we also have the set needs display method which you can use more generically to just say well draw this whole view this view needs drawing at some point in the future this is of course a common paradigm that you'll be familiar with from other graphic systems other windowing systems by deferring drawing until later until say the end of the current run Luke cycle what you're doing is enabling the app kit to potentially coalesce to combine to satisfy multiple requests to draw a single view or part of a view with a single drawing operation if you tell a view to display itself three times it's going to draw itself three times if it receives 100 set needs display and wrecked calls it will draw itself at the end of the rim loop once so you set pneus needs to playin rec defer your drawing until later when possible one of the things that we've seen on the mailing list is people have recommended display use of the immediate display methods as a means to get around a problem we call the coalescing problem an inefficiency that occasionally appears in certain applications in the drawing system we've fixed that inefficiency in Panther and so we're encouraging you we're saying you no longer really need to do that those methods are there for you to use when when you need them when you really need immediate display when you don't use that needs display and wrecked and so on another thing that you can do to help us out is only invalidate areas of your views that really need to be drawn ask yourself is set needs display yes really the best that I can do oftentimes that is the case when you're a client external to the view object but if you're the view itself and some part of your state has changed well maybe you know that you only need to repaint some corner of yourself you don't need to redraw your text label maybe you just need to change your icon to a different state so rather than marking your entire self generically I know that's always the easiest thing to do just say I need display mark specific areas as needing display and that will save you drawing work when your draw recognize that gets called further on down the line in addition sometimes that people with an optimization has consolidated multiple rectangles on their end before sending a set needs display and wrecked method message to the app kit in order to avoid method call overhead let's say you may have a list of rectangles that you need to draw and you may simply take their union take a bounding rectangle that contains all those rectangles and make only one set needs display in wrecked call to that view telling it just draw everything in here we want to encourage you to send those individual Rex to the app kit now because as a panther and later we can now take advantage of that information to help you draw less a couple of other things you can do on the receiving end when your application receives the draw rect call note that you get a parameter draw rect gives you a bounding rectangle that asks you what to draw there are a lot of applications out there where we see people are just drawing the entire contents of the view for some views that's not very expensive that's their content isn't very complex but whenever possible you should try to only draw whatever's in that rectangle because that's all you're really being asked to do any other drawing you do is going to be clipped out automatically anyway so it's cheaper to eliminate it higher up in the drawing pipeline so here we have an example draw rect implementation supposing we have a view here say like sketch view if you've seen the sketch example that draws a list of objects has a set of objects that draws so we simply have a loop where we iterate over those objects and for each object we use the inner NS intersect rec'd method hopefully you're familiar with the convenience functions that are present in NS geometry date that's a good place to look if you're looking for geometry functions it's a function not a method and so for each object that we are potentially going to draw we ask well is it inside this rectangle if not well then don't even bother drawing it because it's just going to get clipped out doing this alone can save you a fair amount of drawing one other thing you can use is is opaque and this is somewhat mysterious maybe because of the name is opaque is defined by the NS view class there's a default implementation that returns no you'll note that most of the controls in aqua require some drawing to be done behind them in order to show their complete image you know things have rounded corners even if they don't really use transparency they don't cover the entire rectangle that they own they don't cover their entire frame rectangle with drawing by default therefore the app kit cannot assume that if you can be drawn without also having to cause drawing of all the views behind it because they may provide the background you may have the window itself trying to draw a textured metal background so that then your checkboxes checkbox image and then text can be drawn over that so we can't make that assumption by default but by overriding is opaque to return yes in your custom view classes you can potentially save a great deal of drawing you tell the app kit that you're going to cover all of the pixels in your you with a hundred percent opacity so you don't need any drawing to be done behind you this can obviously be a savings if you're implementing a drawing view a document view excuse me likes a sketch view and you have this large document view that's often covering your entire window and your user maximizes your window on a cinema display and and maybe your windows a metal window I mean you obviously want app get to know that it doesn't have to draw that entire textured metal pattern behind every part of your view that is asked to draw because it's never going to show through so if the first thing you do in your draw act implementation is to cover everything with a hundred percent opaque fill then over ideas opaque this one line of code will be one of the most worthwhile things that you've done in your drawing so what's new in Panther what have we changed one of the important things we've done in the view system is that we're preserving areas marked as needing display in greater detail in Jaguar and earlier you may have noticed by experiment that each view really only kept a single rectangle that was marked as needing to be drawn for each view instance anytime we would get a set needs display in wrecked method we were accumulates display and wrecked message we would cumulatively union that rectangle into any existing rectangle that was marked as needing drawn this could lead to unnecessary drawing in particular going to a more sophisticated more detailed representation of areas needing drawing enabled us to solve what's called the coalescing problem and you may have seen this in your app very simply you would sometimes see drawing of views that were in between other views that had been asked to draw within the same trip through the run loop for example we have a UI here where we have a check box in the upper left and a button in the lower right and a table view in between and let's say that this is wired up in code so that toggling the checkbox toggles the button to be enabled and disabled so when you toggle the checkbox it needs drawing to reflect its new state the button is drawing to reflect its new state and in the old system we would end up drawing the table view also because we have to clear the window background between behind that entire rectangle so the fact that we were only maintaining a single rectangle per view and only looking to that rectangle would sometimes lead to unnecessary drawing no more in Panther we've addressed this problem and we've enabled these to draw their contents much more selectively a lot hasn't changed however we try not to change things when we don't have two rectangles are still the invalidation primitive you still use set needs display in wrecked to mark areas of your view as needing drawing draw rect is still the basic method to override when you're creating a custom view subclass so that's where you put your drawing instructions it is still called once each time the hbu needs drawing even though we have this list of rectangles that need to be drawn we're only calling draw rect once to ask you to do all your drawing for that path in addition of course for compatibility we had to make sure that your existing draw rect implementations required no modification whatsoever for your apps to work on Panther so your applications i want to emphasize one had a great deal of the benefit of this automatically and but we do provide api for you to get at this information we're keeping around more information about what's dirty interview what needs to be drawn and should you take an interest in that information and want to use it to more tightly constrain your drawing it is available to you we have this new API this new method on NS view get Rex being drawn count it returns by reference of C style array and account of elements in that array of NS wrecked structures you call this on entry into your draw rect method and it gives you back a list of rectangles that more tightly bound the area needing drawing app kit automatically clips to that list of rectangles this is how we're compatible with existing draw rect implementation so even if you draw everything in your view you can even ignore the a wrecked parameter to draw rect you will be clipped down to be just the area that app kit wants you to draw also you'll note that NS wrecked that drawer ex existing NS wrecked parameter is still useful as a bounding box on the area on the set of rectangle so that you're retrieving so you can use it to do trivial rejection testing as we'll see and more easily reject objects very quickly that can't possibly be within the list that we need to draw also you don't need to worry managing memory for the reckless that's taken care of for you it exists for the lifetime of the draw rect invocation so to help make this more concrete hopefully here's an illustration we can look at let's say we have a view and we have a list of rectangles that has been marked have been marked dirty each of these corresponds to a set needs display in wrecked call for the view and there's some redundancy there what would happen on jaguar and earlier as i said is these would be coalesced into a single rectangle that would be sent to draw rect as its parameter that is the rectangle we would clip to and that is the rectangle that you're being asked to draw beginning on Panther however we have a list of rectangles that we are clipping to we have a list of rectangles that really more accurately bounds the area you're being asked to draw as I said you still get the bounding rectangle of those rectangles as your parameter to draw rect and you'll notice that there's some simplification that goes on this isn't just the same thing as the list of rectangles that's been marked as needing display we've eliminated the redundancy and also these rectangles do not overlap each other they may abut 181 another exactly but they do not overlap so there's no redundancy there so how would you use this API well here we have a sample draw rect method for view like a sketch view that draws some objects the first thing we do you'll note on entry into draw rect is we invoke get Rex being drawn count we get the list of rectangles that are being drawn and then we go into that same loop over the list of objects that we know we need to display in our document for each of those objects we first intersect it not with any of the rectangles in the list but with the single rectangle parameter to draw back with the bounding wrecked this is called trivial rejection test if we know that if that object that we're trying to draw does not intersect that overall bounding rectangle there's no possible way that it could be could intersect any of the sub rectangles that we're really needing to draw here so we can reject the object out of hand if it does pass that test we then go in and test it against the list of rectangles usually unless you have a pathological case of invalidation that is a list of a handful of rectangles and it's direct if the object intersects any of those rectangles you I hadn't draw it then so it's sort of a weeding out process in 3d graphics this is often called culling to distinguish it from the much lower level process of clipping where you're talking about actual fragments being drawn being clipped out at the graphic system level if we can eliminate things at the document object model level then we can eliminate a lot of potential processing it would have to be done for nothing because it's just going to be clipped out anyway Oh jump the gun on myself so this is a common thing to want to do to first do the trivial rejection test and then test against list of rectangles only if necessary so we provided a convenience API to make this easier to do needs to draw rect you pass it in a rectangle that bounds the thing that you want to draw or we want to find out if you really need to draw and if that method returns a boolean yes you go ahead and you draw the object so we've taken this code and reduced it down to this for the simple case you'll note that if you're running on Panther and later you know that this is this is really no more complicated than testing against the erect parameter that we get to draw rect we've got the same number of lines of code so you can take advantage of this to very easily weed out stuff with almost no effort that you really don't need to draw so maybe we don't have a list of objects what if we have sort of a monolithic object likes a PDF image or some other type of image how can we take advantage of this list of rectangles to draw more efficiently to reduce the amount of drawing we do to the absolute minimum well for an image for example we can look at that rectangle list as specifying sections of the image that we can sort of cookie cutter out and blit into just the areas that we're being asked to draw and that's what we do here in this draw rect method which is somewhat more complex but I want to focus your attention on just the parts in orange again on entry into draw rect we're getting the list of rectangles that we're being asked to draw then instead of because we only have one object to draw here right we only have one image instead of iterating over a list of objects we're iterating over the list of rectangles for each of those rectangles we've been asked to draw we basically figure out what part of the image if any intersects this rectangle doing this sort of cookie cutter operation will take that part of the image and just draw exactly that section on the screen which is very easy to do to draw sub rectangles of an image so by doing so we eliminate unnecessary drawing of parts of the image that don't need to be drawn so this is potentially looking at this this is a little more complicated than then more simple Jaguar and earlier draw rect method and the takeaway point here from today's talk is that I want you to go off with is not so much that you need to go and revise all your draw rect methods to use this new API do it in every single case it's not worth your time in every single case for the most part what we've done is solve the coalescing problem at the container view level at the level of glasses like NS view and NS box in the app kit that contain other views however if you have something like a document view like a sketch view that draws a lot of complex object maybe it may be an ical calendar view you know each of those objects in an ical calendar well it's got a nice rounded corners to it and it's got both still in an outline and it can be semi-transparent it's got text on it and maybe another little icon in there each of those things is costly to draw and you potentially have a lot of them and maybe you have this you can have this view size pretty large on your cinema display and be dragging calendar items around and for each drag for each time the mouse moves a little bit you're going to have to do some redrawing erase the thing in the old position draw it in the new position that's potentially a lot of drawing if you're dragging big distances in one jump that can be coalesced into a lot of drawings so cases like that complex document views that have a lot of drawing to do that have complex content those are cases where you would expect that you can get some benefit from this slightly additional complexity in your draw rect method there are some observations we can make about this last example for one thing since we're we're doing this sort of cookie cutter technique that I described is just drawing sections of the image we don't really need the clipping that app kid is providing for us app kit is enforcing clipping to the list of rectangles that were being asked to draw but clipping is an expensive operation potential it costs a certain amount to set up that flipping state in in courts and if you have a disjoint set of rectangles that you're clipping to that can be more expensive than say clipping to a single rectangle or better yet not clipping at all so if we don't need this clipping isn't there some way we can avoid the performance cost that's involved in setting it up and using it and in fact we can now with a new API in Panther wants default clipping and as you has a default implementation that returns yet you can override this to return no to tell the app kit that your view is going to be responsible for itself that you promise you're not going to draw anything that lies outside of those recs you're going to draw exactly within those boundaries and by doing so you can reap the benefits of not having that clipping set up for you whether this is important than or not again whether this makes a difference in your application depends a great deal on on how often your view is asked to draw how much it's asked to draw how complex its content is but this is something that's there for you to try out if you if you're really focusing on optimizing drawing performance in a particular view class there are some different possible implementation strategies that this wants default clipping method frees you to use for example one thing that we could do is we did with the image view we can make our outer but outer loop be over the list of rectangles to be drawn rather than some list of objects to be drawn and we can choose to clip we can enforce our own clipping to one rectangle at a time just because you override wants default clipping and you return no doesn't mean you can't enforce your own clipping and maybe you want to use simple simpler clipping clipping to a single rectangle of time again this is very application dependent depends on the content of your view you have to use your own judgment we encourage you to use profiling tools like quartz debug and sampler to figure out well is your appt spending a lot of time drawing first of all and which views is it spending a lot of time drawing during different activities figure out where you can get the benefit and then try using these api so we've seen a few new API is and Panther that we hope you'll find useful we have the concept of being hidden that is now supported makes it very easy to hide views on Panther going for written we also look at some techniques that you can use to almost as easily hide your views in Jaguar applications we also introduced some new api's that you should be aware of that can enable you to do less drawing to avoid unnecessary or redundant drawing in your in your custom view classes these are there for you to use if you want to I don't want you to have to worry about them if you're implementing a simple little custom control that just draws a few things covers a very small area you're probably not going to get much benefit from these you don't need to worry about it and particularly for new Koko developers who may be with us today we want to emphasize that you can implement your draw rect methods as before you can ignore this list of rectangles you don't even have to ask for it you can draw and we will automatically clip for you but it's there for you to use if you'd like to and with that I'd like to invite John Randolph back up to do the wrap up after which we'll be happy to take your questions John okay thank you troy and i will just mention that all we talked about today about optimizing your view drawing the easiest way to optimize all your drawing is just deploy only on new g5 we're working on it believe me there's people there are people in production who are burning the midnight oil on that so we talked a little bit today about how to draw using classes in the covo framework and how that got different for how that's changing for Panther and I did show you one example of how to do some fairly sophisticated things without a lot of code so we'll just repeat the cocoa mantra here simple things simple and poss and complex things possible so let me mention the road map here some of these we've already gone past here are the ones that are coming up that you might be interested in cocoa performance techniques tomorrow morning cocoa tips and tricks which has a lot of the details about how particular clever things were drawn in some some cocoa apps that apple has put out Doug Davidson will be giving you the in-depth talk on what's new in cocoa text in session for 27 if you want to talk to us about what you'd like to see in cocoa what you like change and so forth you can come to the cocoa feedback forum who to contact about this you can reach me often on the cocoa dev mailing list if you have questions about our 2d drawing API and our 3d drawing API Travis Browne is the graphics and imaging evangelists and apple developer relations his boss John Glenn xia is the guy to go to for other questions having to do with how how we're evolving our software offerings John is also the guy to go to to get a UI review of your app he is still the the user experience evangelist and you can also send your cut your cocoa questions to developer tech support DTS at apple com so there's also this mailing list called Coco feedback at grouped apple com I know the manager so the cocoa frameworks teams read that list most of the engineers do too if you want to get a into our collective consciousness then you should you can send it a cocoa feedback I would suggest that you also file feature requests at bug reporter apple com and for more information on cocoa there's a host of documentation on any machine that's got the the developer tools installed there is well the applicant reference and so forth there's a lot of sample code right there on your machine and developer examples app kit there's a lot of other samples up on the dts website so if you go to developer apple com click the sample code link click cocoa after that a lot of examples and more being added all the time today there's a very good selection of cocoa books out this was not the case two years ago today there are a lot of them to choose from I see some of the authors in the audience here and let's see a couple more book titles here I suggest that you join the cocoa dev mailing list it's a very good place to get help when you're learning how to how to code in cocoa or when you're going for more advanced subjects and I also recommend the mailing list that's run by our friends at the Omni group which you can get to at Omni group com