line_layout.rs

  1use crate::{px, FontId, GlyphId, Pixels, PlatformTextSystem, Point, Size};
  2use collections::FxHashMap;
  3use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
  4use smallvec::SmallVec;
  5use std::{
  6    borrow::Borrow,
  7    hash::{Hash, Hasher},
  8    ops::Range,
  9    sync::Arc,
 10};
 11
 12/// A laid out and styled line of text
 13#[derive(Default, Debug)]
 14pub struct LineLayout {
 15    /// The font size for this line
 16    pub font_size: Pixels,
 17    /// The width of the line
 18    pub width: Pixels,
 19    /// The ascent of the line
 20    pub ascent: Pixels,
 21    /// The descent of the line
 22    pub descent: Pixels,
 23    /// The shaped runs that make up this line
 24    pub runs: Vec<ShapedRun>,
 25    /// The length of the line in utf-8 bytes
 26    pub len: usize,
 27}
 28
 29/// A run of text that has been shaped .
 30#[derive(Debug)]
 31pub struct ShapedRun {
 32    /// The font id for this run
 33    pub font_id: FontId,
 34    /// The glyphs that make up this run
 35    pub glyphs: SmallVec<[ShapedGlyph; 8]>,
 36}
 37
 38/// A single glyph, ready to paint.
 39#[derive(Clone, Debug)]
 40pub struct ShapedGlyph {
 41    /// The ID for this glyph, as determined by the text system.
 42    pub id: GlyphId,
 43
 44    /// The position of this glyph in its containing line.
 45    pub position: Point<Pixels>,
 46
 47    /// The index of this glyph in the original text.
 48    pub index: usize,
 49
 50    /// Whether this glyph is an emoji
 51    pub is_emoji: bool,
 52}
 53
 54impl LineLayout {
 55    /// The index for the character at the given x coordinate
 56    pub fn index_for_x(&self, x: Pixels) -> Option<usize> {
 57        if x >= self.width {
 58            None
 59        } else {
 60            for run in self.runs.iter().rev() {
 61                for glyph in run.glyphs.iter().rev() {
 62                    if glyph.position.x <= x {
 63                        return Some(glyph.index);
 64                    }
 65                }
 66            }
 67            Some(0)
 68        }
 69    }
 70
 71    /// closest_index_for_x returns the character boundary closest to the given x coordinate
 72    /// (e.g. to handle aligning up/down arrow keys)
 73    pub fn closest_index_for_x(&self, x: Pixels) -> usize {
 74        let mut prev_index = 0;
 75        let mut prev_x = px(0.);
 76
 77        for run in self.runs.iter() {
 78            for glyph in run.glyphs.iter() {
 79                if glyph.position.x >= x {
 80                    if glyph.position.x - x < x - prev_x {
 81                        return glyph.index;
 82                    } else {
 83                        return prev_index;
 84                    }
 85                }
 86                prev_index = glyph.index;
 87                prev_x = glyph.position.x;
 88            }
 89        }
 90
 91        self.len
 92    }
 93
 94    /// The x position of the character at the given index
 95    pub fn x_for_index(&self, index: usize) -> Pixels {
 96        for run in &self.runs {
 97            for glyph in &run.glyphs {
 98                if glyph.index >= index {
 99                    return glyph.position.x;
100                }
101            }
102        }
103        self.width
104    }
105
106    /// The corresponding Font at the given index
107    pub fn font_id_for_index(&self, index: usize) -> Option<FontId> {
108        for run in &self.runs {
109            for glyph in &run.glyphs {
110                if glyph.index >= index {
111                    return Some(run.font_id);
112                }
113            }
114        }
115        None
116    }
117
118    fn compute_wrap_boundaries(
119        &self,
120        text: &str,
121        wrap_width: Pixels,
122    ) -> SmallVec<[WrapBoundary; 1]> {
123        let mut boundaries = SmallVec::new();
124
125        let mut first_non_whitespace_ix = None;
126        let mut last_candidate_ix = None;
127        let mut last_candidate_x = px(0.);
128        let mut last_boundary = WrapBoundary {
129            run_ix: 0,
130            glyph_ix: 0,
131        };
132        let mut last_boundary_x = px(0.);
133        let mut prev_ch = '\0';
134        let mut glyphs = self
135            .runs
136            .iter()
137            .enumerate()
138            .flat_map(move |(run_ix, run)| {
139                run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| {
140                    let character = text[glyph.index..].chars().next().unwrap();
141                    (
142                        WrapBoundary { run_ix, glyph_ix },
143                        character,
144                        glyph.position.x,
145                    )
146                })
147            })
148            .peekable();
149
150        while let Some((boundary, ch, x)) = glyphs.next() {
151            if ch == '\n' {
152                continue;
153            }
154
155            if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
156                last_candidate_ix = Some(boundary);
157                last_candidate_x = x;
158            }
159
160            if ch != ' ' && first_non_whitespace_ix.is_none() {
161                first_non_whitespace_ix = Some(boundary);
162            }
163
164            let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x);
165            let width = next_x - last_boundary_x;
166            if width > wrap_width && boundary > last_boundary {
167                if let Some(last_candidate_ix) = last_candidate_ix.take() {
168                    last_boundary = last_candidate_ix;
169                    last_boundary_x = last_candidate_x;
170                } else {
171                    last_boundary = boundary;
172                    last_boundary_x = x;
173                }
174
175                boundaries.push(last_boundary);
176            }
177            prev_ch = ch;
178        }
179
180        boundaries
181    }
182}
183
184/// A line of text that has been wrapped to fit a given width
185#[derive(Default, Debug)]
186pub struct WrappedLineLayout {
187    /// The line layout, pre-wrapping.
188    pub unwrapped_layout: Arc<LineLayout>,
189
190    /// The boundaries at which the line was wrapped
191    pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>,
192
193    /// The width of the line, if it was wrapped
194    pub wrap_width: Option<Pixels>,
195}
196
197/// A boundary at which a line was wrapped
198#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
199pub struct WrapBoundary {
200    /// The index in the run just before the line was wrapped
201    pub run_ix: usize,
202    /// The index of the glyph just before the line was wrapped
203    pub glyph_ix: usize,
204}
205
206impl WrappedLineLayout {
207    /// The length of the underlying text, in utf8 bytes.
208    #[allow(clippy::len_without_is_empty)]
209    pub fn len(&self) -> usize {
210        self.unwrapped_layout.len
211    }
212
213    /// The width of this line, in pixels, whether or not it was wrapped.
214    pub fn width(&self) -> Pixels {
215        self.wrap_width
216            .unwrap_or(Pixels::MAX)
217            .min(self.unwrapped_layout.width)
218    }
219
220    /// The size of the whole wrapped text, for the given line_height.
221    /// can span multiple lines if there are multiple wrap boundaries.
222    pub fn size(&self, line_height: Pixels) -> Size<Pixels> {
223        Size {
224            width: self.width(),
225            height: line_height * (self.wrap_boundaries.len() + 1),
226        }
227    }
228
229    /// The ascent of a line in this layout
230    pub fn ascent(&self) -> Pixels {
231        self.unwrapped_layout.ascent
232    }
233
234    /// The descent of a line in this layout
235    pub fn descent(&self) -> Pixels {
236        self.unwrapped_layout.descent
237    }
238
239    /// The wrap boundaries in this layout
240    pub fn wrap_boundaries(&self) -> &[WrapBoundary] {
241        &self.wrap_boundaries
242    }
243
244    /// The font size of this layout
245    pub fn font_size(&self) -> Pixels {
246        self.unwrapped_layout.font_size
247    }
248
249    /// The runs in this layout, sans wrapping
250    pub fn runs(&self) -> &[ShapedRun] {
251        &self.unwrapped_layout.runs
252    }
253
254    /// The index corresponding to a given position in this layout for the given line height.
255    pub fn index_for_position(
256        &self,
257        position: Point<Pixels>,
258        line_height: Pixels,
259    ) -> Option<usize> {
260        let wrapped_line_ix = (position.y / line_height) as usize;
261
262        let wrapped_line_start_x = if wrapped_line_ix > 0 {
263            let Some(line_start_boundary) = self.wrap_boundaries.get(wrapped_line_ix - 1) else {
264                return None;
265            };
266            let run = &self.unwrapped_layout.runs[line_start_boundary.run_ix];
267            run.glyphs[line_start_boundary.glyph_ix].position.x
268        } else {
269            Pixels::ZERO
270        };
271
272        let wrapped_line_end_x = if wrapped_line_ix < self.wrap_boundaries.len() {
273            let next_wrap_boundary_ix = wrapped_line_ix;
274            let next_wrap_boundary = self.wrap_boundaries[next_wrap_boundary_ix];
275            let run = &self.unwrapped_layout.runs[next_wrap_boundary.run_ix];
276            run.glyphs[next_wrap_boundary.glyph_ix].position.x
277        } else {
278            self.unwrapped_layout.width
279        };
280
281        let mut position_in_unwrapped_line = position;
282        position_in_unwrapped_line.x += wrapped_line_start_x;
283        if position_in_unwrapped_line.x > wrapped_line_end_x {
284            None
285        } else {
286            self.unwrapped_layout
287                .index_for_x(position_in_unwrapped_line.x)
288        }
289    }
290}
291
292pub(crate) struct LineLayoutCache {
293    previous_frame: Mutex<FrameCache>,
294    current_frame: RwLock<FrameCache>,
295    platform_text_system: Arc<dyn PlatformTextSystem>,
296}
297
298#[derive(Default)]
299struct FrameCache {
300    lines: FxHashMap<Arc<CacheKey>, Arc<LineLayout>>,
301    wrapped_lines: FxHashMap<Arc<CacheKey>, Arc<WrappedLineLayout>>,
302    used_lines: Vec<Arc<CacheKey>>,
303    used_wrapped_lines: Vec<Arc<CacheKey>>,
304}
305
306#[derive(Clone, Default)]
307pub(crate) struct LineLayoutIndex {
308    lines_index: usize,
309    wrapped_lines_index: usize,
310}
311
312impl LineLayoutCache {
313    pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
314        Self {
315            previous_frame: Mutex::default(),
316            current_frame: RwLock::default(),
317            platform_text_system,
318        }
319    }
320
321    pub fn layout_index(&self) -> LineLayoutIndex {
322        let frame = self.current_frame.read();
323        LineLayoutIndex {
324            lines_index: frame.used_lines.len(),
325            wrapped_lines_index: frame.used_wrapped_lines.len(),
326        }
327    }
328
329    pub fn reuse_layouts(&self, range: Range<LineLayoutIndex>) {
330        let mut previous_frame = &mut *self.previous_frame.lock();
331        let mut current_frame = &mut *self.current_frame.write();
332
333        for key in &previous_frame.used_lines[range.start.lines_index..range.end.lines_index] {
334            if let Some((key, line)) = previous_frame.lines.remove_entry(key) {
335                current_frame.lines.insert(key, line);
336            }
337            current_frame.used_lines.push(key.clone());
338        }
339
340        for key in &previous_frame.used_wrapped_lines
341            [range.start.wrapped_lines_index..range.end.wrapped_lines_index]
342        {
343            if let Some((key, line)) = previous_frame.wrapped_lines.remove_entry(key) {
344                current_frame.wrapped_lines.insert(key, line);
345            }
346            current_frame.used_wrapped_lines.push(key.clone());
347        }
348    }
349
350    pub fn truncate_layouts(&self, index: LineLayoutIndex) {
351        let mut current_frame = &mut *self.current_frame.write();
352        current_frame.used_lines.truncate(index.lines_index);
353        current_frame
354            .used_wrapped_lines
355            .truncate(index.wrapped_lines_index);
356    }
357
358    pub fn finish_frame(&self) {
359        let mut prev_frame = self.previous_frame.lock();
360        let mut curr_frame = self.current_frame.write();
361        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
362        curr_frame.lines.clear();
363        curr_frame.wrapped_lines.clear();
364        curr_frame.used_lines.clear();
365        curr_frame.used_wrapped_lines.clear();
366    }
367
368    pub fn layout_wrapped_line(
369        &self,
370        text: &str,
371        font_size: Pixels,
372        runs: &[FontRun],
373        wrap_width: Option<Pixels>,
374    ) -> Arc<WrappedLineLayout> {
375        let key = &CacheKeyRef {
376            text,
377            font_size,
378            runs,
379            wrap_width,
380        } as &dyn AsCacheKeyRef;
381
382        let current_frame = self.current_frame.upgradable_read();
383        if let Some(layout) = current_frame.wrapped_lines.get(key) {
384            return layout.clone();
385        }
386
387        let previous_frame_entry = self.previous_frame.lock().wrapped_lines.remove_entry(key);
388        if let Some((key, layout)) = previous_frame_entry {
389            let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
390            current_frame
391                .wrapped_lines
392                .insert(key.clone(), layout.clone());
393            current_frame.used_wrapped_lines.push(key);
394            layout
395        } else {
396            drop(current_frame);
397
398            let unwrapped_layout = self.layout_line(text, font_size, runs);
399            let wrap_boundaries = if let Some(wrap_width) = wrap_width {
400                unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width)
401            } else {
402                SmallVec::new()
403            };
404            let layout = Arc::new(WrappedLineLayout {
405                unwrapped_layout,
406                wrap_boundaries,
407                wrap_width,
408            });
409            let key = Arc::new(CacheKey {
410                text: text.into(),
411                font_size,
412                runs: SmallVec::from(runs),
413                wrap_width,
414            });
415
416            let mut current_frame = self.current_frame.write();
417            current_frame
418                .wrapped_lines
419                .insert(key.clone(), layout.clone());
420            current_frame.used_wrapped_lines.push(key);
421
422            layout
423        }
424    }
425
426    pub fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> Arc<LineLayout> {
427        let key = &CacheKeyRef {
428            text,
429            font_size,
430            runs,
431            wrap_width: None,
432        } as &dyn AsCacheKeyRef;
433
434        let current_frame = self.current_frame.upgradable_read();
435        if let Some(layout) = current_frame.lines.get(key) {
436            return layout.clone();
437        }
438
439        let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
440        if let Some((key, layout)) = self.previous_frame.lock().lines.remove_entry(key) {
441            current_frame.lines.insert(key.clone(), layout.clone());
442            current_frame.used_lines.push(key);
443            layout
444        } else {
445            let layout = Arc::new(self.platform_text_system.layout_line(text, font_size, runs));
446            let key = Arc::new(CacheKey {
447                text: text.into(),
448                font_size,
449                runs: SmallVec::from(runs),
450                wrap_width: None,
451            });
452            current_frame.lines.insert(key.clone(), layout.clone());
453            current_frame.used_lines.push(key);
454            layout
455        }
456    }
457}
458
459/// A run of text with a single font.
460#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
461pub struct FontRun {
462    pub(crate) len: usize,
463    pub(crate) font_id: FontId,
464}
465
466trait AsCacheKeyRef {
467    fn as_cache_key_ref(&self) -> CacheKeyRef;
468}
469
470#[derive(Clone, Debug, Eq)]
471struct CacheKey {
472    text: String,
473    font_size: Pixels,
474    runs: SmallVec<[FontRun; 1]>,
475    wrap_width: Option<Pixels>,
476}
477
478#[derive(Copy, Clone, PartialEq, Eq, Hash)]
479struct CacheKeyRef<'a> {
480    text: &'a str,
481    font_size: Pixels,
482    runs: &'a [FontRun],
483    wrap_width: Option<Pixels>,
484}
485
486impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
487    fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
488        self.as_cache_key_ref() == other.as_cache_key_ref()
489    }
490}
491
492impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
493
494impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
495    fn hash<H: Hasher>(&self, state: &mut H) {
496        self.as_cache_key_ref().hash(state)
497    }
498}
499
500impl AsCacheKeyRef for CacheKey {
501    fn as_cache_key_ref(&self) -> CacheKeyRef {
502        CacheKeyRef {
503            text: &self.text,
504            font_size: self.font_size,
505            runs: self.runs.as_slice(),
506            wrap_width: self.wrap_width,
507        }
508    }
509}
510
511impl PartialEq for CacheKey {
512    fn eq(&self, other: &Self) -> bool {
513        self.as_cache_key_ref().eq(&other.as_cache_key_ref())
514    }
515}
516
517impl Hash for CacheKey {
518    fn hash<H: Hasher>(&self, state: &mut H) {
519        self.as_cache_key_ref().hash(state);
520    }
521}
522
523impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for Arc<CacheKey> {
524    fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
525        self.as_ref() as &dyn AsCacheKeyRef
526    }
527}
528
529impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
530    fn as_cache_key_ref(&self) -> CacheKeyRef {
531        *self
532    }
533}