Skip to main content

ttf_view/types/
affine2x2.rs

1use crate::types::F2Dot14;
2
3/// A simple [`F2Dot14`] 2×2 affine transformation
4/// <span class="hidden">`[xx, yx; xy, yy]`</span>
5/// <math><mo>[</mo><mtable>
6///   <mtr><mtd><mi>xx</mi></mtd><mtd><mi>yx</mi></mtd></mtr>
7///   <mtr><mtd><mi>xy</mi></mtd><mtd><mi>yy</mi></mtd></mtr>
8/// </mtable><mo>]</mo></math>.
9#[derive(Clone, Copy, PartialEq, Eq, Hash)]
10pub struct Affine2x2 {
11    /// X-component of transformed X-basis vector.
12    pub xx: F2Dot14,
13    /// Y-component of transformed X-basis vector (aka. `scale01` in `glyf`).
14    pub yx: F2Dot14, // aka. scale01
15    /// X-component of transformed Y-basis vector (aka. `scale10` in `glyf`).
16    pub xy: F2Dot14, // aka. scale10
17    /// Y-component of transformed Y-basis vector.
18    pub yy: F2Dot14,
19}
20
21const impl Default for Affine2x2 {
22    fn default() -> Self {
23        Self::IDENTITY
24    }
25}
26
27impl Affine2x2 {
28    /// The identity transform
29    /// <span class="hidden">`[1, 0; 0, 1]`</span>
30    /// <math><mo>[</mo><mtable>
31    ///   <mtr><mtd><mn>1</mn></mtd><mtd><mn>0</mn></mtd></mtr>
32    ///   <mtr><mtd><mn>0</mn></mtd><mtd><mn>1</mn></mtd></mtr>
33    /// </mtable><mo>]</mo></math>.
34    ///
35    /// # Examples
36    ///
37    /// ```
38    /// # use ttf_view::types::Affine2x2;
39    /// assert_eq!(Affine2x2::IDENTITY.to_tuple_f32(), (1.0, 0.0, 0.0, 1.0));
40    /// ```
41    pub const IDENTITY: Self = Self::scale(F2Dot14::ONE);
42
43    /// Creates an [`Affine2x2`] with
44    /// <span class="hidden">`[xx, yx; xy, yy]`</span>
45    /// <math><mo>[</mo><mtable>
46    ///   <mtr><mtd><mi>xx</mi></mtd><mtd><mi>yx</mi></mtd></mtr>
47    ///   <mtr><mtd><mi>xy</mi></mtd><mtd><mi>yy</mi></mtd></mtr>
48    /// </mtable><mo>]</mo></math>.
49    pub const fn new(xx: F2Dot14, yx: F2Dot14, xy: F2Dot14, yy: F2Dot14) -> Self {
50        Self { xx, yx, xy, yy }
51    }
52
53    /// Creates an [`Affine2x2`] with
54    /// <span class="hidden">`[scale, 0; 0, scale]`</span>
55    /// <math><mo>[</mo><mtable>
56    ///   <mtr><mtd><mi>scale</mi></mtd><mtd><mn>0</mn></mtd></mtr>
57    ///   <mtr><mtd><mn>0</mn></mtd><mtd><mi>scale</mi></mtd></mtr>
58    /// </mtable><mo>]</mo></math>.
59    pub const fn scale(scale: F2Dot14) -> Self {
60        Self::new(scale, F2Dot14::ZERO, F2Dot14::ZERO, scale)
61    }
62    /// Creates an [`Affine2x2`] with
63    /// <span class="hidden">`[x, 0; 0, y]`</span>
64    /// <math><mo>[</mo><mtable>
65    ///   <mtr><mtd><mi>x</mi></mtd><mtd><mn>0</mn></mtd></mtr>
66    ///   <mtr><mtd><mn>0</mn></mtd><mtd><mi>y</mi></mtd></mtr>
67    /// </mtable><mo>]</mo></math>.
68    pub const fn scale_xy(x: F2Dot14, y: F2Dot14) -> Self {
69        Self::new(x, F2Dot14::ZERO, F2Dot14::ZERO, y)
70    }
71
72    /// Creates an [`Affine2x2`] with
73    /// <span class="hidden">`[cos(θ),-sin(θ);sin(θ),cos(θ)]`</span>
74    /// <math><mo>[</mo><mtable>
75    ///   <mtr><mtd><mi>cos(θ)</mi></mtd><mtd><mi>-sin(θ)</mi></mtd></mtr>
76    ///   <mtr><mtd><mi>sin(θ)</mi></mtd><mtd><mi>cos(θ)</mi></mtd></mtr>
77    /// </mtable><mo>]</mo></math>,
78    /// rotating the image counter-clockwise by specified angle.
79    pub fn rotation(radians: f32) -> Self {
80        let (sin, cos) = radians.sin_cos();
81        Self::new(
82            F2Dot14::new(cos).unwrap(),
83            F2Dot14::new(-sin).unwrap(),
84            F2Dot14::new(sin).unwrap(),
85            F2Dot14::new(cos).unwrap(),
86        )
87    }
88    /// Creates an [`Affine2x2`] with
89    /// <span class="hidden">`[cos(θ),-sin(θ); sin(θ),cos(θ)]`</span>
90    /// <math><mo>[</mo><mtable>
91    ///   <mtr><mtd><mi>cos(θ)</mi></mtd><mtd><mi>-sin(θ)</mi></mtd></mtr>
92    ///   <mtr><mtd><mi>sin(θ)</mi></mtd><mtd><mi>cos(θ)</mi></mtd></mtr>
93    /// </mtable><mo>]</mo></math>,
94    /// rotating the image counter-clockwise by specified angle.
95    pub fn rotation_degrees(degrees: f32) -> Self {
96        Self::rotation(degrees.to_radians())
97    }
98
99    /// Applies this [`Affine2x2`] transformation to the point
100    /// <span class="hidden">`[x, y]`</span>
101    /// <math><mo>\[</mo><mi>x</mi><mo> </mo><mi>y</mi><mo>\]</mo></math>:
102    ///
103    /// <code class="hidden">\[x,y\]×\[xx,yx; xy,yy\]=\[xx\*x+xy\*y, yx\*x+yy\*y\]</code>
104    /// <math>
105    ///   <mrow><mo>\[</mo><mi>x</mi><mo> </mo><mi>y</mi><mo>\]</mo></mrow>
106    ///   <mo>×</mo>
107    ///   <mo>\[</mo><mtable>
108    ///     <mtr><mtd><mi>xx</mi></mtd><mtd><mi>yx</mi></mtd></mtr>
109    ///     <mtr><mtd><mi>xy</mi></mtd><mtd><mi>yy</mi></mtd></mtr>
110    ///   </mtable><mo>\]</mo>
111    ///   <mo>=</mo>
112    ///   <mrow><mo>\[</mo>
113    ///     <mi>xx</mi><mo>\*</mo><mi>x</mi><mo>+</mo><mi>xy</mi><mo>\*</mo><mi>y</mi>
114    ///   <mo>, </mo>
115    ///     <mi>yx</mi><mo>\*</mo><mi>x</mi><mo>+</mo><mi>yy</mi><mo>\*</mo><mi>y</mi>
116    ///   <mo>\]</mo></mrow>
117    /// </math>
118    ///
119    /// # Examples
120    ///
121    /// ```
122    /// use ttf_view::types::Affine2x2;
123    ///
124    /// assert_eq!(Affine2x2::IDENTITY.map_f32(10.0, -10.0), (10.0, -10.0));
125    /// assert_eq!(Affine2x2::IDENTITY.map_f32(-2583.2, 1842.2), (-2583.2, 1842.2));
126    ///
127    /// let rot90 = Affine2x2::rotation_degrees(90.0);
128    /// assert_eq!(rot90.map_f32(23.5, 9.8), (9.8, -23.5));
129    /// ```
130    pub const fn map(&self, x: F2Dot14, y: F2Dot14) -> (F2Dot14, F2Dot14) {
131        let new_x = F2Dot14::saturating_maddp(self.xx, x, self.xy, y, F2Dot14::ZERO);
132        let new_y = F2Dot14::saturating_maddp(self.yx, x, self.yy, y, F2Dot14::ZERO);
133        (new_x, new_y)
134    }
135
136    /// Applies this [`Affine2x2`] transformation to the point
137    /// <span class="hidden">`[x, y]`</span>
138    /// <math><mo>\[</mo><mi>x</mi><mo> </mo><mi>y</mi><mo>\]</mo></math>.
139    pub const fn map_i16(&self, x: i16, y: i16) -> (i16, i16) {
140        // Cast X and Y to F2Dot14 and back. The results will be the same, since F2Dot14 is
141        // essentially just a wrapper over i16, and there's only scaling and no translation.
142        let (x, y) = self.map(F2Dot14::from_frac_num(x), F2Dot14::from_frac_num(y));
143        (x.frac_num(), y.frac_num())
144    }
145    /// Applies this [`Affine2x2`] transformation to the point
146    /// <span class="hidden">`[x, y]`</span>
147    /// <math><mo>\[</mo><mi>x</mi><mo> </mo><mi>y</mi><mo>\]</mo></math>.
148    pub const fn map_f32(&self, x: f32, y: f32) -> (f32, f32) {
149        (self.xx * x + self.xy * y, self.yx * x + self.yy * y)
150    }
151
152    // self  × other
153    // [A B] × [a b] = [Aa+Bc Ab+Bd] = [xx*xx+yx*xy xx*yx+yx*yy]
154    // [C D]   [c d]   [Ca+Dc Cb+Dd]   [xy*xx+yy*xy xy*yx+yy*yy]
155
156    /// Multiplies this [`Affine2x2`] by another, wrapping and truncating at [`F2Dot14`]'s bounds.
157    pub const fn wrapping_mul(self, rhs: Self) -> Self {
158        Self::new(
159            F2Dot14::wrapping_maddp(self.xx, rhs.xx, self.yx, rhs.xy, F2Dot14::ZERO),
160            F2Dot14::wrapping_maddp(self.xx, rhs.yx, self.yx, rhs.yy, F2Dot14::ZERO),
161            F2Dot14::wrapping_maddp(self.xy, rhs.xx, self.yy, rhs.xy, F2Dot14::ZERO),
162            F2Dot14::wrapping_maddp(self.xy, rhs.yx, self.yy, rhs.yy, F2Dot14::ZERO),
163        )
164    }
165    /// Multiplies this [`Affine2x2`] by another, saturating at [`F2Dot14`]'s bounds.
166    pub const fn saturating_mul(self, rhs: Self) -> Self {
167        Self::new(
168            F2Dot14::saturating_maddp(self.xx, rhs.xx, self.yx, rhs.xy, F2Dot14::ZERO),
169            F2Dot14::saturating_maddp(self.xx, rhs.yx, self.yx, rhs.yy, F2Dot14::ZERO),
170            F2Dot14::saturating_maddp(self.xy, rhs.xx, self.yy, rhs.xy, F2Dot14::ZERO),
171            F2Dot14::saturating_maddp(self.xy, rhs.yx, self.yy, rhs.yy, F2Dot14::ZERO),
172        )
173    }
174    /// Multiplies this [`Affine2x2`] by another, returning `None` if overflow occurs.
175    pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
176        Some(Self::new(
177            F2Dot14::checked_maddp(self.xx, rhs.xx, self.yx, rhs.xy, F2Dot14::ZERO)?,
178            F2Dot14::checked_maddp(self.xx, rhs.yx, self.yx, rhs.yy, F2Dot14::ZERO)?,
179            F2Dot14::checked_maddp(self.xy, rhs.xx, self.yy, rhs.xy, F2Dot14::ZERO)?,
180            F2Dot14::checked_maddp(self.xy, rhs.yx, self.yy, rhs.yy, F2Dot14::ZERO)?,
181        ))
182    }
183
184    /// Creates an [`Affine2x2`] from big-endian bytes.
185    pub const fn from_be_bytes(bytes: [u8; 8]) -> Self {
186        let (&[xx, yx, xy, yy], []) = bytes.as_chunks::<2>() else { panic!() };
187        Self::new(
188            F2Dot14::from_be_bytes(xx),
189            F2Dot14::from_be_bytes(yx),
190            F2Dot14::from_be_bytes(xy),
191            F2Dot14::from_be_bytes(yy),
192        )
193    }
194    /// Returns this [`Affine2x2`] as big-endian bytes.
195    pub const fn to_be_bytes(self) -> [u8; 8] {
196        let buf = [
197            self.xx.to_be_bytes(),
198            self.yx.to_be_bytes(),
199            self.xy.to_be_bytes(),
200            self.yy.to_be_bytes(),
201        ];
202        *buf.as_flattened().as_array().unwrap()
203    }
204
205    /// Returns this [`Affine2x2`]'s `(xx, yx, xy, yy)` as a tuple.
206    pub const fn to_tuple(self) -> (F2Dot14, F2Dot14, F2Dot14, F2Dot14) {
207        (self.xx, self.yx, self.xy, self.yy)
208    }
209    /// Returns this [`Affine2x2`]'s `(xx, yx, xy, yy)` as a tuple of [`f32`]s.
210    pub const fn to_tuple_f32(self) -> (f32, f32, f32, f32) {
211        (self.xx.get(), self.yx.get(), self.xy.get(), self.yy.get())
212    }
213
214    /// Creates an [`Affine2x2`] from a `[xx, yx, xy, yy]` array.
215    pub const fn from_array([xx, yx, xy, yy]: [F2Dot14; 4]) -> Self {
216        Self { xx, yx, xy, yy }
217    }
218    /// Returns this [`Affine2x2`]'s `[xx, yx, xy, yy]` as an array.
219    pub const fn to_array(self) -> [F2Dot14; 4] {
220        [self.xx, self.yx, self.xy, self.yy]
221    }
222}
223
224/// Performs multiplication `*` (panics on overflow in debug configuration).
225const impl std::ops::Mul for Affine2x2 {
226    type Output = Self;
227    fn mul(self, rhs: Self) -> Self::Output {
228        #[cfg(debug_assertions)]
229        return self.checked_mul(rhs).expect("attempt to multiply with overflow");
230        #[cfg(not(debug_assertions))]
231        return self.wrapping_mul(rhs);
232    }
233}
234
235impl std::fmt::Debug for Affine2x2 {
236    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
237        let Self { xx, yx, xy, yy } = *self;
238        write!(f, "[{:?}, {:?}; {:?}, {:?}]", xx, yx, xy, yy)
239    }
240}