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