1use {
2 super::{
3 font::{Font, GlyphId},
4 slice::SliceExt,
5 substr::Substr,
6 },
7 makepad_rustybuzz as rustybuzz,
8 rustybuzz::UnicodeBuffer,
9 std::{
10 collections::{HashMap, VecDeque},
11 hash::Hash,
12 mem,
13 rc::Rc,
14 },
15 unicode_segmentation::UnicodeSegmentation,
16};
17
18#[derive(Debug)]
19pub struct Shaper {
20 reusable_glyphs: Vec<Vec<ShapedGlyph>>,
21 reusable_unicode_buffer: UnicodeBuffer,
22 cache_size: usize,
23 cached_params: VecDeque<ShapeParams>,
24 cached_results: HashMap<ShapeParams, Rc<ShapedText>>,
25}
26
27impl Shaper {
28 pub fn new(settings: Settings) -> Self {
29 Self {
30 reusable_glyphs: Vec::new(),
31 reusable_unicode_buffer: UnicodeBuffer::new(),
32 cache_size: settings.cache_size,
33 cached_params: VecDeque::with_capacity(settings.cache_size),
34 cached_results: HashMap::with_capacity(settings.cache_size),
35 }
36 }
37
38 pub fn get_or_shape(&mut self, params: ShapeParams) -> Rc<ShapedText> {
39 if let Some(result) = self.cached_results.get(¶ms) {
40 return result.clone();
41 }
42 if self.cached_params.len() == self.cache_size {
43 let params = self.cached_params.pop_front().unwrap();
44 self.cached_results.remove(¶ms);
45 }
46 let result = Rc::new(self.shape(params.clone()));
47 self.cached_params.push_back(params.clone());
48 self.cached_results.insert(params, result.clone());
49 result
50 }
51
52 fn shape(&mut self, params: ShapeParams) -> ShapedText {
53 let mut glyphs = Vec::new();
54 if params.fonts.is_empty() {
55 println!("WARNING: encountered empty font family");
56 } else {
57 self.shape_recursive(
58 ¶ms.text,
59 ¶ms.fonts,
60 0,
61 params.text.len(),
62 &mut glyphs,
63 );
64 }
65 ShapedText {
66 text: params.text,
67 width_in_ems: glyphs.iter().map(|glyph| glyph.advance_in_ems).sum(),
68 glyphs,
69 }
70 }
71
72 fn shape_recursive(
73 &mut self,
74 text: &str,
75 fonts: &[Rc<Font>],
76 start: usize,
77 end: usize,
78 out_glyphs: &mut Vec<ShapedGlyph>,
79 ) {
80 let (font, fonts) = fonts.split_first().unwrap();
81 let mut glyphs = self.reusable_glyphs.pop().unwrap_or(Vec::new());
82 self.shape_step(text, font, start, end, &mut glyphs);
83 let mut glyph_groups = glyphs
84 .group_by(|glyph_0, glyph_1| glyph_0.cluster == glyph_1.cluster)
85 .peekable();
86 while let Some(glyph_group) = glyph_groups.next() {
87 if glyph_group.iter().any(|glyph| glyph.id == 0) && !fonts.is_empty() {
88 let missing_start = glyph_group[0].cluster;
89 while glyph_groups.peek().map_or(false, |glyph_group| {
90 glyph_group.iter().any(|glyph| glyph.id == 0)
91 }) {
92 glyph_groups.next();
93 }
94 let missing_end = glyph_groups
95 .peek()
96 .map_or(end, |next_glyph_group| next_glyph_group[0].cluster);
97 self.shape_recursive(text, fonts, missing_start, missing_end, out_glyphs);
98 } else {
99 out_glyphs.extend(glyph_group.iter().cloned());
100 }
101 }
102 drop(glyph_groups);
103 glyphs.clear();
104 self.reusable_glyphs.push(glyphs);
105 }
106
107 fn shape_step(
108 &mut self,
109 text: &str,
110 font: &Rc<Font>,
111 start: usize,
112 end: usize,
113 out_glyphs: &mut Vec<ShapedGlyph>,
114 ) {
115 let mut unicode_buffer = mem::take(&mut self.reusable_unicode_buffer);
116 for (index, grapheme) in text[start..end].grapheme_indices(true) {
117 let cluster = start + index;
118 for char in grapheme.chars() {
119 unicode_buffer.add(char, cluster as u32);
120 }
121 }
122 let mut glyph_buffer = rustybuzz::shape(font.rustybuzz_face(), &[], unicode_buffer);
123 let out_glyph_start = out_glyphs.len();
124 out_glyphs.extend(
125 glyph_buffer
126 .glyph_infos()
127 .iter()
128 .zip(glyph_buffer.glyph_positions())
129 .map(|(glyph_info, glyph_position)| ShapedGlyph {
130 font: font.clone(),
131 id: glyph_info.glyph_id as u16,
132 cluster: glyph_info.cluster as usize,
133 advance_in_ems: glyph_position.x_advance as f32 / font.units_per_em(),
134 offset_in_ems: glyph_position.x_offset as f32 / font.units_per_em(),
135 }),
136 );
137
138 if out_glyphs[out_glyph_start..]
145 .windows(2)
146 .any(|glyphs| {
147 glyphs[0].cluster > glyphs[1].cluster
148 })
149 {
150 out_glyphs.truncate(out_glyph_start);
151 let mut unicode_buffer = glyph_buffer.clear();
152 for (index, _) in text[start..end].grapheme_indices(true) {
153 let cluster = start + index;
154 unicode_buffer.add('□', cluster as u32);
155 }
156 glyph_buffer = rustybuzz::shape(font.rustybuzz_face(), &[], unicode_buffer);
157 out_glyphs.extend(
158 glyph_buffer
159 .glyph_infos()
160 .iter()
161 .zip(glyph_buffer.glyph_positions())
162 .map(|(glyph_info, glyph_position)| ShapedGlyph {
163 font: font.clone(),
164 id: glyph_info.glyph_id as u16,
165 cluster: glyph_info.cluster as usize,
166 advance_in_ems: glyph_position.x_advance as f32 / font.units_per_em(),
167 offset_in_ems: glyph_position.x_offset as f32 / font.units_per_em(),
168 }),
169 );
170 }
171
172 self.reusable_unicode_buffer = glyph_buffer.clear();
173 }
174}
175
176#[derive(Clone, Copy, Debug)]
177pub struct Settings {
178 pub cache_size: usize,
179}
180
181#[derive(Clone, Debug, Eq, Hash, PartialEq)]
182pub struct ShapeParams {
183 pub text: Substr,
184 pub fonts: Rc<[Rc<Font>]>,
185}
186
187#[derive(Clone, Debug)]
188pub struct ShapedText {
189 pub text: Substr,
190 pub width_in_ems: f32,
191 pub glyphs: Vec<ShapedGlyph>,
192}
193
194#[derive(Clone, Debug)]
195pub struct ShapedGlyph {
196 pub font: Rc<Font>,
197 pub id: GlyphId,
198 pub cluster: usize,
199 pub advance_in_ems: f32,
200 pub offset_in_ems: f32,
201}