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 end_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: &[(usize, FontId)],
182 wrap_width: Option<Pixels>,
183 ) -> Arc<WrappedLineLayout> {
184 let key = &CacheKeyRef {
185 text,
186 font_size,
187 runs,
188 } as &dyn AsCacheKeyRef;
189 let curr_frame = self.curr_frame.upgradable_read();
190 if let Some(layout) = curr_frame.get(key) {
191 return layout.clone();
192 }
193
194 let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
195 if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
196 curr_frame.insert(key, layout.clone());
197 layout
198 } else {
199 let layout = self.platform_text_system.layout_line(text, font_size, runs);
200 let wrap_boundaries = wrap_width
201 .map(|wrap_width| layout.compute_wrap_boundaries(text.as_ref(), wrap_width))
202 .unwrap_or_default();
203 let wrapped_line = Arc::new(WrappedLineLayout {
204 layout,
205 text: text.clone(),
206 wrap_boundaries,
207 });
208
209 let key = CacheKey {
210 text: text.clone(),
211 font_size,
212 runs: SmallVec::from(runs),
213 };
214 curr_frame.insert(key, wrapped_line.clone());
215 wrapped_line
216 }
217 }
218}
219
220trait AsCacheKeyRef {
221 fn as_cache_key_ref(&self) -> CacheKeyRef;
222}
223
224#[derive(Eq)]
225struct CacheKey {
226 text: SharedString,
227 font_size: Pixels,
228 runs: SmallVec<[(usize, FontId); 1]>,
229}
230
231#[derive(Copy, Clone, PartialEq, Eq)]
232struct CacheKeyRef<'a> {
233 text: &'a str,
234 font_size: Pixels,
235 runs: &'a [(usize, FontId)],
236}
237
238impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
239 fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
240 self.as_cache_key_ref() == other.as_cache_key_ref()
241 }
242}
243
244impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
245
246impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
247 fn hash<H: Hasher>(&self, state: &mut H) {
248 self.as_cache_key_ref().hash(state)
249 }
250}
251
252impl AsCacheKeyRef for CacheKey {
253 fn as_cache_key_ref(&self) -> CacheKeyRef {
254 CacheKeyRef {
255 text: &self.text,
256 font_size: self.font_size,
257 runs: self.runs.as_slice(),
258 }
259 }
260}
261
262impl PartialEq for CacheKey {
263 fn eq(&self, other: &Self) -> bool {
264 self.as_cache_key_ref().eq(&other.as_cache_key_ref())
265 }
266}
267
268impl Hash for CacheKey {
269 fn hash<H: Hasher>(&self, state: &mut H) {
270 self.as_cache_key_ref().hash(state);
271 }
272}
273
274impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for CacheKey {
275 fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
276 self as &dyn AsCacheKeyRef
277 }
278}
279
280impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
281 fn as_cache_key_ref(&self) -> CacheKeyRef {
282 *self
283 }
284}
285
286impl<'a> Hash for CacheKeyRef<'a> {
287 fn hash<H: Hasher>(&self, state: &mut H) {
288 self.text.hash(state);
289 self.font_size.hash(state);
290 for (len, font_id) in self.runs {
291 len.hash(state);
292 font_id.hash(state);
293 }
294 }
295}