While investigating ways to get the Cairo graphics API to draw a single-pixel line, I found this explanation in the FAQ. It explains why often a single-pixel line gets drawn as a half-intensity line two pixels wide.
The reason that cairo does it this way is so that fills align nicely, at the cost that some strokes do not. It is not possible to set up sample locations in a way so that both fills and strokes with integer coordinates work nicely, so one had to be preferred over the other. One argument in favor of preferring fills is that all fills with integer coordinates align nicely this way. The best that can be done with strokes is to make all even-integer-width strokes align nicely (as they do in cairo) or to make odd-integer-width strokes align (which would then break the fill alignment).
After reading this explanation, I still don’t understand why this is a problem with graphics generally. What do they mean by odd-integer-width strokes breaking the fill alignment? Why is it such a problem to have both fill and stroke aligned to whole pixels?
I guess I don’t really understand why or how the stroke width affects the fill at all.
Why is it such a problem to have both fill and stroke aligned to whole
pixels?
It’s not a problem at all. But you have to understand the used coordinate system and specify your coordinates accordingly.
Here, use integer coordinates for fills and even-width lines, but pixel-midpoint coordinates (i.e. floating point numbers ending with .5
) for odd-width lines.
I don’t really understand why or how the stroke width affects the fill at all.
It’s not the stroke width, it’s the choice of coordinate system. By choosing a coordinate grid aligned with pixel boundaries, fills bounded by integer coordinates align with pixel boundaries, while a line centered at an integer coordinate will extend to the pixels on both sides of the grid line. For lines with a width of 1, this means the line will only cover half of the pixel. Obviously, it is not possible to fill only half of a pixel, so the line will be drawn anti-aliased instead, i.e. The entire pixel will be filled with half intensity.
3