1mod blues;
4mod scale;
5mod widths;
6
7use super::{
8 super::Target,
9 shape::{Shaper, ShaperMode},
10 style::{GlyphStyleMap, ScriptGroup, StyleClass},
11 topo::Dimension,
12 QuirksMode,
13};
14use crate::{attribute::Style, collections::SmallVec, FontRef};
15use alloc::vec::Vec;
16use raw::types::{F2Dot14, Fixed, GlyphId};
17#[cfg(feature = "std")]
18use std::sync::{Arc, RwLock};
19
20pub use blues::{BlueZones, ScaledBlue, UnscaledBlue};
21pub(crate) use blues::{ScaledBlues, UnscaledBlues};
22pub(crate) use scale::{compute_unscaled_style_metrics, scale_style_metrics};
23
24pub(crate) const MAX_WIDTHS: usize = 16;
29
30#[derive(Clone, Default, Debug)]
37pub struct UnscaledAxisMetrics {
38 pub dim: Dimension,
40 pub(crate) widths: UnscaledWidths,
41 pub width_metrics: WidthMetrics,
43 pub(crate) blues: UnscaledBlues,
44}
45
46impl UnscaledAxisMetrics {
47 pub fn widths(&self) -> &[i32] {
49 self.widths.as_slice()
50 }
51
52 pub fn blues(&self) -> &[UnscaledBlue] {
54 self.blues.as_slice()
55 }
56
57 pub fn max_width(&self) -> Option<i32> {
59 self.widths.last().copied()
60 }
61}
62
63#[derive(Clone, Default, Debug)]
65pub struct ScaledAxisMetrics {
66 pub dim: Dimension,
68 pub scale: i32,
70 pub delta: i32,
72 pub(crate) widths: ScaledWidths,
73 pub width_metrics: WidthMetrics,
75 pub(crate) blues: ScaledBlues,
76}
77
78impl ScaledAxisMetrics {
79 pub fn widths(&self) -> &[ScaledWidth] {
81 self.widths.as_slice()
82 }
83
84 pub fn blues(&self) -> &[ScaledBlue] {
86 self.blues.as_slice()
87 }
88}
89
90#[derive(Clone, Default, Debug)]
99pub struct UnscaledStyleMetrics {
100 pub(crate) class_ix: u16,
102 pub digits_have_same_width: bool,
104 pub(crate) axes: [UnscaledAxisMetrics; 2],
106}
107
108impl UnscaledStyleMetrics {
109 pub fn style_class(&self) -> &'static StyleClass {
111 &super::style::STYLE_CLASSES[self.class_ix as usize]
112 }
113
114 pub fn horizontal_metrics(&self) -> &UnscaledAxisMetrics {
116 &self.axes[0]
117 }
118
119 pub fn vertical_metrics(&self) -> &UnscaledAxisMetrics {
121 &self.axes[1]
122 }
123}
124
125#[derive(Clone, Debug)]
129pub(crate) enum UnscaledStyleMetricsSet {
130 Precomputed(Vec<UnscaledStyleMetrics>),
131 #[cfg(feature = "std")]
132 Lazy(Arc<RwLock<Vec<Option<UnscaledStyleMetrics>>>>),
133}
134
135impl UnscaledStyleMetricsSet {
136 pub fn precomputed(
139 font: &FontRef,
140 coords: &[F2Dot14],
141 shaper_mode: ShaperMode,
142 style_map: &GlyphStyleMap,
143 ) -> Self {
144 let shaper = Shaper::new(font, shaper_mode);
148 let mut vec = Vec::with_capacity(style_map.metrics_count());
149 vec.extend(style_map.metrics_styles().map(|style| {
150 compute_unscaled_style_metrics(&shaper, coords, style, QuirksMode::default())
151 }));
152 Self::Precomputed(vec)
153 }
154
155 #[cfg(feature = "std")]
158 pub fn lazy(style_map: &GlyphStyleMap) -> Self {
159 let vec = vec![None; style_map.metrics_count()];
160 Self::Lazy(Arc::new(RwLock::new(vec)))
161 }
162
163 pub fn get(
166 &self,
167 font: &FontRef,
168 coords: &[F2Dot14],
169 shaper_mode: ShaperMode,
170 style_map: &GlyphStyleMap,
171 glyph_id: GlyphId,
172 ) -> Option<UnscaledStyleMetrics> {
173 let style = style_map.style(glyph_id)?;
174 let index = style_map.metrics_index(style)?;
175 match self {
176 Self::Precomputed(metrics) => metrics.get(index).cloned(),
177 #[cfg(feature = "std")]
178 Self::Lazy(lazy) => {
179 let read = lazy.read().unwrap();
180 let entry = read.get(index)?;
181 if let Some(metrics) = &entry {
182 return Some(metrics.clone());
183 }
184 core::mem::drop(read);
185 let shaper = Shaper::new(font, shaper_mode);
189 let style_class = style.style_class()?;
190 let metrics = compute_unscaled_style_metrics(
191 &shaper,
192 coords,
193 style_class,
194 QuirksMode::default(),
195 );
196 let mut entry = lazy.write().unwrap();
197 *entry.get_mut(index)? = Some(metrics.clone());
198 Some(metrics)
199 }
200 }
201 }
202}
203
204#[derive(Clone, Default, Debug)]
206pub struct ScaledStyleMetrics {
207 pub scale: Scale,
209 pub(crate) axes: [ScaledAxisMetrics; 2],
211}
212
213impl ScaledStyleMetrics {
214 pub fn horizontal_metrics(&self) -> &ScaledAxisMetrics {
216 &self.axes[0]
217 }
218
219 pub fn vertical_metrics(&self) -> &ScaledAxisMetrics {
221 &self.axes[1]
222 }
223}
224
225#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
227pub struct WidthMetrics {
228 pub edge_distance_threshold: i32,
230 pub standard_width: i32,
232 pub is_extra_light: bool,
234}
235
236pub(crate) type UnscaledWidths = SmallVec<i32, MAX_WIDTHS>;
237
238#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
240pub struct ScaledWidth {
241 pub scaled: i32,
243 pub fitted: i32,
245}
246
247pub(crate) type ScaledWidths = SmallVec<ScaledWidth, MAX_WIDTHS>;
248
249#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
251pub struct ScaleFlags(pub(crate) u32);
252
253impl ScaleFlags {
256 pub const HORIZONTAL_SNAP: Self = Self(1 << 0);
258 pub const VERTICAL_SNAP: Self = Self(1 << 1);
260 pub const STEM_ADJUST: Self = Self(1 << 2);
262 pub const MONO: Self = Self(1 << 3);
264 pub const NO_HORIZONTAL: Self = Self(1 << 4);
266 pub const NO_VERTICAL: Self = Self(1 << 5);
268 pub const NO_ADVANCE: Self = Self(1 << 6);
270}
271
272impl ScaleFlags {
273 pub const fn from_bits_truncate(bits: u32) -> Self {
275 Self(bits & 0b1111111)
276 }
277
278 pub const fn to_bits(self) -> u32 {
280 self.0
281 }
282
283 pub const fn contains(self, other: Self) -> bool {
285 (self.0 & other.0) == other.0
286 }
287
288 pub const fn intersects(self, other: Self) -> bool {
290 (self.0 & other.0) != 0
291 }
292}
293
294impl core::ops::Not for ScaleFlags {
295 type Output = Self;
296
297 fn not(self) -> Self::Output {
298 Self(!self.0)
299 }
300}
301
302impl core::ops::BitOr for ScaleFlags {
303 type Output = Self;
304
305 fn bitor(self, rhs: Self) -> Self::Output {
306 Self(self.0 | rhs.0)
307 }
308}
309
310impl core::ops::BitOrAssign for ScaleFlags {
311 fn bitor_assign(&mut self, rhs: Self) {
312 self.0 |= rhs.0;
313 }
314}
315
316impl core::ops::BitAnd for ScaleFlags {
317 type Output = Self;
318
319 fn bitand(self, rhs: Self) -> Self::Output {
320 Self(self.0 & rhs.0)
321 }
322}
323
324impl core::ops::BitAndAssign for ScaleFlags {
325 fn bitand_assign(&mut self, rhs: Self) {
326 self.0 &= rhs.0;
327 }
328}
329
330#[derive(Copy, Clone, Default, Debug)]
333pub struct Scale {
334 pub flags: ScaleFlags,
336 pub x_scale: i32,
338 pub y_scale: i32,
340 pub x_delta: i32,
342 pub y_delta: i32,
344 pub size: f32,
346 pub units_per_em: i32,
348}
349
350impl Scale {
351 pub fn new(
353 size: f32,
354 units_per_em: i32,
355 font_style: Style,
356 target: Target,
357 group: ScriptGroup,
358 ) -> Self {
359 let scale =
360 (Fixed::from_bits((size * 64.0) as i32) / Fixed::from_bits(units_per_em)).to_bits();
361 let mut flags = ScaleFlags::default();
362 let is_italic = font_style != Style::Normal;
363 let is_mono = target == Target::Mono;
364 let is_light = target.is_light() || target.preserve_linear_metrics();
365 if is_mono || target.is_lcd() {
367 flags |= ScaleFlags::HORIZONTAL_SNAP;
368 }
369 if is_mono || target.is_vertical_lcd() {
371 flags |= ScaleFlags::VERTICAL_SNAP;
372 }
373 if !(target.is_lcd() || is_light) {
375 flags |= ScaleFlags::STEM_ADJUST;
376 }
377 if is_mono {
378 flags |= ScaleFlags::MONO;
379 }
380 if group == ScriptGroup::Default {
381 if target.is_lcd() || is_light || is_italic {
385 flags |= ScaleFlags::NO_HORIZONTAL;
386 }
387 } else {
388 flags |= ScaleFlags::NO_ADVANCE;
391 }
392 if group != ScriptGroup::Default {
395 flags |= ScaleFlags::NO_ADVANCE;
396 }
397 Self {
398 x_scale: scale,
399 y_scale: scale,
400 x_delta: 0,
401 y_delta: 0,
402 size,
403 units_per_em,
404 flags,
405 }
406 }
407}
408
409pub(crate) fn sort_and_quantize_widths(widths: &mut UnscaledWidths, threshold: i32) {
411 if widths.len() <= 1 {
412 return;
413 }
414 widths.sort_unstable();
415 let table = widths.as_mut_slice();
416 let mut cur_ix = 0;
417 let mut cur_val = table[cur_ix];
418 let last_ix = table.len() - 1;
419 let mut ix = 1;
420 while ix < table.len() {
423 if (table[ix] - cur_val) > threshold || ix == last_ix {
424 let mut sum = 0;
425 if (table[ix] - cur_val <= threshold) && ix == last_ix {
427 ix += 1;
428 }
429 for val in &mut table[cur_ix..ix] {
430 sum += *val;
431 *val = 0;
432 }
433 table[cur_ix] = sum / ix as i32;
434 if ix < last_ix {
435 cur_ix = ix + 1;
436 cur_val = table[cur_ix];
437 }
438 }
439 ix += 1;
440 }
441 cur_ix = 1;
442 for ix in 1..table.len() {
444 if table[ix] != 0 {
445 table[cur_ix] = table[ix];
446 cur_ix += 1;
447 }
448 }
449 widths.truncate(cur_ix);
450}
451
452pub(crate) fn fixed_mul(a: i32, b: i32) -> i32 {
459 (Fixed::from_bits(a) * Fixed::from_bits(b)).to_bits()
460}
461
462pub(crate) fn fixed_div(a: i32, b: i32) -> i32 {
463 (Fixed::from_bits(a) / Fixed::from_bits(b)).to_bits()
464}
465
466pub(crate) fn fixed_mul_div(a: i32, b: i32, c: i32) -> i32 {
467 Fixed::from_bits(a)
468 .mul_div(Fixed::from_bits(b), Fixed::from_bits(c))
469 .to_bits()
470}
471
472pub(crate) fn pix_round(a: i32) -> i32 {
473 (a + 32) & !63
474}
475
476pub(crate) fn pix_floor(a: i32) -> i32 {
477 a & !63
478}
479
480#[cfg(test)]
481mod tests {
482 use super::{
483 super::{
484 shape::{Shaper, ShaperMode},
485 style::STYLE_CLASSES,
486 },
487 *,
488 };
489 use raw::TableProvider;
490
491 #[test]
492 fn sort_widths() {
493 assert_eq!(sort_widths_helper(&[1], 10), &[1]);
496 assert_eq!(sort_widths_helper(&[1], 20), &[1]);
497 assert_eq!(sort_widths_helper(&[60, 20, 40, 35], 10), &[20, 35, 13, 60]);
498 assert_eq!(sort_widths_helper(&[60, 20, 40, 35], 20), &[31, 60]);
499 }
500
501 fn sort_widths_helper(widths: &[i32], threshold: i32) -> Vec<i32> {
502 let mut widths2 = UnscaledWidths::new();
503 for width in widths {
504 widths2.push(*width);
505 }
506 sort_and_quantize_widths(&mut widths2, threshold);
507 widths2.into_iter().collect()
508 }
509
510 #[test]
511 fn precomputed_style_set() {
512 let font = FontRef::new(font_test_data::NOTOSERIFHEBREW_AUTOHINT_METRICS).unwrap();
513 let coords = &[];
514 let shaper = Shaper::new(&font, ShaperMode::Nominal);
515 let glyph_count = font.maxp().unwrap().num_glyphs() as u32;
516 let style_map = GlyphStyleMap::new(glyph_count, &shaper);
517 let style_set =
518 UnscaledStyleMetricsSet::precomputed(&font, coords, ShaperMode::Nominal, &style_map);
519 let UnscaledStyleMetricsSet::Precomputed(set) = &style_set else {
520 panic!("we definitely made a precomputed style set");
521 };
522 assert_eq!(STYLE_CLASSES[set[0].class_ix as usize].name, "Latin");
524 assert_eq!(STYLE_CLASSES[set[1].class_ix as usize].name, "Hebrew");
525 assert_eq!(
526 STYLE_CLASSES[set[2].class_ix as usize].name,
527 "CJKV ideographs"
528 );
529 assert_eq!(set.len(), 3);
530 }
531
532 #[test]
533 fn lazy_style_set() {
534 let font = FontRef::new(font_test_data::NOTOSERIFHEBREW_AUTOHINT_METRICS).unwrap();
535 let coords = &[];
536 let shaper = Shaper::new(&font, ShaperMode::Nominal);
537 let glyph_count = font.maxp().unwrap().num_glyphs() as u32;
538 let style_map = GlyphStyleMap::new(glyph_count, &shaper);
539 let style_set = UnscaledStyleMetricsSet::lazy(&style_map);
540 let all_empty = lazy_set_presence(&style_set);
541 assert_eq!(all_empty, [false; 3]);
543 let metrics2 = style_set
545 .get(
546 &font,
547 coords,
548 ShaperMode::Nominal,
549 &style_map,
550 GlyphId::new(0),
551 )
552 .unwrap();
553 assert_eq!(
554 STYLE_CLASSES[metrics2.class_ix as usize].name,
555 "CJKV ideographs"
556 );
557 let only_cjk = lazy_set_presence(&style_set);
558 assert_eq!(only_cjk, [false, false, true]);
559 let metrics1 = style_set
561 .get(
562 &font,
563 coords,
564 ShaperMode::Nominal,
565 &style_map,
566 GlyphId::new(1),
567 )
568 .unwrap();
569 assert_eq!(STYLE_CLASSES[metrics1.class_ix as usize].name, "Hebrew");
570 let hebrew_and_cjk = lazy_set_presence(&style_set);
571 assert_eq!(hebrew_and_cjk, [false, true, true]);
572 let metrics0 = style_set
574 .get(
575 &font,
576 coords,
577 ShaperMode::Nominal,
578 &style_map,
579 GlyphId::new(15),
580 )
581 .unwrap();
582 assert_eq!(STYLE_CLASSES[metrics0.class_ix as usize].name, "Latin");
583 let all_present = lazy_set_presence(&style_set);
584 assert_eq!(all_present, [true; 3]);
585 }
586
587 fn lazy_set_presence(style_set: &UnscaledStyleMetricsSet) -> Vec<bool> {
588 let UnscaledStyleMetricsSet::Lazy(set) = &style_set else {
589 panic!("we definitely made a lazy style set");
590 };
591 set.read()
592 .unwrap()
593 .iter()
594 .map(|opt| opt.is_some())
595 .collect()
596 }
597}