swash/scale/
hinting_cache.rs
1use alloc::vec::Vec;
2use skrifa::{
3 instance::{NormalizedCoord, Size},
4 outline::{
5 HintingInstance, HintingMode, LcdLayout, OutlineGlyphCollection, OutlineGlyphFormat,
6 },
7};
8
9const MAX_CACHED_HINT_INSTANCES: usize = 8;
13
14pub(crate) struct HintingKey<'a> {
15 pub id: [u64; 2],
16 pub outlines: &'a OutlineGlyphCollection<'a>,
17 pub size: Size,
18 pub coords: &'a [NormalizedCoord],
19}
20
21impl<'a> HintingKey<'a> {
22 fn new_instance(&self) -> Option<HintingInstance> {
23 HintingInstance::new(self.outlines, self.size, self.coords, HINTING_MODE).ok()
24 }
25}
26
27const HINTING_MODE: HintingMode = HintingMode::Smooth {
28 lcd_subpixel: Some(LcdLayout::Horizontal),
29 preserve_linear_metrics: true,
30};
31
32#[derive(Default)]
33pub(super) struct HintingCache {
34 glyf_entries: Vec<HintingEntry>,
37 cff_entries: Vec<HintingEntry>,
38 serial: u64,
39}
40
41impl HintingCache {
42 pub(super) fn get(&mut self, key: &HintingKey) -> Option<&HintingInstance> {
43 let entries = match key.outlines.format()? {
44 OutlineGlyphFormat::Glyf => &mut self.glyf_entries,
45 OutlineGlyphFormat::Cff | OutlineGlyphFormat::Cff2 => &mut self.cff_entries,
46 };
47 let (entry_ix, is_current) = find_hinting_entry(entries, key)?;
48 let entry = entries.get_mut(entry_ix)?;
49 self.serial += 1;
50 entry.serial = self.serial;
51 if !is_current {
52 entry.id = key.id;
53 entry
54 .instance
55 .reconfigure(key.outlines, key.size, key.coords, HINTING_MODE)
56 .ok()?;
57 }
58 Some(&entry.instance)
59 }
60}
61
62struct HintingEntry {
63 id: [u64; 2],
64 instance: HintingInstance,
65 serial: u64,
66}
67
68fn find_hinting_entry(entries: &mut Vec<HintingEntry>, key: &HintingKey) -> Option<(usize, bool)> {
69 let mut found_serial = u64::MAX;
70 let mut found_index = 0;
71 for (ix, entry) in entries.iter().enumerate() {
72 if entry.id == key.id
73 && entry.instance.size() == key.size
74 && entry.instance.location().coords() == key.coords
75 {
76 return Some((ix, true));
77 }
78 if entry.serial < found_serial {
79 found_serial = entry.serial;
80 found_index = ix;
81 }
82 }
83 if entries.len() < MAX_CACHED_HINT_INSTANCES {
84 let instance = key.new_instance()?;
85 let ix = entries.len();
86 entries.push(HintingEntry {
87 id: key.id,
88 instance,
89 serial: 0,
90 });
91 Some((ix, true))
92 } else {
93 Some((found_index, false))
94 }
95}