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 end_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: &[(usize, FontId)],
182        wrap_width: Option<Pixels>,
183    ) -> Arc<WrappedLineLayout> {
184        let key = &CacheKeyRef {
185            text,
186            font_size,
187            runs,
188        } as &dyn AsCacheKeyRef;
189        let curr_frame = self.curr_frame.upgradable_read();
190        if let Some(layout) = curr_frame.get(key) {
191            return layout.clone();
192        }
193
194        let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
195        if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
196            curr_frame.insert(key, layout.clone());
197            layout
198        } else {
199            let layout = self.platform_text_system.layout_line(text, font_size, runs);
200            let wrap_boundaries = wrap_width
201                .map(|wrap_width| layout.compute_wrap_boundaries(text.as_ref(), wrap_width))
202                .unwrap_or_default();
203            let wrapped_line = Arc::new(WrappedLineLayout {
204                layout,
205                text: text.clone(),
206                wrap_boundaries,
207            });
208
209            let key = CacheKey {
210                text: text.clone(),
211                font_size,
212                runs: SmallVec::from(runs),
213            };
214            curr_frame.insert(key, wrapped_line.clone());
215            wrapped_line
216        }
217    }
218}
219
220trait AsCacheKeyRef {
221    fn as_cache_key_ref(&self) -> CacheKeyRef;
222}
223
224#[derive(Eq)]
225struct CacheKey {
226    text: SharedString,
227    font_size: Pixels,
228    runs: SmallVec<[(usize, FontId); 1]>,
229}
230
231#[derive(Copy, Clone, PartialEq, Eq)]
232struct CacheKeyRef<'a> {
233    text: &'a str,
234    font_size: Pixels,
235    runs: &'a [(usize, FontId)],
236}
237
238impl<'a> PartialEq for (dyn AsCacheKeyRef + 'a) {
239    fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
240        self.as_cache_key_ref() == other.as_cache_key_ref()
241    }
242}
243
244impl<'a> Eq for (dyn AsCacheKeyRef + 'a) {}
245
246impl<'a> Hash for (dyn AsCacheKeyRef + 'a) {
247    fn hash<H: Hasher>(&self, state: &mut H) {
248        self.as_cache_key_ref().hash(state)
249    }
250}
251
252impl AsCacheKeyRef for CacheKey {
253    fn as_cache_key_ref(&self) -> CacheKeyRef {
254        CacheKeyRef {
255            text: &self.text,
256            font_size: self.font_size,
257            runs: self.runs.as_slice(),
258        }
259    }
260}
261
262impl PartialEq for CacheKey {
263    fn eq(&self, other: &Self) -> bool {
264        self.as_cache_key_ref().eq(&other.as_cache_key_ref())
265    }
266}
267
268impl Hash for CacheKey {
269    fn hash<H: Hasher>(&self, state: &mut H) {
270        self.as_cache_key_ref().hash(state);
271    }
272}
273
274impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for CacheKey {
275    fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
276        self as &dyn AsCacheKeyRef
277    }
278}
279
280impl<'a> AsCacheKeyRef for CacheKeyRef<'a> {
281    fn as_cache_key_ref(&self) -> CacheKeyRef {
282        *self
283    }
284}
285
286impl<'a> Hash for CacheKeyRef<'a> {
287    fn hash<H: Hasher>(&self, state: &mut H) {
288        self.text.hash(state);
289        self.font_size.hash(state);
290        for (len, font_id) in self.runs {
291            len.hash(state);
292            font_id.hash(state);
293        }
294    }
295}