1use crate::error::Error;
4use crate::utils::status_to_result;
5use libc::c_double;
6
7#[repr(C)]
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub 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
20impl Default for Matrix {
21 fn default() -> Self {
22 Self::identity()
23 }
24}
25
26impl Matrix {
27 pub(crate) fn ptr(&self) -> *const ffi::Matrix {
28 self as *const Matrix as _
29 }
30
31 pub(crate) fn mut_ptr(&mut self) -> *mut ffi::Matrix {
32 self as *mut Matrix as _
33 }
34
35 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
46 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
57 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")]
69 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")]
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")]
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")]
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")]
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")]
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")]
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")]
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)]
131mod tests {
132 use super::*;
133
134 #[test]
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]
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]
161 fn inverting_invalid_matrix_panics() {
162 let mut matrix = Matrix::null();
163 matrix.invert();
164 }
165
166 #[test]
167 fn valid_matrix_try_invert() {
168 let matrix = Matrix::identity();
169 assert_eq!(matrix.try_invert().unwrap(), Matrix::identity());
170 }
171
172 #[test]
173 fn valid_matrix_invert() {
174 let mut matrix = Matrix::identity();
175 matrix.invert();
176 assert_eq!(matrix, Matrix::identity());
177 }
178}