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)]
 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        for key in &previous_frame.used_lines[range.start.lines_index..range.end.lines_index] {
389            if let Some((key, line)) = previous_frame.lines.remove_entry(key) {
390                current_frame.lines.insert(key, line);
391            }
392            current_frame.used_lines.push(key.clone());
393        }
394
395        for key in &previous_frame.used_wrapped_lines
396            [range.start.wrapped_lines_index..range.end.wrapped_lines_index]
397        {
398            if let Some((key, line)) = previous_frame.wrapped_lines.remove_entry(key) {
399                current_frame.wrapped_lines.insert(key, line);
400            }
401            current_frame.used_wrapped_lines.push(key.clone());
402        }
403    }
404
405    pub fn truncate_layouts(&self, index: LineLayoutIndex) {
406        let mut current_frame = &mut *self.current_frame.write();
407        current_frame.used_lines.truncate(index.lines_index);
408        current_frame
409            .used_wrapped_lines
410            .truncate(index.wrapped_lines_index);
411    }
412
413    pub fn finish_frame(&self) {
414        let mut prev_frame = self.previous_frame.lock();
415        let mut curr_frame = self.current_frame.write();
416        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
417        curr_frame.lines.clear();
418        curr_frame.wrapped_lines.clear();
419        curr_frame.used_lines.clear();
420        curr_frame.used_wrapped_lines.clear();
421    }
422
423    pub fn layout_wrapped_line(
424        &self,
425        text: &str,
426        font_size: Pixels,
427        runs: &[FontRun],
428        wrap_width: Option<Pixels>,
429    ) -> Arc<WrappedLineLayout> {
430        let key = &CacheKeyRef {
431            text,
432            font_size,
433            runs,
434            wrap_width,
435        } as &dyn AsCacheKeyRef;
436
437        let current_frame = self.current_frame.upgradable_read();
438        if let Some(layout) = current_frame.wrapped_lines.get(key) {
439            return layout.clone();
440        }
441
442        let previous_frame_entry = self.previous_frame.lock().wrapped_lines.remove_entry(key);
443        if let Some((key, layout)) = previous_frame_entry {
444            let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
445            current_frame
446                .wrapped_lines
447                .insert(key.clone(), layout.clone());
448            current_frame.used_wrapped_lines.push(key);
449            layout
450        } else {
451            drop(current_frame);
452
453            let unwrapped_layout = self.layout_line(text, font_size, runs);
454            let wrap_boundaries = if let Some(wrap_width) = wrap_width {
455                unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width)
456            } else {
457                SmallVec::new()
458            };
459            let layout = Arc::new(WrappedLineLayout {
460                unwrapped_layout,
461                wrap_boundaries,
462                wrap_width,
463            });
464            let key = Arc::new(CacheKey {
465                text: text.into(),
466                font_size,
467                runs: SmallVec::from(runs),
468                wrap_width,
469            });
470
471            let mut current_frame = self.current_frame.write();
472            current_frame
473                .wrapped_lines
474                .insert(key.clone(), layout.clone());
475            current_frame.used_wrapped_lines.push(key);
476
477            layout
478        }
479    }
480
481    pub fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> Arc<LineLayout> {
482        let key = &CacheKeyRef {
483            text,
484            font_size,
485            runs,
486            wrap_width: None,
487        } as &dyn AsCacheKeyRef;
488
489        let current_frame = self.current_frame.upgradable_read();
490        if let Some(layout) = current_frame.lines.get(key) {
491            return layout.clone();
492        }
493
494        let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
495        if let Some((key, layout)) = self.previous_frame.lock().lines.remove_entry(key) {
496            current_frame.lines.insert(key.clone(), layout.clone());
497            current_frame.used_lines.push(key);
498            layout
499        } else {
500            let layout = Arc::new(self.platform_text_system.layout_line(text, font_size, runs));
501            let key = Arc::new(CacheKey {
502                text: text.into(),
503                font_size,
504                runs: SmallVec::from(runs),
505                wrap_width: None,
506            });
507            current_frame.lines.insert(key.clone(), layout.clone());
508            current_frame.used_lines.push(key);
509            layout
510        }
511    }
512}
513
514/// A run of text with a single font.
515#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
516pub struct FontRun {
517    pub(crate) len: usize,
518    pub(crate) font_id: FontId,
519}
520
521trait AsCacheKeyRef {
522    fn as_cache_key_ref(&self) -> CacheKeyRef;
523}
524
525#[derive(Clone, Debug, Eq)]
526struct CacheKey {
527    text: String,
528    font_size: Pixels,
529    runs: SmallVec<[FontRun; 1]>,
530    wrap_width: Option<Pixels>,
531}
532
533#[derive(Copy, Clone, PartialEq, Eq, Hash)]
534struct CacheKeyRef<'a> {
535    text: &'a str,
536    font_size: Pixels,
537    runs: &'a [FontRun],
538    wrap_width: Option<Pixels>,
539}
540
541impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
542    fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
543        self.as_cache_key_ref() == other.as_cache_key_ref()
544    }
545}
546
547impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
548
549impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
550    fn hash<H: Hasher>(&self, state: &mut H) {
551        self.as_cache_key_ref().hash(state)
552    }
553}
554
555impl AsCacheKeyRef for CacheKey {
556    fn as_cache_key_ref(&self) -> CacheKeyRef {
557        CacheKeyRef {
558            text: &self.text,
559            font_size: self.font_size,
560            runs: self.runs.as_slice(),
561            wrap_width: self.wrap_width,
562        }
563    }
564}
565
566impl PartialEq for CacheKey {
567    fn eq(&self, other: &Self) -> bool {
568        self.as_cache_key_ref().eq(&other.as_cache_key_ref())
569    }
570}
571
572impl Hash for CacheKey {
573    fn hash<H: Hasher>(&self, state: &mut H) {
574        self.as_cache_key_ref().hash(state);
575    }
576}
577
578impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for Arc<CacheKey> {
579    fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
580        self.as_ref() as &dyn AsCacheKeyRef
581    }
582}
583
584impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
585    fn as_cache_key_ref(&self) -> CacheKeyRef {
586        *self
587    }
588}