[cairo] bug: result depends on 'right/left'

Carl Worth cworth at cworth.org
Mon Apr 14 11:59:07 PDT 2008


On Sun, 13 Apr 2008 22:55:05 +0200, calmar wrote:
> Assuming line width is 1 pixel.
>
> When I want a point at position 10,10, I can achive this with:

Are you drawing with CAIRO_ANTIALIAS_NONE? If so, are you using any
version of cairo earlier than 1.6.0? If so (again) there were some
bugs in this area.

> ,-------------------------------------------
> | cairo_move_to 10,10 + cairo_line_to 11,11
> |
> | or
> |
> | cairo_move_to 11,10 + cairo_line_to 10,10
> `-------------------------------------------
>
> that seems to be quite complex to handle - thinking about if you
> draw from left to right or vice versa

As you are rightly suggesting, the direction in which you draw
identical geometry should not affect the result here[*]. So if you are
seeing that, then it's a bug, but it's also likely that it's a bug
that was already fixed, (as described above).

> Theoretically in both cases there should be a line 2 pixels long?
>
> Anyway, maybe I err

Let me attempt to describe the theory. The way to understand things is
to first draw an integer device-pixel grid, (by putting integer
coordinates on the lines of the grid), like this:

  9        10        11        12 (X)
 9+---------+---------+---------+
  |         |         |         |
  |         |         |         |
  |         |         |         |
10+---------+---------+---------+
  |         |         |         |
  |         |         |         |
  |         |         |         |
11+---------+---------+---------+
  |         |         |         |
  |         |         |         |
  |         |         |         |
12+---------+---------+---------+
(Y)

Next, I'll assume that you're using the default identity
transformation, so that user-space coordinates such as (10,10) map
directly to (10,10) on the device-pixel grid.

If I were trying to light up a single pixel by using cairo_stroke,
(we discussed elsethread that using cairo_rectangle;cairo_fill is much
easier, but I want to help you understand cairo_stroke), I would do:

	cairo_move_to (cr, 10, 10.5);
	cairo_line_to (cr, 11, 10.5);

Giving me a path connecting two points:

  9        10        11        12 (X)
 9+---------+---------+---------+
  |         |         |         |
  |         |         |         |
  |         |         |         |
10+---------+---------+---------+
  |         |         |         |
  |         *.........*         |
  |         |         |         |
11+---------+---------+---------+
  |         |         |         |
  |         |         |         |
  |         |         |         |
12+---------+---------+---------+
(Y)

Then when calling cairo_stroke, cairo internally computes the stroke
outline by expanding outward from the path based on the line width,
(I'll assume 1.0 here). It can also extend out from the end points
with line caps, (but the default CAIRO_LINE_CAP_BUTT means that it
doesn't). So the stroke outline lies exactly on integer-grid-lines of
the device-pixel grid like so:

  9        10        11        12 (X)
 9+---------+---------+---------+
  |         |         |         |
  |         |         |         |
  |         |         |         |
10+---------*=========*---------+
  |         #         #         |
  |         #         #         |
  |         #         #         |
11+---------*=========*---------+
  |         |         |         |
  |         |         |         |
  |         |         |         |
12+---------+---------+---------+
(Y)

Finally, cairo fills that outline by performing one or more samples
for each pixel. If you are using CAIRO_ANTIALIAS_NONE then cairo
samples once at the center of each "pixel square", (or at each
half-integer position on the grid). So for reliable results, you want
to ensure that your outline lies on the integer positions. Putting the
outline on any half-integer positions gets you into the area where
there were bugs fixed between cairo 1.4 and 1.6.

In cairo 1.6, (and more precisely inside pixman 0.10.0), the sampling
position for CAIRO_ANTIALIAS_NONE is effectively adjusted by an
infintesimal amount *down* in both X and Y. So that should be reliable
going forward, (but really, you're being kinder to yourself if you
just don't put your geometry on half-integer boundaries).

Without CAIRO_ANTIALIAS_NONE, it's still often desirable to put
geometry on integer boundaries. This allows for sharp transitions
between pixels that are not filled at all to pixels that are filled
100%, (as opposed to a softer "blur" at the edges of your
geometry). It would be nice if the physical size of pixels were so
small that such blurring wouldn't be objectionable, but that's just
not the case for typical display environments yet, (where a laptop
display might only have on the order of 100 pixels per inch).

The integer-grid-line approach described above for understanding
exactly how cairo works should be generally useful. It's definitely a
design goal of cairo that you should always be able to predict exactly
what result you should get by going through an exercise like the one
demonstrated here.

I hope that helps,

-Carl

[*] Direction *can* actually be relevant when computing "insideness"
for filling intersecting paths with the default WINDING fill rule, but
that's a totally separate issue.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : http://lists.cairographics.org/archives/cairo/attachments/20080414/5a49c827/attachment.pgp 


More information about the cairo mailing list