line_layout.rs

  1use crate::{px, FontId, GlyphId, Pixels, PlatformTextSystem, Point, SharedString};
  2use derive_more::{Deref, DerefMut};
  3use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
  4use smallvec::SmallVec;
  5use std::{
  6    borrow::Borrow,
  7    collections::HashMap,
  8    hash::{Hash, Hasher},
  9    sync::Arc,
 10};
 11
 12#[derive(Default, Debug)]
 13pub struct LineLayout {
 14    pub font_size: Pixels,
 15    pub width: Pixels,
 16    pub ascent: Pixels,
 17    pub descent: Pixels,
 18    pub runs: Vec<ShapedRun>,
 19}
 20
 21#[derive(Debug)]
 22pub struct ShapedRun {
 23    pub font_id: FontId,
 24    pub glyphs: SmallVec<[ShapedGlyph; 8]>,
 25}
 26
 27#[derive(Clone, Debug)]
 28pub struct ShapedGlyph {
 29    pub id: GlyphId,
 30    pub position: Point<Pixels>,
 31    pub index: usize,
 32    pub is_emoji: bool,
 33}
 34
 35impl LineLayout {
 36    pub fn index_for_x(&self, x: Pixels) -> Option<usize> {
 37        if x >= self.width {
 38            None
 39        } else {
 40            for run in self.runs.iter().rev() {
 41                for glyph in run.glyphs.iter().rev() {
 42                    if glyph.position.x <= x {
 43                        return Some(glyph.index);
 44                    }
 45                }
 46            }
 47            Some(0)
 48        }
 49    }
 50
 51    pub fn x_for_index(&self, index: usize) -> Pixels {
 52        for run in &self.runs {
 53            for glyph in &run.glyphs {
 54                if glyph.index >= index {
 55                    return glyph.position.x;
 56                }
 57            }
 58        }
 59        self.width
 60    }
 61
 62    pub fn font_for_index(&self, index: usize) -> Option<FontId> {
 63        for run in &self.runs {
 64            for glyph in &run.glyphs {
 65                if glyph.index >= index {
 66                    return Some(run.font_id);
 67                }
 68            }
 69        }
 70
 71        None
 72    }
 73
 74    fn compute_wrap_boundaries(
 75        &self,
 76        text: &str,
 77        wrap_width: Pixels,
 78    ) -> SmallVec<[WrapBoundary; 1]> {
 79        let mut boundaries = SmallVec::new();
 80
 81        let mut first_non_whitespace_ix = None;
 82        let mut last_candidate_ix = None;
 83        let mut last_candidate_x = px(0.);
 84        let mut last_boundary = WrapBoundary {
 85            run_ix: 0,
 86            glyph_ix: 0,
 87        };
 88        let mut last_boundary_x = px(0.);
 89        let mut prev_ch = '\0';
 90        let mut glyphs = self
 91            .runs
 92            .iter()
 93            .enumerate()
 94            .flat_map(move |(run_ix, run)| {
 95                run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| {
 96                    let character = text[glyph.index..].chars().next().unwrap();
 97                    (
 98                        WrapBoundary { run_ix, glyph_ix },
 99                        character,
100                        glyph.position.x,
101                    )
102                })
103            })
104            .peekable();
105
106        while let Some((boundary, ch, x)) = glyphs.next() {
107            if ch == '\n' {
108                continue;
109            }
110
111            if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
112                last_candidate_ix = Some(boundary);
113                last_candidate_x = x;
114            }
115
116            if ch != ' ' && first_non_whitespace_ix.is_none() {
117                first_non_whitespace_ix = Some(boundary);
118            }
119
120            let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x);
121            let width = next_x - last_boundary_x;
122            if width > wrap_width && boundary > last_boundary {
123                if let Some(last_candidate_ix) = last_candidate_ix.take() {
124                    last_boundary = last_candidate_ix;
125                    last_boundary_x = last_candidate_x;
126                } else {
127                    last_boundary = boundary;
128                    last_boundary_x = x;
129                }
130
131                boundaries.push(last_boundary);
132            }
133            prev_ch = ch;
134        }
135
136        boundaries
137    }
138}
139
140#[derive(Deref, DerefMut, Default, Debug)]
141pub struct WrappedLineLayout {
142    #[deref]
143    #[deref_mut]
144    pub layout: LineLayout,
145    pub text: SharedString,
146    pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>,
147}
148
149#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
150pub struct WrapBoundary {
151    pub run_ix: usize,
152    pub glyph_ix: usize,
153}
154
155pub(crate) struct LineLayoutCache {
156    prev_frame: Mutex<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
157    curr_frame: RwLock<HashMap<CacheKey, Arc<WrappedLineLayout>>>,
158    platform_text_system: Arc<dyn PlatformTextSystem>,
159}
160
161impl LineLayoutCache {
162    pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
163        Self {
164            prev_frame: Mutex::new(HashMap::new()),
165            curr_frame: RwLock::new(HashMap::new()),
166            platform_text_system,
167        }
168    }
169
170    pub fn start_frame(&self) {
171        let mut prev_frame = self.prev_frame.lock();
172        let mut curr_frame = self.curr_frame.write();
173        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
174        curr_frame.clear();
175    }
176
177    pub fn layout_line(
178        &self,
179        text: &SharedString,
180        font_size: Pixels,
181        runs: &[FontRun],
182        wrap_width: Option<Pixels>,
183    ) -> Arc<WrappedLineLayout> {
184        let key = &CacheKeyRef {
185            text,
186            font_size,
187            runs,
188            wrap_width,
189        } as &dyn AsCacheKeyRef;
190        let curr_frame = self.curr_frame.upgradable_read();
191        if let Some(layout) = curr_frame.get(key) {
192            return layout.clone();
193        }
194
195        let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
196        if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
197            curr_frame.insert(key, layout.clone());
198            layout
199        } else {
200            let layout = self.platform_text_system.layout_line(text, font_size, runs);
201            let wrap_boundaries = wrap_width
202                .map(|wrap_width| layout.compute_wrap_boundaries(text.as_ref(), wrap_width))
203                .unwrap_or_default();
204            let wrapped_line = Arc::new(WrappedLineLayout {
205                layout,
206                text: text.clone(),
207                wrap_boundaries,
208            });
209
210            let key = CacheKey {
211                text: text.clone(),
212                font_size,
213                runs: SmallVec::from(runs),
214                wrap_width,
215            };
216            curr_frame.insert(key, wrapped_line.clone());
217            wrapped_line
218        }
219    }
220}
221
222#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
223pub struct FontRun {
224    pub(crate) len: usize,
225    pub(crate) font_id: FontId,
226}
227
228trait AsCacheKeyRef {
229    fn as_cache_key_ref(&self) -> CacheKeyRef;
230}
231
232#[derive(Eq)]
233struct CacheKey {
234    text: SharedString,
235    font_size: Pixels,
236    runs: SmallVec<[FontRun; 1]>,
237    wrap_width: Option<Pixels>,
238}
239
240#[derive(Copy, Clone, PartialEq, Eq, Hash)]
241struct CacheKeyRef<'a> {
242    text: &'a str,
243    font_size: Pixels,
244    runs: &'a [FontRun],
245    wrap_width: Option<Pixels>,
246}
247
248impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
249    fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
250        self.as_cache_key_ref() == other.as_cache_key_ref()
251    }
252}
253
254impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
255
256impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
257    fn hash<H: Hasher>(&self, state: &mut H) {
258        self.as_cache_key_ref().hash(state)
259    }
260}
261
262impl AsCacheKeyRef for CacheKey {
263    fn as_cache_key_ref(&self) -> CacheKeyRef {
264        CacheKeyRef {
265            text: &self.text,
266            font_size: self.font_size,
267            runs: self.runs.as_slice(),
268            wrap_width: self.wrap_width,
269        }
270    }
271}
272
273impl PartialEq for CacheKey {
274    fn eq(&self, other: &Self) -> bool {
275        self.as_cache_key_ref().eq(&other.as_cache_key_ref())
276    }
277}
278
279impl Hash for CacheKey {
280    fn hash<H: Hasher>(&self, state: &mut H) {
281        self.as_cache_key_ref().hash(state);
282    }
283}
284
285impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for CacheKey {
286    fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
287        self as &dyn AsCacheKeyRef
288    }
289}
290
291impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
292    fn as_cache_key_ref(&self) -> CacheKeyRef {
293        *self
294    }
295}