Mac OS X Reference Library Apple Developer
Search

Supporting Resolution Independence

This chapter describes how to start adapting your application to take advantage of resolution independence.

Supporting Resolution Independence in Cocoa

Cocoa applications require little work to support resolution independence because the Cocoa frameworks handle the scaling for you. However, depending on how you manipulate windows and the views within them, you may need to make some changes. Of course, in addition to code changes, you may need to provide higher-resolution versions of any custom artwork.

Because of scaling, the coordinates of the window frame and its top-level view (the frame view) are not always the same. For example, say you have a window frame view with dimensions 80 x 80 points. If the scale factor is 1.0, there is a 1 : 1 correspondence between the units of the frame view and its owning window; that is, the window is displayed as being 80 x 80 pixels. However, if the scale factor is 1.25, the window size is displayed 25% larger, resulting in a 100 x 100 pixel window. Any calls that return the size of the window return 100 x 100.

Note: In the resolution-independent world, all Cocoa views are scaled if the scale factor is not 1.0; however, if the scaling for a view is due only to the scale factor, the NSView method isRotatedOrScaledFromBase returns NO. This result minimizes possible overhead from scrolling and similar operations.

An application must not assume that the window frame and the view frames within the window use the same coordinate system. For example, an application that positions a view based on the window frame does not always get correct results.

Resolution-Independent Compositing

Historically, compositing was done in the base coordinate system of the image being rendered, regardless of the coordinate system of the owning view. To allow compositing to support resolution independence, you can assume that all base coordinates are transformed by the current scale factor.

Here are some common cases.

  1. Compositing a 72 dpi 100 x 100 source image in a 1.25 scale factor window.

    A 72 dpi 100 x 100 image (stored in an NSImageRep object) contains 100 x 100 pixels. When composited into a view in a scaled window, the image is scaled to fill 125 x 125 pixels using the proper interpolation algorithm. Any coordinate transforms on the destination view (aside from the window scaling) are ignored.

  2. Compositing a 90 dpi 100 x 100 source image in a 1.25 scale factor window.

    A 90 dpi 100 x 100 image contains 125 x 125 pixels. When composited into a view in a scaled window, this image (rendered from an NSCachedImageRep object) contains 125 x 125 pixels, so no interpolation is needed. Any coordinate transforms on the destination view (aside from the window scaling) are ignored.

  3. Creating an NSCachedImageRep object from a 72 dpi 100 x 100 source image to display in a 1.25 scale factor window.

    A 72 dpi 100 x 100 source image contains 100 x 100 pixels. The NSCachedImageRep object is created with a size of 100 x 100, but holds 125 x 125 pixels because of the scale factor. The source image is scaled to fit the required pixel size using the proper interpolation algorithm. When the cached image is drawn, the pixels are copied 1 to 1 from the cached image to the destination window.

Cocoa Bitmapped Images

Each NSImageRep object that contains bitmapped data indicates its resolution (dots-per-inch) because the image size is defined in points as well as in pixel width and height. A 72 dpi NSImageRep object has a 1 : 1 correspondence between points and pixels, while a 144 dpi NSImageRep object has a 1 : 2 correspondence between points and pixels. NSCachedImageRep objects are stored already scaled to the destination window; for example, a 100 x 100 NSCachedImageRep for a window with a scale factor of 1.25 would report a size of 100 x 100 points, but pixel dimensions of 125 x 125.

Detecting the Scale Factor

Cocoa supports several methods that your application can use to obtain scale factor information. It should be noted that most Cocoa applications do not need to use these methods.

To obtain the global scale factor (as set in Quartz Debug), use the userSpaceScaleFactor method in the NSScreen class:

@interface NSScreen : NSObject
...
- (CGFloat)userSpaceScaleFactor;
...
@end

To obtain the scale factor for a particular window, use the userSpaceScaleFactor method in the NSWindow class:

@interface NSWindow : NSResponder
...
- (CGFloat)userSpaceScaleFactor;
...
@end

If you want to create a window that should not be scaled (for example, a custom window), you can specify the NSUnscaledWindowMask mask at window creation time. For unscaled windows, the userSpaceScaleFactor method returns 1.0.

Coordinate Conversion in Cocoa

To support resolution independence, you may need to convert rectangles or points from the coordinate system of one NSView instance to another (typically the superview or subview), or from one NSView instance to the containing window. The NSView class defines six methods that convert rectangles, points, and sizes in either direction:

Convert to the receiver from the specified view

Convert from the receiver to the specified view

convertPoint:fromView:

convertPoint:toView:

convertRect:fromView:

convertRect:toView:

convertSize:fromView:

convertSize:toView:

The convert...:fromView: methods convert the values to the receiver's coordinate system, from the coordinate system of the view passed as the second parameter. If nil is passed as the view, the values are assumed to be in the window's base coordinate system and are converted to the receiver's coordinate system. The convert..:toView: methods do the inverse, converting values in the receiver's coordinate system to the coordinate system of the view passed as a parameter. If the view parameter is nil, the values are converted to the base coordinate system of the receiver's window.

For converting to and from the screen coordinate system, NSWindow defines the convertBaseToScreen: and convertScreenToBase: methods.

For more information about coordinate conversion in views, see the chapter Working with the View Hierarchy in View Programming Guide. The chapter Coordinate Systems and Transforms in Cocoa Drawing Guide may also be helpful.

Coordinate Conversion in Mac OS X v10.5

In Mac OS X v10.5, NSView provides a new set of methods that should be used when performing pixel alignment of view content. These methods provide the means to transform geometry to and from a base coordinate space that is pixel-aligned with the backing store into which the view is being drawn.

Convert to the base coordinate system

Convert from the base coordinate system

convertPointToBase:

convertPointFromBase:

convertSizeToBase:

convertSizeFromBase:

convertRectToBase:

convertRectFromBase:

These new coordinate transform methods provide a way to abstract view content drawing code from the details of particular backing store configurations, and always achieve correct pixel alignment without having to special-case for layer-backed vs. conventional view rendering mode.

For more information, see Application Kit Release Notes (Snow Leopard).

Changes to the deviceDescription Method

Both the NSWindow and NSScreen classes define a deviceDescription method. This method returns a dictionary containing a NSDeviceResolution key. The NSDeviceResolution key has historically contained an NSSize value of (72.0, 72.0). In Mac OS X v10.4 and later, NSDeviceResolution contains an NSSize value of (72.0 * scale factor, 72.0 * scale factor).

Supporting Resolution Independence in Carbon

Carbon applications have two scaling options: framework-scaled mode and magnified mode. You set these modes on a window-by-window basis by setting the appropriate attribute at window creation time. If you do not select a scale mode, the system assumes magnified mode by default. You can specify the scale mode of a window only at window creation time.

Framework-Scaled Mode

As described previously, a window can use the framework-scaled mode if it uses HIView-based controls (that is, it uses compositing mode) and draws exclusively with Quartz. At drawing time, a scaling transform is applied to the Quartz context used by the views. Also, in order to support older functions, any window coordinate information (window bounds, mouse position, and so on) is automatically translated to reflect the proper window- or view-centric origin before being passed to the application.

A major benefit of framework-scaled mode is that windows loaded from nib files automatically work at all scale factors with no need to reposition their contents.

You specify framework-scaled mode by setting the kWindowFrameworkScaledAttribute attribute at window creation time or by choosing Framework Scaled for the Scaling popup button in the window inspector in Interface Builder version 2.5 and later.

While specifying framework-scaled mode means most of the scaling work is handled for you, you still need to supply higher-resolution versions of any custom artwork, such as icons, background images, and so on.

Magnified Mode

Magnified mode is the default rendering mode. If the scale factor is not 1.0, all windows that are not tagged as being in framework-scaled mode are scaled in magnified mode. As described previously, the window is simply scaled to match the scale factor.

Important: Because magnified windows do not look as crisp as properly scaled windows, you should adopt framework scaling as soon as possible.

In magnified mode, all onscreen coordinates are mapped to their user space equivalents when passed to the application. For example, if the scale factor is 2.0, a mouse click onscreen at a particular pixel is mapped to its window or view-centric equivalent location when passed in a mouse-down event.

Detecting Scale Information

To determine the scale factor for your application, you can use the HIGetScaleFactor function:

CGFloat HIGetScaleFactor (void);

If you need to determine the scale mode for a particular window (and also the application scale factor), you can call the HIWindowGetScaleMode function:

OSStatus HIWindowGetScaleMode (
    HIWindowRef inWindow,
    HIWindowScaleMode *outMode,
    CGFloat *outScaleFactor);

On output, outMode returns one of the following values:

kHIWindowScaleModeUnscaled

The window is not scaled at all because the scale factor is 1.0.

kHIWindowScaleModeMagnified

The window‘s backing store is being magnified because the scale factor is not equal to 1.0 and because the window was not created with the framework-scaled attribute.

kHIWindowScaleModeFrameworkScaled

The window‘s contents are scaled to match the scale factor because the scale factor is not equal to 1.0 and because the window was created with the framework-scaled attribute.

Note: A fourth scale mode named kHIWindowScaleModeApplicationScaled was available in Mac OS X v10.4 but was never fully implemented and is not supported at all in Mac OS X v10.5 and later.

Coordinate Conversion in Carbon

Because the scale mode in Carbon applications can be on a window-by-window basis, you may often need to convert between the various coordinate systems involved. The HIGeometry programming interface (see HIGeometry Reference) provides three functions that simplify conversion:

These conversion functions require you to specify the source and destination coordinate spaces as well as any associated objects, if required. For example, if you wanted to translate a point into view coordinates, you must specify the HIView to which the coordinates refer. You specify the coordinate spaces by passing the following constants:

The conversion functions take floating-point coordinates, which means that rounding may be necessary in certain cases. Which way to round depends on whether the system is more forgiving of overstating or understating the value. For example:

You can use the Quartz 2D functions CGRectInset and CGRectIntegral to simplify inset and outset operations. The BSD Library functions ceil and floor (available in math.h) may also be useful.

Keep in mind that HIShapeRef values take only integer coordinates. If you attempt to create a shape from floating-point coordinates (for example, by calling HIShapeCreateWithRect on an HIRect object), the call automatically rounds any non-integer coordinates to outset the shape. To avoid unexpected results, you should round any coordinates appropriately (inset or outset) before creating an HIShape based upon it.

Custom Drawing in Carbon

If your application uses custom controls or menus, you may need to make some changes to make them compatible with resolution independence.

Custom Controls

If you are still using QuickDraw to draw, you should adopt Quartz. If you are using the Appearance Manager to draw control elements, use HITheme (which is Quartz-savvy) instead.

In framework-scaled mode, the Quartz context passed to your custom view in the kEventControlDraw event has already been transformed to match the scale factor, so you probably won't need to update your drawing code.

Custom Menus

If your application still uses custom MDEFs, the Menu Manager creates windows to hold them and scales them appropriately, so they are effectively in magnified mode. However, you should consider updating your MDEFs to custom HIView-based menus.

When using view-based menus, the Menu Manager can automatically scale them in framework-scaled mode. Currently a workaround exists that allows kEventMenuDrawItem and kEventMenuDrawItemContent handlers to use QuickDraw calls even in framework-scaled mode. The standard menu view creates a temporary GWorld object and sets it to the current QuickDraw port before sending any menu drawing events. After the draw event, the Menu Manager copies the contents of the graphics world into the view. However, this workaround should be considered a temporary fix and you should plan to update your menu drawing handlers to draw into the supplied CGContext event parameter.

Unsupported Technologies

The following Carbon technologies will not be updated to support resolution independence:

Resolution Independence Support in Java

Java SE (Standard Edition) 6 in Mac OS X v10.5 supports resolution independence at runtime.

All drawing is done in framework-scaled mode. Text, vector drawing, and most system controls are drawn correctly scaled with no additional work on your part. Any bitmap images are magnified to fit the designated space.

All Java drawing methods (and their associated parameters) interpret coordinates as points, not pixels. Currently, no resolution independence–specific methods exist.

Accessing the Scaling Transform

Even if you rely on framework scaling, there may be cases where you want to know in advance how to scale your content. To do so, you can use the Quartz 2D function CGContextGetUserSpaceToDeviceSpaceTransform.

CGAffineTransform CGContextGetUserSpaceToDeviceSpaceTransform (
    CGContextRef theContext);

This call returns the transform matrix used to resize your window, converting from user space (that is, where you draw into the context) to the coordinate space of the display device. For example, you may want to transform your window to device space to determine the new coordinates of its elements. You can adjust these coordinates to make sure that window elements line up correctly, then do a reverse transform to obtain the user space coordinates needed for the best presentation at that scale factor.

Note: The transform you receive describes the sum of all the transformations applied to the graphics context, not just the scaling. For example, the transform includes any rotation or translation applied to the context.

For simple conversions between user space and device space, you can also use one of the Quartz conversion functions described in CGContext Reference. These functions convert only global coordinates, so you need to perform additional calculations to translate the results to view-centric coordinates.

Other Issues

Cross-Process Communication

If your application interacts with other applications, you need to make sure that all the applications agree on the coordinate system; otherwise, strange behavior may result.

Apple’s accessibility interfaces support resolution independence, so you don’t need to worry about translating between coordinate systems when supporting accessibility. The accessibility interfaces always return coordinates in screen pixels.

For the accessibility Carbon events that have event parameters containing coordinates, an event handler can ask for the parameter value in either screen pixel or 72DPI global coordinates, depending on which parameter type is used. For example, typeHIPoint and typeHIPoint72DPIGlobal return 72DPI global coordinates, while typeHIPointScreenPixel returns screen pixel coordinates. Similar parameter type constants are available for HIRect, HISize, and CGFloat.

OpenGL

Most OpenGL problems with resolution independence are caused by a mismatch between the screen pixels and the points of the drawing environment. The Cocoa class NSOpenGLView has been updated to handle common problems. If you are drawing directly to the screen (that is, on a pixel-by-pixel basis), you need to obtain the current scale factor and scale all your images manually.




Last updated: 2007-05-04

Did this document help you? Yes It's good, but... Not helpful...