line_layout.rs

  1use crate::{px, EntityId, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size};
  2use collections::{FxHashMap, FxHashSet};
  3use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
  4use smallvec::SmallVec;
  5use std::{
  6    borrow::Borrow,
  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    pub fn index_for_position(
203        &self,
204        position: Point<Pixels>,
205        line_height: Pixels,
206    ) -> Option<usize> {
207        let wrapped_line_ix = (position.y / line_height) as usize;
208
209        let wrapped_line_start_x = if wrapped_line_ix > 0 {
210            let wrap_boundary_ix = wrapped_line_ix - 1;
211            let wrap_boundary = self.wrap_boundaries[wrap_boundary_ix];
212            let run = &self.unwrapped_layout.runs[wrap_boundary.run_ix];
213            run.glyphs[wrap_boundary.glyph_ix].position.x
214        } else {
215            Pixels::ZERO
216        };
217
218        let wrapped_line_end_x = if wrapped_line_ix < self.wrap_boundaries.len() {
219            let next_wrap_boundary_ix = wrapped_line_ix;
220            let next_wrap_boundary = self.wrap_boundaries[next_wrap_boundary_ix];
221            let run = &self.unwrapped_layout.runs[next_wrap_boundary.run_ix];
222            run.glyphs[next_wrap_boundary.glyph_ix].position.x
223        } else {
224            self.unwrapped_layout.width
225        };
226
227        let mut position_in_unwrapped_line = position;
228        position_in_unwrapped_line.x += wrapped_line_start_x;
229        if position_in_unwrapped_line.x > wrapped_line_end_x {
230            None
231        } else {
232            self.unwrapped_layout
233                .index_for_x(position_in_unwrapped_line.x)
234        }
235    }
236}
237
238pub(crate) struct LineLayoutCache {
239    view_stack: Mutex<Vec<EntityId>>,
240    previous_frame: Mutex<FxHashMap<CacheKey, Arc<LineLayout>>>,
241    current_frame: RwLock<FxHashMap<CacheKey, Arc<LineLayout>>>,
242    previous_frame_wrapped: Mutex<FxHashMap<CacheKey, Arc<WrappedLineLayout>>>,
243    current_frame_wrapped: RwLock<FxHashMap<CacheKey, Arc<WrappedLineLayout>>>,
244    platform_text_system: Arc<dyn PlatformTextSystem>,
245}
246
247impl LineLayoutCache {
248    pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
249        Self {
250            view_stack: Mutex::default(),
251            previous_frame: Mutex::default(),
252            current_frame: RwLock::default(),
253            previous_frame_wrapped: Mutex::default(),
254            current_frame_wrapped: RwLock::default(),
255            platform_text_system,
256        }
257    }
258
259    pub fn finish_frame(&self, reused_views: &FxHashSet<EntityId>) {
260        debug_assert_eq!(self.view_stack.lock().len(), 0);
261
262        let mut prev_frame = self.previous_frame.lock();
263        let mut curr_frame = self.current_frame.write();
264        for (key, layout) in prev_frame.drain() {
265            if key
266                .parent_view_id
267                .map_or(false, |view_id| reused_views.contains(&view_id))
268            {
269                curr_frame.insert(key, layout);
270            }
271        }
272        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
273
274        let mut prev_frame_wrapped = self.previous_frame_wrapped.lock();
275        let mut curr_frame_wrapped = self.current_frame_wrapped.write();
276        for (key, layout) in prev_frame_wrapped.drain() {
277            if key
278                .parent_view_id
279                .map_or(false, |view_id| reused_views.contains(&view_id))
280            {
281                curr_frame_wrapped.insert(key, layout);
282            }
283        }
284        std::mem::swap(&mut *prev_frame_wrapped, &mut *curr_frame_wrapped);
285    }
286
287    pub fn with_view<R>(&self, view_id: EntityId, f: impl FnOnce() -> R) -> R {
288        self.view_stack.lock().push(view_id);
289        let result = f();
290        self.view_stack.lock().pop();
291        result
292    }
293
294    fn parent_view_id(&self) -> Option<EntityId> {
295        self.view_stack.lock().last().copied()
296    }
297
298    pub fn layout_wrapped_line(
299        &self,
300        text: &str,
301        font_size: Pixels,
302        runs: &[FontRun],
303        wrap_width: Option<Pixels>,
304    ) -> Arc<WrappedLineLayout> {
305        let key = &CacheKeyRef {
306            text,
307            font_size,
308            runs,
309            wrap_width,
310            parent_view_id: self.parent_view_id(),
311        } as &dyn AsCacheKeyRef;
312
313        let current_frame = self.current_frame_wrapped.upgradable_read();
314        if let Some(layout) = current_frame.get(key) {
315            return layout.clone();
316        }
317
318        let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
319        if let Some((key, layout)) = self.previous_frame_wrapped.lock().remove_entry(key) {
320            current_frame.insert(key, layout.clone());
321            layout
322        } else {
323            let unwrapped_layout = self.layout_line(text, font_size, runs);
324            let wrap_boundaries = if let Some(wrap_width) = wrap_width {
325                unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width)
326            } else {
327                SmallVec::new()
328            };
329            let layout = Arc::new(WrappedLineLayout {
330                unwrapped_layout,
331                wrap_boundaries,
332                wrap_width,
333            });
334            let key = CacheKey {
335                text: text.into(),
336                font_size,
337                runs: SmallVec::from(runs),
338                wrap_width,
339                parent_view_id: self.parent_view_id(),
340            };
341            current_frame.insert(key, layout.clone());
342            layout
343        }
344    }
345
346    pub fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> Arc<LineLayout> {
347        let key = &CacheKeyRef {
348            text,
349            font_size,
350            runs,
351            wrap_width: None,
352            parent_view_id: self.parent_view_id(),
353        } as &dyn AsCacheKeyRef;
354
355        let current_frame = self.current_frame.upgradable_read();
356        if let Some(layout) = current_frame.get(key) {
357            return layout.clone();
358        }
359
360        let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
361        if let Some((key, layout)) = self.previous_frame.lock().remove_entry(key) {
362            current_frame.insert(key, layout.clone());
363            layout
364        } else {
365            let layout = Arc::new(self.platform_text_system.layout_line(text, font_size, runs));
366            let key = CacheKey {
367                text: text.into(),
368                font_size,
369                runs: SmallVec::from(runs),
370                wrap_width: None,
371                parent_view_id: self.parent_view_id(),
372            };
373            current_frame.insert(key, layout.clone());
374            layout
375        }
376    }
377}
378
379#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
380pub struct FontRun {
381    pub(crate) len: usize,
382    pub(crate) font_id: FontId,
383}
384
385trait AsCacheKeyRef {
386    fn as_cache_key_ref(&self) -> CacheKeyRef;
387}
388
389#[derive(Debug, Eq)]
390struct CacheKey {
391    text: String,
392    font_size: Pixels,
393    runs: SmallVec<[FontRun; 1]>,
394    wrap_width: Option<Pixels>,
395    parent_view_id: Option<EntityId>,
396}
397
398#[derive(Copy, Clone, PartialEq, Eq, Hash)]
399struct CacheKeyRef<'a> {
400    text: &'a str,
401    font_size: Pixels,
402    runs: &'a [FontRun],
403    wrap_width: Option<Pixels>,
404    parent_view_id: Option<EntityId>,
405}
406
407impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
408    fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
409        self.as_cache_key_ref() == other.as_cache_key_ref()
410    }
411}
412
413impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
414
415impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
416    fn hash<H: Hasher>(&self, state: &mut H) {
417        self.as_cache_key_ref().hash(state)
418    }
419}
420
421impl AsCacheKeyRef for CacheKey {
422    fn as_cache_key_ref(&self) -> CacheKeyRef {
423        CacheKeyRef {
424            text: &self.text,
425            font_size: self.font_size,
426            runs: self.runs.as_slice(),
427            wrap_width: self.wrap_width,
428            parent_view_id: self.parent_view_id,
429        }
430    }
431}
432
433impl PartialEq for CacheKey {
434    fn eq(&self, other: &Self) -> bool {
435        self.as_cache_key_ref().eq(&other.as_cache_key_ref())
436    }
437}
438
439impl Hash for CacheKey {
440    fn hash<H: Hasher>(&self, state: &mut H) {
441        self.as_cache_key_ref().hash(state);
442    }
443}
444
445impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for CacheKey {
446    fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
447        self as &dyn AsCacheKeyRef
448    }
449}
450
451impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
452    fn as_cache_key_ref(&self) -> CacheKeyRef {
453        *self
454    }
455}