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
This commit is contained in:
Andrea Canciani 2010-12-17 11:04:41 +01:00
parent ada6057b8c
commit 51594d9787
6 changed files with 353 additions and 272 deletions

View file

@ -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;
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;
}
_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;

View file

@ -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
_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 = {{
static const pixman_transform_t pixman_identity_transform = {{
{1 << 16, 0, 0},
{ 0, 1 << 16, 0},
{ 0, 0, 1 << 16}
}};
if (_cairo_matrix_is_identity (matrix)) {
*pixman_transform = pixman_identity_transform;
} else {
cairo_matrix_t inv;
unsigned max_iterations;
static cairo_status_t
_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix,
pixman_transform_t *pixman_transform,
double xc,
double yc)
{
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[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[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;
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.
/* 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 (! pixman_transform_point_3d (pixman_transform, &vector))
return;
if (!_cairo_matrix_is_translation (matrix))
return FALSE;
x = pixman_fixed_to_double (vector.vector[0]);
y = pixman_fixed_to_double (vector.vector[1]);
cairo_matrix_transform_point (&inv, &x, &y);
tx = matrix->x0 + *x_offset;
ty = matrix->y0 + *y_offset;
/* Ideally, the vector should now be (xc, yc).
* We can now compensate for the resulting error.
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;
}
/**
* _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;
is_pixman_translation = _cairo_matrix_is_pixman_translation (matrix,
filter,
x_offset,
y_offset);
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;
tx = m.x0;
ty = m.y0;
norm = fmax (fabs (tx), fabs (ty));
if (dx == 0 && dy == 0)
break;
} while (--max_iterations);
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);
}
}

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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