#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
/** * SECTION:cairo-matrix * @Title: cairo_matrix_t * @Short_Description: Generic matrix operations * @See_Also: #cairo_t * * #cairo_matrix_t is used throughout cairo to convert between different * coordinate spaces. A #cairo_matrix_t holds an affine transformation, * such as a scale, rotation, shear, or a combination of these. * The transformation of a point (<literal>x</literal>,<literal>y</literal>) * is given by: * * <programlisting> * x_new = xx * x + xy * y + x0; * y_new = yx * x + yy * y + y0; * </programlisting> * * The current transformation matrix of a #cairo_t, represented as a * #cairo_matrix_t, defines the transformation from user-space * coordinates to device-space coordinates. See cairo_get_matrix() and * cairo_set_matrix().
**/
/** * cairo_matrix_init_identity: * @matrix: a #cairo_matrix_t * * Modifies @matrix to be an identity transformation. * * Since: 1.0
**/ void
cairo_matrix_init_identity (cairo_matrix_t *matrix)
{
cairo_matrix_init (matrix,
1, 0,
0, 1,
0, 0);
}
/** * cairo_matrix_init: * @matrix: a #cairo_matrix_t * @xx: xx component of the affine transformation * @yx: yx component of the affine transformation * @xy: xy component of the affine transformation * @yy: yy component of the affine transformation * @x0: X translation component of the affine transformation * @y0: Y translation component of the affine transformation * * Sets @matrix to be the affine transformation given by * @xx, @yx, @xy, @yy, @x0, @y0. The transformation is given * by: * <programlisting> * x_new = xx * x + xy * y + x0; * y_new = yx * x + yy * y + y0; * </programlisting> * * Since: 1.0
**/ void
cairo_matrix_init (cairo_matrix_t *matrix, double xx, double yx,
/** * _cairo_matrix_get_affine: * @matrix: a #cairo_matrix_t * @xx: location to store xx component of matrix * @yx: location to store yx component of matrix * @xy: location to store xy component of matrix * @yy: location to store yy component of matrix * @x0: location to store x0 (X-translation component) of matrix, or %NULL * @y0: location to store y0 (Y-translation component) of matrix, or %NULL * * Gets the matrix values for the affine transformation that @matrix represents. * See cairo_matrix_init(). * * * This function is a leftover from the old public API, but is still * mildly useful as an internal means for getting at the matrix * members in a positional way. For example, when reassigning to some * external matrix type, or when renaming members to more meaningful * names (such as a,b,c,d,e,f) for particular manipulations.
**/ void
_cairo_matrix_get_affine (const cairo_matrix_t *matrix, double *xx, double *yx, double *xy, double *yy, double *x0, double *y0)
{
*xx = matrix->xx;
*yx = matrix->yx;
*xy = matrix->xy;
*yy = matrix->yy;
if (x0)
*x0 = matrix->x0; if (y0)
*y0 = matrix->y0;
}
/** * cairo_matrix_init_translate: * @matrix: a #cairo_matrix_t * @tx: amount to translate in the X direction * @ty: amount to translate in the Y direction * * Initializes @matrix to a transformation that translates by @tx and * @ty in the X and Y dimensions, respectively. * * Since: 1.0
**/ void
cairo_matrix_init_translate (cairo_matrix_t *matrix, double tx, double ty)
{
cairo_matrix_init (matrix,
1, 0,
0, 1,
tx, ty);
}
/** * cairo_matrix_translate: * @matrix: a #cairo_matrix_t * @tx: amount to translate in the X direction * @ty: amount to translate in the Y direction * * Applies a translation by @tx, @ty to the transformation in * @matrix. The effect of the new transformation is to first translate * the coordinates by @tx and @ty, then apply the original transformation * to the coordinates. * * Since: 1.0
**/ void
cairo_matrix_translate (cairo_matrix_t *matrix, double tx, double ty)
{
cairo_matrix_t tmp;
cairo_matrix_init_translate (&tmp, tx, ty);
cairo_matrix_multiply (matrix, &tmp, matrix);
}
/** * cairo_matrix_init_scale: * @matrix: a #cairo_matrix_t * @sx: scale factor in the X direction * @sy: scale factor in the Y direction * * Initializes @matrix to a transformation that scales by @sx and @sy * in the X and Y dimensions, respectively. * * Since: 1.0
**/ void
cairo_matrix_init_scale (cairo_matrix_t *matrix, double sx, double sy)
{
cairo_matrix_init (matrix,
sx, 0,
0, sy,
0, 0);
}
/** * cairo_matrix_scale: * @matrix: a #cairo_matrix_t * @sx: scale factor in the X direction * @sy: scale factor in the Y direction * * Applies scaling by @sx, @sy to the transformation in @matrix. The * effect of the new transformation is to first scale the coordinates * by @sx and @sy, then apply the original transformation to the coordinates. * * Since: 1.0
**/ void
cairo_matrix_scale (cairo_matrix_t *matrix, double sx, double sy)
{
cairo_matrix_t tmp;
cairo_matrix_init_scale (&tmp, sx, sy);
cairo_matrix_multiply (matrix, &tmp, matrix);
}
/** * cairo_matrix_init_rotate: * @matrix: a #cairo_matrix_t * @radians: angle of rotation, in radians. The direction of rotation * is defined such that positive angles rotate in the direction from * the positive X axis toward the positive Y axis. With the default * axis orientation of cairo, positive angles rotate in a clockwise * direction. * * Initialized @matrix to a transformation that rotates by @radians. * * Since: 1.0
**/ void
cairo_matrix_init_rotate (cairo_matrix_t *matrix, double radians)
{ double s; double c;
s = sin (radians);
c = cos (radians);
cairo_matrix_init (matrix,
c, s,
-s, c,
0, 0);
}
/** * cairo_matrix_rotate: * @matrix: a #cairo_matrix_t * @radians: angle of rotation, in radians. The direction of rotation * is defined such that positive angles rotate in the direction from * the positive X axis toward the positive Y axis. With the default * axis orientation of cairo, positive angles rotate in a clockwise * direction. * * Applies rotation by @radians to the transformation in * @matrix. The effect of the new transformation is to first rotate the * coordinates by @radians, then apply the original transformation * to the coordinates. * * Since: 1.0
**/ void
cairo_matrix_rotate (cairo_matrix_t *matrix, double radians)
{
cairo_matrix_t tmp;
cairo_matrix_init_rotate (&tmp, radians);
cairo_matrix_multiply (matrix, &tmp, matrix);
}
/** * cairo_matrix_multiply: * @result: a #cairo_matrix_t in which to store the result * @a: a #cairo_matrix_t * @b: a #cairo_matrix_t * * Multiplies the affine transformations in @a and @b together * and stores the result in @result. The effect of the resulting * transformation is to first apply the transformation in @a to the * coordinates and then apply the transformation in @b to the * coordinates. * * It is allowable for @result to be identical to either @a or @b. * * Since: 1.0
**/ /* * XXX: The ordering of the arguments to this function corresponds * to [row_vector]*A*B. If we want to use column vectors instead, * then we need to switch the two arguments and fix up all * uses.
*/ void
cairo_matrix_multiply (cairo_matrix_t *result, const cairo_matrix_t *a, const cairo_matrix_t *b)
{
cairo_matrix_t r;
/** * cairo_matrix_transform_distance: * @matrix: a #cairo_matrix_t * @dx: X component of a distance vector. An in/out parameter * @dy: Y component of a distance vector. An in/out parameter * * Transforms the distance vector (@dx,@dy) by @matrix. This is * similar to cairo_matrix_transform_point() except that the translation * components of the transformation are ignored. The calculation of * the returned vector is as follows: * * <programlisting> * dx_new = xx * dx + xy * dy; * dy_new = yx * dx + yy * dy; * </programlisting> * * Since: 1.0
**/ void
cairo_matrix_transform_distance (const cairo_matrix_t *matrix, double *dx, double *dy)
{ double new_x, new_y;
if (is_tight) { /* it's tight if and only if the four corner points form an axis-aligned rectangle. And that's true if and only if we can derive corners 0 and 3 from corners 1 and 2 in one of two straightforward ways... We could use a tolerance here but for now we'll fall back to FALSE in the case of floating point error.
*/
*is_tight =
(quad_x[1] == quad_x[0] && quad_y[1] == quad_y[3] &&
quad_x[2] == quad_x[3] && quad_y[2] == quad_y[0]) ||
(quad_x[1] == quad_x[3] && quad_y[1] == quad_y[0] &&
quad_x[2] == quad_x[0] && quad_y[2] == quad_y[3]);
}
}
/* This function isn't a correct adjoint in that the implicit 1 in the homogeneous result should actually be ad-bc instead. But, since this adjoint is only used in the computation of the inverse, which
divides by det (A)=ad-bc anyway, everything works out in the end. */ staticvoid
_cairo_matrix_compute_adjoint (cairo_matrix_t *matrix)
{ /* adj (A) = transpose (C:cofactor (A,i,j)) */ double a, b, c, d, tx, ty;
/** * cairo_matrix_invert: * @matrix: a #cairo_matrix_t * * Changes @matrix to be the inverse of its original value. Not * all transformation matrices have inverses; if the matrix * collapses points together (it is <firstterm>degenerate</firstterm>), * then it has no inverse and this function will fail. * * Returns: If @matrix has an inverse, modifies @matrix to * be the inverse matrix and returns %CAIRO_STATUS_SUCCESS. Otherwise, * returns %CAIRO_STATUS_INVALID_MATRIX. * * Since: 1.0
**/
cairo_status_t
cairo_matrix_invert (cairo_matrix_t *matrix)
{ double det;
a = matrix->xx; b = matrix->yx;
c = matrix->xy; d = matrix->yy;
return a*d - b*c;
}
/** * _cairo_matrix_compute_basis_scale_factors: * @matrix: a matrix * @basis_scale: the scale factor in the direction of basis * @normal_scale: the scale factor in the direction normal to the basis * @x_basis: basis to use. X basis if true, Y basis otherwise. * * Computes |Mv| and det(M)/|Mv| for v=[1,0] if x_basis is true, and v=[0,1] * otherwise, and M is @matrix. * * Return value: the scale factor of @matrix on the height of the font, * or 1.0 if @matrix is %NULL.
**/
cairo_status_t
_cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix, double *basis_scale, double *normal_scale,
cairo_bool_t x_basis)
{ double det;
det = _cairo_matrix_compute_determinant (matrix);
if (! ISFINITE (det)) return _cairo_error (CAIRO_STATUS_INVALID_MATRIX);
if (det == 0)
{
*basis_scale = *normal_scale = 0;
} else
{ double x = x_basis != 0; double y = x == 0; double major, minor;
cairo_matrix_transform_distance (matrix, &x, &y);
major = hypot (x, y); /* * ignore mirroring
*/ if (det < 0)
det = -det; if (major)
minor = det / major; else
minor = 0.0; if (x_basis)
{
*basis_scale = major;
*normal_scale = minor;
} else
{
*basis_scale = minor;
*normal_scale = major;
}
}
return CAIRO_STATUS_SUCCESS;
}
cairo_bool_t
_cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix, int *itx, int *ity)
{ if (_cairo_matrix_is_translation (matrix))
{
cairo_fixed_t x0_fixed = _cairo_fixed_from_double (matrix->x0);
cairo_fixed_t y0_fixed = _cairo_fixed_from_double (matrix->y0);
if (_cairo_fixed_is_integer (x0_fixed) &&
_cairo_fixed_is_integer (y0_fixed))
{ if (itx)
*itx = _cairo_fixed_integer_part (x0_fixed); if (ity)
*ity = _cairo_fixed_integer_part (y0_fixed);
returnTRUE;
}
}
returnFALSE;
}
#define SCALING_EPSILON _cairo_fixed_to_double(1)
/* This only returns true if the matrix is 90 degree rotations or * flips. It appears calling code is relying on this. It will return * false for other rotations even if the scale is one. Approximations * are allowed to handle matricies filled in using trig functions * such as sin(M_PI_2).
*/
cairo_bool_t
_cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix)
{ /* check that the determinant is near +/-1 */ double det = _cairo_matrix_compute_determinant (matrix); if (fabs (det * det - 1.0) < SCALING_EPSILON) { /* check that one axis is close to zero */ if (fabs (matrix->xy) < SCALING_EPSILON &&
fabs (matrix->yx) < SCALING_EPSILON) returnTRUE; if (fabs (matrix->xx) < SCALING_EPSILON &&
fabs (matrix->yy) < SCALING_EPSILON) returnTRUE; /* If rotations are allowed then it must instead test for * orthogonality. This is xx*xy+yx*yy ~= 0.
*/
} returnFALSE;
}
/* By pixel exact here, we mean a matrix that is composed only of * 90 degree rotations, flips, and integer translations and produces a 1:1 * mapping between source and destination pixels. If we transform an image * with a pixel-exact matrix, filtering is not useful.
*/
cairo_bool_t
_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix)
{
cairo_fixed_t x0_fixed, y0_fixed;
if (! _cairo_matrix_has_unity_scale (matrix)) returnFALSE;
/* A circle in user space is transformed into an ellipse in device space.
The following is a derivation of a formula to calculate the length of the major axis for this ellipse; this is useful for error bounds calculations.
Thanks to Walter Brisken <wbrisken@aoc.nrao.edu> for this derivation:
1. First some notation:
All capital letters represent vectors in two dimensions. A prime ' represents a transformed coordinate. Matrices are written in underlined form, ie _R_. Lowercase letters represent scalar real values.
2. The question has been posed: What is the maximum expansion factor achieved by the linear transformation
X' = X _R_
where _R_ is a real-valued 2x2 matrix with entries:
_R_ = [a b] [c d] .
In other words, what is the maximum radius, MAX[ |X'| ], reached for any X on the unit circle ( |X| = 1 ) ?
3. Some useful formulae
(A) through (C) below are standard double-angle formulae. (D) is a lesser known result and is derived below:
f = (a² + b² + c² + d²)/2 g = (a² + b² - c² - d²)/2 h = (a*c + d*d) φ = 2*θ
It is clear that MAX[ |X'| ] = sqrt(MAX[ r² ]). Here we determine MAX[ r² ] using (D) from above:
MAX[ r² ] = f + sqrt(g² + h²)
And finally
MAX[ |X'| ] = sqrt( f + sqrt(g² + h²) )
Which is the solution to this problem.
Walter Brisken 2004/10/08
(Note that the minor axis length is at the minimum of the above solution, which is just sqrt ( f - sqrt(g² + h²) ) given the symmetry of (D)).
For another derivation of the same result, using Singular Value Decomposition, see doc/tutorial/src/singular.c.
*/
/* determine the length of the major axis of a circle of the given radius
after applying the transformation matrix. */ double
_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix, double radius)
{ double a, b, c, d, f, g, h, i, j;
if (_cairo_matrix_has_unity_scale (matrix)) return radius;
/* 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;
/* 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;
/* 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 (dx == 0 && dy == 0) return CAIRO_STATUS_SUCCESS;
} while (--max_iterations);
/* We didn't find an exact match between cairo and pixman, but
* the matrix should be mostly correct */ return CAIRO_STATUS_SUCCESS;
}
/** * _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)) returnFALSE;
if (matrix->x0 == 0. && matrix->y0 == 0.) returnTRUE;
tx = matrix->x0 + *x_offset;
ty = matrix->y0 + *y_offset;
/** * _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;
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.
*/
tx = m.x0;
ty = m.y0;
norm = MAX (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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.