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