10.8 Transforms
The fz_matrix structure is used to represent 2 dimensional matrices used for
transforming points, shapes and other geometry.
The six fields of the fz_matrix structure correspond to a matrix of the
form:
Such transformation matrices can be used to represent a wide range of different
operations, including translations, rotations, scales, sheers, and any combination
thereof.
Typically, a matrix will be created for a specific purpose, such as a scale, or a
translation. For this reason, we have dedicated construction calls.
/* fz_scale: Create a scaling matrix. The returned matrix is of the form [ sx 0 0 sy 0 0 ]. m: Pointer to the matrix to populate sx, sy: Scaling factors along the X- and Y-axes. A scaling factor of 1.0 will not cause any scaling along the relevant axis. Returns m. Does not throw exceptions. */ fz_matrix *fz_scale(fz_matrix *m, float sx, float sy); /* fz_shear: Create a shearing matrix. The returned matrix is of the form [ 1 sy sx 1 0 0 ]. m: pointer to place to store returned matrix sx, sy: Shearing factors. A shearing factor of 0.0 will not cause any shearing along the relevant axis. Returns m. Does not throw exceptions. */ fz_matrix *fz_shear(fz_matrix *m, float sx, float sy); /* fz_rotate: Create a rotation matrix. The returned matrix is of the form [ cos(deg) sin(deg) -sin(deg) cos(deg) 0 0 ]. m: Pointer to place to store matrix degrees: Degrees of counter clockwise rotation. Values less than zero and greater than 360 are handled as expected. Returns m. Does not throw exceptions. */ fz_matrix *fz_rotate(fz_matrix *m, float degrees); /* fz_translate: Create a translation matrix. The returned matrix is of the form [ 1 0 0 1 tx ty ]. m: A place to store the created matrix. tx, ty: Translation distances along the X- and Y-axes. A translation of 0 will not cause any translation along the relevant axis. Returns m. Does not throw exceptions. */ fz_matrix *fz_translate(fz_matrix *m, float tx, float ty);
Mathematically, points are transformed by multiplying them (extended to 3 elements
long). For example (x’
,y’
), the point given by mapping (x
,y
) through such a matrix is calculated as follows:
There are various functions in MuPDF to perform such transformations:
/* fz_transform_point: Apply a transformation to a point. transform: Transformation matrix to apply. See fz_concat, fz_scale, fz_rotate and fz_translate for how to create a matrix. point: Pointer to point to update. Returns transform (unchanged). Does not throw exceptions. */ fz_point *fz_transform_point(fz_point *restrict point, const fz_matrix *restrict transform); fz_point *fz_transform_point_xy(fz_point *restrict point, const fz_matrix *restrict transform, float x, float y);
Rectangles can be transformed using the following function, which allows for
the fact that the image of a rectangle may ‘flip’ the rectangle (i.e. that a
minimum coordinate may end up as a maximum one after translation, and vice
versa):
/* fz_transform_rect: Apply a transform to a rectangle. After the four corner points of the axis-aligned rectangle have been transformed it may not longer be axis-aligned. So a new axis-aligned rectangle is created covering at least the area of the transformed rectangle. transform: Transformation matrix to apply. See fz_concat, fz_scale and fz_rotate for how to create a matrix. rect: Rectangle to be transformed. The two special cases fz_empty_rect and fz_infinite_rect, may be used but are returned unchanged as expected. Does not throw exceptions. */ fz_rect *fz_transform_rect(fz_rect *restrict rect, const fz_matrix *restrict transform);
Also, it can be useful to transform a point, ignoring the translation components of a
transformation, so we have a convenience function for this:
/* fz_transform_vector: Apply a transformation to a vector. transform: Transformation matrix to apply. See fz_concat, fz_scale and fz_rotate for how to create a matrix. Any translation will be ignored. vector: Pointer to vector to update. Does not throw exceptions. */ fz_point *fz_transform_vector(fz_point *restrict vector, const fz_matrix *restrict transform);
Transformations can be combined by multiplying their representative matrices
together. Transforming a point by applying matrix A then matrix B, will give
identical results to transforming the point by AB.
MuPDF provides an API for combining matrices in this way:
/* fz_concat: Multiply two matrices. The order of the two matrices are important since matrix multiplication is not commutative. Returns result. Does not throw exceptions. */ fz_matrix *fz_concat(fz_matrix *result, const fz_matrix *left, const fz_matrix *right);
Alternatively, operations can be specifically applied to existing matrices. Because of
the non-commutative nature of matrix operations, it matters whether the new
operation is applied before or after the existing matrix.
For example, if you have a matrix that performs a rotation, and you wish to combine
that with a translation, you must decide whether you want the translation to occur
before the rotation (‘pre’) or afterwards (‘post’).
MuPDF has various API functions for such operations:
/* fz_pre_scale: Scale a matrix by premultiplication. m: Pointer to the matrix to scale sx, sy: Scaling factors along the X- and Y-axes. A scaling factor of 1.0 will not cause any scaling along the relevant axis. Returns m (updated). Does not throw exceptions. */ fz_matrix *fz_pre_scale(fz_matrix *m, float sx, float sy); /* fz_post_scale: Scale a matrix by postmultiplication. m: Pointer to the matrix to scale sx, sy: Scaling factors along the X- and Y-axes. A scaling factor of 1.0 will not cause any scaling along the relevant axis. Returns m (updated). Does not throw exceptions. */ fz_matrix *fz_post_scale(fz_matrix *m, float sx, float sy); /* fz_pre_shear: Premultiply a matrix with a shearing matrix. The shearing matrix is of the form [ 1 sy sx 1 0 0 ]. m: pointer to matrix to premultiply sx, sy: Shearing factors. A shearing factor of 0.0 will not cause any shearing along the relevant axis. Returns m (updated). Does not throw exceptions. */ fz_matrix *fz_pre_shear(fz_matrix *m, float sx, float sy); /* fz_pre_rotate: Rotate a transformation by premultiplying. The premultiplied matrix is of the form [ cos(deg) sin(deg) -sin(deg) cos(deg) 0 0 ]. m: Pointer to matrix to premultiply. degrees: Degrees of counter clockwise rotation. Values less than zero and greater than 360 are handled as expected. Returns m (updated). Does not throw exceptions. */ fz_matrix *fz_pre_rotate(fz_matrix *m, float degrees); /* fz_pre_translate: Translate a matrix by premultiplication. m: The matrix to translate tx, ty: Translation distances along the X- and Y-axes. A translation of 0 will not cause any translation along the relevant axis. Returns m. Does not throw exceptions. */ fz_matrix *fz_pre_translate(fz_matrix *m, float tx, float ty);
Finally, sometimes it is useful to find the matrix that would represent the reverse
of a given transformation. This can be achieved by ‘inverting’ the matrix.
This is not possible in all cases, but can be achieved for most ‘well-behaved’
transformations.
/* fz_invert_matrix: Create an inverse matrix. inverse: Place to store inverse matrix. matrix: Matrix to invert. A degenerate matrix, where the determinant is equal to zero, can not be inverted and the original matrix is returned instead. Returns inverse. Does not throw exceptions. */ fz_matrix *fz_invert_matrix(fz_matrix *inverse, const fz_matrix *matrix); /* fz_try_invert_matrix: Attempt to create an inverse matrix. inverse: Place to store inverse matrix. matrix: Matrix to invert. A degenerate matrix, where the determinant is equal to zero, can not be inverted. Returns 1 if matrix is degenerate (singular), or 0 otherwise. Does not throw exceptions. */ int fz_try_invert_matrix(fz_matrix *inverse, const fz_matrix *matrix);