Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2007-161
$eventId
ID of event: wwdc2007
$eventContentId
ID of session without event part: 161
$eventShortId
Shortened ID of event: wwdc07
$year
Year of session: 2007
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2007] [Session 161] Making Your...

WWDC07 • Session 161

Making Your Custom Controls, Icons, and Artwork Resolution Independent

Mac OS X Essentials • 48:06

If your application uses custom controls or artwork, make this session a top priority. Modern displays vary in size and pixels per inch. Find out how to design a rich, scalable user interface for your application. The session will discuss guidelines for revising icons and artwork, new functions to adopt, testing strategies, performance concerns, as well as common problems and solutions.

Speaker: Guy Fullerton

Unlisted on Apple Developer site

Downloads from Apple

SD Video (163.8 MB)

Transcript

This transcript has potential transcription errors. We are working on an improved version.

Good afternoon. Welcome to the Resolution Independence session. My name is Guy Fullerton, and I'm going to show you the basics for what you need to know to make your application resolution independent. But before I go into that, let's talk a little bit about why. Lots of technology industries keep giving you more and bigger for your money, and display hardware is no different, right? In mass storage we went from megabytes to gigabytes to terabytes. In optical media we go from CDs to DVD to Blu-ray. And LCD displays keep getting denser and denser and denser.

And if we look at what this does, imagine a 23-inch display at a hundred DPI, well, you can also imagine two hundred DPI, or maybe three hundred DPI. So really, you can think of high-resolution displays as kind of HD for the computer desktop. Well, what are we going to do with all of that increased density? So again, here's a hundred DPI display on a 23-inch display.

If we switch that to a 200 DPI display, wow, that's a lot of screen real estate, right? And a lot of people might be drooling over that if you're used to doing big image processing and so forth. But if you were actually to sit in front of this 23-inch display at 200 DPI, you'd probably find the text really hard to read. So we've got to do something better with that increased display density. So to show you what I mean, let's zoom in on the top left corner of that display.

Okay, so this is Mac OS, well, this is actually a Tiger screenshot as you know it. And if you were to zoom in that close on your own display, you'd see that it's pretty pixilated. Now normally when you're at a regular viewing distance it looks pretty good, but when you start to scrutinize it, you can see some pixilation on the text and the graphics and so forth. So if we instead use all of that increased display density to increase the sharpness of the text, you can get a much better looking interface. So again, this is normal resolution, and this is what high resolution can be.

So let's talk about a time table. In the past we've told you that we expect your applications to be resolution independent in 2008, and you can see we're already moving toward that direction with our most recent MacBook Pro model that's out. It's a 17-inch model that now has an option for a 133 DPI display. Now a 133DPI is about the maximum display density that most people can comfortably read, and for me it's even a pain. So we really need to get software support out there so that we can go to even denser displays in the future.

And display density isn't even the only reason why you might want to go resolution independent. Any time you might want to blow up the interface to make it look crisper and sharper is a perfectly valid reason, even if it's on normal density hardware. Say for example you have a 39-inch cinema display, and you want to give a demo to a bunch of people in a room, and they're fifteen to twenty feet away.

Well, you could use resolution independence to zoom up your screen, and not use the sort of built-in zoom that we already have available on the various OS releases, but zoom it up and make it look great at the same time. Similarly you can think of some possible accessibility solutions for low-vision users that take advantage of resolution independence as well on normal displays. So now I want to go into some terminology that'll help understanding how to adopt.

The first is the notion of points versus pixels. In traditional Mac OS development, most developers think of points and pixels as the same thing. To explain what I mean, let's think about both a text example and a graphical example. When you request a 13 point font, generally that draws about 13 pixels tall.

If you have an image that's 100 pixels by 100 pixels, generally you plot that in a rectangle that's 100 points by 100 points. And that's just the mindset we've had for a long time. We need to break that mindset to be resolution independent. So this now means that points and pixels may not map on a one-to-one basis.

Now the good news is that even though you need to change your mindset, you generally don't need to change your coding practices, right? All of your .nibs are already working in terms of points. Just think about when you're laying them out that you're laying them out in terms of points and not pixels, and similarly when you have code that positions or resizes views, think of that as resizing them in points.

Now it's not all free. Sometimes you actually do need to think in pixels to get an optimum user experience, and I'll talk about some of those details a little bit later on. But this isn't really anything new. A lot of applications that print already do the same kind of thing when they print, right? They might look at the density of the printing device, and choose a different hairline thickness for a particular line or something like that.

So a lot of your code may already do this kind of thing. So let me show you a graphical example of what I mean. At a normal resolution, when you create a push button, you ask for it to be 20 points tall, and at a normal resolution it ends up being 20 pixels tall.

Now let's go to a 3 times resolution. Okay, here we have a 3 times resolution push button that looks a lot crisper. We got crisper text, crisper graphics. You still request that as a 20 point tall push button, and it just so happens that it takes up 60 pixels of vertical screen real estate.

So the next piece of terminology is the concept of a scaling mode. A scaling mode is a mechanism for you to mark a particular window as resolution independent. And the best scaling mode is called framework-scaled mode. This is the way your window can look its best. It's going to give you great looking text, and great looking graphics, all the system controls are going to look fantastic in there.

All of your custom art, however, needs to be revved to look fantastic in framework-scaled mode. Now don't get me wrong, your custom art is still going to look okay, it's still going to get scaled up to match the system controls in terms of size, but it won't look as good.

It's going to be interpolated or a little fuzzy or grainy, and it may stand out a little bit. Now the way we make this work is we see your request for a window, window size, and we interpret that in points. So for example, if you request a 200 point tall window at a 3 times scale factor, a 3 times resolution, that window's actually going to be 200 pixels tall, and the window backing buffer is going to be 200 pixels tall. So since we've got more pixels to render your text and graphics in there, we can make them crisper, and then when it's blit to the screen, it ends up looking great.

The next scaling mode is called magnified mode, and this is for Carbon apps. Unmodified Carbon windows, when running in resolution independence mode, get magnified windows. These magnified windows match in terms of size to framework- scaled windows, but they don't match the crispness. The way magnified mode windows work is your backing buffer is the same size it's always been, even at regular resolution. And the window server scales up those contents, so it's going to be interpolated, excuse me, interpolated or a little bit grainy. Now you can actually get framework-scaled windows in Carbon, in Carbon applications, but I'm going to talk about that in a little bit.

The third mode isn't really a resolution independence mode, but I like to include it here. It's called small mode, and the reason I have it crossed out here is because A) you don't want to be in this boat, and B) we don't really support it on Leopard. A small mode is a fallback compatibility mode for applications that don't work under resolution independence.

As we test various applications, we've found that some of them make very bad assumptions about points equaling pixels, or they do some of the bad things that we'll talk about later in the session, that prevents that application from looking at all decent in a resolution independent mode. And therefore that app, if it got to customers, would need to run in small mode. It's essentially a completely unscaled window. It's 1 point per pixel, but it's going to look very tiny compared to resolution independent windows.

So when I say this isn't supported in Leopard, what I really mean is that we have no way for the user to choose to use small mode for your application, and the OS is not automatically forcing bad applications into small mode. When resolution independence actually becomes a user feature, we're very likely to put a switch in place.

Think of the Finder Info window switches where you can run something in Rosetta or a 32-bit mode. We'll probably allow the user to pick your application and say I want to run this in small mode because it's got some bug when it's running in one of the other modes.

So the next thing I'd like to talk about is the scale factor. Simply, the scale factor is the ratio of pixels to points. So at normal resolution, let's say we've got a 3 point tall window. Well, that window's going to be 3 pixels tall. But if we use a 3 times scale factor, that window will end up being 9 pixels tall.

To set the scale factor, and effectively turn on resolution independence, we put a new feature into Quartz Debug, and I could talk to you about it, but it's probably easier to see what's going on with a demo. So now I'd like to bring up David McCloud to run us through Quartz Debug and a few other things.

( Applause )

Thanks, Guy.

So like Guy said, I'm just going to go through a few of these topics in action. Let's get right into it. The first thing I want to show you is how to set the system scale factor to turn on resolution independence. So you go to your Leopard build, into your Developer directory, in the Applications directory, and there's an alias to it in Graphics Tools, they moved it to Performance Tools recently, but you just launch Quartz Debug. And up in the Tools menu there's a Show User Interface Resolution window that you can bring up.

Well, let's just turn on resolution independence by setting a scale factor to 2.0. You saw the doc jump a little bit there. Generally, applications before they'll become resolution independent and start using the scale factor have to be restarted. The doc is a kind of a special exception cause it's running all the time like that. You'll notice in the background the Finder is unaffected, it stayed at the scale factor it was running at. So to get this changed in the Finder, I'd have to relaunch it.

So I'm just going to launch TextEdit here. So you see TextEdit has come up and the window is much larger. There's actually, like Guy said, a bigger backing buffer, there's a lot more pixels sitting there waiting to be filled up by TextEdit. You'll notice the menu bar is bigger, it's in its own window. It's in its own window, and it's also been scaled up, the big, bigger backing buffer.

And you'll notice the controls are very sharp, the text on the controls very sharp. Just zoom in on one here, zoop. So you can see it's using as many pixels as it can, it's packing them into that backing buffer. And let's just open a document quickly, you can see text, text renders very crisply.

So the thing about TextEdit we all know, TextEdit is a Cocoa app. Cocoa apps run in framework-scaled mode by default. So most of the work you have to do is done for free. Carbon, like Guy said, is a bit more work. So let's run a Carbon app. I downloaded the BBEdit demo here.

So I'm not picking on BBEdit. I love BBEdit, I use it every day, I think it's a great text editor. But it's a good example of a Carbon app that's running in magnified scaling mode. And what that means is the backing buffer is just the size it would be at 1.0, and the bits are blown up by CG at the last moment to fill up the available area.

You can see that it's not as crisp, the controls are a little blurry, text will be a little blurry. Let's just open the same text as we had before. I'll slide it over so we compare TextEdit and BBEdit at the same time here. Let me zoom in. And you can see, things are a lot more fuzzy in BBEdit running in magnified mode than TextEdit running in framework-scaled mode.

Also like Guy said, you can do magnified mode in Carbon apps. There's a little bit more work, he's going to get into that. And I'd like to let him do that. Guy?

- Thank you, David. Okay, back to the slides, please.

( Applause )

Okay, so let's talk about how Leopard supports resolution independence. In Leopard resolution independence is still a developer feature.

We're in the process of revving all the system controls to support high-resolution artwork. The text system supports high resolution drawing, and we've expanded a number of API sets to be more resolution independent. Cocoa, in fact, has even added some system controls in the last couple of releases, and, of course, all those new things are resolution independent.

Icon Services has been revised to use yet another larger icon version, so that we can display great looking icons in a resolution independent Finder and other places. And, of course, even though we're changing all the system underneath, we need a way to test that. So we picked a few of the applications that ship on Leopard, and we're making those resolution independent savvy as well.

So if you fire up Quartz Debug, change the scale factor, log out and log in, you'll see a resolution independent Finder. Now interestingly, that will reveal some of the resolution independence bugs in Leopard. We're not done, we expect you to see a lot of bugs, so please write those up when you see them.

We also added a bunch of new standard art, which serves a variety of purposes, and one of those main driving reasons was for resolution independence. And we did that via NSImage. NSImage has a named image mechanism which traditionally looks in your bundle for particular named artwork. In some cases it actually looks for system- supplied artwork, so we expanded that batch of system-supplied art with a wide variety of things.

So if you need the standard info icon, or the color panel icon, or so forth, you can ask for that from NSImage. Now there's one important thing to note here. Let's say you want the network icon, that's fantastic. If you want the network icon, you can ask for it and draw it.

But let's say you need a big gray globe for some reason. Well I don't want you to ask for the network icon just because it's a big gray globe. I only want you to ask for the network icon cause you're going to display the network icon. So, please, do not use the standard art for what it looks like, use it for what it's named. And we're going to expand the documentation on that, telling you exactly what purposes the standard art is used for.

And, of course, the reason for that is it allows us to change the standard art in system versions without breaking your application's usage. Some of the standard art we're putting in place are what we call template images. This is essentially black-and-white artwork that's intended to be colorized, or sometimes engraved or chiseled, or various other effects added to it for display on certain system controls. So there's a bunch of those templatized images as well.

Now as we've mentioned a couple times, Cocoa automatically gets framework-scaled windows, which means adopting resolution independence in Cocoa is pretty darn easy. Most of the Cocoa controls automatically scale up with great looking text and graphics, but your application's own content may need to be revised to look good.

And for the most part, your Cocoa app will just run, and it'll just look great, right? So I highly recommend flipping on a higher scale factor, running your app, and seeing what needs to change. It looks pretty good. The one major exception in terms of functionality is NSQuickdrawView.

QuickDraw is implicitly tied to the notion that points equal pixels. And in resolution independence mode that's just not true. So QuickDraw just doesn't work correctly in resolution independence, and we have no plans to change that. So if you're using NSQuickdrawView, you're going to need to rethink that implementation strategy in your application.

For Carbon the story's a little bit longer. Carbon app windows come up in magnified mode. Carbon apps can adopt framework-scaled mode, you just have to jump through a few more hoops to do that. Okay, there we go. The first thing to do is eliminate all QuickDraw drawing, and what that allows you to do is switch on HIView compositing mode. Once you've done that, now it's safe to turn on the framework-scaled attribute for your window, and that will bring up your Carbon windows in framework-scaled mode. Once you've gotten through all those hurdles, the adoption strategy is basically the same as a Cocoa application.

We added a couple new APIs to Carbon to support resolution independence. The main ones have to do with NSImages. I'm only showing one of them here, and that's HIViewDrawNSImage. So Carbon apps can use the named NSImages, fetch your desired piece of artwork, and draw it in a particular HIView coordinate space, and that works just fine.

It's a great way for Carbon apps to get access to the standard technology, it allows some Carbon developers to start getting their feet wet with Cocoa, and so on. If you do need finer grain control over images on the disk, you can always go down to a lower level service like ImageIO, that'll allow you to look at various attributes of the image in the file and so forth, and you can make different decisions if you need to.

Dashboard has its own notion of framework-scaled mode. If you set a scale factor, log out, log in, and bring up Dashboard, you'll see that the widgets have scaled up. Now if a widget uses any system controls, generally those are going to look pretty good. But if a widget uses custom art, which a lot of widgets do, that custom art is scaled up, just like custom art in any other sort of app, and can look grainy because it's interpolated.

So if you went to rev your Dashboard widget to support great looking art and resolution independence, you can take advantage of some CSS media query extensions that -webkit added that allow you to query the scale factor, and make intelligent decisions about which images to supply as part of your widget. And, of course, right now these are private extensions. You're welcome to use them, we're going to work with the standards committee, when we can get them standardized, you'll see similar extensions, just without the -webkit in front.

So now let's take a little step-by-step look at how an application can become resolution independent. The first thing you need to do is dig through your applications bundle and find all your artwork. Once you've got a mess of artwork, start asking yourself questions. Are you using that piece of art to do something that the system already can do for you? If so, that's a great time to adopt a new standard control.

Maybe you have custom art in your bundle to do the Info window button. Well, now we offer an Info window button, so if you're running on Leopard, you should use the standard art for that. Additionally, Icon Services already offers a ton of different icons that you might be able to use. Not everybody knows what's available, I strongly recommend you take a look at the icons.h header to see what's available there.

And the HITheme APIs are a great way to draw the Aqua interface when you're not in a control, or not in a standard view. Now as we looked at our own applications, sometimes we found a piece of art that was just a simple rectangle fill, or maybe it was a frame or something like that.

Well, it turns out that those are really easy to draw procedurally, so we just switched to that procedural drawing. That allows you to scrape that old artwork out of your bundle, and you don't have to have your users downloading it, you don't have to maintain it and so forth. Simplifies your builds and so on.

So once you've examined all your art, toss out anything you don't need. Now, of course, if you need to run on older system versions, you're going to need to leave that art in. But at least you get to ignore it for your Leopard code paths. Anything that's left needs to be revised, or at least examined for resolution independence.

So when you start revising your custom artwork, there's one main question. Do I go to bitmaps? Do I use vectors? What should I do here? Well, the answer is it depends. Both formats have advantages. Vectors are very small, both on disk and in memory. And the cool thing about vectors is they can scale up to any size and still look pretty good.

It also, because vectors are easily manipulated, it's a little easier to write the code that deals with them. As you'll see from bitmaps, well, I'll talk about some of the details a little bit later. Let's just jump right into bitmaps. Some UI designers love bitmaps. There's just no question, they're better for some things. UI designers can pixel tweak them. A lot of times vectors can't get them exactly what they want, so they're going to demand bitmaps. Of course, bitmaps are the only way to get something truly photorealistic.

And sometimes vectors are not appropriate for very small uses. The best example are file system icons. Our HI guidelines say that your smallest file system icons for your documents are not supposed to be as detailed as the large icons, they're not supposed to include all these details which when shrunk down would muddy the overall image. So these are times when you really do need to use bitmaps.

So if you decide to use PDF, or another vector format for some of your art, here's a couple of recommendations. We found that it's fantastic for doing monochrome art. The named template images that we're adding to NSImage, most of those are, actually I believe all of those are PDF. We found it works fantastic for those kinds of situations.

And for bitmap, as I mentioned before, Icon Services icons can go all the way up to 512 by 512. Make sure you use lossless compression, and we recommend that each piece of bitmap art is delivered in two resolutions, a normal resolution piece of art, plus a 4 times the resolution piece of art.

And we've also found that multi-image .tiff files are a great way to deliver that art. It turns out that .tiff can support multiple images per file. So in a single file you can have a normal resolution, a 4 times resolution, heck, you can have 4 or 6 resolutions if you need to in there. Just be aware of the size concerns.

If you have a multi-image .tiff that has a normal resolution and a 4 times resolution piece of art in it, that's going to be about 17 times as big as a single image .tiff at normal resolution, right? So if that size is going to be a problem for you, let's say you have an app that needs to be downloaded as quickly as possible, maybe 4 times art is not the right format. Maybe it's right for you to do a normal resolution and a 2 times resolution, and that's perfectly fine.

NSImage and ImageIO completely support multi-image .tiff files, so you're going to get full support there. If multi-image .tiff is not really the right solution for you, you can always use .png or various other formats. If you use one of those other formats that doesn't support multiple images per file, it's a little bit harder to write the code to deal with those. You're going to have to determine some naming convention for your own code, and figure out which image to load based on the scale factor. And it's just a little bit more hassle that you can let the system do for you if you use multi-image .tiff.

So once you've generated a whole bunch of revised art, it's time to adopt it. Some things are really easy. You can drop your new Icon Services icons directly in your bundle. If you're not already using NSImage to draw your other art, go ahead and use it, it fully supports .tiff, .png, PDF, and so forth. And again, if you need finer grain control than what NSImage provides, feel free to use a lower level API.

We added a few tools in Leopard to make your life easier. Preview now correctly displays all of the individual images in a multi-image .tiff file. We've revved the Icon Composer application that lets you build Icon Services icons. It now supports 512 by 512 artwork, and adds a few other convenience features.

And we have a command-line utility called tiffutil, that allows you to take individual .tiff files and merge them together into a single multi-image .tiff file. And it has a -cathidpicheck argument that you can pass to it that makes sure each of your, each of your individual .tiff files is in a format appropriate for NSImage.

Once you've adopted all that art, it's time to test. Of course 1.0 scale factor is normal resolution. You'll still want to test that because you changed your code paths, but the real meat is when you switch to a higher resolution. So switch to 2 or 3, but make sure you also test the nonintegral scale factors, particularly 1.25 and 1.5. These we think are the key scale factors that will work well on a wide variety of future displays.

And in fact testing at 1.25 and 1.5 is going to expose a lot more bugs than the integral scale factors. Some of the pitfalls I'll talk about later really only happen at the nonintegral scale factors. Now Tiger also supported resolution independence as a developer feature, but we've come a long, long way since then.

So please test on Leopard. Leopard has all of our latest bug fixes for resolution independence, it has more standard art and so forth. So you're kind of wasting your time if you're testing resolution independence on Tiger, cause we're never planning on making it a user feature on Tiger. And when you see bugs in your application, fix them. And if you see bugs in the system, please report them to us. We love to fix any of the bugs you can find.

Now when you find bugs, generally the first step in fixing them is using the scale factor in some fashion. Sometimes you'll do it implicitly, sometimes you need to do that explicitly. So we have a couple different ways of fetching the scale factor. The preferred way is to ask a window what its scale factor is. In theory in the future we might want different windows to have different scale factors.

Right now there's only a global scale factor, but still you should use the window-based scale factor fetching APIs for now. If you do for some reason need to know the global scale factor, we also have a couple APIs to use for those. And for both Carbon and Cocoa applications these APIs are available.

Okay, so let's talk about some of the common pitfalls that you might see while becoming resolution independent. The first general class of problems are coordinate system mismatches. Essentially that's where you're assuming points and pixels are the same thing. There's one important thing to note here. Window coordinates in Cocoa are pixels, whereas window coordinates for views in both Carbon and Cocoa are in points.

So if you're ever taking window coordinates and trying to apply them to the view, you're going to need to do some conversion in there, or vice versa. If you're not doing this conversion, generally what these problems manifest as is your window coming up too small, or a control coming up too big, or a control that's partly scrolled out of view, or something like that.

So if you do see one of these problems, the key is converting. We have APIs that you can convert between points and pixels in various ways. You can ask an NSView to convert to window coordinates, so to go from points to pixels. Similarly you can ask NSWindow to do certain conversions. And for Carbon applications we have a number of conversion APIs for points, rectangles, and sizes.

One concrete case that we see quite a lot of coordinate conversion problems is with OpenGL. Anybody that uses OpenGL directly without going through some of the standard system behavior will probably need to do a conversion when they set the GL surface size. The GL surface size is always interpreted in pixels.

So for example, if you have a view that's 100 points by 100 points, and it's running at a 2 times scale factor, and you ask its glViewport to be 100 by 100, that's going to be 100 pixels by 100 pixels, so it's only going to take up a tiny portion of your view.

Of course, to fix this, you just need to convert from points to pixels, and then call the glViewport API. So now to show a few problems in an application bring up, I'd like to invite David back up, and he'll run you through that.

( Applause )

Thank you.

Forgot to clean up here, so I'm just going to quit BBEdit and TextEdit, we're not going to mess with them any more. But we're going to leave the scale factor window up. Okay, I wrote a small sample app that shows some of the good things that you get for free in your app when you turn on resolution independence. And it also shows a couple of the pitfalls, and we'll just go through and fix those really quick.

So I have a little sample app, we'll just run it at 1.0 without resolution independence turned on. And it's just a simple app that has an OpenGL view, custom control, a couple of system controls. And I also have some bitmap images. If you look down here in the bottom right, it's a simulation of a little palette to turn on and off some of the sides of a cube.

The custom control is controlled by a few system controls here, you can, I'm not an OpenGL expert, I thought I would try and do it for fun to show some of the OpenGL pitfalls. But interestingly, let me show you if I change the user interface resolution to 1.5, and run that app again, you see it comes up bigger. All the system-provided controls, like the sliders and the circular slider, the dial-looking thing, they look pretty good. I can zoom in on them, they look crisp, just like I showed you earlier.

Interestingly, when I first started writing this, previously when we did this in Tiger, the OpenGL view was a problem even in NSOpenGL. And I was going to show you guys how to fix that, but since you're using NSOpenGL view already, it just works. They've gone ahead and fixed that, that's one of the bugs we've addressed in Leopard. You can still, if you're using your own OpenGL surface and managing it on your own, not through NSOpenGL, or NSOpenGL view, you're going to go ahead and do that tweak that Guy suggested earlier in the slides.

You'll notice also something else that is working nicely is my custom control. When I made the custom control, I kept resolution independence in mind. And it's just an arrow and a circle, so rather than having some bitmap art and doing it in a custom fashion, I decided at that point I'm going to need this resolution in the future, I'm going to use some procedural drawing, and I'm going to draw this in code.

So this just uses NSBeziers, you can do it with core graphics and use Quartz directly. But these work nicely without you doing any work. So that's a few things that look right, and are working well. How about a couple of things that aren't working right. Let's go down here and look at some of the things on the bevel buttons here.

So you can see the art doesn't look great when it's been expanded. That's cause I had some one X art in a bitmap, and I put it on a bevel button. The other thing that's actually more important is a pixel rounding problem. If you look at the bevel button that has the green icon on it, the lines of the bevel buttons don't look right, they're not like a palette, they're supposed to be edge to edge. So we're going to go ahead and fix some things with this palette. Quit the app.

First let's go ahead and look at, I'm just going to quickly show you that custom view I made. It's the orientation view. It's pretty simple to go ahead and make a circle, fill it in, and then make an arrow, rotate it the right direction, and fill it in just using some Bezier stuff. I come from a Carbon background, so I just took a look at how to do this with Beziers, I haven't used Beziers a lot, and it was very straight forward. I've done a lot of core graphics stuff, but this is just as simple using Quartz.

So let's go fix those bevel buttons that I used to make the palettes. So the first thing that I want to address in there is the alignment, where that problem was where the edges weren't touching each other. I actually have a method in here called alignFaceButtons on the controller of the window. So once the window is loaded from the .nib, I have some information about where those bevel buttons are, and I'm going to, I just cycled through them and quickly fixed them, and I'll show you how that works.

I get the scale factor from the window, and if it's not 1.0, I'm going to do something to fix the problem. If it's 1.0 it's going to work great. I could refine this to check and see if it's nonintegral or not. Like if it's going to work fine at 2.0 because it won't form any pixel cracks, because in this particular case those bevel buttons are 45 pixels in size, they're squares. When you run at scale factor of 1.5, you multiply 45 by 1.5, you know you're going to get some integral value in there, and that's where the problem with the alignment comes in.

So for each of those bevel buttons, I go through and I get its frame. And then I convert that frame to window pixels to find out how many pixels there are. And that's the nonintegral value that's causing the problem. So once I get those nonintegral values, I go ahead and I convert them back into a rectangle that's been integralized in pixels on the window. So that'll make them line up afterwards. I put those, I convert those back to points. Now we started out with integral points, converted them up to pixels where they were nonintegral, made the, made those integral, I'm converting them back, you're going to get nonintegral points.

So that later on when the system goes to draw the bevel button, it's going to up convert the pixels to do the drawing of the bevel button, that's how the control drawing works in the system. And it's going to convert those, and they're going to nicely convert into nice round integralized values. And I go ahead and I set the frame on them. So I've tuned on this method, I'm just going to run the app again and see if it worked.

I zoom in close here, and you can see without doing much to it, I've lined them all up and they look like a palette should look again where all the edges are touching each other. An important thing to note here is you don't have to do this for every control on your window. In this particular case, I need them to line up because they're arranged side by side, and I need the pixel alignment.

But if you look at the other views, like the system views, or even the custom view I wrote, those might not be at perfect integral values, but they draw quite fine. So you don't really need to mess with them, I've only gone through and adjusted these ones that are special case. So this is not something you have to do for every view in your whole window, or else we would go ahead and do it for you.

I still have the problem where the icons don't look great. I know they're not the fanciest icons in the world, I'm not an artist, I'm an engineer, and I made them. Apple does have some good artists, so they'll draw that nice UI for you. So I'll go through, and I'm going to show you how to put some 1x and 4x art in so that the, I don't know if they're going to look any better, cause I also drew them.

( Laughter )

But I'll at least show you that you're going to use different art.

I guess I should stop scaling. So I'm going to use the tiffutil that Guy mentioned earlier, and I'm going to take, I have some different versions of the art, and I'm going to catonate them together and make a multi-image .tiff out of them. So it's called tiffutil, and I'm going to use the special flag, -cat. -cat means catonate, I'm going to catonate them all together, but there's a special version of it for high DPI stuff called -cathidpicheck.

I'm going to work on my yellow image here, and I have a 32 by 32, 72 DPI vers2ion of my image, that's the one you've been seeing so far. And I'm going to catonate another version I have. It's a 128 by 128, that's 4 times the number of pixels on each axis. The important thing is it's a 288 DPI image, so it's 4 times bigger in pixels, but it's a 288 DPI image. So that means it's the same size, it's just that there's more pixels packed into the image.

And I have to output that to a file where there are, where it's going to end up so that it's a multi-image .tiff with both the images in there. This is the image I've been using so far on my project, so we're just going to recompile the project, and it's going to start using this newly overwritten image.

Two images written, there's a new file. So that's .tiff utility telling me everything went well. If you do something like you don't have the dots per inch set properly on your 4x image, or your 4x image is not some other way 4 times bigger than your1x image, tiffutil will tell you what you need to correct. So I've gone ahead and I've changed that one.

Let's go back, run the app again. And when you look at it now, you can see that everything except for the yellow icon looks the way it used to. The yellow icon is drawing with the higher resolution art. I can zoom in on it, like I promised you, it's not perfectly beautiful art, but you can see that there's more detail in it to be used.

So it's taking the 4x image, and it's scaling it down to fit it into the 1.5 scaled interface. Rather than the other ones, which are taking 1x art and upscaling it, that means it has less information to base the new upscaled image on, and you end up with a better result. And that's it for me. Let's go back to Guy so he can finish up.

( Applause )

Thank you, David. So one important point from David's presentation is that he was always, he was already forward thinking about resolution independence. He made some art as vectors, because he knew it would be resolution independent in the future. Some of his custom art was already vectors, and he was also using standard system controls, which is always a great thing to do, cause that means we can fix bugs for you.

Okay, so let's talk about more of the problems you're going to see. This is something we like to call pixel cracks. Pixel cracks, there's a couple different kinds of pixel cracks in this screen shot. The grid that's in the background of the window, that's one sort of pixel crack.

There's also a horizontal line on the popup button that crosses just above the word sizes there. And the OK button has a bunch of vertical lines on it. So these occur when adjacent images, or images that should be adjacent are being drawn on point values that do not map to integral pixels.

So let me show you that in a little bit more detail. Let's say we have a 5 point tall green line that we want to draw directly above a 5 point tall red line. At normal resolutions, 5 points is 5 pixels, so you get a perfect 5-pixel tall green line above a 5-pixel tall red line.

Now let's take a look at what it looks like if we were running at a 1.5 scale factor. Well, 10 points at 1.5 scale factor amounts to 15 pixels. So our 5-point tall green line turns into a 7.5-pixel tall green line, and the same is true for the red, the red line Now you can't actually draw in 7.5 pixels, so that means both of those lines are going to end up touching 8 pixels. And they're going to touch a shared pixel, and you're going to get a blend of red and green, which is going to look somewhat orangey brown like that. And that's what a pixel crack is. So let's talk about how to fix that.

Okay, so the first thing we need to do is we want to make sure that that red line and that green line both draw on integral pixel boundaries. So we need to calculate what a 5-point tall green line would draw into in terms of pixel boundaries. And like we said, it's going to be 7.5 pixels tall. So we need to round one way.

We're just going to arbitrarily pick to round that green line up. So we're going to round 7.5 pixels up to 8, and we're going to round, for the red line, 7.5 pixels down to 7. And then if we draw in those pixel spaces, you end up getting nicely joined images. Now the green line is a little bit bigger than the red, the red line, but it ends up looking better that way than if they were blurred together.

Let me go into that in a little bit more detail. So what you need to do first is take your points space coordinates and convert them to pixel space coordinates. And this shows a little bit of Cocoa code you can use to convert to pixel space coordinates by converting to the window space.

Then you use floor() and seal() to find the nearest integral pixel coordinates. And then you convert back from those pixel coordinates into point coordinates. And as David showed you with the bevel button example, those point coordinates will be nonintegral. And that may feel a little bit odd to you, why am I, why am I drawing a line that's 5.2 points tall? And it feels weird, but it's actually correct, cause it's going to map to an integral number of pixels. And then you pass those fractional point coordinates to the drawing routine, and everything looks great.

Another common problem are the use of offscreens. The frameworks will automatically scale up windows, especially for Cocoa, you're going to get frameworks scaled automatically. We automatically make window backing buffers bigger. That's not true for the use of offscreens. If you're using offscreens in your application, it's your responsibility to make them bigger to match the scale factor.

So if you want a 3 point tall offscreen, and you're running at a 3 times scale factor, you need to make sure your offscreen is actually 9 pixels tall. So you multiply your desired point height times the scale factor to get the pixel height that you need. If you're doing this wrong, generally what occurs is you either get too small a drawing in some portion of your application, or depending on the routine you're using to blit your offscreen to the window, you may get correctly-sized art, but it ends up looking blurry.

Another problem you might see, which is an artifact of scaling down high-resolution images is banding or jaggy art. If this is a problem for you, you've got a couple possible solutions. One thing you can do is use Quartz's higher quality interpolation. You can turn that on via either Cocoa or via the Quartz APIs. But, of course, there's a cost. Higher quality interpolation is going to take a little bit more time.

So if you can't afford that time, if you're rendering some image for a particularly high performant need, the other option is to use additional bitmaps. There's nothing wrong with shipping five different high res bitmaps for a certain piece of art. So if you really want your 1.25 scale factor of your app to look great, ship a normal resolution piece of art, a 1.25 piece of art, and a 4 times piece of art, or any number of intermediate pieces that you need.

Another thing that we've seen a few applications do which will send you directly to the small mode bucket is drawing directly to the screen. You can't draw directly to the screen in a resolution independent fashion unless you jump through more hoops. And, well, I don't really want to explain how you can do it, cause you shouldn't be doing it. Don't draw directly to the screen. The better approach is to use an overlay window. That way it's got its own backing buffer, you can draw into it as much as you want, and you're not going to disturb any other content on the screen.

If your application supports plug-ins, you're going to have to do some work that a system engineers already did for Mac OS. We looked at all of our APIs that communicated in terms of points and rectangles, and we had to decide whether a given API was operating on a point space or a pixel space. So if you have the same sort of APIs for your plug-ins, you're going to want to make those definitions as well, so that your plug-in developers know when to convert their coordinates before calling your APIs.

Now a harder story is if your plug-in model uses QuickDraw. As I said before, QuickDraw just doesn't work in resolution independence mode. So if you have a Quick-Draw-based plug-in API, you're going to need to give your plug-in developers a new API in order for your application to go resolution independent.

Developers always need their applications to run earlier than the latest and greatest OS release, and we're always telling you to adopt the latest and greatest OS features. Resolution independence is no different. So if in the process of becoming resolution independent you adopt several new controls that are only available on Leopard, well you're still going to need to use your custom controls on older OS releases. So you'll probably have to do a runtime version check and see whether you're running on Tiger or Leopard, or earlier, and make the right decisions there.

If you want to use multi-image .tiff files on earlier OS releases, go ahead and do that, however, you need to make sure your multi-image .tiff files are correctly assembled with tiffutil. And that's one of the jobs of that -cathidpicheck flag. So long as your individual images all have the same size property, they can have different pixel property, pixel size properties, and it will look great using NSImage even on earlier system versions. So this is if you don't have your images with the same size parameter, -cathidpicheck will warn you about that so you can fix it, make the changes, and then build your multi-image .tiff file with the correct art.

So that's about it. It's not really all that hard to get to resolution independence. Clearly you have to spend a lot of time revising your artwork, and that's going to be a burden on some UI designers, but the code changes generally are pretty minimal. Carbon developers, of course, have a few more hoops to jump through. So now is another time when Carbon developers need to look seriously about why they're developing with Carbon.

Maybe it's the right time, maybe this is a little bit more of a push to go to Cocoa, and maybe this is the right time for those Carbon developers to go to Cocoa. So seriously consider that. Because if you get to Cocoa first, your resolution independence path is likely to be a little bit easier.

And, of course, the Leopard resolution independence support is not complete. It's not going to be a user release for resolution independence in Leopard, so there are bugs in the system. We'd love to hear about any bugs you find, we don't mind duplicates. Even if you're sure somebody's already filed it, please file it. We don't mind marking it as a duplicate, we'd rather have the issue tracked. So, please, file those bugs.

Deric Horn is the evangelist that takes care of the frameworks, and part of his role is handling resolution independence, so send him lots of emails about it, not me please. And we have a lab immediately following this session downstairs, so feel free to drop down. You can talk to a number of us in person, we can help you decide which is the best format for artwork and so forth, and we'd love to hear feedback about what APIs you need.