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 finish_frame(&self) {
351        let mut prev_frame = self.previous_frame.lock();
352        let mut curr_frame = self.current_frame.write();
353        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
354        curr_frame.lines.clear();
355        curr_frame.wrapped_lines.clear();
356        curr_frame.used_lines.clear();
357        curr_frame.used_wrapped_lines.clear();
358    }
359
360    pub fn layout_wrapped_line(
361        &self,
362        text: &str,
363        font_size: Pixels,
364        runs: &[FontRun],
365        wrap_width: Option<Pixels>,
366    ) -> Arc<WrappedLineLayout> {
367        let key = &CacheKeyRef {
368            text,
369            font_size,
370            runs,
371            wrap_width,
372        } as &dyn AsCacheKeyRef;
373
374        let current_frame = self.current_frame.upgradable_read();
375        if let Some(layout) = current_frame.wrapped_lines.get(key) {
376            return layout.clone();
377        }
378
379        let previous_frame_entry = self.previous_frame.lock().wrapped_lines.remove_entry(key);
380        if let Some((key, layout)) = previous_frame_entry {
381            let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
382            current_frame
383                .wrapped_lines
384                .insert(key.clone(), layout.clone());
385            current_frame.used_wrapped_lines.push(key);
386            layout
387        } else {
388            drop(current_frame);
389
390            let unwrapped_layout = self.layout_line(text, font_size, runs);
391            let wrap_boundaries = if let Some(wrap_width) = wrap_width {
392                unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width)
393            } else {
394                SmallVec::new()
395            };
396            let layout = Arc::new(WrappedLineLayout {
397                unwrapped_layout,
398                wrap_boundaries,
399                wrap_width,
400            });
401            let key = Arc::new(CacheKey {
402                text: text.into(),
403                font_size,
404                runs: SmallVec::from(runs),
405                wrap_width,
406            });
407
408            let mut current_frame = self.current_frame.write();
409            current_frame
410                .wrapped_lines
411                .insert(key.clone(), layout.clone());
412            current_frame.used_wrapped_lines.push(key);
413
414            layout
415        }
416    }
417
418    pub fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> Arc<LineLayout> {
419        let key = &CacheKeyRef {
420            text,
421            font_size,
422            runs,
423            wrap_width: None,
424        } as &dyn AsCacheKeyRef;
425
426        let current_frame = self.current_frame.upgradable_read();
427        if let Some(layout) = current_frame.lines.get(key) {
428            return layout.clone();
429        }
430
431        let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
432        if let Some((key, layout)) = self.previous_frame.lock().lines.remove_entry(key) {
433            current_frame.lines.insert(key.clone(), layout.clone());
434            current_frame.used_lines.push(key);
435            layout
436        } else {
437            let layout = Arc::new(self.platform_text_system.layout_line(text, font_size, runs));
438            let key = Arc::new(CacheKey {
439                text: text.into(),
440                font_size,
441                runs: SmallVec::from(runs),
442                wrap_width: None,
443            });
444            current_frame.lines.insert(key.clone(), layout.clone());
445            current_frame.used_lines.push(key);
446            layout
447        }
448    }
449}
450
451/// A run of text with a single font.
452#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
453pub struct FontRun {
454    pub(crate) len: usize,
455    pub(crate) font_id: FontId,
456}
457
458trait AsCacheKeyRef {
459    fn as_cache_key_ref(&self) -> CacheKeyRef;
460}
461
462#[derive(Clone, Debug, Eq)]
463struct CacheKey {
464    text: String,
465    font_size: Pixels,
466    runs: SmallVec<[FontRun; 1]>,
467    wrap_width: Option<Pixels>,
468}
469
470#[derive(Copy, Clone, PartialEq, Eq, Hash)]
471struct CacheKeyRef<'a> {
472    text: &'a str,
473    font_size: Pixels,
474    runs: &'a [FontRun],
475    wrap_width: Option<Pixels>,
476}
477
478impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
479    fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
480        self.as_cache_key_ref() == other.as_cache_key_ref()
481    }
482}
483
484impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
485
486impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
487    fn hash<H: Hasher>(&self, state: &mut H) {
488        self.as_cache_key_ref().hash(state)
489    }
490}
491
492impl AsCacheKeyRef for CacheKey {
493    fn as_cache_key_ref(&self) -> CacheKeyRef {
494        CacheKeyRef {
495            text: &self.text,
496            font_size: self.font_size,
497            runs: self.runs.as_slice(),
498            wrap_width: self.wrap_width,
499        }
500    }
501}
502
503impl PartialEq for CacheKey {
504    fn eq(&self, other: &Self) -> bool {
505        self.as_cache_key_ref().eq(&other.as_cache_key_ref())
506    }
507}
508
509impl Hash for CacheKey {
510    fn hash<H: Hasher>(&self, state: &mut H) {
511        self.as_cache_key_ref().hash(state);
512    }
513}
514
515impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for Arc<CacheKey> {
516    fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
517        self.as_ref() as &dyn AsCacheKeyRef
518    }
519}
520
521impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
522    fn as_cache_key_ref(&self) -> CacheKeyRef {
523        *self
524    }
525}