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}