1use super::num;
4use crate::Cursor;
5use types::Fixed;
6
7pub type FontMatrix = types::Matrix<Fixed>;
9
10pub fn combine_scaled(a: &FontMatrix, b: &FontMatrix, scale: i32) -> FontMatrix {
16 let a = a.elements();
18 let b = b.elements();
19 let val = Fixed::from_i32(scale);
20 let xx = a[0].mul_div(b[0], val) + a[2].mul_div(b[1], val);
21 let yx = a[1].mul_div(b[0], val) + a[3].mul_div(b[1], val);
22 let xy = a[0].mul_div(b[2], val) + a[2].mul_div(b[3], val);
23 let yy = a[1].mul_div(b[2], val) + a[3].mul_div(b[3], val);
24 let x = b[4];
25 let y = b[5];
26 let dx = x.mul_div(a[0], val) + y.mul_div(a[2], val);
27 let dy = x.mul_div(a[1], val) + y.mul_div(a[3], val);
28 FontMatrix::from_elements([xx, yx, xy, yy, dx, dy])
29}
30
31pub(crate) fn is_degenerate(matrix: &FontMatrix) -> bool {
34 let [mut xx, mut yx, mut xy, mut yy, ..] = matrix.elements().map(|x| x.to_bits() as i64);
35 let val = xx.abs() | yx.abs() | xy.abs() | yy.abs();
36 if val == 0 || val > 0x7FFFFFFF {
37 return true;
38 }
39 let msb = 32 - (val as i32).leading_zeros() - 1;
41 let shift = msb as i32 - 12;
42 if shift > 0 {
43 xx >>= shift;
44 xy >>= shift;
45 yx >>= shift;
46 yy >>= shift;
47 }
48 let temp1 = 32 * (xx * yy - xy * yx).abs();
49 let temp2 = (xx * xx) + (xy * xy) + (yx * yx) + (yy * yy);
50 if temp1 <= temp2 {
51 return true;
52 }
53 false
54}
55
56#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
58pub struct Transform {
59 pub matrix: FontMatrix,
61 pub scale: Option<Fixed>,
65}
66
67impl Transform {
68 pub const IDENTITY: Self = Self {
70 matrix: FontMatrix::IDENTITY,
71 scale: None,
72 };
73
74 pub fn compute_scale(ppem: f32, upem: i32) -> Fixed {
76 Fixed::from_bits((ppem * 64.0) as i32) / Fixed::from_bits(upem.max(1))
77 }
78
79 pub fn transform_h_metric(&self, metric: Fixed) -> Fixed {
82 let mut metric = Fixed::from_bits(metric.to_i32());
83 if self.matrix.xx != Fixed::ONE {
84 metric *= self.matrix.xx;
86 }
87 metric += self.matrix.dx;
89 if let Some(scale) = self.scale {
90 Fixed::from_bits((metric * scale).to_bits() << 10)
93 } else {
94 Fixed::from_bits(metric.to_bits() << 16)
96 }
97 }
98}
99
100#[derive(Copy, Clone, PartialEq, Eq, Debug)]
102pub struct ScaledFontMatrix {
103 pub matrix: FontMatrix,
105 pub scale: i32,
107}
108
109impl ScaledFontMatrix {
110 pub(crate) fn parse(cursor: &mut Cursor) -> Option<Self> {
114 let mut values = [Fixed::ZERO; 6];
115 let mut scalings = [0i32; 6];
116 let mut max_scaling = i32::MIN;
117 let mut min_scaling = i32::MAX;
118 for (value, scaling) in values.iter_mut().zip(&mut scalings) {
119 let (v, s) = num::parse_fixed_dynamic(cursor).ok()?;
120 if v != Fixed::ZERO {
121 max_scaling = max_scaling.max(s);
122 min_scaling = min_scaling.min(s);
123 }
124 *value = v;
125 *scaling = s;
126 }
127 if !(-9..=0).contains(&max_scaling)
128 || (max_scaling - min_scaling < 0)
129 || (max_scaling - min_scaling) > 9
130 {
131 return None;
132 }
133 for (value, scaling) in values.iter_mut().zip(scalings) {
134 if *value == Fixed::ZERO {
135 continue;
136 }
137 let divisor = num::BCD_POWER_TENS[(max_scaling - scaling) as usize];
138 let half_divisor = divisor >> 1;
139 if *value < Fixed::ZERO {
140 if i32::MIN + half_divisor < value.to_bits() {
141 *value = Fixed::from_bits((value.to_bits() - half_divisor) / divisor);
142 } else {
143 *value = Fixed::from_bits(i32::MIN / divisor);
144 }
145 } else if i32::MAX - half_divisor > value.to_bits() {
146 *value = Fixed::from_bits((value.to_bits() + half_divisor) / divisor);
147 } else {
148 *value = Fixed::from_bits(i32::MAX / divisor);
149 }
150 }
151 let matrix = FontMatrix::from_elements(values);
152 if is_degenerate(&matrix) {
154 return None;
155 }
156 let scale = num::BCD_POWER_TENS[(-max_scaling) as usize];
157 Some(Self { matrix, scale })
158 }
159
160 #[must_use]
163 pub fn normalize(&self) -> Self {
164 let mut matrix = self.matrix.elements();
166 let mut scaled_upem = self.scale;
167 let factor = if matrix[3] != Fixed::ZERO {
168 matrix[3].abs()
169 } else {
170 matrix[1].abs()
172 };
173 if factor != Fixed::ONE {
174 scaled_upem = (Fixed::from_bits(scaled_upem) / factor).to_bits();
175 for value in &mut matrix {
176 *value /= factor;
177 }
178 }
179 for offset in matrix[4..6].iter_mut() {
181 *offset = Fixed::from_bits(offset.to_bits() >> 16);
182 }
183 Self {
184 matrix: FontMatrix::from_elements(matrix),
185 scale: scaled_upem,
186 }
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 #[test]
195 fn compute_scale() {
196 assert_eq!(Transform::compute_scale(1000.0, 1000).to_bits(), 64 << 16);
197 assert_eq!(Transform::compute_scale(500.0, 1000).to_bits(), 32 << 16);
198 assert_eq!(Transform::compute_scale(2000.0, 1000).to_bits(), 128 << 16);
199 assert_eq!(Transform::compute_scale(16.0, 1000).to_bits(), 67109);
200 }
201
202 #[test]
203 fn h_metric_identity_integral() {
204 for metric in [Fixed::ZERO, Fixed::ONE, Fixed::NEG_ONE, Fixed::from_i32(42)] {
205 assert_eq!(Transform::IDENTITY.transform_h_metric(metric), metric);
206 }
207 }
208
209 #[test]
210 fn h_metric_identity_fractional() {
211 for metric in [
214 Fixed::from_f64(42.5),
215 Fixed::from_f64(-20.1),
216 Fixed::from_f64(18.8),
217 ] {
218 assert_eq!(
219 Transform::IDENTITY.transform_h_metric(metric),
220 metric.round()
221 );
222 }
223 }
224
225 #[test]
226 fn h_metric_matrix_scale() {
227 let transform = Transform {
228 matrix: FontMatrix::from_elements([2.0, 0.0, 0.0, 1.0, 0.0, 0.0].map(Fixed::from_f64)),
229 scale: None,
230 };
231 let pairs = [(42.5, 86.0), (-20.1, -40.0), (18.8, 38.0)];
233 for (metric, transformed_metric) in pairs {
234 assert_eq!(
235 transform
236 .transform_h_metric(Fixed::from_f64(metric))
237 .to_f64(),
238 transformed_metric
239 );
240 }
241 }
242
243 #[test]
244 fn h_metric_matrix_scale_offset() {
245 let transform = Transform {
246 matrix: FontMatrix::from_elements(
247 [2.0, 0.0, 0.0, 1.0, 10.0 / 65536.0, 0.0].map(Fixed::from_f64),
248 ),
249 scale: None,
250 };
251 let pairs = [(42.5, 96.0), (-20.1, -30.0), (18.8, 48.0)];
253 for (metric, transformed_metric) in pairs {
254 assert_eq!(
255 transform
256 .transform_h_metric(Fixed::from_f64(metric))
257 .to_f64(),
258 transformed_metric
259 );
260 }
261 }
262
263 #[test]
264 fn h_metric_scale() {
265 let transform = Transform {
266 matrix: FontMatrix::IDENTITY,
267 scale: Some(Fixed::from_i32(32)),
269 };
270 let pairs = [(42.5, 21.5), (-20.1, -10.0), (18.8, 9.5)];
272 for (metric, transformed_metric) in pairs {
273 assert_eq!(
274 transform
275 .transform_h_metric(Fixed::from_f64(metric))
276 .to_f64(),
277 transformed_metric
278 );
279 }
280 }
281
282 #[test]
283 fn h_metric_scale_matrix_scale_offset() {
284 let transform = Transform {
285 matrix: FontMatrix::from_elements(
286 [4.0, 0.0, 0.0, 1.0, 10.0 / 65536.0, 0.0].map(Fixed::from_f64),
287 ),
288 scale: Some(Fixed::from_i32(32)),
290 };
291 let pairs = [(42.5, 91.0), (-20.1, -35.0), (18.8, 43.0)];
293 for (metric, transformed_metric) in pairs {
294 assert_eq!(
295 transform
296 .transform_h_metric(Fixed::from_f64(metric))
297 .to_f64(),
298 transformed_metric
299 );
300 }
301 }
302
303 #[test]
305 fn degenerate_matrix_check_doesnt_overflow() {
306 let matrix = FontMatrix::from_elements([
308 Fixed::from_bits(639999672),
309 Fixed::ZERO,
310 Fixed::ZERO,
311 Fixed::from_bits(639999672),
312 Fixed::ZERO,
313 Fixed::ZERO,
314 ]);
315 is_degenerate(&matrix);
317 is_degenerate(&FontMatrix::from_elements([Fixed::MAX; 6]));
319 is_degenerate(&FontMatrix::from_elements([Fixed::MIN; 6]));
321 }
322
323 #[test]
324 fn normalize_matrix() {
325 let matrix = ScaledFontMatrix {
328 matrix: FontMatrix::from_elements([65536, 0, 0, 32768, 0, 0].map(Fixed::from_bits)),
329 scale: 1,
330 };
331 let normalized = matrix.normalize();
332 let expected_normalized = [131072, 0, 0, 65536, 0, 0].map(Fixed::from_bits);
333 assert_eq!(normalized.matrix.elements(), expected_normalized);
334 assert_eq!(normalized.scale, 2);
335 }
336
337 #[test]
338 fn combine_matrix() {
339 let a = FontMatrix::from_elements([0.5, 0.75, -1.0, 2.0, 0.0, 0.0].map(Fixed::from_f64));
340 let b = FontMatrix::from_elements([1.5, -1.0, 0.25, -1.0, 1.0, 2.0].map(Fixed::from_f64));
341 let expected = [1.75, -0.875, 1.125, -1.8125, -1.5, 4.75].map(Fixed::from_f64);
342 let result = combine_scaled(&a, &b, 1);
343 assert_eq!(result.elements(), expected);
344 }
345}