1use crate::{px, FontId, GlyphId, Pixels, PlatformTextSystem, Point, SharedString};
2use derive_more::{Deref, DerefMut};
3use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
4use smallvec::SmallVec;
5use std::{
6 borrow::Borrow,
7 collections::HashMap,
8 hash::{Hash, Hasher},
9 sync::Arc,
10};
11
12#[derive(Default, Debug)]
13pub struct LineLayout {
14 pub font_size: Pixels,
15 pub width: Pixels,
16 pub ascent: Pixels,
17 pub descent: Pixels,
18 pub runs: Vec<ShapedRun>,
19}
20
21#[derive(Debug)]
22pub struct ShapedRun {
23 pub font_id: FontId,
24 pub glyphs: SmallVec<[ShapedGlyph; 8]>,
25}
26
27#[derive(Clone, Debug)]
28pub struct ShapedGlyph {
29 pub id: GlyphId,
30 pub position: Point<Pixels>,
31 pub index: usize,
32 pub is_emoji: bool,
33}
34
35impl LineLayout {
36 pub fn index_for_x(&self, x: Pixels) -> Option<usize> {
37 if x >= self.width {
38 None
39 } else {
40 for run in self.runs.iter().rev() {
41 for glyph in run.glyphs.iter().rev() {
42 if glyph.position.x <= x {
43 return Some(glyph.index);
44 }
45 }
46 }
47 Some(0)
48 }
49 }
50
51 pub fn x_for_index(&self, index: usize) -> Pixels {
52 for run in &self.runs {
53 for glyph in &run.glyphs {
54 if glyph.index >= index {
55 return glyph.position.x;
56 }
57 }
58 }
59 self.width
60 }
61
62 pub fn font_for_index(&self, index: usize) -> Option<FontId> {
63 for run in &self.runs {
64 for glyph in &run.glyphs {
65 if glyph.index >= index {
66 return Some(run.font_id);
67 }
68 }
69 }
70
71 None
72 }
73
74 fn compute_wrap_boundaries(
75 &self,
76 text: &str,
77 wrap_width: Pixels,
78 ) -> SmallVec<[WrapBoundary; 1]> {
79 let mut boundaries = SmallVec::new();
80
81 let mut first_non_whitespace_ix = None;
82 let mut last_candidate_ix = None;
83 let mut last_candidate_x = px(0.);
84 let mut last_boundary = WrapBoundary {
85 run_ix: 0,
86 glyph_ix: 0,
87 };
88 let mut last_boundary_x = px(0.);
89 let mut prev_ch = '\0';
90 let mut glyphs = self
91 .runs
92 .iter()
93 .enumerate()
94 .flat_map(move |(run_ix, run)| {
95 run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| {
96 let character = text[glyph.index..].chars().next().unwrap();
97 (
98 WrapBoundary { run_ix, glyph_ix },
99 character,
100 glyph.position.x,
101 )
102 })
103 })
104 .peekable();
105
106 while let Some((boundary, ch, x)) = glyphs.next() {
107 if ch == '\n' {
108 continue;
109 }
110
111 if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
112 last_candidate_ix = Some(boundary);
113 last_candidate_x = x;
114 }
115
116 if ch != ' ' && first_non_whitespace_ix.is_none() {
117 first_non_whitespace_ix = Some(boundary);
118 }
119
120 let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x);
121 let width = next_x - last_boundary_x;
122 if width > wrap_width && boundary > last_boundary {
123 if let Some(last_candidate_ix) = last_candidate_ix.take() {
124 last_boundary = last_candidate_ix;
125 last_boundary_x = last_candidate_x;
126 } else {
127 last_boundary = boundary;
128 last_boundary_x = x;
129 }
130
131 boundaries.push(last_boundary);
132 }
133 prev_ch = ch;
134 }
135
136 boundaries
137 }
138}
139
140#[derive(Deref, DerefMut, Default, Debug)]
141pub struct WrappedLineLayout {
142 #[deref]
143 #[deref_mut]
144 pub layout: LineLayout,
145 pub text: SharedString,
146 pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>,
147}
148
149#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
150pub struct WrapBoundary {
151 pub run_ix: usize,
152 pub glyph_ix: usize,
153}
154
155pub(crate) struct LineLayoutCache {
156 prev_frame: Mutex<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
157 curr_frame: RwLock<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
158 platform_text_system: Arc<dyn PlatformTextSystem>,
159}
160
161impl LineLayoutCache {
162 pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
163 Self {
164 prev_frame: Mutex::new(HashMap::new()),
165 curr_frame: RwLock::new(HashMap::new()),
166 platform_text_system,
167 }
168 }
169
170 pub fn start_frame(&self) {
171 let mut prev_frame = self.prev_frame.lock();
172 let mut curr_frame = self.curr_frame.write();
173 std::mem::swap(&mut *prev_frame, &mut *curr_frame);
174 curr_frame.clear();
175 }
176
177 pub fn layout_line(
178 &self,
179 text: &SharedString,
180 font_size: Pixels,
181 runs: &[FontRun],
182 wrap_width: Option<Pixels>,
183 ) -> Arc<WrappedLineLayout> {
184 let key = &CacheKeyRef {
185 text,
186 font_size,
187 runs,
188 wrap_width,
189 } as &dyn AsCacheKeyRef;
190 let curr_frame = self.curr_frame.upgradable_read();
191 if let Some(layout) = curr_frame.get(key) {
192 return layout.clone();
193 }
194
195 let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
196 if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
197 curr_frame.insert(key, layout.clone());
198 layout
199 } else {
200 let layout = self.platform_text_system.layout_line(text, font_size, runs);
201 let wrap_boundaries = wrap_width
202 .map(|wrap_width| layout.compute_wrap_boundaries(text.as_ref(), wrap_width))
203 .unwrap_or_default();
204 let wrapped_line = Arc::new(WrappedLineLayout {
205 layout,
206 text: text.clone(),
207 wrap_boundaries,
208 });
209
210 let key = CacheKey {
211 text: text.clone(),
212 font_size,
213 runs: SmallVec::from(runs),
214 wrap_width,
215 };
216 curr_frame.insert(key, wrapped_line.clone());
217 wrapped_line
218 }
219 }
220}
221
222#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
223pub struct FontRun {
224 pub(crate) len: usize,
225 pub(crate) font_id: FontId,
226}
227
228trait AsCacheKeyRef {
229 fn as_cache_key_ref(&self) -> CacheKeyRef;
230}
231
232#[derive(Eq)]
233struct CacheKey {
234 text: SharedString,
235 font_size: Pixels,
236 runs: SmallVec<[FontRun; 1]>,
237 wrap_width: Option<Pixels>,
238}
239
240#[derive(Copy, Clone, PartialEq, Eq, Hash)]
241struct CacheKeyRef<'a> {
242 text: &'a str,
243 font_size: Pixels,
244 runs: &'a [FontRun],
245 wrap_width: Option<Pixels>,
246}
247
248impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
249 fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
250 self.as_cache_key_ref() == other.as_cache_key_ref()
251 }
252}
253
254impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
255
256impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
257 fn hash<H: Hasher>(&self, state: &mut H) {
258 self.as_cache_key_ref().hash(state)
259 }
260}
261
262impl AsCacheKeyRef for CacheKey {
263 fn as_cache_key_ref(&self) -> CacheKeyRef {
264 CacheKeyRef {
265 text: &self.text,
266 font_size: self.font_size,
267 runs: self.runs.as_slice(),
268 wrap_width: self.wrap_width,
269 }
270 }
271}
272
273impl PartialEq for CacheKey {
274 fn eq(&self, other: &Self) -> bool {
275 self.as_cache_key_ref().eq(&other.as_cache_key_ref())
276 }
277}
278
279impl Hash for CacheKey {
280 fn hash<H: Hasher>(&self, state: &mut H) {
281 self.as_cache_key_ref().hash(state);
282 }
283}
284
285impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for CacheKey {
286 fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
287 self as &dyn AsCacheKeyRef
288 }
289}
290
291impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
292 fn as_cache_key_ref(&self) -> CacheKeyRef {
293 *self
294 }
295}