[cairo] Pixman sampling coordinates

Bertram Felgenhauer bertram.felgenhauer at googlemail.com
Tue Jan 22 23:11:03 PST 2008

Carl Worth wrote:
> Only... I went to update the reference images for the three test cases
> that were impacted by this pixman change, (rectangle-rounding-error,
> rotate-image-surface-paint, and unantialiased-shapes). And after I
> update the images two of the tests pass, but
> rotate-image-surface-paint is passing in the no-offset case while
> failing in the case of a 25x25 offset.


> Something's really fishy there, because an integer offset should not
> be changing the sampling behavior.
> So, chalk another point up for the test suite. But, Bertram, what did
> we miss in the image sampling code?

Nothing really, this is a different rounding error. The problem here
is basically that sqrt(2)/2 can't be represented exactly.

In that test, we get the following transformations from device space to
the source image:

offset 0:
   sqrt(2)/2  sqrt(2)/2  5 - 10 sqrt(2)
  -sqrt(2)/2  sqrt(2)/2  5

offset 25:
   sqrt(2)/2  sqrt(2)/2  5 - 35 sqrt(2)
  -sqrt(2)/2  sqrt(2)/2  5
which _cairo_matrix_to_pixman_matrix() converts to (hex, grabbed from
a debug printf)

offset 0:
  0.b505   0.b505   -9.2463
 -0.b505   0.b505    5.0000

offset 25:
  0.b505   0.b505  -2c.7f5b
 -0.b505   0.b505    5.0000

Now plug in the sampling point of one of the pixels on the critical
diagonal, say (10 1/2, 9 1/2) and (35 1/2, 34 1/2), respectively.

For offset 0, the result of the transform is (5.0000, 5.b505),
while for offset 35, the result is (5.0003, 5.b505).

After subtracting that epsilon we added, that becomes (4.ffff, 5.b505)
vs. (5.0002, 5.b505), so the fetched pixel is different.

One way to fix this behaviour (integer translations affecting the
output), would be to give _cairo_matrix_to_pixman_matrix() a reference
point to anchor the transform, like this (untested):

_cairo_matrix_to_pixman_matrix_with_anchor (
    const cairo_matrix_t *matrix,
    pixman_transform_t   *pixman_transform,
    double x, double y)
    /* set up the rotation part of the transformation */
    pixman_transform->matrix[0][0] =
        _cairo_fixed_16_16_from_double (matrix->xx);
    pixman_transform->matrix[0][1] =
        _cairo_fixed_16_16_from_double (matrix->xy);
    pixman_transform->matrix[0][2] = 0;

    pixman_transform->matrix[1][0] =
        _cairo_fixed_16_16_from_double (matrix->yx);
    pixman_transform->matrix[1][1] =
        _cairo_fixed_16_16_from_double (matrix->yy);
    pixman_transform->matrix[1][2] = 0;

    /* look where pixman puts the reference point */
    pixman_vector_t vector;
    vector.vector[0] = _cairo_fixed_16_16_from_double (x);
    vector.vector[1] = _cairo_fixed_16_16_from_double (y);
    vector.vector[2] = 1 << 16;

    pixman_transform_point_3d (pixman_transform, &vector);

    /* figure out where we want to reference point to be */
    cairo_matrix_transform_point (matrix, &x, &y);

    /* adjust pixman's transformation accordingly */
    pixman_transform->matrix[0][2] =
        _cairo_fixed_16_16_from_double (x) - vector.vector[0];
    pixman_transform->matrix[1][2] =
        _cairo_fixed_16_16_from_double (y) - vector.vector[1];

The reference point will have to be fixed relative to image sources
and patterns - their origin seems to be a good choice.


More information about the cairo mailing list