When you’re drawing graphics in your app, something that’s really important to make them look good is to pixel align them. This is the process of making sure all of the boundaries for a shape line up exactly with the screen’s pixel grid.
The problem when you don’t pixel align your graphics is that when they end up between pixels, they get all blurry like this:
(On the right is a zoomed in version of the same image.) With pixel aligning, the same drawing becomes:
Everything is beautiful again! Luckily, pixel aligning is easy to do. The main steps are: convert all of the relevant coordinates to the pixel grid, round them, then convert them back. There’s one little things to keep in mind while doing this- when you stroke (outline) something instead of filling it, the geometry works a bit differently. Here’s what a pixel aligned rectangle fill should look like on a pixel grid:
Note that the coordinates of the rectangle (shown in red) line up with the pixel grid points (which are integer pixel values). On the other hand, a pixel aligned rectangle stroke looks like this:
Now, the rectangle’s coordinates end up in between the pixels! In fact, they should be at the centers of the pixels (integer and a half values). And in case you weren’t already confused, here’s a rectangle with a two pixel stroke:
Now the coordinates are back to integer values again. Luckily, this is pretty easy to generalize: odd stroke sizes should be rounded to integer and a half pixel values and even stroke sizes (including a fill, which is a zero size stroke) should be rounded to integer pixel values. What about decimal stroke values that are neither even or odd? Well there’s not much you can do to pixel align in that case since the stroke itself being a non-integer pixel size is already un-pixel-align-able.
Here’s some code to help you make all your drawing nice and pixel aligned:
LTPixelAlign is an
NSView category that adds methods to pixel align
NSPoint‘s, and stroke widths. The idea is that you can call these from your
drawWithRect: method to properly align the coordinates you use for drawing. Naturally these calls are resolution independence friendly- they rely on the view converting coordinates to pixels (with
convertRectToBase: and friends) which takes the current resolution into account. At 1x, 1.25x, and 1.5x UI Resolutions, the non-aligned and pixel-aligned drawing shown above looks like:
If you’re using any additional geometry transforms (like
NSAffineTransform) while drawing, you’ll have to take those into account when pixel aligning. For scaling and translating, you should transform the coordinates, then pixel align them, then un-transform the results. For rotations and shears, you’re usually better off just not pixel aligning, since there’s no way to make your transformed graphics line up with the pixel grid.
Of course if this sounds too complicated, you can always just use Opacity’s source code exporting, which does all this for you…