diff options
author | Andrea Canciani <ranma42@gmail.com> | 2010-12-17 11:04:41 +0100 |
---|---|---|
committer | Andrea Canciani <ranma42@gmail.com> | 2011-01-02 18:31:42 +0100 |
commit | 51594d9787905618de608a367c3a5fc0544c52e3 (patch) | |
tree | 47487f06cfdac82952d37befa52ba03c5e8e5485 | |
parent | ada6057b8ccab133909b127850c41abb3216a842 (diff) |
matrix: Cairo matrix to pixman transform/offset conversion
Xlib, XCB and image use the same code to convert a cairo_matrix_t to a
backend-specific transform.
The code did not handle correctly some matrices, thus a new function
that performs the conversion in a more generic way was added and used
in the backends instead of fixing the repeated code.
Fixes part of https://bugs.freedesktop.org/show_bug.cgi?id=32215
-rw-r--r-- | src/cairo-image-surface.c | 164 | ||||
-rw-r--r-- | src/cairo-matrix.c | 315 | ||||
-rw-r--r-- | src/cairo-pattern.c | 25 | ||||
-rw-r--r-- | src/cairo-xcb-surface-render.c | 75 | ||||
-rw-r--r-- | src/cairo-xlib-surface.c | 35 | ||||
-rw-r--r-- | src/cairoint.h | 19 |
6 files changed, 357 insertions, 276 deletions
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c index 07998cd1..010aa325 100644 --- a/src/cairo-image-surface.c +++ b/src/cairo-image-surface.c @@ -856,25 +856,6 @@ _cairo_image_surface_unset_clip_region (cairo_image_surface_t *surface) pixman_image_set_clip_region32 (surface->pixman_image, NULL); } -static double -_pixman_nearest_sample (double d) -{ - return ceil (d - .5); -} - -static cairo_bool_t -_nearest_sample (cairo_filter_t filter, double *tx, double *ty) -{ - if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) { - *tx = _pixman_nearest_sample (*tx); - *ty = _pixman_nearest_sample (*ty); - } else { - if (*tx != floor (*tx) || *ty != floor (*ty)) - return FALSE; - } - return fabs (*tx) < PIXMAN_MAX_INT && fabs (*ty) < PIXMAN_MAX_INT; -} - #if HAS_ATOMIC_OPS static pixman_image_t *__pixman_transparent_image; static pixman_image_t *__pixman_black_image; @@ -1093,9 +1074,10 @@ _pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern, pixman_image_t *pixman_image; pixman_gradient_stop_t pixman_stops_static[2]; pixman_gradient_stop_t *pixman_stops = pixman_stops_static; + pixman_transform_t pixman_transform; cairo_matrix_t matrix = pattern->base.matrix; - double tx, ty; unsigned int i; + cairo_status_t status; if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) { pixman_stops = _cairo_malloc_ab (pattern->n_stops, @@ -1182,49 +1164,19 @@ _pixman_image_for_gradient (const cairo_gradient_pattern_t *pattern, if (unlikely (pixman_image == NULL)) return NULL; - tx = pattern->base.matrix.x0; - ty = pattern->base.matrix.y0; - if (! _cairo_matrix_is_translation (&pattern->base.matrix) || - ! _nearest_sample (pattern->base.filter, &tx, &ty)) - { - pixman_transform_t pixman_transform; - - if (tx != 0. || ty != 0.) { - cairo_matrix_t m, inv; - cairo_status_t status; - double x, y; - - /* pixman also limits the [xy]_offset to 16 bits so evenly - * spread the bits between the two. - */ - inv = pattern->base.matrix; - status = cairo_matrix_invert (&inv); - assert (status == CAIRO_STATUS_SUCCESS); - - x = floor (inv.x0 / 2); - y = floor (inv.y0 / 2); - tx = -x; - ty = -y; - cairo_matrix_init_translate (&inv, x, y); - cairo_matrix_multiply (&m, &inv, &pattern->base.matrix); - _cairo_matrix_to_pixman_matrix (&m, &pixman_transform, - extents->x + extents->width/2., - extents->y + extents->height/2.); - } else { - tx = ty = 0; - _cairo_matrix_to_pixman_matrix (&pattern->base.matrix, - &pixman_transform, - extents->x + extents->width/2., - extents->y + extents->height/2.); - } - - if (! pixman_image_set_transform (pixman_image, &pixman_transform)) { + *ix = *iy = 0; + status = _cairo_matrix_to_pixman_matrix_offset (&matrix, pattern->base.filter, + extents->x + extents->width/2., + extents->y + extents->height/2., + &pixman_transform, ix, iy); + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { + if (unlikely (status != CAIRO_STATUS_SUCCESS) || + ! pixman_image_set_transform (pixman_image, &pixman_transform)) + { pixman_image_unref (pixman_image); return NULL; } } - *ix = tx; - *iy = ty; { pixman_repeat_t pixman_repeat; @@ -1373,16 +1325,15 @@ _pixman_image_for_surface (const cairo_surface_pattern_t *pattern, cairo_matrix_t *dst_device_transform, int *ix, int *iy) { + pixman_transform_t pixman_transform; pixman_image_t *pixman_image; + cairo_matrix_t m; cairo_rectangle_int_t sample; cairo_extend_t extend; cairo_filter_t filter; - double tx, ty; + cairo_status_t status; cairo_bool_t undo_src_transform = FALSE; - tx = pattern->base.matrix.x0; - ty = pattern->base.matrix.y0; - extend = pattern->base.extend; filter = sampled_area (pattern, extents, &sample); @@ -1424,12 +1375,11 @@ _pixman_image_for_surface (const cairo_surface_pattern_t *pattern, } /* avoid allocating a 'pattern' image if we can reuse the original */ + *ix = *iy = 0; if (extend == CAIRO_EXTEND_NONE && - _cairo_matrix_is_translation (&pattern->base.matrix) && - _nearest_sample (filter, &tx, &ty)) + _cairo_matrix_is_pixman_translation (&pattern->base.matrix, + filter, ix, iy)) { - *ix = tx; - *iy = ty; return pixman_image_ref (source->pixman_image); } @@ -1466,12 +1416,12 @@ _pixman_image_for_surface (const cairo_surface_pattern_t *pattern, } } + *ix = sub->extents.x; + *iy = sub->extents.y; if (is_contained && - _cairo_matrix_is_translation (&pattern->base.matrix) && - _nearest_sample (filter, &tx, &ty)) + _cairo_matrix_is_pixman_translation (&pattern->base.matrix, + filter, ix, iy)) { - *ix = tx + sub->extents.x; - *iy = ty + sub->extents.y; return pixman_image_ref (source->pixman_image); } @@ -1488,6 +1438,8 @@ _pixman_image_for_surface (const cairo_surface_pattern_t *pattern, } } + *ix = *iy = 0; + if (pixman_image == NULL) { struct acquire_source_cleanup *cleanup; cairo_image_surface_t *image; @@ -1543,61 +1495,33 @@ _pixman_image_for_surface (const cairo_surface_pattern_t *pattern, undo_src_transform = TRUE; } - if (! _cairo_matrix_is_translation (&pattern->base.matrix) || - ! _nearest_sample (filter, &tx, &ty)) - { - pixman_transform_t pixman_transform; - cairo_matrix_t m; - - m = pattern->base.matrix; - if (undo_src_transform) { - cairo_matrix_t sm; - - cairo_matrix_init_scale (&sm, - dst_device_transform->xx, - dst_device_transform->yy); - cairo_matrix_multiply (&m, &m, &sm); - } - - if (m.x0 != 0. || m.y0 != 0.) { - cairo_matrix_t inv; - cairo_status_t status; - double x, y; - - /* pixman also limits the [xy]_offset to 16 bits so evenly - * spread the bits between the two. - */ - inv = m; - status = cairo_matrix_invert (&inv); - assert (status == CAIRO_STATUS_SUCCESS); - - x = floor (inv.x0 / 2); - y = floor (inv.y0 / 2); - tx = -x; - ty = -y; - cairo_matrix_init_translate (&inv, x, y); - cairo_matrix_multiply (&m, &inv, &m); - } else { - tx = ty = 0; - } + m = pattern->base.matrix; + if (undo_src_transform) { + cairo_matrix_t sm; - _cairo_matrix_to_pixman_matrix (&m, &pixman_transform, - extents->x + extents->width/2., - extents->y + extents->height/2.); - if (! pixman_image_set_transform (pixman_image, &pixman_transform)) { - pixman_image_unref (pixman_image); - return NULL; - } + cairo_matrix_init_scale (&sm, + dst_device_transform->xx, + dst_device_transform->yy); + cairo_matrix_multiply (&m, &m, &sm); } - *ix = tx; - *iy = ty; - if (_cairo_matrix_has_unity_scale (&pattern->base.matrix) && - tx == pattern->base.matrix.x0 && - ty == pattern->base.matrix.y0) + status = _cairo_matrix_to_pixman_matrix_offset (&m, filter, + extents->x + extents->width/2., + extents->y + extents->height/2., + &pixman_transform, ix, iy); + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { + /* If the transform is an identity, we don't need to set it + * and we can use any filtering, so choose the fastest one. */ pixman_image_set_filter (pixman_image, PIXMAN_FILTER_NEAREST, NULL, 0); } + else if (unlikely (status != CAIRO_STATUS_SUCCESS || + ! pixman_image_set_transform (pixman_image, + &pixman_transform))) + { + pixman_image_unref (pixman_image); + return NULL; + } else { pixman_filter_t pixman_filter; diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c index 2536ebff..124fbe56 100644 --- a/src/cairo-matrix.c +++ b/src/cairo-matrix.c @@ -36,6 +36,9 @@ #include "cairoint.h" #include "cairo-error-private.h" +#include <float.h> + +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ #if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE) #define ISFINITE(x) isfinite (x) @@ -906,87 +909,265 @@ _cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, */ } -void +static const pixman_transform_t pixman_identity_transform = {{ + {1 << 16, 0, 0}, + { 0, 1 << 16, 0}, + { 0, 0, 1 << 16} + }}; + +static cairo_status_t _cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, pixman_transform_t *pixman_transform, double xc, double yc) { - static const pixman_transform_t pixman_identity_transform = {{ - {1 << 16, 0, 0}, - { 0, 1 << 16, 0}, - { 0, 0, 1 << 16} - }}; + cairo_matrix_t inv; + unsigned max_iterations; + + 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] = _cairo_fixed_16_16_from_double (matrix->x0); + + 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] = _cairo_fixed_16_16_from_double (matrix->y0); + + pixman_transform->matrix[2][0] = 0; + pixman_transform->matrix[2][1] = 0; + pixman_transform->matrix[2][2] = 1 << 16; + + /* The conversion above breaks cairo's translation invariance: + * a translation of (a, b) in device space translates to + * a translation of (xx * a + xy * b, yx * a + yy * b) + * for cairo, while pixman uses rounded versions of xx ... yy. + * This error increases as a and b get larger. + * + * To compensate for this, we fix the point (xc, yc) in pattern + * space and adjust pixman's transform to agree with cairo's at + * that point. + */ - if (_cairo_matrix_is_identity (matrix)) { - *pixman_transform = pixman_identity_transform; - } else { - cairo_matrix_t inv; - unsigned max_iterations; - - 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] = _cairo_fixed_16_16_from_double (matrix->x0); - - 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] = _cairo_fixed_16_16_from_double (matrix->y0); - - pixman_transform->matrix[2][0] = 0; - pixman_transform->matrix[2][1] = 0; - pixman_transform->matrix[2][2] = 1 << 16; - - /* The conversion above breaks cairo's translation invariance: - * a translation of (a, b) in device space translates to - * a translation of (xx * a + xy * b, yx * a + yy * b) - * for cairo, while pixman uses rounded versions of xx ... yy. - * This error increases as a and b get larger. - * - * To compensate for this, we fix the point (xc, yc) in pattern - * space and adjust pixman's transform to agree with cairo's at - * that point. + if (_cairo_matrix_has_unity_scale (matrix)) + return CAIRO_STATUS_SUCCESS; + + if (unlikely (fabs (matrix->xx) > PIXMAN_MAX_INT || + fabs (matrix->xy) > PIXMAN_MAX_INT || + fabs (matrix->x0) > PIXMAN_MAX_INT || + fabs (matrix->yx) > PIXMAN_MAX_INT || + fabs (matrix->yy) > PIXMAN_MAX_INT || + fabs (matrix->y0) > PIXMAN_MAX_INT)) + { + return _cairo_error (CAIRO_STATUS_INVALID_MATRIX); + } + + /* Note: If we can't invert the transformation, skip the adjustment. */ + inv = *matrix; + if (cairo_matrix_invert (&inv) != CAIRO_STATUS_SUCCESS) + return CAIRO_STATUS_SUCCESS; + + /* find the pattern space coordinate that maps to (xc, yc) */ + max_iterations = 5; + do { + double x,y; + pixman_vector_t vector; + cairo_fixed_16_16_t dx, dy; + + vector.vector[0] = _cairo_fixed_16_16_from_double (xc); + vector.vector[1] = _cairo_fixed_16_16_from_double (yc); + vector.vector[2] = 1 << 16; + + /* If we can't transform the reference point, skip the adjustment. */ + if (! pixman_transform_point_3d (pixman_transform, &vector)) + return CAIRO_STATUS_SUCCESS; + + x = pixman_fixed_to_double (vector.vector[0]); + y = pixman_fixed_to_double (vector.vector[1]); + cairo_matrix_transform_point (&inv, &x, &y); + + /* Ideally, the vector should now be (xc, yc). + * We can now compensate for the resulting error. */ + x -= xc; + y -= yc; + cairo_matrix_transform_distance (matrix, &x, &y); + dx = _cairo_fixed_16_16_from_double (x); + dy = _cairo_fixed_16_16_from_double (y); + pixman_transform->matrix[0][2] -= dx; + pixman_transform->matrix[1][2] -= dy; - if (_cairo_matrix_has_unity_scale (matrix)) - return; + if (dx == 0 && dy == 0) + return CAIRO_STATUS_SUCCESS; + } while (--max_iterations); - /* Note: If we can't invert the transformation, skip the adjustment. */ - inv = *matrix; - if (cairo_matrix_invert (&inv) != CAIRO_STATUS_SUCCESS) - return; + /* We didn't find an exact match between cairo and pixman, but + * the matrix should be mostly correct */ + return CAIRO_STATUS_SUCCESS; +} - /* find the pattern space coordinate that maps to (xc, yc) */ - xc += .5; yc += .5; /* offset for the pixel centre */ - max_iterations = 5; - do { - double x,y; - pixman_vector_t vector; - cairo_fixed_16_16_t dx, dy; +static inline double +_pixman_nearest_sample (double d) +{ + return ceil (d - .5); +} - vector.vector[0] = _cairo_fixed_16_16_from_double (xc); - vector.vector[1] = _cairo_fixed_16_16_from_double (yc); - vector.vector[2] = 1 << 16; +/** + * _cairo_matrix_is_pixman_translation: + * @matrix: a matrix + * @filter: the filter to be used on the pattern transformed by @matrix + * @x_offset: the translation in the X direction + * @y_offset: the translation in the Y direction + * + * Checks if @matrix translated by (x_offset, y_offset) can be + * represented using just an offset (within the range pixman can + * accept) and an identity matrix. + * + * Passing a non-zero value in x_offset/y_offset has the same effect + * as applying cairo_matrix_translate (matrix, x_offset, y_offset) and + * setting x_offset and y_offset to 0. + * + * Upon return x_offset and y_offset contain the translation vector if + * the return value is %TRUE. If the return value is %FALSE, they will + * not be modified. + * + * Return value: %TRUE if @matrix can be represented as a pixman + * translation, %FALSE otherwise. + **/ +cairo_bool_t +_cairo_matrix_is_pixman_translation (const cairo_matrix_t *matrix, + cairo_filter_t filter, + int *x_offset, + int *y_offset) +{ + double tx, ty; + + if (!_cairo_matrix_is_translation (matrix)) + return FALSE; + + tx = matrix->x0 + *x_offset; + ty = matrix->y0 + *y_offset; + + if (filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) { + tx = _pixman_nearest_sample (tx); + ty = _pixman_nearest_sample (ty); + } else if (tx != floor (tx) || ty != floor (ty)) { + return FALSE; + } + + if (fabs (tx) > PIXMAN_MAX_INT || fabs (ty) > PIXMAN_MAX_INT) + return FALSE; + + *x_offset = _cairo_lround (tx); + *y_offset = _cairo_lround (ty); + return TRUE; +} - if (! pixman_transform_point_3d (pixman_transform, &vector)) - return; +/** + * _cairo_matrix_to_pixman_matrix_offset: + * @matrix: a matrix + * @filter: the filter to be used on the pattern transformed by @matrix + * @xc: the X coordinate of the point to fix in pattern space + * @yc: the Y coordinate of the point to fix in pattern space + * @out_transform: the transformation which best approximates @matrix + * @x_offset: the translation in the X direction + * @y_offset: the translation in the Y direction + * + * This function tries to represent @matrix translated by (x_offset, + * y_offset) as a %pixman_transform_t and an translation. + * + * Passing a non-zero value in x_offset/y_offset has the same effect + * as applying cairo_matrix_translate (matrix, x_offset, y_offset) and + * setting x_offset and y_offset to 0. + * + * If it is possible to represent the matrix with an identity + * %pixman_transform_t and a translation within the valid range for + * pixman, this function will set @out_transform to be the identity, + * @x_offset and @y_offset to be the translation vector and will + * return %CAIRO_INT_STATUS_NOTHING_TO_DO. Otherwise it will try to + * evenly divide the translational component of @matrix between + * @out_transform and (@x_offset, @y_offset). + * + * Upon return x_offset and y_offset contain the translation vector. + * + * Return value: %CAIRO_INT_STATUS_NOTHING_TO_DO if the out_transform + * is the identity, %CAIRO_STATUS_INVALID_MATRIX if it was not + * possible to represent @matrix as a pixman_transform_t without + * overflows, %CAIRO_STATUS_SUCCESS otherwise. + **/ +cairo_status_t +_cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t *matrix, + cairo_filter_t filter, + double xc, + double yc, + pixman_transform_t *out_transform, + int *x_offset, + int *y_offset) +{ + cairo_bool_t is_pixman_translation; - x = pixman_fixed_to_double (vector.vector[0]); - y = pixman_fixed_to_double (vector.vector[1]); - cairo_matrix_transform_point (&inv, &x, &y); + is_pixman_translation = _cairo_matrix_is_pixman_translation (matrix, + filter, + x_offset, + y_offset); - /* Ideally, the vector should now be (xc, yc). - * We can now compensate for the resulting error. + if (is_pixman_translation) { + *out_transform = pixman_identity_transform; + return CAIRO_INT_STATUS_NOTHING_TO_DO; + } else { + cairo_matrix_t m; + + m = *matrix; + cairo_matrix_translate (&m, *x_offset, *y_offset); + if (m.x0 != 0.0 || m.y0 != 0.0) { + double tx, ty, norm; + int i, j; + + /* pixman also limits the [xy]_offset to 16 bits so evenly + * spread the bits between the two. + * + * To do this, find the solutions of: + * |x| = |x*m.xx + y*m.xy + m.x0| + * |y| = |x*m.yx + y*m.yy + m.y0| + * + * and select the one whose maximum norm is smallest. */ - x -= xc; - y -= yc; - cairo_matrix_transform_distance (matrix, &x, &y); - dx = _cairo_fixed_16_16_from_double (x); - dy = _cairo_fixed_16_16_from_double (y); - pixman_transform->matrix[0][2] -= dx; - pixman_transform->matrix[1][2] -= dy; - - if (dx == 0 && dy == 0) - break; - } while (--max_iterations); + tx = m.x0; + ty = m.y0; + norm = fmax (fabs (tx), fabs (ty)); + + for (i = -1; i < 2; i+=2) { + for (j = -1; j < 2; j+=2) { + double x, y, den, new_norm; + + den = (m.xx + i) * (m.yy + j) - m.xy * m.yx; + if (fabs (den) < DBL_EPSILON) + continue; + + x = m.y0 * m.xy - m.x0 * (m.yy + j); + y = m.x0 * m.yx - m.y0 * (m.xx + i); + + den = 1 / den; + x *= den; + y *= den; + + new_norm = fmax (fabs (x), fabs (y)); + if (norm > new_norm) { + norm = new_norm; + tx = x; + ty = y; + } + } + } + + tx = floor (tx); + ty = floor (ty); + *x_offset = -tx; + *y_offset = -ty; + cairo_matrix_translate (&m, tx, ty); + } else { + *x_offset = 0; + *y_offset = 0; + } + + return _cairo_matrix_to_pixman_matrix (&m, out_transform, xc, yc); } } diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c index 34a5cf44..c69e70ba 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -2126,7 +2126,7 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat pixman_transform_t pixman_transform; cairo_status_t status; cairo_bool_t repeat = FALSE; - + int ix, iy; pixman_gradient_stop_t pixman_stops_static[2]; pixman_gradient_stop_t *pixman_stops = pixman_stops_static; unsigned int i; @@ -2286,12 +2286,21 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat return image->base.status; } - _cairo_matrix_to_pixman_matrix (&matrix, &pixman_transform, - width/2., height/2.); - if (!pixman_image_set_transform (pixman_image, &pixman_transform)) { - cairo_surface_destroy (&image->base); - pixman_image_unref (pixman_image); - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + ix = x; + iy = y; + status = _cairo_matrix_to_pixman_matrix_offset (&matrix, + pattern->base.filter, + width/2., height/2., + &pixman_transform, + &ix, &iy); + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { + if (unlikely (status != CAIRO_STATUS_SUCCESS) || + ! pixman_image_set_transform (pixman_image, &pixman_transform)) + { + cairo_surface_destroy (&image->base); + pixman_image_unref (pixman_image); + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + } } switch (pattern->base.extend) { @@ -2313,7 +2322,7 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat pixman_image, NULL, image->pixman_image, - x, y, + ix, iy, 0, 0, 0, 0, width, height); diff --git a/src/cairo-xcb-surface-render.c b/src/cairo-xcb-surface-render.c index e3d1ac47..068ab3d3 100644 --- a/src/cairo-xcb-surface-render.c +++ b/src/cairo-xcb-surface-render.c @@ -428,33 +428,6 @@ _pattern_is_supported (uint32_t flags, return pattern->type != CAIRO_PATTERN_TYPE_MESH; } -static double -_pixman_nearest_sample (double d) -{ - return ceil (d - .5); -} - -static cairo_bool_t -_nearest_sample (const cairo_matrix_t *m, - cairo_filter_t filter, - double *tx, double *ty) -{ - *tx = m->x0; - *ty = m->y0; - if ((filter == CAIRO_FILTER_FAST || filter == CAIRO_FILTER_NEAREST) - && _cairo_matrix_has_unity_scale (m)) - { - *tx = _pixman_nearest_sample (*tx); - *ty = _pixman_nearest_sample (*ty); - } - else - { - if (*tx != floor (*tx) || *ty != floor (*ty)) - return FALSE; - } - return fabs (*tx) < PIXMAN_MAX_INT && fabs (*ty) < PIXMAN_MAX_INT; -} - static void _cairo_xcb_picture_set_matrix (cairo_xcb_picture_t *picture, const cairo_matrix_t *matrix, @@ -462,46 +435,19 @@ _cairo_xcb_picture_set_matrix (cairo_xcb_picture_t *picture, double xc, double yc) { xcb_render_transform_t transform; - cairo_matrix_t m; - double tx, ty; - - m = *matrix; - if (_nearest_sample (&m, filter, &tx, &ty)) - m.x0 = m.y0 = 0; - else - tx = ty = 0; - - if (! _cairo_matrix_is_identity (&m)) { - cairo_matrix_t inv; - cairo_status_t status; - - inv = m; - status = cairo_matrix_invert (&inv); - assert (status == CAIRO_STATUS_SUCCESS); - - if (m.x0 != 0. || m.y0 != 0.) { - double x, y; - - /* pixman also limits the [xy]_offset to 16 bits so evenly - * spread the bits between the two. - */ - x = floor (inv.x0 / 2); - y = floor (inv.y0 / 2); - tx = -x; - ty = -y; - cairo_matrix_init_translate (&inv, x, y); - cairo_matrix_multiply (&m, &inv, &m); - } else { - if (tx != 0. || ty != 0.) - cairo_matrix_transform_point (&inv, &tx, &ty); - } - } + pixman_transform_t *pixman_transform; + cairo_status_t ignored; /* Casting between pixman_transform_t and xcb_render_transform_t is safe * because they happen to be the exact same type. */ - _cairo_matrix_to_pixman_matrix (&m, - (pixman_transform_t *) &transform, xc, yc); + pixman_transform = (pixman_transform_t *) &transform; + + picture->x = picture->x0; + picture->y = picture->y0; + ignored = _cairo_matrix_to_pixman_matrix_offset (matrix, filter, xc, yc, + pixman_transform, + &picture->x, &picture->y); if (memcmp (&picture->transform, &transform, sizeof (xcb_render_transform_t))) { _cairo_xcb_connection_render_set_picture_transform (_picture_to_connection (picture), @@ -510,9 +456,6 @@ _cairo_xcb_picture_set_matrix (cairo_xcb_picture_t *picture, picture->transform = transform; } - - picture->x = picture->x0 + tx; - picture->y = picture->y0 + ty; } static void diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c index 76cf5d15..f59b61b2 100644 --- a/src/cairo-xlib-surface.c +++ b/src/cairo-xlib-surface.c @@ -1623,17 +1623,28 @@ static cairo_status_t _cairo_xlib_surface_set_matrix (cairo_xlib_display_t *display, cairo_xlib_surface_t *surface, const cairo_matrix_t *matrix, + cairo_filter_t filter, double xc, - double yc) + double yc, + int *x_offset, + int *y_offset) { XTransform xtransform; + pixman_transform_t *pixman_transform; + cairo_status_t status; /* Casting between pixman_transform_t and XTransform is safe because * they happen to be the exact same type. */ - _cairo_matrix_to_pixman_matrix (matrix, - (pixman_transform_t *) &xtransform, - xc, yc); + pixman_transform = (pixman_transform_t *) &xtransform; + + status = _cairo_matrix_to_pixman_matrix_offset (matrix, filter, xc, yc, + pixman_transform, + x_offset, y_offset); + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) + status = CAIRO_STATUS_SUCCESS; + if (unlikely (status != CAIRO_STATUS_SUCCESS)) + return status; if (memcmp (&xtransform, &surface->xtransform, sizeof (XTransform)) == 0) return CAIRO_STATUS_SUCCESS; @@ -1757,11 +1768,11 @@ _cairo_xlib_surface_set_component_alpha (cairo_xlib_surface_t *surface, } static cairo_int_status_t -_cairo_xlib_surface_set_attributes (cairo_xlib_display_t *display, - cairo_xlib_surface_t *surface, - const cairo_surface_attributes_t *attributes, - double xc, - double yc) +_cairo_xlib_surface_set_attributes (cairo_xlib_display_t *display, + cairo_xlib_surface_t *surface, + cairo_surface_attributes_t *attributes, + double xc, + double yc) { cairo_int_status_t status; XRenderPictureAttributes pa; @@ -1770,7 +1781,11 @@ _cairo_xlib_surface_set_attributes (cairo_xlib_display_t *display, _cairo_xlib_surface_ensure_src_picture (display, surface); status = _cairo_xlib_surface_set_matrix (display, surface, - &attributes->matrix, xc, yc); + &attributes->matrix, + attributes->filter, + xc, yc, + &attributes->x_offset, + &attributes->y_offset); if (unlikely (status)) return status; diff --git a/src/cairoint.h b/src/cairoint.h index 780ee05d..6c34c5b7 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -2070,11 +2070,20 @@ cairo_private double _cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, double radius) cairo_pure; -cairo_private void -_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix, - pixman_transform_t *pixman_transform, - double xc, - double yc); +cairo_private cairo_bool_t +_cairo_matrix_is_pixman_translation (const cairo_matrix_t *matrix, + cairo_filter_t filter, + int *out_x_offset, + int *out_y_offset); + +cairo_private cairo_status_t +_cairo_matrix_to_pixman_matrix_offset (const cairo_matrix_t *matrix, + cairo_filter_t filter, + double xc, + double yc, + pixman_transform_t *out_transform, + int *out_x_offset, + int *out_y_offset); /* cairo-traps.c */ cairo_private void |