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    sync::Arc,
  9};
 10
 11#[derive(Default, Debug)]
 12pub struct LineLayout {
 13    pub font_size: Pixels,
 14    pub width: Pixels,
 15    pub ascent: Pixels,
 16    pub descent: Pixels,
 17    pub runs: Vec<ShapedRun>,
 18    pub len: usize,
 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    /// closest_index_for_x returns the character boundary closest to the given x coordinate
 52    /// (e.g. to handle aligning up/down arrow keys)
 53    pub fn closest_index_for_x(&self, x: Pixels) -> usize {
 54        let mut prev_index = 0;
 55        let mut prev_x = px(0.);
 56
 57        for run in self.runs.iter() {
 58            for glyph in run.glyphs.iter() {
 59                if glyph.position.x >= x {
 60                    if glyph.position.x - x < x - prev_x {
 61                        return glyph.index;
 62                    } else {
 63                        return prev_index;
 64                    }
 65                }
 66                prev_index = glyph.index;
 67                prev_x = glyph.position.x;
 68            }
 69        }
 70
 71        self.len
 72    }
 73
 74    pub fn x_for_index(&self, index: usize) -> Pixels {
 75        for run in &self.runs {
 76            for glyph in &run.glyphs {
 77                if glyph.index >= index {
 78                    return glyph.position.x;
 79                }
 80            }
 81        }
 82        self.width
 83    }
 84
 85    fn compute_wrap_boundaries(
 86        &self,
 87        text: &str,
 88        wrap_width: Pixels,
 89    ) -> SmallVec<[WrapBoundary; 1]> {
 90        let mut boundaries = SmallVec::new();
 91
 92        let mut first_non_whitespace_ix = None;
 93        let mut last_candidate_ix = None;
 94        let mut last_candidate_x = px(0.);
 95        let mut last_boundary = WrapBoundary {
 96            run_ix: 0,
 97            glyph_ix: 0,
 98        };
 99        let mut last_boundary_x = px(0.);
100        let mut prev_ch = '\0';
101        let mut glyphs = self
102            .runs
103            .iter()
104            .enumerate()
105            .flat_map(move |(run_ix, run)| {
106                run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| {
107                    let character = text[glyph.index..].chars().next().unwrap();
108                    (
109                        WrapBoundary { run_ix, glyph_ix },
110                        character,
111                        glyph.position.x,
112                    )
113                })
114            })
115            .peekable();
116
117        while let Some((boundary, ch, x)) = glyphs.next() {
118            if ch == '\n' {
119                continue;
120            }
121
122            if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
123                last_candidate_ix = Some(boundary);
124                last_candidate_x = x;
125            }
126
127            if ch != ' ' && first_non_whitespace_ix.is_none() {
128                first_non_whitespace_ix = Some(boundary);
129            }
130
131            let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x);
132            let width = next_x - last_boundary_x;
133            if width > wrap_width && boundary > last_boundary {
134                if let Some(last_candidate_ix) = last_candidate_ix.take() {
135                    last_boundary = last_candidate_ix;
136                    last_boundary_x = last_candidate_x;
137                } else {
138                    last_boundary = boundary;
139                    last_boundary_x = x;
140                }
141
142                boundaries.push(last_boundary);
143            }
144            prev_ch = ch;
145        }
146
147        boundaries
148    }
149}
150
151#[derive(Default, Debug)]
152pub struct WrappedLineLayout {
153    pub unwrapped_layout: Arc<LineLayout>,
154    pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>,
155    pub wrap_width: Option<Pixels>,
156}
157
158#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
159pub struct WrapBoundary {
160    pub run_ix: usize,
161    pub glyph_ix: usize,
162}
163
164impl WrappedLineLayout {
165    pub fn len(&self) -> usize {
166        self.unwrapped_layout.len
167    }
168
169    pub fn width(&self) -> Pixels {
170        self.wrap_width
171            .unwrap_or(Pixels::MAX)
172            .min(self.unwrapped_layout.width)
173    }
174
175    pub fn size(&self, line_height: Pixels) -> Size<Pixels> {
176        Size {
177            width: self.width(),
178            height: line_height * (self.wrap_boundaries.len() + 1),
179        }
180    }
181
182    pub fn ascent(&self) -> Pixels {
183        self.unwrapped_layout.ascent
184    }
185
186    pub fn descent(&self) -> Pixels {
187        self.unwrapped_layout.descent
188    }
189
190    pub fn wrap_boundaries(&self) -> &[WrapBoundary] {
191        &self.wrap_boundaries
192    }
193
194    pub fn font_size(&self) -> Pixels {
195        self.unwrapped_layout.font_size
196    }
197
198    pub fn runs(&self) -> &[ShapedRun] {
199        &self.unwrapped_layout.runs
200    }
201
202    pub fn index_for_position(
203        &self,
204        position: Point<Pixels>,
205        line_height: Pixels,
206    ) -> Option<usize> {
207        let wrapped_line_ix = (position.y / line_height) as usize;
208
209        let wrapped_line_start_x = if wrapped_line_ix > 0 {
210            let wrap_boundary_ix = wrapped_line_ix - 1;
211            let wrap_boundary = self.wrap_boundaries[wrap_boundary_ix];
212            let run = &self.unwrapped_layout.runs[wrap_boundary.run_ix];
213            run.glyphs[wrap_boundary.glyph_ix].position.x
214        } else {
215            Pixels::ZERO
216        };
217
218        let wrapped_line_end_x = if wrapped_line_ix < self.wrap_boundaries.len() {
219            let next_wrap_boundary_ix = wrapped_line_ix;
220            let next_wrap_boundary = self.wrap_boundaries[next_wrap_boundary_ix];
221            let run = &self.unwrapped_layout.runs[next_wrap_boundary.run_ix];
222            run.glyphs[next_wrap_boundary.glyph_ix].position.x
223        } else {
224            self.unwrapped_layout.width
225        };
226
227        let mut position_in_unwrapped_line = position;
228        position_in_unwrapped_line.x += wrapped_line_start_x;
229        if position_in_unwrapped_line.x > wrapped_line_end_x {
230            None
231        } else {
232            self.unwrapped_layout
233                .index_for_x(position_in_unwrapped_line.x)
234        }
235    }
236}
237
238pub(crate) struct LineLayoutCache {
239    previous_frame: Mutex<FxHashMap<CacheKey, Arc<LineLayout>>>,
240    current_frame: RwLock<FxHashMap<CacheKey, Arc<LineLayout>>>,
241    previous_frame_wrapped: Mutex<FxHashMap<CacheKey, Arc<WrappedLineLayout>>>,
242    current_frame_wrapped: RwLock<FxHashMap<CacheKey, Arc<WrappedLineLayout>>>,
243    platform_text_system: Arc<dyn PlatformTextSystem>,
244}
245
246impl LineLayoutCache {
247    pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
248        Self {
249            previous_frame: Mutex::default(),
250            current_frame: RwLock::default(),
251            previous_frame_wrapped: Mutex::default(),
252            current_frame_wrapped: RwLock::default(),
253            platform_text_system,
254        }
255    }
256
257    pub fn start_frame(&self) {
258        let mut prev_frame = self.previous_frame.lock();
259        let mut curr_frame = self.current_frame.write();
260        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
261        curr_frame.clear();
262    }
263
264    pub fn layout_wrapped_line(
265        &self,
266        text: &str,
267        font_size: Pixels,
268        runs: &[FontRun],
269        wrap_width: Option<Pixels>,
270    ) -> Arc<WrappedLineLayout> {
271        let key = &CacheKeyRef {
272            text,
273            font_size,
274            runs,
275            wrap_width,
276        } as &dyn AsCacheKeyRef;
277
278        let current_frame = self.current_frame_wrapped.upgradable_read();
279        if let Some(layout) = current_frame.get(key) {
280            return layout.clone();
281        }
282
283        let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
284        if let Some((key, layout)) = self.previous_frame_wrapped.lock().remove_entry(key) {
285            current_frame.insert(key, layout.clone());
286            layout
287        } else {
288            let unwrapped_layout = self.layout_line(text, font_size, runs);
289            let wrap_boundaries = if let Some(wrap_width) = wrap_width {
290                unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width)
291            } else {
292                SmallVec::new()
293            };
294            let layout = Arc::new(WrappedLineLayout {
295                unwrapped_layout,
296                wrap_boundaries,
297                wrap_width,
298            });
299            let key = CacheKey {
300                text: text.into(),
301                font_size,
302                runs: SmallVec::from(runs),
303                wrap_width,
304            };
305            current_frame.insert(key, layout.clone());
306            layout
307        }
308    }
309
310    pub fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> Arc<LineLayout> {
311        let key = &CacheKeyRef {
312            text,
313            font_size,
314            runs,
315            wrap_width: None,
316        } as &dyn AsCacheKeyRef;
317
318        let current_frame = self.current_frame.upgradable_read();
319        if let Some(layout) = current_frame.get(key) {
320            return layout.clone();
321        }
322
323        let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
324        if let Some((key, layout)) = self.previous_frame.lock().remove_entry(key) {
325            current_frame.insert(key, layout.clone());
326            layout
327        } else {
328            let layout = Arc::new(self.platform_text_system.layout_line(text, font_size, runs));
329            let key = CacheKey {
330                text: text.into(),
331                font_size,
332                runs: SmallVec::from(runs),
333                wrap_width: None,
334            };
335            current_frame.insert(key, layout.clone());
336            layout
337        }
338    }
339}
340
341#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
342pub struct FontRun {
343    pub(crate) len: usize,
344    pub(crate) font_id: FontId,
345}
346
347trait AsCacheKeyRef {
348    fn as_cache_key_ref(&self) -> CacheKeyRef;
349}
350
351#[derive(Eq)]
352struct CacheKey {
353    text: String,
354    font_size: Pixels,
355    runs: SmallVec<[FontRun; 1]>,
356    wrap_width: Option<Pixels>,
357}
358
359#[derive(Copy, Clone, PartialEq, Eq, Hash)]
360struct CacheKeyRef<'a> {
361    text: &'a str,
362    font_size: Pixels,
363    runs: &'a [FontRun],
364    wrap_width: Option<Pixels>,
365}
366
367impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
368    fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
369        self.as_cache_key_ref() == other.as_cache_key_ref()
370    }
371}
372
373impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
374
375impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
376    fn hash<H: Hasher>(&self, state: &mut H) {
377        self.as_cache_key_ref().hash(state)
378    }
379}
380
381impl AsCacheKeyRef for CacheKey {
382    fn as_cache_key_ref(&self) -> CacheKeyRef {
383        CacheKeyRef {
384            text: &self.text,
385            font_size: self.font_size,
386            runs: self.runs.as_slice(),
387            wrap_width: self.wrap_width,
388        }
389    }
390}
391
392impl PartialEq for CacheKey {
393    fn eq(&self, other: &Self) -> bool {
394        self.as_cache_key_ref().eq(&other.as_cache_key_ref())
395    }
396}
397
398impl Hash for CacheKey {
399    fn hash<H: Hasher>(&self, state: &mut H) {
400        self.as_cache_key_ref().hash(state);
401    }
402}
403
404impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for CacheKey {
405    fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
406        self as &dyn AsCacheKeyRef
407    }
408}
409
410impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
411    fn as_cache_key_ref(&self) -> CacheKeyRef {
412        *self
413    }
414}