summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrea Canciani <ranma42@gmail.com>2010-12-17 11:04:41 +0100
committerAndrea Canciani <ranma42@gmail.com>2011-01-02 18:31:42 +0100
commit51594d9787905618de608a367c3a5fc0544c52e3 (patch)
tree47487f06cfdac82952d37befa52ba03c5e8e5485
parentada6057b8ccab133909b127850c41abb3216a842 (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.c164
-rw-r--r--src/cairo-matrix.c315
-rw-r--r--src/cairo-pattern.c25
-rw-r--r--src/cairo-xcb-surface-render.c75
-rw-r--r--src/cairo-xlib-surface.c35
-rw-r--r--src/cairoint.h19
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