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:

( a  b  0 )
( c  d  0 )
  e  f  1

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:

                         (  a  b  0 )
(x ′ y′  1 ) = (x  y  1 )(  c  d  0 )
                            e  f  1

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