1use super::num;
4use crate::Cursor;
5use types::Fixed;
6
7pub type FontMatrix = types::Matrix<Fixed>;
9
10fn 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]
165 pub fn combine(&self, sub: &ScaledFontMatrix) -> ScaledFontMatrix {
166 let scaling = if self.scale > 1 && sub.scale > 1 {
167 self.scale.min(sub.scale)
168 } else {
169 1
170 };
171 let matrix = combine_scaled(&self.matrix, &sub.matrix, scaling);
172 let scale = Fixed::from_bits(sub.scale)
173 .mul_div(Fixed::from_bits(self.scale), Fixed::from_bits(scaling));
174 ScaledFontMatrix {
175 matrix,
176 scale: scale.to_bits(),
177 }
178 .normalize()
179 }
180
181 #[must_use]
184 pub fn normalize(&self) -> Self {
185 let mut matrix = self.matrix.elements();
187 let mut scaled_upem = self.scale;
188 let factor = if matrix[3] != Fixed::ZERO {
189 matrix[3].abs()
190 } else {
191 matrix[1].abs()
193 };
194 if factor != Fixed::ONE {
195 scaled_upem = (Fixed::from_bits(scaled_upem) / factor).to_bits();
196 for value in &mut matrix {
197 *value /= factor;
198 }
199 }
200 for offset in matrix[4..6].iter_mut() {
202 *offset = Fixed::from_bits(offset.to_bits() >> 16);
203 }
204 Self {
205 matrix: FontMatrix::from_elements(matrix),
206 scale: scaled_upem,
207 }
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn compute_scale() {
217 assert_eq!(Transform::compute_scale(1000.0, 1000).to_bits(), 64 << 16);
218 assert_eq!(Transform::compute_scale(500.0, 1000).to_bits(), 32 << 16);
219 assert_eq!(Transform::compute_scale(2000.0, 1000).to_bits(), 128 << 16);
220 assert_eq!(Transform::compute_scale(16.0, 1000).to_bits(), 67109);
221 }
222
223 #[test]
224 fn h_metric_identity_integral() {
225 for metric in [Fixed::ZERO, Fixed::ONE, Fixed::NEG_ONE, Fixed::from_i32(42)] {
226 assert_eq!(Transform::IDENTITY.transform_h_metric(metric), metric);
227 }
228 }
229
230 #[test]
231 fn h_metric_identity_fractional() {
232 for metric in [
235 Fixed::from_f64(42.5),
236 Fixed::from_f64(-20.1),
237 Fixed::from_f64(18.8),
238 ] {
239 assert_eq!(
240 Transform::IDENTITY.transform_h_metric(metric),
241 metric.round()
242 );
243 }
244 }
245
246 #[test]
247 fn h_metric_matrix_scale() {
248 let transform = Transform {
249 matrix: FontMatrix::from_elements([2.0, 0.0, 0.0, 1.0, 0.0, 0.0].map(Fixed::from_f64)),
250 scale: None,
251 };
252 let pairs = [(42.5, 86.0), (-20.1, -40.0), (18.8, 38.0)];
254 for (metric, transformed_metric) in pairs {
255 assert_eq!(
256 transform
257 .transform_h_metric(Fixed::from_f64(metric))
258 .to_f64(),
259 transformed_metric
260 );
261 }
262 }
263
264 #[test]
265 fn h_metric_matrix_scale_offset() {
266 let transform = Transform {
267 matrix: FontMatrix::from_elements(
268 [2.0, 0.0, 0.0, 1.0, 10.0 / 65536.0, 0.0].map(Fixed::from_f64),
269 ),
270 scale: None,
271 };
272 let pairs = [(42.5, 96.0), (-20.1, -30.0), (18.8, 48.0)];
274 for (metric, transformed_metric) in pairs {
275 assert_eq!(
276 transform
277 .transform_h_metric(Fixed::from_f64(metric))
278 .to_f64(),
279 transformed_metric
280 );
281 }
282 }
283
284 #[test]
285 fn h_metric_scale() {
286 let transform = Transform {
287 matrix: FontMatrix::IDENTITY,
288 scale: Some(Fixed::from_i32(32)),
290 };
291 let pairs = [(42.5, 21.5), (-20.1, -10.0), (18.8, 9.5)];
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]
304 fn h_metric_scale_matrix_scale_offset() {
305 let transform = Transform {
306 matrix: FontMatrix::from_elements(
307 [4.0, 0.0, 0.0, 1.0, 10.0 / 65536.0, 0.0].map(Fixed::from_f64),
308 ),
309 scale: Some(Fixed::from_i32(32)),
311 };
312 let pairs = [(42.5, 91.0), (-20.1, -35.0), (18.8, 43.0)];
314 for (metric, transformed_metric) in pairs {
315 assert_eq!(
316 transform
317 .transform_h_metric(Fixed::from_f64(metric))
318 .to_f64(),
319 transformed_metric
320 );
321 }
322 }
323
324 #[test]
326 fn degenerate_matrix_check_doesnt_overflow() {
327 let matrix = FontMatrix::from_elements([
329 Fixed::from_bits(639999672),
330 Fixed::ZERO,
331 Fixed::ZERO,
332 Fixed::from_bits(639999672),
333 Fixed::ZERO,
334 Fixed::ZERO,
335 ]);
336 is_degenerate(&matrix);
338 is_degenerate(&FontMatrix::from_elements([Fixed::MAX; 6]));
340 is_degenerate(&FontMatrix::from_elements([Fixed::MIN; 6]));
342 }
343
344 #[test]
345 fn normalize_matrix() {
346 let matrix = ScaledFontMatrix {
349 matrix: FontMatrix::from_elements([65536, 0, 0, 32768, 0, 0].map(Fixed::from_bits)),
350 scale: 1,
351 };
352 let normalized = matrix.normalize();
353 let expected_normalized = [131072, 0, 0, 65536, 0, 0].map(Fixed::from_bits);
354 assert_eq!(normalized.matrix.elements(), expected_normalized);
355 assert_eq!(normalized.scale, 2);
356 }
357
358 #[test]
359 fn combine_matrix() {
360 let a = FontMatrix::from_elements([0.5, 0.75, -1.0, 2.0, 0.0, 0.0].map(Fixed::from_f64));
361 let b = FontMatrix::from_elements([1.5, -1.0, 0.25, -1.0, 1.0, 2.0].map(Fixed::from_f64));
362 let expected = [1.75, -0.875, 1.125, -1.8125, -1.5, 4.75].map(Fixed::from_f64);
363 let result = combine_scaled(&a, &b, 1);
364 assert_eq!(result.elements(), expected);
365 }
366}