Skip to main content

pfx_text/
prefill.rs

1use std::collections::BTreeSet;
2use std::sync::Arc;
3use std::sync::atomic::{AtomicUsize, Ordering};
4
5use cosmic_text::Font;
6use swash::scale::ScaleContext;
7
8use crate::{
9    Anchor, Error, Face, GlyphImage, NumberForms, PageKind, RasterKey, Representation, Span,
10    TextEngine,
11    number::{bin_keys, char_lines, unique_chars},
12    raster,
13};
14
15#[derive(Clone, Debug, PartialEq)]
16pub struct Prefill {
17    pub face: Face,
18    pub chars: String,
19    pub scale: f32,
20    pub representation: Representation,
21    pub bins: [bool; 4],
22    pub number: Option<NumberForms>,
23}
24
25type Work = (RasterKey, Arc<Font>);
26type Image = (PageKind, Option<GlyphImage>);
27type Keyed = (
28    cosmic_text::fontdb::ID,
29    cosmic_text::fontdb::Weight,
30    RasterKey,
31);
32
33impl TextEngine {
34    pub fn prefill(&mut self, list: &[Prefill]) -> Result<usize, Error> {
35        let threads = std::thread::available_parallelism().map_or(1, |count| count.get());
36        self.prefill_on(list, threads)
37    }
38
39    pub fn prefill_on(&mut self, list: &[Prefill], threads: usize) -> Result<usize, Error> {
40        let mut pending = Vec::new();
41        let mut seen = BTreeSet::new();
42        for entry in list {
43            for (font_id, font_weight, key) in self.prefill_keys(entry)? {
44                if seen.contains(&key) || self.shared.touch(&key).is_some() {
45                    continue;
46                }
47                let font = self
48                    .fonts
49                    .get_font(font_id, font_weight)
50                    .ok_or(Error::UnsupportedGlyph)?;
51                seen.insert(key.clone());
52                pending.push((key, font));
53            }
54        }
55        let images = generate(&pending, threads);
56        let count = pending.len();
57        for ((key, _), (kind, image)) in pending.into_iter().zip(images) {
58            self.shared.insert(key, kind, image)?;
59        }
60        Ok(count)
61    }
62
63    fn prefill_keys(&mut self, entry: &Prefill) -> Result<Vec<Keyed>, Error> {
64        let glyphs = match entry.number {
65            Some(forms) => self.number_glyph_keys(
66                &entry.face,
67                &entry.chars,
68                entry.scale,
69                forms,
70                entry.representation,
71            )?,
72            None => {
73                let spans = [Span {
74                    text: char_lines(&unique_chars(&entry.chars)),
75                    face: entry.face.clone(),
76                    color: [0.0, 0.0, 0.0, 1.0],
77                }];
78                let key =
79                    self.layout_block(&spans, None, entry.scale, Anchor::Start, None, None)?;
80                let spots = self.block_spots(key, &spans);
81                self.blocks.remove(&key);
82                let mut glyphs = Vec::with_capacity(spots.len());
83                for spot in spots {
84                    let key = self.spot_key(&spot, entry.representation, entry.scale)?;
85                    let keys = match entry.representation {
86                        Representation::Coverage => bin_keys(&key).to_vec(),
87                        Representation::Msdf => vec![key],
88                    };
89                    glyphs.push((spot, keys));
90                }
91                glyphs
92            }
93        };
94        let passes = match entry.representation {
95            Representation::Coverage => (0..4).filter(|&bin| entry.bins[bin]).collect(),
96            Representation::Msdf => vec![0],
97        };
98        let mut keys = Vec::new();
99        for bin in passes {
100            for (spot, glyph_keys) in &glyphs {
101                keys.push((spot.font_id, spot.font_weight, glyph_keys[bin].clone()));
102            }
103        }
104        Ok(keys)
105    }
106}
107
108fn generate(pending: &[Work], threads: usize) -> Vec<Image> {
109    let count = pending.len();
110    let next = AtomicUsize::new(0);
111    let work = || {
112        let mut context = ScaleContext::new();
113        let mut done = Vec::new();
114        loop {
115            let at = next.fetch_add(1, Ordering::Relaxed);
116            let Some((key, font)) = pending.get(at) else {
117                break;
118            };
119            done.push((at, raster(&mut context, font, key)));
120        }
121        done
122    };
123    let threads = threads.clamp(1, count.max(1));
124    let finished = if threads == 1 {
125        vec![work()]
126    } else {
127        std::thread::scope(|scope| {
128            let handles = (0..threads).map(|_| scope.spawn(work)).collect::<Vec<_>>();
129            handles
130                .into_iter()
131                .map(|handle| {
132                    handle
133                        .join()
134                        .unwrap_or_else(|panic| std::panic::resume_unwind(panic))
135                })
136                .collect::<Vec<_>>()
137        })
138    };
139    let mut finished = finished.into_iter().flatten().collect::<Vec<_>>();
140    finished.sort_unstable_by_key(|&(at, _)| at);
141    finished.into_iter().map(|(_, image)| image).collect()
142}