Because screens are relatively low-resolution devices, drawing glitches are often more noticeable on a screen than they are on higher-resolution devices such as printers. Drawing glitches can occur when you render content in a way that requires tweaking to match the underlying pixels sent to the screen. For example, images and shapes drawn on non-pixel boundaries might require aliasing and therefore might appear less crisp than those drawn exactly on pixel boundaries. In addition, scaling an image to fit into a different-sized area requires interpolation, which can introduce artifacts and graininess.
Although pixel-alignment issues can occur on any version of Mac OS X, they are more likely to occur as the operating system changes to support resolution independence. Under resolution independence, units in the user coordinate space and device coordinate space are no longer required to maintain a one-to-one relationship. For high-resolution screens, this means that a single unit in user space may be backed by multiple pixels in device space. So even if your user-space coordinates fall on integral unit boundaries, they may still be misaligned in device space. The presence of extra pixels can also lead to pixel cracks, which occur when misaligned shapes leave small gaps because they do not fill the intended drawing area entirely.
If your images or shapes are not drawing the way you expect, or if your graphics display evidence of pixel cracks, you can remove many of these issues by adjusting the coordinate values you use to draw your content. The following steps are not required if the current scale factor is 1.0 but would be required for other scale factors.
Convert the user-space point, size, or rectangle value to device space.
Normalize the value in device space so that it is aligned to the appropriate pixel boundary.
Convert the normalized value back to user space.
Draw your content using the adjusted value.
Mac OS X provides several functions for normalizing coordinate values once they are in device space, including the following:
To normalize an entire rectangle, use the NSIntegralRect function in Cocoa or the CGRectIntegral function in C-based code. These functions return a rectangle that is slightly larger than the original, but whose origin and bounds fall on integral coordinate boundaries.
To round a floating-point number down to the nearest integral value, use the floor function defined in math.h. For more information, see the floor(3) man page.
To round a floating-point value up to the nearest integral value, use the ceil function defined in math.h. For more information, see the ceil(3) man page.
The sections that follow discuss the options for converting coordinates to and from device space. For additional information about supporting resolution independence, see “Resolution-Independent User Interface.”
Accessing the Current Scale Factor
Converting Coordinate Values
Knowing the current scale factor can help you make decisions about how best to render your content. The NSWindow and NSScreen classes both include a userSpaceScaleFactor method that you can call to obtain the current scale factor, if any, for your application. In Mac OS X v10.5 and earlier, this method usually returns 1.0, indicating that the user space and device space have the same resolution (where one point equals one pixel). At some point though, this method may return a value that is greater than 1.0. For example, a value of 1.25 would indicate a screen resolution of approximately 90 dpi, while a value of 2.0 would indicate a screen resolution of 144 dpi.
If you want to know the actual resolution of a particular screen, the NSScreen class includes information about the display resolution in its device description dictionary (accessed using the deviceDescription method). You can use this information (instead of multiplying scale factors) to determine the appropriate resolution to use for your images.
In Mac OS X v10.5, several methods were added to NSView to simplify the conversion between user space and device space coordinates:
convertPointToBase:
convertSizeToBase:
convertRectToBase:
convertPointFromBase:
convertSizeFromBase:
convertRectFromBase:
These convenience methods make it possible to convert values to and from the base (device) coordinate system. They take into account the current backing store configuration for the view, including whether it is backed by a layer.
To change the coordinate values of an NSPoint structure, the beginning of your view’s drawRect: method might have code similar to the following:
- (void)drawRect:(NSRect)rect |
{ |
NSPoint myPoint = NSMakePoint(1.0, 2.0); |
CGFloat scaleFactor = [[self window] userSpaceScaleFactor]; |
if (scaleFactor != 1.0) |
{ |
NSPoint tempPoint = [self convertPointToBase:myPoint]; |
tempPoint.x = floor(tempPoint.x); |
tempPoint.y = floor(tempPoint.y); |
myPoint = [self convertPointFromBase:tempPoint]; |
} |
// Draw the content at myPoint |
} |
It is up to you to determine which normalization function is best suited for your drawing code. The preceding example uses the floor function to normalize the origin of the given shape but you might use a combination of floor and ceil depending on the position of other content in your view.
Last updated: 2007-10-31