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
 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        mut position: Point<Pixels>,
258        line_height: Pixels,
259    ) -> Result<usize, usize> {
260        let wrapped_line_ix = (position.y / line_height) as usize;
261
262        let wrapped_line_start_index;
263        let wrapped_line_start_x;
264        if wrapped_line_ix > 0 {
265            let Some(line_start_boundary) = self.wrap_boundaries.get(wrapped_line_ix - 1) else {
266                return Err(0);
267            };
268            let run = &self.unwrapped_layout.runs[line_start_boundary.run_ix];
269            let glyph = &run.glyphs[line_start_boundary.glyph_ix];
270            wrapped_line_start_index = glyph.index;
271            wrapped_line_start_x = glyph.position.x;
272        } else {
273            wrapped_line_start_index = 0;
274            wrapped_line_start_x = Pixels::ZERO;
275        };
276
277        let wrapped_line_end_index;
278        let wrapped_line_end_x;
279        if wrapped_line_ix < self.wrap_boundaries.len() {
280            let next_wrap_boundary_ix = wrapped_line_ix;
281            let next_wrap_boundary = self.wrap_boundaries[next_wrap_boundary_ix];
282            let run = &self.unwrapped_layout.runs[next_wrap_boundary.run_ix];
283            let glyph = &run.glyphs[next_wrap_boundary.glyph_ix];
284            wrapped_line_end_index = glyph.index;
285            wrapped_line_end_x = glyph.position.x;
286        } else {
287            wrapped_line_end_index = self.unwrapped_layout.len;
288            wrapped_line_end_x = self.unwrapped_layout.width;
289        };
290
291        let mut position_in_unwrapped_line = position;
292        position_in_unwrapped_line.x += wrapped_line_start_x;
293        if position_in_unwrapped_line.x < wrapped_line_start_x {
294            Err(wrapped_line_start_index)
295        } else if position_in_unwrapped_line.x >= wrapped_line_end_x {
296            Err(wrapped_line_end_index)
297        } else {
298            Ok(self
299                .unwrapped_layout
300                .index_for_x(position_in_unwrapped_line.x)
301                .unwrap())
302        }
303    }
304
305    /// Returns the pixel position for the given byte index.
306    pub fn position_for_index(&self, index: usize, line_height: Pixels) -> Option<Point<Pixels>> {
307        let mut line_start_ix = 0;
308        let mut line_end_indices = self
309            .wrap_boundaries
310            .iter()
311            .map(|wrap_boundary| {
312                let run = &self.unwrapped_layout.runs[wrap_boundary.run_ix];
313                let glyph = &run.glyphs[wrap_boundary.glyph_ix];
314                glyph.index
315            })
316            .chain([self.len()])
317            .enumerate();
318        for (ix, line_end_ix) in line_end_indices {
319            let line_y = ix as f32 * line_height;
320            if index < line_start_ix {
321                break;
322            } else if index > line_end_ix {
323                line_start_ix = line_end_ix;
324                continue;
325            } else {
326                let line_start_x = self.unwrapped_layout.x_for_index(line_start_ix);
327                let x = self.unwrapped_layout.x_for_index(index) - line_start_x;
328                return Some(point(x, line_y));
329            }
330        }
331
332        None
333    }
334}
335
336pub(crate) struct LineLayoutCache {
337    previous_frame: Mutex<FrameCache>,
338    current_frame: RwLock<FrameCache>,
339    platform_text_system: Arc<dyn PlatformTextSystem>,
340}
341
342#[derive(Default)]
343struct FrameCache {
344    lines: FxHashMap<Arc<CacheKey>, Arc<LineLayout>>,
345    wrapped_lines: FxHashMap<Arc<CacheKey>, Arc<WrappedLineLayout>>,
346    used_lines: Vec<Arc<CacheKey>>,
347    used_wrapped_lines: Vec<Arc<CacheKey>>,
348}
349
350#[derive(Clone, Default)]
351pub(crate) struct LineLayoutIndex {
352    lines_index: usize,
353    wrapped_lines_index: usize,
354}
355
356impl LineLayoutCache {
357    pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
358        Self {
359            previous_frame: Mutex::default(),
360            current_frame: RwLock::default(),
361            platform_text_system,
362        }
363    }
364
365    pub fn layout_index(&self) -> LineLayoutIndex {
366        let frame = self.current_frame.read();
367        LineLayoutIndex {
368            lines_index: frame.used_lines.len(),
369            wrapped_lines_index: frame.used_wrapped_lines.len(),
370        }
371    }
372
373    pub fn reuse_layouts(&self, range: Range<LineLayoutIndex>) {
374        let mut previous_frame = &mut *self.previous_frame.lock();
375        let mut current_frame = &mut *self.current_frame.write();
376
377        for key in &previous_frame.used_lines[range.start.lines_index..range.end.lines_index] {
378            if let Some((key, line)) = previous_frame.lines.remove_entry(key) {
379                current_frame.lines.insert(key, line);
380            }
381            current_frame.used_lines.push(key.clone());
382        }
383
384        for key in &previous_frame.used_wrapped_lines
385            [range.start.wrapped_lines_index..range.end.wrapped_lines_index]
386        {
387            if let Some((key, line)) = previous_frame.wrapped_lines.remove_entry(key) {
388                current_frame.wrapped_lines.insert(key, line);
389            }
390            current_frame.used_wrapped_lines.push(key.clone());
391        }
392    }
393
394    pub fn truncate_layouts(&self, index: LineLayoutIndex) {
395        let mut current_frame = &mut *self.current_frame.write();
396        current_frame.used_lines.truncate(index.lines_index);
397        current_frame
398            .used_wrapped_lines
399            .truncate(index.wrapped_lines_index);
400    }
401
402    pub fn finish_frame(&self) {
403        let mut prev_frame = self.previous_frame.lock();
404        let mut curr_frame = self.current_frame.write();
405        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
406        curr_frame.lines.clear();
407        curr_frame.wrapped_lines.clear();
408        curr_frame.used_lines.clear();
409        curr_frame.used_wrapped_lines.clear();
410    }
411
412    pub fn layout_wrapped_line(
413        &self,
414        text: &str,
415        font_size: Pixels,
416        runs: &[FontRun],
417        wrap_width: Option<Pixels>,
418    ) -> Arc<WrappedLineLayout> {
419        let key = &CacheKeyRef {
420            text,
421            font_size,
422            runs,
423            wrap_width,
424        } as &dyn AsCacheKeyRef;
425
426        let current_frame = self.current_frame.upgradable_read();
427        if let Some(layout) = current_frame.wrapped_lines.get(key) {
428            return layout.clone();
429        }
430
431        let previous_frame_entry = self.previous_frame.lock().wrapped_lines.remove_entry(key);
432        if let Some((key, layout)) = previous_frame_entry {
433            let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
434            current_frame
435                .wrapped_lines
436                .insert(key.clone(), layout.clone());
437            current_frame.used_wrapped_lines.push(key);
438            layout
439        } else {
440            drop(current_frame);
441
442            let unwrapped_layout = self.layout_line(text, font_size, runs);
443            let wrap_boundaries = if let Some(wrap_width) = wrap_width {
444                unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width)
445            } else {
446                SmallVec::new()
447            };
448            let layout = Arc::new(WrappedLineLayout {
449                unwrapped_layout,
450                wrap_boundaries,
451                wrap_width,
452            });
453            let key = Arc::new(CacheKey {
454                text: text.into(),
455                font_size,
456                runs: SmallVec::from(runs),
457                wrap_width,
458            });
459
460            let mut current_frame = self.current_frame.write();
461            current_frame
462                .wrapped_lines
463                .insert(key.clone(), layout.clone());
464            current_frame.used_wrapped_lines.push(key);
465
466            layout
467        }
468    }
469
470    pub fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> Arc<LineLayout> {
471        let key = &CacheKeyRef {
472            text,
473            font_size,
474            runs,
475            wrap_width: None,
476        } as &dyn AsCacheKeyRef;
477
478        let current_frame = self.current_frame.upgradable_read();
479        if let Some(layout) = current_frame.lines.get(key) {
480            return layout.clone();
481        }
482
483        let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
484        if let Some((key, layout)) = self.previous_frame.lock().lines.remove_entry(key) {
485            current_frame.lines.insert(key.clone(), layout.clone());
486            current_frame.used_lines.push(key);
487            layout
488        } else {
489            let layout = Arc::new(self.platform_text_system.layout_line(text, font_size, runs));
490            let key = Arc::new(CacheKey {
491                text: text.into(),
492                font_size,
493                runs: SmallVec::from(runs),
494                wrap_width: None,
495            });
496            current_frame.lines.insert(key.clone(), layout.clone());
497            current_frame.used_lines.push(key);
498            layout
499        }
500    }
501}
502
503/// A run of text with a single font.
504#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
505pub struct FontRun {
506    pub(crate) len: usize,
507    pub(crate) font_id: FontId,
508}
509
510trait AsCacheKeyRef {
511    fn as_cache_key_ref(&self) -> CacheKeyRef;
512}
513
514#[derive(Clone, Debug, Eq)]
515struct CacheKey {
516    text: String,
517    font_size: Pixels,
518    runs: SmallVec<[FontRun; 1]>,
519    wrap_width: Option<Pixels>,
520}
521
522#[derive(Copy, Clone, PartialEq, Eq, Hash)]
523struct CacheKeyRef<'a> {
524    text: &'a str,
525    font_size: Pixels,
526    runs: &'a [FontRun],
527    wrap_width: Option<Pixels>,
528}
529
530impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
531    fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
532        self.as_cache_key_ref() == other.as_cache_key_ref()
533    }
534}
535
536impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
537
538impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
539    fn hash<H: Hasher>(&self, state: &mut H) {
540        self.as_cache_key_ref().hash(state)
541    }
542}
543
544impl AsCacheKeyRef for CacheKey {
545    fn as_cache_key_ref(&self) -> CacheKeyRef {
546        CacheKeyRef {
547            text: &self.text,
548            font_size: self.font_size,
549            runs: self.runs.as_slice(),
550            wrap_width: self.wrap_width,
551        }
552    }
553}
554
555impl PartialEq for CacheKey {
556    fn eq(&self, other: &Self) -> bool {
557        self.as_cache_key_ref().eq(&other.as_cache_key_ref())
558    }
559}
560
561impl Hash for CacheKey {
562    fn hash<H: Hasher>(&self, state: &mut H) {
563        self.as_cache_key_ref().hash(state);
564    }
565}
566
567impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for Arc<CacheKey> {
568    fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
569        self.as_ref() as &dyn AsCacheKeyRef
570    }
571}
572
573impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
574    fn as_cache_key_ref(&self) -> CacheKeyRef {
575        *self
576    }
577}