APIs for Supporting High Resolution

This chapter highlights the APIs you should use, and points out the older methods and functions you should no longer use. You’ll also find information on APIs added or modified to support high resolution. Please also see the appropriate reference documentation for each API mentioned in this chapter.

Converting Coordinates

There are very few, if any, situations for which you need to use device coordinates directly. You should be able to accomplish any task that relates to drawing geometry by using the APIs described in this section.

In all cases it is best to rely on using one of the APIs that support high resolution rather than trying to manage values yourself. Although multiplying by a scale factor (as shown below) might produce the desired result:

    NSNumber *myValue = [[NSNumber alloc] initWithDouble:value.y * scaleFactor];

the preferred approach is to use a conversion method:

    NSNumber *myValue = [[NSNumber alloc]
                        initWithDouble:[self convertPointToBacking:value].y];

You might be tempted to use device coordinates if your app allows users to choose a screen resolution, such as for a game. However, keep in mind that with high resolution, users will be unaware of the pixel dimensions. You should refer to display dimensions only in points.

Converting to and from Views

To support high resolution, you might 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 values to the receiver’s coordinate system from the coordinate system of the view passed as the second parameter. If you pass nil as the view, the values are assumed to be in the window coordinate system and are converted to the receiver coordinate system. The convert..:toView: methods perform the inverse operation—converting values in the receiver 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 coordinate system of the receiver’s window.

NSView also defines the centerScanRect: method, which converts a given rectangle to device coordinates, adjusts the rectangle to lie in the center of the area (pixels), and then converts the resulting rectangle back to the receiver’s coordinate system (points). Although this method works well for high resolution, some situations might require more precise control over the rounding behavior of the alignment operation on each edge of a rectangle. If you need a high level of control, consider using backingAlignedRect:options: (see “Aligning a Rectangle on Pixel Boundaries”).

For more information about coordinate conversion in views, see:

Converting to and from Layers

The NSView class provides methods for converting between a view’s local coordinate system and the interior coordinate system of the layer (for layer-backed views). Use these methods when you have custom layer trees and need to position the layers appropriately in the parent view.

The coordinate system of a layer that backs an NSView object is not necessarily identical to its local view coordinate system. For example, Core Animation layers always use an unflipped coordinate system, whereas the NSView class allows a given view class to choose whether or not it is flipped. For the case of a flipped NSView object that is layer-backed, the following conversion methods account for this difference.

Convert to the view’s layer coordinate system

Convert from the view’s layer coordinate system

convertPointToLayer:

convertPointFromLayer:

convertSizeToLayer:

convertSizeFromLayer:

convertRectToLayer:

convertRectFromLayer:

Converting to and from the Screen

The NSWindow class provides these methods for converting between window local coordinates and screen global coordinates:

Use them instead of the deprecated convertBaseToScreen: and convertScreenToBase: methods.

Converting to and from the Backing Store

Views, windows, and screens each have their own backing coordinate system. In other words, backing store coordinates are relative to an object; they do not refer to absolute positions onscreen. By default, coordinate values increase up and to the right in coordinate system units.

The backing coordinate system is suitable for pixel alignment for that specific object. Always use the same object for round-tripping to and from the backing store.

Each of the following methods converts between the object’s local coordinate system and a pixel-aligned coordinate system that matches the characteristics of the backing store for that object. In the case of the NSScreen class, the backing coordinate system is the native frame buffer of the display.

For more information on each method, see the appropriate reference documentation (NSView Class Reference, NSWindow Class Reference, NSScreen Class Reference.

Aligning a Rectangle on Pixel Boundaries

Achieving consistent pixel alignment for high resolution often requires more control over rounding behaviors than the NSView class centerScanRect: method offers. The NSView, NSWindow, and NSScreen classes all provide a backingAlignedRect:options: method.

The backingAlignedRect:options: method accepts rectangles in local coordinates and ensures that the result is aligned on backing store pixel boundaries, subject to specific rounding hints given in the options argument. Use NSAlignmentOptions constants to specify how to treat each edge of the rectangle. You can push a rectangle’s width and height inward, outward, or closest pixel boundary.

For example, this code:

NSRect rect = {0.3,0.3,10.0,10.0};
NSAlignmentOptions alignOpts = NSAlignMinXOutward | NSAlignMinYOutward |
                               NSAlignWidthOutward | NSAlignMaxYOutward ;
NSRect alignedRect = [self backingAlignedRect:rect options:alignOpts];

produces a rectangle with this origin and size:

{{0, 0}, {10, 11}}

For a complete list of options, see NSAlignmentOptions in Foundation Constants Reference.

Getting Scale Information

Objects in an app, such as custom layers, windows, and screens, might not have the same resolution. When you need to find out scaling information for an object, choose the API that’s appropriate for that object.

CALayer

The contentsScale property of the CALayer class defines the mapping between the coordinate space of the layer (measured in points) and the backing store (measured in pixels). You can change this value as needed to indicate to Core Animation that the bitmap of the backing layer needs to be bigger or smaller.

For example, to avoid blurry text for a layer that is magnified when composited to the screen, use the contentsScale property to specify a text-layer bitmap at twice the layer size, with mipmaps enabled.

NSScreen

The backingScaleFactor method of the NSScreen class returns the scale factor that represents the number of backing store pixels that correspond to each linear unit in screen space on the NSScreen object. You should not use this method except in the rare case when the explicit scale factor is needed. Instead, use the backing store conversion methods (see “Converting to and from the Backing Store”).

Note that the value returned by backingScaleFactor does not represent anything concrete, such as pixel density or physical size, because it can vary based on the configured display mode. For example, the display might be in a mirrored configuration that is still scaled for high resolution, resulting in pixel geometry that might not match the native resolution of the display device.

NSWindow

The backingScaleFactor method of the NSWindow class returns the scale factor for a specific window. As with its NSScreen counterpart, it is preferable that you use the backing store conversion methods.

CGContextRef

The preferred way to get the scaling information for a CGContext object is to call the conversion function CGContextConvertRectToDeviceSpace:

deviceRect = CGContextConvertRectToDeviceSpace(context, userRect);

Then you can divide deviceRect.size.height by userRect.size.height. This works for both implicitly scaled window contexts and explicitly scaled bitmap contexts.

An alternative is to get the transform applied to the CGContext object by calling the function CGContextGetUserSpaceToDeviceSpaceTransform. The scaling information is in the a and d components of the returned transform. For example:

CGAffineTransform  deviceTransform =
        CGContextGetUserSpaceToDeviceSpaceTransform(myContext);
NSLog(@"x-scaling = %f y-scaling = %f", deviceTransform.a, deviceTransform.d);

If you applied any additional scaling to the context, that will be reflected in the values. Note that this function reports a scale for the implicitly scaled window contexts, and it does not handle bitmap contexts because those are not implicitly scaled.

For simple conversions between user space and device space, you can also use one of the conversion functions listed below (for details, see CGContext Reference). However, they convert only global coordinates, so you need to perform additional calculations to translate the results to view-centric coordinates.

Drawing Images with NSImage and Core Image

When drawing images, the system needs to know about the source and destination resolution in order to apply the appropriate scaling. For that reason, you should use methods that provide information about the source rectangle.

When working with NSImage objects, choose one of these methods, which allow you to specify a source rectangle. That method will then draw all or part of the image in the current coordinate system:

NSImage drawing methods whose names do not begin with “draw” are deprecated (see “Deprecated APIs”).

When working with a Core Image context, use the method drawImage:inRect:fromRect: and specify the exact bounds of the destination. If the you create the CIContext object with a CGContextRef, the inRect: parameter is in points. If you create the CIContext object with a CGLContext object, the inRect: parameter is in pixels. The fromRect: parameter is always in pixel dimensions.

Do not use drawImage:atPoint:fromRect: because this method is ambiguous as to the units of the dimensions, so it might not work as expected in a high-resolution environment.

Additions and Changes for OS X v10.7.4

Additions to AppKit

NSImage Class

The default behavior for NSImage is to choose the smallest image representation that has at least as many pixels as the destination rectangle on both the horizontal and vertical axes. The default works well for most cases. If you find the default doesn’t work well for your app, use the matchesOnlyOnBestFittingAxis property of the NSImage class to adjust the image-choosing behavior.

-(BOOL)matchesOnlyOnBestFittingAxis

Controls how NSImage chooses an image representation for a destination rectangle. Returns the current setting. The default setting is NO. When set to YES, NSImage chooses the smallest image representation that has at least as many pixels as the destination rectangle on either the horizontal or vertical axis.

setMatchesOnlyOnBestFittingAxis:

Sets the property that controls how NSImage chooses an image representation for a destination rectangle.

Use the following to manage content and scale for custom Core Animation layers.

layerContentsForContentsScale:

Provides the contents for a layer at a given scale.

recommendedLayerContentsScale:

Provides the system with the optimal scaling to use for a layer.

NSView Class

For more details, see “Handle Dynamic Changes in Window Resolution Only When You Must” and “Manage Core Animation Layer Contents and Scale.”

NSLayerDelegateContentsScaleUpdating

This protocol defines an optional CALayer delegate method for handling resolution changes, allowing you to manage scale and contents for a layer hosted in a view.

layer:shouldInheritContentsScale:fromWindow:

Invoked when a resolution changes occurs for the window that hosts the layer.

viewDidChangeBackingProperties

Is invoked when the view’s backing properties change. The default implementation does nothing. Your app can provide an implementation if it needs to swap assets when a view’s backing properties change.

NSWindow Class

For more details, see “Handle Dynamic Changes in Window Resolution Only When You Must.”

NSWindowDidChangeBackingPropertiesNotification

Is sent when a window’s backing properties change.

windowDidChangeBackingProperties:

Is invoked when the window’s backing properties change. The default implementation does nothing. Your app can provide an implementation if it needs to swap assets when a window’s backing properties change.

NSBackingPropertyOldColorSpaceKey

Indicates the color space of the window prior to the change in backing store.

NSBackingPropertyOldScaleFactorKey

Indicates the backing properties of the window prior to the change in backing store.

Additions to Carbon

HIWindowGetBackingScaleFactor

Replaces the HIGetScaleFactor function; see “Getting Scale Factors.”

kHIWindowBitHighResolutionCapable

Represents the bit that sets the high-resolution-capable attribute.

kWindowHighResolutionCapableAttribute

Designates a window as being capable of supporting high-resolution content.

Additions and Changes for OS X v10.8

NSImage Class

Use this method for offscreen drawing. See “Use the Block-Based Drawing Method for Offscreen Images.”

+ (id)imageWithSize:(NSSize)size flipped:(BOOL)drawingHandlerShouldBeCalledWithFlippedContext drawingHandler:(BOOL (^)(NSRect dstRect))drawingHandler;

The drawing handler is a block that can be invoked whenever the image is drawn to, and on whatever thread the drawing occurs. You should make sure that any state you access within the block is done in a thread-safe manner.

The code in the block is the same code that you would use between the lockFocus and unlockFocus methods.

Quartz Display Services

These functions return points (not pixels) as of OS X v10.8:

size_t CGDisplayModeGetWidth(CGDisplayModeRef mode);

Returns the width in points of the specified display mode.

size_t CGDisplayModeGetHeight(CGDisplayModeRef mode);

Returns the height in points of the specified display mode.

Deprecated APIs

If your code uses any of the methods or constants listed in these sections, you need to replace them to allow your app to support high resolution.

Converting to and from the Base Coordinate System

The following methods of the NSView class are deprecated:

The following methods of the NSWindow class are deprecated:

The appropriate replacement depends on the conversion you want to perform:

Getting Scale Factors

These are not compatible with the high-resolution model in OS X:

  • The userSpaceScaleFactor methods of the NSScreen and NSWindow classes

  • The HIGetScaleFactor function; use HIWindowGetBackingScaleFactor instead

Creating an Unscaled Window

The NSUnscaledWindowMask constant of the NSWindow class is deprecated. This mask currently does nothing. The scale factor for a window backing store is dynamic and is dependent on the screen on which the window is placed. If you currently use this mask to achieve pixel-precise rendering, you should replace it with the backing store conversion methods (see “Converting to and from the Backing Store”).

Drawing Images

The NSImage class methods compositeToPoint:... and dissolveToPoint:... operate on the base coordinate system. The behavior of these methods is not compatible with high resolution in OS X because there is no way to specify the source rectangle.

Instead, you should use one of the methods that begin with draw, such as:

These methods allow you to specify a source rectangle, and they draw all or part of the image in the current coordinate system.