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    fn compute_wrap_boundaries(
107        &self,
108        text: &str,
109        wrap_width: Pixels,
110    ) -> SmallVec<[WrapBoundary; 1]> {
111        let mut boundaries = SmallVec::new();
112
113        let mut first_non_whitespace_ix = None;
114        let mut last_candidate_ix = None;
115        let mut last_candidate_x = px(0.);
116        let mut last_boundary = WrapBoundary {
117            run_ix: 0,
118            glyph_ix: 0,
119        };
120        let mut last_boundary_x = px(0.);
121        let mut prev_ch = '\0';
122        let mut glyphs = self
123            .runs
124            .iter()
125            .enumerate()
126            .flat_map(move |(run_ix, run)| {
127                run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| {
128                    let character = text[glyph.index..].chars().next().unwrap();
129                    (
130                        WrapBoundary { run_ix, glyph_ix },
131                        character,
132                        glyph.position.x,
133                    )
134                })
135            })
136            .peekable();
137
138        while let Some((boundary, ch, x)) = glyphs.next() {
139            if ch == '\n' {
140                continue;
141            }
142
143            if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
144                last_candidate_ix = Some(boundary);
145                last_candidate_x = x;
146            }
147
148            if ch != ' ' && first_non_whitespace_ix.is_none() {
149                first_non_whitespace_ix = Some(boundary);
150            }
151
152            let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x);
153            let width = next_x - last_boundary_x;
154            if width > wrap_width && boundary > last_boundary {
155                if let Some(last_candidate_ix) = last_candidate_ix.take() {
156                    last_boundary = last_candidate_ix;
157                    last_boundary_x = last_candidate_x;
158                } else {
159                    last_boundary = boundary;
160                    last_boundary_x = x;
161                }
162
163                boundaries.push(last_boundary);
164            }
165            prev_ch = ch;
166        }
167
168        boundaries
169    }
170}
171
172/// A line of text that has been wrapped to fit a given width
173#[derive(Default, Debug)]
174pub struct WrappedLineLayout {
175    /// The line layout, pre-wrapping.
176    pub unwrapped_layout: Arc<LineLayout>,
177
178    /// The boundaries at which the line was wrapped
179    pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>,
180
181    /// The width of the line, if it was wrapped
182    pub wrap_width: Option<Pixels>,
183}
184
185/// A boundary at which a line was wrapped
186#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
187pub struct WrapBoundary {
188    /// The index in the run just before the line was wrapped
189    pub run_ix: usize,
190    /// The index of the glyph just before the line was wrapped
191    pub glyph_ix: usize,
192}
193
194impl WrappedLineLayout {
195    /// The length of the underlying text, in utf8 bytes.
196    #[allow(clippy::len_without_is_empty)]
197    pub fn len(&self) -> usize {
198        self.unwrapped_layout.len
199    }
200
201    /// The width of this line, in pixels, whether or not it was wrapped.
202    pub fn width(&self) -> Pixels {
203        self.wrap_width
204            .unwrap_or(Pixels::MAX)
205            .min(self.unwrapped_layout.width)
206    }
207
208    /// The size of the whole wrapped text, for the given line_height.
209    /// can span multiple lines if there are multiple wrap boundaries.
210    pub fn size(&self, line_height: Pixels) -> Size<Pixels> {
211        Size {
212            width: self.width(),
213            height: line_height * (self.wrap_boundaries.len() + 1),
214        }
215    }
216
217    /// The ascent of a line in this layout
218    pub fn ascent(&self) -> Pixels {
219        self.unwrapped_layout.ascent
220    }
221
222    /// The descent of a line in this layout
223    pub fn descent(&self) -> Pixels {
224        self.unwrapped_layout.descent
225    }
226
227    /// The wrap boundaries in this layout
228    pub fn wrap_boundaries(&self) -> &[WrapBoundary] {
229        &self.wrap_boundaries
230    }
231
232    /// The font size of this layout
233    pub fn font_size(&self) -> Pixels {
234        self.unwrapped_layout.font_size
235    }
236
237    /// The runs in this layout, sans wrapping
238    pub fn runs(&self) -> &[ShapedRun] {
239        &self.unwrapped_layout.runs
240    }
241
242    /// The index corresponding to a given position in this layout for the given line height.
243    pub fn index_for_position(
244        &self,
245        position: Point<Pixels>,
246        line_height: Pixels,
247    ) -> Option<usize> {
248        let wrapped_line_ix = (position.y / line_height) as usize;
249
250        let wrapped_line_start_x = if wrapped_line_ix > 0 {
251            let Some(line_start_boundary) = self.wrap_boundaries.get(wrapped_line_ix - 1) else {
252                return None;
253            };
254            let run = &self.unwrapped_layout.runs[line_start_boundary.run_ix];
255            run.glyphs[line_start_boundary.glyph_ix].position.x
256        } else {
257            Pixels::ZERO
258        };
259
260        let wrapped_line_end_x = if wrapped_line_ix < self.wrap_boundaries.len() {
261            let next_wrap_boundary_ix = wrapped_line_ix;
262            let next_wrap_boundary = self.wrap_boundaries[next_wrap_boundary_ix];
263            let run = &self.unwrapped_layout.runs[next_wrap_boundary.run_ix];
264            run.glyphs[next_wrap_boundary.glyph_ix].position.x
265        } else {
266            self.unwrapped_layout.width
267        };
268
269        let mut position_in_unwrapped_line = position;
270        position_in_unwrapped_line.x += wrapped_line_start_x;
271        if position_in_unwrapped_line.x > wrapped_line_end_x {
272            None
273        } else {
274            self.unwrapped_layout
275                .index_for_x(position_in_unwrapped_line.x)
276        }
277    }
278}
279
280pub(crate) struct LineLayoutCache {
281    previous_frame: Mutex<FrameCache>,
282    current_frame: RwLock<FrameCache>,
283    platform_text_system: Arc<dyn PlatformTextSystem>,
284}
285
286#[derive(Default)]
287struct FrameCache {
288    lines: FxHashMap<Arc<CacheKey>, Arc<LineLayout>>,
289    wrapped_lines: FxHashMap<Arc<CacheKey>, Arc<WrappedLineLayout>>,
290    used_lines: Vec<Arc<CacheKey>>,
291    used_wrapped_lines: Vec<Arc<CacheKey>>,
292}
293
294#[derive(Clone, Default)]
295pub(crate) struct LineLayoutIndex {
296    lines_index: usize,
297    wrapped_lines_index: usize,
298}
299
300impl LineLayoutCache {
301    pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
302        Self {
303            previous_frame: Mutex::default(),
304            current_frame: RwLock::default(),
305            platform_text_system,
306        }
307    }
308
309    pub fn layout_index(&self) -> LineLayoutIndex {
310        let frame = self.current_frame.read();
311        LineLayoutIndex {
312            lines_index: frame.used_lines.len(),
313            wrapped_lines_index: frame.used_wrapped_lines.len(),
314        }
315    }
316
317    pub fn reuse_layouts(&self, range: Range<LineLayoutIndex>) {
318        let mut previous_frame = &mut *self.previous_frame.lock();
319        let mut current_frame = &mut *self.current_frame.write();
320
321        for key in &previous_frame.used_lines[range.start.lines_index..range.end.lines_index] {
322            if let Some((key, line)) = previous_frame.lines.remove_entry(key) {
323                current_frame.lines.insert(key, line);
324            }
325            current_frame.used_lines.push(key.clone());
326        }
327
328        for key in &previous_frame.used_wrapped_lines
329            [range.start.wrapped_lines_index..range.end.wrapped_lines_index]
330        {
331            if let Some((key, line)) = previous_frame.wrapped_lines.remove_entry(key) {
332                current_frame.wrapped_lines.insert(key, line);
333            }
334            current_frame.used_wrapped_lines.push(key.clone());
335        }
336    }
337
338    pub fn finish_frame(&self) {
339        let mut prev_frame = self.previous_frame.lock();
340        let mut curr_frame = self.current_frame.write();
341        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
342        curr_frame.lines.clear();
343        curr_frame.wrapped_lines.clear();
344        curr_frame.used_lines.clear();
345        curr_frame.used_wrapped_lines.clear();
346    }
347
348    pub fn layout_wrapped_line(
349        &self,
350        text: &str,
351        font_size: Pixels,
352        runs: &[FontRun],
353        wrap_width: Option<Pixels>,
354    ) -> Arc<WrappedLineLayout> {
355        let key = &CacheKeyRef {
356            text,
357            font_size,
358            runs,
359            wrap_width,
360        } as &dyn AsCacheKeyRef;
361
362        let current_frame = self.current_frame.upgradable_read();
363        if let Some(layout) = current_frame.wrapped_lines.get(key) {
364            return layout.clone();
365        }
366
367        let previous_frame_entry = self.previous_frame.lock().wrapped_lines.remove_entry(key);
368        if let Some((key, layout)) = previous_frame_entry {
369            let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
370            current_frame
371                .wrapped_lines
372                .insert(key.clone(), layout.clone());
373            current_frame.used_wrapped_lines.push(key);
374            layout
375        } else {
376            drop(current_frame);
377
378            let unwrapped_layout = self.layout_line(text, font_size, runs);
379            let wrap_boundaries = if let Some(wrap_width) = wrap_width {
380                unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width)
381            } else {
382                SmallVec::new()
383            };
384            let layout = Arc::new(WrappedLineLayout {
385                unwrapped_layout,
386                wrap_boundaries,
387                wrap_width,
388            });
389            let key = Arc::new(CacheKey {
390                text: text.into(),
391                font_size,
392                runs: SmallVec::from(runs),
393                wrap_width,
394            });
395
396            let mut current_frame = self.current_frame.write();
397            current_frame
398                .wrapped_lines
399                .insert(key.clone(), layout.clone());
400            current_frame.used_wrapped_lines.push(key);
401
402            layout
403        }
404    }
405
406    pub fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> Arc<LineLayout> {
407        let key = &CacheKeyRef {
408            text,
409            font_size,
410            runs,
411            wrap_width: None,
412        } as &dyn AsCacheKeyRef;
413
414        let current_frame = self.current_frame.upgradable_read();
415        if let Some(layout) = current_frame.lines.get(key) {
416            return layout.clone();
417        }
418
419        let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
420        if let Some((key, layout)) = self.previous_frame.lock().lines.remove_entry(key) {
421            current_frame.lines.insert(key.clone(), layout.clone());
422            current_frame.used_lines.push(key);
423            layout
424        } else {
425            let layout = Arc::new(self.platform_text_system.layout_line(text, font_size, runs));
426            let key = Arc::new(CacheKey {
427                text: text.into(),
428                font_size,
429                runs: SmallVec::from(runs),
430                wrap_width: None,
431            });
432            current_frame.lines.insert(key.clone(), layout.clone());
433            current_frame.used_lines.push(key);
434            layout
435        }
436    }
437}
438
439/// A run of text with a single font.
440#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
441pub struct FontRun {
442    pub(crate) len: usize,
443    pub(crate) font_id: FontId,
444}
445
446trait AsCacheKeyRef {
447    fn as_cache_key_ref(&self) -> CacheKeyRef;
448}
449
450#[derive(Clone, Debug, Eq)]
451struct CacheKey {
452    text: String,
453    font_size: Pixels,
454    runs: SmallVec<[FontRun; 1]>,
455    wrap_width: Option<Pixels>,
456}
457
458#[derive(Copy, Clone, PartialEq, Eq, Hash)]
459struct CacheKeyRef<'a> {
460    text: &'a str,
461    font_size: Pixels,
462    runs: &'a [FontRun],
463    wrap_width: Option<Pixels>,
464}
465
466impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
467    fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
468        self.as_cache_key_ref() == other.as_cache_key_ref()
469    }
470}
471
472impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
473
474impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
475    fn hash<H: Hasher>(&self, state: &mut H) {
476        self.as_cache_key_ref().hash(state)
477    }
478}
479
480impl AsCacheKeyRef for CacheKey {
481    fn as_cache_key_ref(&self) -> CacheKeyRef {
482        CacheKeyRef {
483            text: &self.text,
484            font_size: self.font_size,
485            runs: self.runs.as_slice(),
486            wrap_width: self.wrap_width,
487        }
488    }
489}
490
491impl PartialEq for CacheKey {
492    fn eq(&self, other: &Self) -> bool {
493        self.as_cache_key_ref().eq(&other.as_cache_key_ref())
494    }
495}
496
497impl Hash for CacheKey {
498    fn hash<H: Hasher>(&self, state: &mut H) {
499        self.as_cache_key_ref().hash(state);
500    }
501}
502
503impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for Arc<CacheKey> {
504    fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
505        self.as_ref() as &dyn AsCacheKeyRef
506    }
507}
508
509impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
510    fn as_cache_key_ref(&self) -> CacheKeyRef {
511        *self
512    }
513}