1 // Take a look at the license at the top of the repository in the LICENSE file.
2 
3 use crate::error::Error;
4 use crate::utils::status_to_result;
5 use libc::c_double;
6 
7 #[repr(C)]
8 #[derive(Debug, Clone, Copy, PartialEq)]
9 pub struct Matrix {
10     pub xx: c_double,
11     pub yx: c_double,
12 
13     pub xy: c_double,
14     pub yy: c_double,
15 
16     pub x0: c_double,
17     pub y0: c_double,
18 }
19 
20 impl Default for Matrix {
default() -> Self21     fn default() -> Self {
22         Self::identity()
23     }
24 }
25 
26 impl Matrix {
ptr(&self) -> *const ffi::Matrix27     pub(crate) fn ptr(&self) -> *const ffi::Matrix {
28         self as *const Matrix as _
29     }
30 
mut_ptr(&mut self) -> *mut ffi::Matrix31     pub(crate) fn mut_ptr(&mut self) -> *mut ffi::Matrix {
32         self as *mut Matrix as _
33     }
34 
null() -> Self35     pub(crate) fn null() -> Self {
36         Self {
37             xx: 0.0,
38             yx: 0.0,
39             xy: 0.0,
40             yy: 0.0,
41             x0: 0.0,
42             y0: 0.0,
43         }
44     }
45 
identity() -> Self46     pub fn identity() -> Self {
47         Self {
48             xx: 1.0,
49             yx: 0.0,
50             xy: 0.0,
51             yy: 1.0,
52             x0: 0.0,
53             y0: 0.0,
54         }
55     }
56 
new(xx: f64, yx: f64, xy: f64, yy: f64, x0: f64, y0: f64) -> Self57     pub fn new(xx: f64, yx: f64, xy: f64, yy: f64, x0: f64, y0: f64) -> Self {
58         Self {
59             xx,
60             yx,
61             xy,
62             yy,
63             x0,
64             y0,
65         }
66     }
67 
68     #[doc(alias = "cairo_matrix_multiply")]
multiply(left: &Matrix, right: &Matrix) -> Matrix69     pub fn multiply(left: &Matrix, right: &Matrix) -> Matrix {
70         let mut matrix = Self::null();
71         unsafe {
72             ffi::cairo_matrix_multiply(matrix.mut_ptr(), left.ptr(), right.ptr());
73         }
74         matrix
75     }
76 
77     #[doc(alias = "cairo_matrix_translate")]
translate(&mut self, tx: f64, ty: f64)78     pub fn translate(&mut self, tx: f64, ty: f64) {
79         unsafe { ffi::cairo_matrix_translate(self.mut_ptr(), tx, ty) }
80     }
81 
82     #[doc(alias = "cairo_matrix_scale")]
scale(&mut self, sx: f64, sy: f64)83     pub fn scale(&mut self, sx: f64, sy: f64) {
84         unsafe { ffi::cairo_matrix_scale(self.mut_ptr(), sx, sy) }
85     }
86 
87     #[doc(alias = "cairo_matrix_rotate")]
rotate(&mut self, angle: f64)88     pub fn rotate(&mut self, angle: f64) {
89         unsafe { ffi::cairo_matrix_rotate(self.mut_ptr(), angle) }
90     }
91 
92     #[doc(alias = "cairo_matrix_invert")]
invert(&mut self)93     pub fn invert(&mut self) {
94         let status = unsafe { ffi::cairo_matrix_invert(self.mut_ptr()) };
95         status_to_result(status).expect("Failed to invert the matrix")
96     }
97 
98     #[doc(alias = "cairo_matrix_invert")]
try_invert(&self) -> Result<Matrix, Error>99     pub fn try_invert(&self) -> Result<Matrix, Error> {
100         let mut matrix = *self;
101 
102         let status = unsafe { ffi::cairo_matrix_invert(matrix.mut_ptr()) };
103         status_to_result(status)?;
104         Ok(matrix)
105     }
106 
107     #[doc(alias = "cairo_matrix_transform_distance")]
transform_distance(&self, _dx: f64, _dy: f64) -> (f64, f64)108     pub fn transform_distance(&self, _dx: f64, _dy: f64) -> (f64, f64) {
109         let mut dx = _dx;
110         let mut dy = _dy;
111 
112         unsafe {
113             ffi::cairo_matrix_transform_distance(self.ptr(), &mut dx, &mut dy);
114         }
115         (dx, dy)
116     }
117 
118     #[doc(alias = "cairo_matrix_transform_point")]
transform_point(&self, _x: f64, _y: f64) -> (f64, f64)119     pub fn transform_point(&self, _x: f64, _y: f64) -> (f64, f64) {
120         let mut x = _x;
121         let mut y = _y;
122 
123         unsafe {
124             ffi::cairo_matrix_transform_point(self.ptr(), &mut x, &mut y);
125         }
126         (x, y)
127     }
128 }
129 
130 #[cfg(test)]
131 mod tests {
132     use super::*;
133 
134     #[test]
memory_layout_is_ffi_equivalent()135     fn memory_layout_is_ffi_equivalent() {
136         macro_rules! dummy_values {
137             ($Matrix: ident) => {
138                 $Matrix {
139                     xx: 1.0,
140                     yx: 2.0,
141                     xy: 3.0,
142                     yy: 4.0,
143                     x0: 5.0,
144                     y0: 6.0,
145                 }
146             };
147         }
148         use crate::ffi::Matrix as FfiMatrix;
149         let transmuted: Matrix = unsafe { std::mem::transmute(dummy_values!(FfiMatrix)) };
150         assert_eq!(transmuted, dummy_values!(Matrix));
151     }
152 
153     #[test]
invalid_matrix_does_not_invert()154     fn invalid_matrix_does_not_invert() {
155         let matrix = Matrix::null();
156         assert!(matrix.try_invert().is_err());
157     }
158 
159     #[test]
160     #[should_panic]
inverting_invalid_matrix_panics()161     fn inverting_invalid_matrix_panics() {
162         let mut matrix = Matrix::null();
163         matrix.invert();
164     }
165 
166     #[test]
valid_matrix_try_invert()167     fn valid_matrix_try_invert() {
168         let matrix = Matrix::identity();
169         assert_eq!(matrix.try_invert().unwrap(), Matrix::identity());
170     }
171 
172     #[test]
valid_matrix_invert()173     fn valid_matrix_invert() {
174         let mut matrix = Matrix::identity();
175         matrix.invert();
176         assert_eq!(matrix, Matrix::identity());
177     }
178 }
179