Path Drawing Performance


In this section, some Quartz drawing issues are presented that affect not only the appearance of Quartz 2D drawings, but also the performance of working with Quartz 2D graphics. These issues focus, very narrowly, on specific situations, situations that arise often enough to be worth mentioning here.

The Half-Pixel Line "Problem"

In most pixel-based graphics libraries, the computer draws its graphics on pixel boundaries. Much time up to this point has been spent describing how Quartz 2D is a resolution independent API. When using Quartz 2D to draw to the screen, programmers often run into a problem where the horizontal and vertical lines that Quartz draws appear blurry or indistinct. This problem is particularly noticeable with one-point wide lines when the developer expects the line to draw one pixel wide, and it actually draws over two pixels. This is the result of the way Quartz 2D draws its lines and treats pixel boundaries. Figure 6.12 is a conceptualization of a pixel grid positioned over a one-point wide line as drawn in user space.

Figure 6.12. Quartz Drawing a Line on a Bitmap


The black rectangle in the figure represents a line that Quartz 2D has mapped from user space onto a pixel grid. You might imagine that this horizontal line was specified with the endpoints (1.4, 1) and (8.65, 1). The line was drawn with a line with of 1. When Quartz maps the endpoints of the lines onto the pixel grid, however, it maps the y coordinate whose value is 1 to a line between two pixels. Because the line extends a half a pixel on either side of that boundary, the line covers two pixels instead of one. When Quartz 2D rasterizes this line, it will shade the pixels on either side of the boundary with a tint of the stroke color. The resulting line ends up looking fuzzy and indistinct because its image has been spread across two pixels instead of the one pixel that the programmer expected.

There are a number of ways to solve this problem. Perhaps the most obvious is to add or subtract a half a point from the coordinates of the line. This places the integer valued coordinates of user space at the centers of pixels instead of on the boundaries between pixels. For example, if you choose to add a half point, the coordinates in the previous example would be (1.9, 1.5) and (9.15, 1.5). Another way to achieve the same effect without having to change each point you specify is to offset the transformation matrix of user space by ½ pixel in either coordinate direction. A third potential solution is simply to turn off antialiasing for the context using CGContextSetShouldAntialias.

Antialiasing in Complex Paths

When working with complex paths, there is a problem that is very similar to the half pixel line problem that affects rendering performance. The problem comes about as a result of antialiasing. Consider the path shown in Figure 6.13

Figure 6.13. Intersecting Paths with Crossover Pixels


Figure 6.13 illustrates two intersecting path segments. These segments are drawn over the pixel grid that they will eventually be rasterized onto. Two of these pixels are called out in the diagram because both segments intersect those pixels simultaneously.

When the computer is drawing these segments, it will apply some color to both of the crossover pixels. If the segments are drawn separately, it is correct for the computer to color the crossover pixels once when the first segment is drawn and then again when the second segment is laid down on top of it. If the two segments are part of the same path, however, the crossover pixels should be colored only once, but with roughly twice the contribution from the path.

To correctly render these two segments when they are part of the same path, the computer must recognize that the two segments intersect and figure out what pixels the segments share. This calculation can be very time consuming. If you create a complex path with many self-intersections, running these calculations can be a significant performance problem.

If you run into this problem in your own application, there are a couple of things you might try to alleviate the problem. They stem almost directly from the nature of the problem itself, so you can try splitting your complex path into one or more simpler paths and draw them sequentially rather than all at once.

This will generate a slightly different image than drawing a single path. If your path has lots of small bends and curves that are less than a pixel in size, you might consider simplifying the path to remove those variations that are too small to be seen at the resolution you are rasterizing them. Finally, if necessary, you might consider rendering the complex paths without antialiasing (perhaps as the user is manipulating them) and leave the final, higher quality rendering for a separate step that is done only at the discretion of the user.

Drawing Many Lines and Rectangles

The process by which Quartz 2D renders lines of arbitrary width with antialiasing is a bit more involved than the line drawing mechanisms of traditional graphics libraries. As a result, programs that generate a large number of lines and/or rectangles as part of their graphics often find the performance of Quartz 2D disappointing. Typically this is the result of a direct mapping that takes some combination of MoveTo/LineTo commands into a similar set of Quartz 2D drawing commands.

When you have a large number of lines to draw, you have several choices. Apple has enumerated four of these choices in a set of sample code that directly addresses the performance characteristics of drawing a large number of lines. This code sample is available on the web at http://developer.apple.com/samplecode/QuartzLines/QuartzLines.html

This code sample compares the performance of drawing lines in the following ways:

  • Each line is drawn as a separate path.

  • Several lines are collected together into a single path.

  • The lines are drawn using bulk drawing routines in Tiger.

  • Finally, the code limits the number of lines drawn.

These drawing techniques are listed roughly in order of increasing performance.

The bulk drawing routine mentioned for Tiger is CGContextStrokeLineSegments. This routine accepts an array of points and treats them as the endpoints of a series of connected line segments. The computer will stroke this poly-line using the current settings of the context in a very efficient manner.

By far the most effective of the techniques demonstrated in Apple's sample is the last one, limiting the number of lines you draw only to those that the user will see. One obvious way to incorporate this suggestion into your application is to only draw shapes that will intersect the visible area of user space. However, your application may also choose to filter out lines that have little effect on the final image. If a line will take up less than a pixel when transformed from user space to the output device, then it may not be worthwhile to even submit it to Quartz for rasterization. If you can cull many such lines from your graphic, you may receive a significant performance boost. By the same token, if you draw one line using a opaque color and overwrite that line with another that is also opaque, then the computation that went into drawing the first line has largely been wasted. This kind of thing might happen if you are trying to draw a signal trace with 1000 samples into a bitmap that is only 100 pixels wide. If you can prefilter the data set to remove lines that are obscuring one another, you can improve the performance of your application.

NSFrameRect and Friends

If you are working in Cocoa and drawing to a pixel-based context and are willing to trade a bit of the precision that extends from Quartz 2D's rendering process for higher performance when drawing rectangles, you can use the Cocoa routine NSFrameRect.

NSFrameRect speeds up the rendering of a simple stroked rectangle by sacrificing some of the calculations that Quartz 2D typically uses to properly calculate the line joins at the corner of a rectangular path. If you wish to draw a rectangle quickly and can sacrifice that precision, then NSFrameRect and the related routines NSFrameRectWithWidth and NSFrameRectWithWidthUsingOperation may be just the ticket.

This chapter was a first introduction to creating line art in Quartz 2D. Paths are an important tool for creating graphics in Quartz 2D, and this chapter showed how paths are constructed from segments and contours. The discussion of line art in Quartz 2D continues in the next chapter where the text explores the options available when stroking or filling a path.




Quartz 2D Graphics for Mac OS X Developers
Quartz 2D Graphics for Mac OS X Developers
ISBN: 0321336631
EAN: 2147483647
Year: 2006
Pages: 100

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net