line_layout.rs

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