skrifa/color/
transform.rs

1//! Contains a [`Transform`] object holding values of an affine transformation matrix.
2use std::ops::{Mul, MulAssign};
3
4use read_fonts::ReadError;
5
6use super::instance::ResolvedPaint;
7
8#[cfg(feature = "libm")]
9#[allow(unused_imports)]
10use core_maths::*;
11
12#[cfg(test)]
13use serde::{Deserialize, Serialize};
14
15#[derive(Clone, Debug, PartialEq)]
16#[cfg_attr(test, derive(Serialize, Deserialize))]
17/// A transformation matrix to be applied to the drawing canvas.
18///
19/// Factors are specified in column-order, meaning that
20/// for a vector `(x,y)` the transformed position `x'` of the vector
21/// is calculated by
22/// `x' = xx * x + xy * y + dx`,
23/// and the transformed position y' is calculated by
24/// `y' = yx * x + yy * y + dy`.
25#[derive(Copy)]
26pub struct Transform {
27    pub xx: f32,
28    pub yx: f32,
29    pub xy: f32,
30    pub yy: f32,
31    pub dx: f32,
32    pub dy: f32,
33}
34
35impl MulAssign for Transform {
36    fn mul_assign(&mut self, rhs: Self) {
37        *self = *self * rhs;
38    }
39}
40
41impl Mul for Transform {
42    type Output = Self;
43
44    fn mul(self, rhs: Self) -> Self::Output {
45        fn muladdmul(a: f32, b: f32, c: f32, d: f32) -> f32 {
46            a * b + c * d
47        }
48        Self {
49            xx: muladdmul(self.xx, rhs.xx, self.xy, rhs.yx),
50            xy: muladdmul(self.xx, rhs.xy, self.xy, rhs.yy),
51            dx: muladdmul(self.xx, rhs.dx, self.xy, rhs.dy) + self.dx,
52            yx: muladdmul(self.yx, rhs.xx, self.yy, rhs.yx),
53            yy: muladdmul(self.yx, rhs.xy, self.yy, rhs.yy),
54            dy: muladdmul(self.yx, rhs.dx, self.yy, rhs.dy) + self.dy,
55        }
56    }
57}
58
59impl Default for Transform {
60    fn default() -> Self {
61        Transform {
62            xx: 1.0,
63            yx: 0.0,
64            xy: 0.0,
65            yy: 1.0,
66            dx: 0.0,
67            dy: 0.0,
68        }
69    }
70}
71
72impl TryFrom<&ResolvedPaint<'_>> for Transform {
73    type Error = ReadError;
74
75    fn try_from(paint: &ResolvedPaint<'_>) -> Result<Self, Self::Error> {
76        match paint {
77            ResolvedPaint::Rotate {
78                angle,
79                around_center,
80                ..
81            } => {
82                let sin_v = (angle * 180.0).to_radians().sin();
83                let cos_v = (angle * 180.0).to_radians().cos();
84                let mut out_transform = Transform {
85                    xx: cos_v,
86                    xy: -sin_v,
87                    yx: sin_v,
88                    yy: cos_v,
89                    ..Default::default()
90                };
91
92                fn scalar_dot_product(a: f32, b: f32, c: f32, d: f32) -> f32 {
93                    a * b + c * d
94                }
95
96                if let Some(center) = around_center {
97                    out_transform.dx = scalar_dot_product(sin_v, center.y, 1.0 - cos_v, center.x);
98                    out_transform.dy = scalar_dot_product(-sin_v, center.x, 1.0 - cos_v, center.y);
99                }
100                Ok(out_transform)
101            }
102            ResolvedPaint::Scale {
103                scale_x,
104                scale_y,
105                around_center,
106                paint: _,
107            } => {
108                let mut out_transform = Transform {
109                    xx: *scale_x,
110                    yy: *scale_y,
111                    ..Transform::default()
112                };
113
114                if let Some(center) = around_center {
115                    out_transform.dx = center.x - scale_x * center.x;
116                    out_transform.dy = center.y - scale_y * center.y;
117                }
118                Ok(out_transform)
119            }
120            ResolvedPaint::Skew {
121                x_skew_angle,
122                y_skew_angle,
123                around_center,
124                paint: _,
125            } => {
126                let tan_x = (x_skew_angle * 180.0).to_radians().tan();
127                let tan_y = (y_skew_angle * 180.0).to_radians().tan();
128                let mut out_transform = Transform {
129                    xy: -tan_x,
130                    yx: tan_y,
131                    ..Transform::default()
132                };
133
134                if let Some(center) = around_center {
135                    out_transform.dx = tan_x * center.y;
136                    out_transform.dy = -tan_y * center.x;
137                }
138                Ok(out_transform)
139            }
140            ResolvedPaint::Transform {
141                xx,
142                yx,
143                xy,
144                yy,
145                dx,
146                dy,
147                paint: _,
148            } => Ok(Transform {
149                xx: *xx,
150                yx: *yx,
151                xy: *xy,
152                yy: *yy,
153                dx: *dx,
154                dy: *dy,
155            }),
156            ResolvedPaint::Translate { dx, dy, .. } => Ok(Transform {
157                dx: *dx,
158                dy: *dy,
159                ..Default::default()
160            }),
161            _ => Err(ReadError::MalformedData(
162                "ResolvedPaint cannot be converted into a transform.",
163            )),
164        }
165    }
166}