line_layout.rs

  1use crate::{px, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size};
  2use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
  3use smallvec::SmallVec;
  4use std::{
  5    borrow::Borrow,
  6    collections::HashMap,
  7    hash::{Hash, Hasher},
  8    sync::Arc,
  9};
 10
 11#[derive(Default, Debug)]
 12pub struct LineLayout {
 13    pub font_size: Pixels,
 14    pub width: Pixels,
 15    pub ascent: Pixels,
 16    pub descent: Pixels,
 17    pub runs: Vec<ShapedRun>,
 18    pub len: usize,
 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    /// closest_index_for_x returns the character boundary closest to the given x coordinate
 52    /// (e.g. to handle aligning up/down arrow keys)
 53    pub fn closest_index_for_x(&self, x: Pixels) -> usize {
 54        let mut prev_index = 0;
 55        let mut prev_x = px(0.);
 56
 57        for run in self.runs.iter() {
 58            for glyph in run.glyphs.iter() {
 59                if glyph.position.x >= x {
 60                    if glyph.position.x - x < x - prev_x {
 61                        return glyph.index;
 62                    } else {
 63                        return prev_index;
 64                    }
 65                }
 66                prev_index = glyph.index;
 67                prev_x = glyph.position.x;
 68            }
 69        }
 70
 71        self.len
 72    }
 73
 74    pub fn x_for_index(&self, index: usize) -> Pixels {
 75        for run in &self.runs {
 76            for glyph in &run.glyphs {
 77                if glyph.index >= index {
 78                    return glyph.position.x;
 79                }
 80            }
 81        }
 82        self.width
 83    }
 84
 85    fn compute_wrap_boundaries(
 86        &self,
 87        text: &str,
 88        wrap_width: Pixels,
 89    ) -> SmallVec<[WrapBoundary; 1]> {
 90        let mut boundaries = SmallVec::new();
 91
 92        let mut first_non_whitespace_ix = None;
 93        let mut last_candidate_ix = None;
 94        let mut last_candidate_x = px(0.);
 95        let mut last_boundary = WrapBoundary {
 96            run_ix: 0,
 97            glyph_ix: 0,
 98        };
 99        let mut last_boundary_x = px(0.);
100        let mut prev_ch = '\0';
101        let mut glyphs = self
102            .runs
103            .iter()
104            .enumerate()
105            .flat_map(move |(run_ix, run)| {
106                run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| {
107                    let character = text[glyph.index..].chars().next().unwrap();
108                    (
109                        WrapBoundary { run_ix, glyph_ix },
110                        character,
111                        glyph.position.x,
112                    )
113                })
114            })
115            .peekable();
116
117        while let Some((boundary, ch, x)) = glyphs.next() {
118            if ch == '\n' {
119                continue;
120            }
121
122            if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
123                last_candidate_ix = Some(boundary);
124                last_candidate_x = x;
125            }
126
127            if ch != ' ' && first_non_whitespace_ix.is_none() {
128                first_non_whitespace_ix = Some(boundary);
129            }
130
131            let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x);
132            let width = next_x - last_boundary_x;
133            if width > wrap_width && boundary > last_boundary {
134                if let Some(last_candidate_ix) = last_candidate_ix.take() {
135                    last_boundary = last_candidate_ix;
136                    last_boundary_x = last_candidate_x;
137                } else {
138                    last_boundary = boundary;
139                    last_boundary_x = x;
140                }
141
142                boundaries.push(last_boundary);
143            }
144            prev_ch = ch;
145        }
146
147        boundaries
148    }
149}
150
151#[derive(Default, Debug)]
152pub struct WrappedLineLayout {
153    pub unwrapped_layout: Arc<LineLayout>,
154    pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>,
155    pub wrap_width: Option<Pixels>,
156}
157
158#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
159pub struct WrapBoundary {
160    pub run_ix: usize,
161    pub glyph_ix: usize,
162}
163
164impl WrappedLineLayout {
165    pub fn len(&self) -> usize {
166        self.unwrapped_layout.len
167    }
168
169    pub fn width(&self) -> Pixels {
170        self.wrap_width
171            .unwrap_or(Pixels::MAX)
172            .min(self.unwrapped_layout.width)
173    }
174
175    pub fn size(&self, line_height: Pixels) -> Size<Pixels> {
176        Size {
177            width: self.width(),
178            height: line_height * (self.wrap_boundaries.len() + 1),
179        }
180    }
181
182    pub fn ascent(&self) -> Pixels {
183        self.unwrapped_layout.ascent
184    }
185
186    pub fn descent(&self) -> Pixels {
187        self.unwrapped_layout.descent
188    }
189
190    pub fn wrap_boundaries(&self) -> &[WrapBoundary] {
191        &self.wrap_boundaries
192    }
193
194    pub fn font_size(&self) -> Pixels {
195        self.unwrapped_layout.font_size
196    }
197
198    pub fn runs(&self) -> &[ShapedRun] {
199        &self.unwrapped_layout.runs
200    }
201}
202
203pub(crate) struct LineLayoutCache {
204    previous_frame: Mutex<HashMap<CacheKey, Arc<LineLayout>>>,
205    current_frame: RwLock<HashMap<CacheKey, Arc<LineLayout>>>,
206    previous_frame_wrapped: Mutex<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
207    current_frame_wrapped: RwLock<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
208    platform_text_system: Arc<dyn PlatformTextSystem>,
209}
210
211impl LineLayoutCache {
212    pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
213        Self {
214            previous_frame: Mutex::default(),
215            current_frame: RwLock::default(),
216            previous_frame_wrapped: Mutex::default(),
217            current_frame_wrapped: RwLock::default(),
218            platform_text_system,
219        }
220    }
221
222    pub fn start_frame(&self) {
223        let mut prev_frame = self.previous_frame.lock();
224        let mut curr_frame = self.current_frame.write();
225        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
226        curr_frame.clear();
227    }
228
229    pub fn layout_wrapped_line(
230        &self,
231        text: &str,
232        font_size: Pixels,
233        runs: &[FontRun],
234        wrap_width: Option<Pixels>,
235    ) -> Arc<WrappedLineLayout> {
236        let key = &CacheKeyRef {
237            text,
238            font_size,
239            runs,
240            wrap_width,
241        } as &dyn AsCacheKeyRef;
242
243        let current_frame = self.current_frame_wrapped.upgradable_read();
244        if let Some(layout) = current_frame.get(key) {
245            return layout.clone();
246        }
247
248        let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
249        if let Some((key, layout)) = self.previous_frame_wrapped.lock().remove_entry(key) {
250            current_frame.insert(key, layout.clone());
251            layout
252        } else {
253            let unwrapped_layout = self.layout_line(text, font_size, runs);
254            let wrap_boundaries = if let Some(wrap_width) = wrap_width {
255                unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width)
256            } else {
257                SmallVec::new()
258            };
259            let layout = Arc::new(WrappedLineLayout {
260                unwrapped_layout,
261                wrap_boundaries,
262                wrap_width,
263            });
264            let key = CacheKey {
265                text: text.into(),
266                font_size,
267                runs: SmallVec::from(runs),
268                wrap_width,
269            };
270            current_frame.insert(key, layout.clone());
271            layout
272        }
273    }
274
275    pub fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> Arc<LineLayout> {
276        let key = &CacheKeyRef {
277            text,
278            font_size,
279            runs,
280            wrap_width: None,
281        } as &dyn AsCacheKeyRef;
282
283        let current_frame = self.current_frame.upgradable_read();
284        if let Some(layout) = current_frame.get(key) {
285            return layout.clone();
286        }
287
288        let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
289        if let Some((key, layout)) = self.previous_frame.lock().remove_entry(key) {
290            current_frame.insert(key, layout.clone());
291            layout
292        } else {
293            let layout = Arc::new(self.platform_text_system.layout_line(text, font_size, runs));
294            let key = CacheKey {
295                text: text.into(),
296                font_size,
297                runs: SmallVec::from(runs),
298                wrap_width: None,
299            };
300            current_frame.insert(key, layout.clone());
301            layout
302        }
303    }
304}
305
306#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
307pub struct FontRun {
308    pub(crate) len: usize,
309    pub(crate) font_id: FontId,
310}
311
312trait AsCacheKeyRef {
313    fn as_cache_key_ref(&self) -> CacheKeyRef;
314}
315
316#[derive(Eq)]
317struct CacheKey {
318    text: String,
319    font_size: Pixels,
320    runs: SmallVec<[FontRun; 1]>,
321    wrap_width: Option<Pixels>,
322}
323
324#[derive(Copy, Clone, PartialEq, Eq, Hash)]
325struct CacheKeyRef<'a> {
326    text: &'a str,
327    font_size: Pixels,
328    runs: &'a [FontRun],
329    wrap_width: Option<Pixels>,
330}
331
332impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
333    fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
334        self.as_cache_key_ref() == other.as_cache_key_ref()
335    }
336}
337
338impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
339
340impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
341    fn hash<H: Hasher>(&self, state: &mut H) {
342        self.as_cache_key_ref().hash(state)
343    }
344}
345
346impl AsCacheKeyRef for CacheKey {
347    fn as_cache_key_ref(&self) -> CacheKeyRef {
348        CacheKeyRef {
349            text: &self.text,
350            font_size: self.font_size,
351            runs: self.runs.as_slice(),
352            wrap_width: self.wrap_width,
353        }
354    }
355}
356
357impl PartialEq for CacheKey {
358    fn eq(&self, other: &Self) -> bool {
359        self.as_cache_key_ref().eq(&other.as_cache_key_ref())
360    }
361}
362
363impl Hash for CacheKey {
364    fn hash<H: Hasher>(&self, state: &mut H) {
365        self.as_cache_key_ref().hash(state);
366    }
367}
368
369impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for CacheKey {
370    fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
371        self as &dyn AsCacheKeyRef
372    }
373}
374
375impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
376    fn as_cache_key_ref(&self) -> CacheKeyRef {
377        *self
378    }
379}