mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-07 07:28:02 +02:00
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:
parent
ada6057b8c
commit
51594d9787
6 changed files with 353 additions and 272 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue