text_layout_cache.rs

  1use crate::{
  2    black, point, px, Bounds, FontId, Glyph, Hsla, LineLayout, Pixels, PlatformTextSystem, Point,
  3    Run, RunStyle, UnderlineStyle, WindowContext,
  4};
  5use anyhow::Result;
  6use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
  7use smallvec::SmallVec;
  8use std::{
  9    borrow::Borrow,
 10    collections::HashMap,
 11    hash::{Hash, Hasher},
 12    sync::Arc,
 13};
 14
 15pub(crate) struct TextLayoutCache {
 16    prev_frame: Mutex<HashMap<CacheKeyValue, Arc<LineLayout>>>,
 17    curr_frame: RwLock<HashMap<CacheKeyValue, Arc<LineLayout>>>,
 18    fonts: Arc<dyn PlatformTextSystem>,
 19}
 20
 21impl TextLayoutCache {
 22    pub fn new(fonts: Arc<dyn PlatformTextSystem>) -> Self {
 23        Self {
 24            prev_frame: Mutex::new(HashMap::new()),
 25            curr_frame: RwLock::new(HashMap::new()),
 26            fonts,
 27        }
 28    }
 29
 30    pub fn finish_frame(&self) {
 31        let mut prev_frame = self.prev_frame.lock();
 32        let mut curr_frame = self.curr_frame.write();
 33        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
 34        curr_frame.clear();
 35    }
 36
 37    pub fn layout_str<'a>(
 38        &'a self,
 39        text: &'a str,
 40        font_size: Pixels,
 41        runs: &'a [(usize, RunStyle)],
 42    ) -> Line {
 43        let key = &CacheKeyRef {
 44            text,
 45            font_size,
 46            runs,
 47        } as &dyn CacheKey;
 48        let curr_frame = self.curr_frame.upgradable_read();
 49        if let Some(layout) = curr_frame.get(key) {
 50            return Line::new(layout.clone(), runs);
 51        }
 52
 53        let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
 54        if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
 55            curr_frame.insert(key, layout.clone());
 56            Line::new(layout, runs)
 57        } else {
 58            let layout = Arc::new(self.fonts.layout_line(text, font_size, runs));
 59            let key = CacheKeyValue {
 60                text: text.into(),
 61                font_size,
 62                runs: SmallVec::from(runs),
 63            };
 64            curr_frame.insert(key, layout.clone());
 65            Line::new(layout, runs)
 66        }
 67    }
 68}
 69
 70trait CacheKey {
 71    fn key(&self) -> CacheKeyRef;
 72}
 73
 74impl<'a> PartialEq for (dyn CacheKey + 'a) {
 75    fn eq(&self, other: &dyn CacheKey) -> bool {
 76        self.key() == other.key()
 77    }
 78}
 79
 80impl<'a> Eq for (dyn CacheKey + 'a) {}
 81
 82impl<'a> Hash for (dyn CacheKey + 'a) {
 83    fn hash<H: Hasher>(&self, state: &mut H) {
 84        self.key().hash(state)
 85    }
 86}
 87
 88#[derive(Eq)]
 89struct CacheKeyValue {
 90    text: String,
 91    font_size: Pixels,
 92    runs: SmallVec<[(usize, RunStyle); 1]>,
 93}
 94
 95impl CacheKey for CacheKeyValue {
 96    fn key(&self) -> CacheKeyRef {
 97        CacheKeyRef {
 98            text: self.text.as_str(),
 99            font_size: self.font_size,
100            runs: self.runs.as_slice(),
101        }
102    }
103}
104
105impl PartialEq for CacheKeyValue {
106    fn eq(&self, other: &Self) -> bool {
107        self.key().eq(&other.key())
108    }
109}
110
111impl Hash for CacheKeyValue {
112    fn hash<H: Hasher>(&self, state: &mut H) {
113        self.key().hash(state);
114    }
115}
116
117impl<'a> Borrow<dyn CacheKey + 'a> for CacheKeyValue {
118    fn borrow(&self) -> &(dyn CacheKey + 'a) {
119        self as &dyn CacheKey
120    }
121}
122
123#[derive(Copy, Clone)]
124struct CacheKeyRef<'a> {
125    text: &'a str,
126    font_size: Pixels,
127    runs: &'a [(usize, RunStyle)],
128}
129
130impl<'a> CacheKey for CacheKeyRef<'a> {
131    fn key(&self) -> CacheKeyRef {
132        *self
133    }
134}
135
136impl<'a> PartialEq for CacheKeyRef<'a> {
137    fn eq(&self, other: &Self) -> bool {
138        self.text == other.text
139            && self.font_size == other.font_size
140            && self.runs.len() == other.runs.len()
141            && self.runs.iter().zip(other.runs.iter()).all(
142                |((len_a, style_a), (len_b, style_b))| {
143                    len_a == len_b && style_a.font == style_b.font
144                },
145            )
146    }
147}
148
149impl<'a> Hash for CacheKeyRef<'a> {
150    fn hash<H: Hasher>(&self, state: &mut H) {
151        self.text.hash(state);
152        self.font_size.hash(state);
153        for (len, style_id) in self.runs {
154            len.hash(state);
155            style_id.font.hash(state);
156        }
157    }
158}
159
160#[derive(Default, Debug, Clone)]
161pub struct Line {
162    layout: Arc<LineLayout>,
163    style_runs: SmallVec<[StyleRun; 32]>,
164}
165
166#[derive(Debug, Clone)]
167struct StyleRun {
168    len: u32,
169    color: Hsla,
170    underline: UnderlineStyle,
171}
172
173#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
174pub struct ShapedBoundary {
175    pub run_ix: usize,
176    pub glyph_ix: usize,
177}
178
179impl Line {
180    pub fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self {
181        let mut style_runs = SmallVec::new();
182        for (len, style) in runs {
183            style_runs.push(StyleRun {
184                len: *len as u32,
185                color: style.color,
186                underline: style.underline.clone().unwrap_or_default(),
187            });
188        }
189        Self { layout, style_runs }
190    }
191
192    pub fn runs(&self) -> &[Run] {
193        &self.layout.runs
194    }
195
196    pub fn width(&self) -> Pixels {
197        self.layout.width
198    }
199
200    pub fn font_size(&self) -> Pixels {
201        self.layout.font_size
202    }
203
204    pub fn x_for_index(&self, index: usize) -> Pixels {
205        for run in &self.layout.runs {
206            for glyph in &run.glyphs {
207                if glyph.index >= index {
208                    return glyph.position.x;
209                }
210            }
211        }
212        self.layout.width
213    }
214
215    pub fn font_for_index(&self, index: usize) -> Option<FontId> {
216        for run in &self.layout.runs {
217            for glyph in &run.glyphs {
218                if glyph.index >= index {
219                    return Some(run.font_id);
220                }
221            }
222        }
223
224        None
225    }
226
227    pub fn len(&self) -> usize {
228        self.layout.len
229    }
230
231    pub fn is_empty(&self) -> bool {
232        self.layout.len == 0
233    }
234
235    pub fn index_for_x(&self, x: Pixels) -> Option<usize> {
236        if x >= self.layout.width {
237            None
238        } else {
239            for run in self.layout.runs.iter().rev() {
240                for glyph in run.glyphs.iter().rev() {
241                    if glyph.position.x <= x {
242                        return Some(glyph.index);
243                    }
244                }
245            }
246            Some(0)
247        }
248    }
249
250    // todo!
251    pub fn paint(
252        &self,
253        origin: Point<Pixels>,
254        visible_bounds: Bounds<Pixels>,
255        line_height: Pixels,
256        cx: &mut WindowContext,
257    ) -> Result<()> {
258        let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
259        let baseline_offset = point(px(0.), padding_top + self.layout.ascent);
260
261        let mut style_runs = self.style_runs.iter();
262        let mut run_end = 0;
263        let mut color = black();
264        let mut underline = None;
265
266        for run in &self.layout.runs {
267            cx.text_system().with_font(run.font_id, |system, font| {
268                let max_glyph_width = system.bounding_box(font, self.layout.font_size)?.size.width;
269
270                for glyph in &run.glyphs {
271                    let glyph_origin = origin + baseline_offset + glyph.position;
272                    if glyph_origin.x > visible_bounds.upper_right().x {
273                        break;
274                    }
275
276                    let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
277                    if glyph.index >= run_end {
278                        if let Some(style_run) = style_runs.next() {
279                            if let Some((_, underline_style)) = &mut underline {
280                                if style_run.underline != *underline_style {
281                                    finished_underline = underline.take();
282                                }
283                            }
284                            if style_run.underline.thickness > px(0.) {
285                                underline.get_or_insert((
286                                    point(
287                                        glyph_origin.x,
288                                        origin.y
289                                            + baseline_offset.y
290                                            + (self.layout.descent * 0.618),
291                                    ),
292                                    UnderlineStyle {
293                                        color: style_run.underline.color,
294                                        thickness: style_run.underline.thickness,
295                                        squiggly: style_run.underline.squiggly,
296                                    },
297                                ));
298                            }
299
300                            run_end += style_run.len as usize;
301                            color = style_run.color;
302                        } else {
303                            run_end = self.layout.len;
304                            finished_underline = underline.take();
305                        }
306                    }
307
308                    if glyph_origin.x + max_glyph_width < visible_bounds.origin.x {
309                        continue;
310                    }
311
312                    if let Some((_underline_origin, _underline_style)) = finished_underline {
313                        // cx.scene().insert(Underline {
314                        //     origin: underline_origin,
315                        //     width: glyph_origin.x - underline_origin.x,
316                        //     thickness: underline_style.thickness.into(),
317                        //     color: underline_style.color.unwrap(),
318                        //     squiggly: underline_style.squiggly,
319                        // });
320                    }
321
322                    // if glyph.is_emoji {
323                    //     cx.scene().push_image_glyph(scene::ImageGlyph {
324                    //         font_id: run.font_id,
325                    //         font_size: self.layout.font_size,
326                    //         id: glyph.id,
327                    //         origin: glyph_origin,
328                    //     });
329                    // } else {
330                    //     cx.scene().push_glyph(scene::Glyph {
331                    //         font_id: run.font_id,
332                    //         font_size: self.layout.font_size,
333                    //         id: glyph.id,
334                    //         origin: glyph_origin,
335                    //         color,
336                    //     });
337                    // }
338                }
339
340                anyhow::Ok(())
341            })??;
342        }
343
344        if let Some((_underline_start, _underline_style)) = underline.take() {
345            let _line_end_x = origin.x + self.layout.width;
346            // cx.scene().push_underline(Underline {
347            //     origin: underline_start,
348            //     width: line_end_x - underline_start.x,
349            //     color: underline_style.color,
350            //     thickness: underline_style.thickness.into(),
351            //     squiggly: underline_style.squiggly,
352            // });
353        }
354
355        Ok(())
356    }
357
358    pub fn paint_wrapped(
359        &self,
360        origin: Point<Pixels>,
361        _visible_bounds: Bounds<Pixels>,
362        line_height: Pixels,
363        boundaries: &[ShapedBoundary],
364        cx: &mut WindowContext,
365    ) -> Result<()> {
366        let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
367        let baseline_offset = point(px(0.), padding_top + self.layout.ascent);
368
369        let mut boundaries = boundaries.into_iter().peekable();
370        let mut color_runs = self.style_runs.iter();
371        let mut style_run_end = 0;
372        let mut _color = black(); // todo!
373        let mut underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
374
375        let mut glyph_origin = origin;
376        let mut prev_position = px(0.);
377        for (run_ix, run) in self.layout.runs.iter().enumerate() {
378            for (glyph_ix, glyph) in run.glyphs.iter().enumerate() {
379                glyph_origin.x += glyph.position.x - prev_position;
380
381                if boundaries
382                    .peek()
383                    .map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
384                {
385                    boundaries.next();
386                    if let Some((_underline_origin, _underline_style)) = underline.take() {
387                        // cx.scene().push_underline(Underline {
388                        //     origin: underline_origin,
389                        //     width: glyph_origin.x - underline_origin.x,
390                        //     thickness: underline_style.thickness.into(),
391                        //     color: underline_style.color.unwrap(),
392                        //     squiggly: underline_style.squiggly,
393                        // });
394                    }
395
396                    glyph_origin = point(origin.x, glyph_origin.y + line_height);
397                }
398                prev_position = glyph.position.x;
399
400                let mut finished_underline = None;
401                if glyph.index >= style_run_end {
402                    if let Some(style_run) = color_runs.next() {
403                        style_run_end += style_run.len as usize;
404                        _color = style_run.color;
405                        if let Some((_, underline_style)) = &mut underline {
406                            if style_run.underline != *underline_style {
407                                finished_underline = underline.take();
408                            }
409                        }
410                        if style_run.underline.thickness > px(0.) {
411                            underline.get_or_insert((
412                                glyph_origin
413                                    + point(
414                                        px(0.),
415                                        baseline_offset.y + (self.layout.descent * 0.618),
416                                    ),
417                                UnderlineStyle {
418                                    color: Some(
419                                        style_run.underline.color.unwrap_or(style_run.color),
420                                    ),
421                                    thickness: style_run.underline.thickness,
422                                    squiggly: style_run.underline.squiggly,
423                                },
424                            ));
425                        }
426                    } else {
427                        style_run_end = self.layout.len;
428                        _color = black();
429                        finished_underline = underline.take();
430                    }
431                }
432
433                if let Some((_underline_origin, _underline_style)) = finished_underline {
434                    // cx.scene().push_underline(Underline {
435                    //     origin: underline_origin,
436                    //     width: glyph_origin.x - underline_origin.x,
437                    //     thickness: underline_style.thickness.into(),
438                    //     color: underline_style.color.unwrap(),
439                    //     squiggly: underline_style.squiggly,
440                    // });
441                }
442
443                cx.text_system().with_font(run.font_id, |system, font| {
444                    let _glyph_bounds = Bounds {
445                        origin: glyph_origin,
446                        size: system.bounding_box(font, self.layout.font_size)?.size,
447                    };
448                    // if glyph_bounds.intersects(visible_bounds) {
449                    //     if glyph.is_emoji {
450                    //         cx.scene().push_image_glyph(scene::ImageGlyph {
451                    //             font_id: run.font_id,
452                    //             font_size: self.layout.font_size,
453                    //             id: glyph.id,
454                    //             origin: glyph_bounds.origin() + baseline_offset,
455                    //         });
456                    //     } else {
457                    //         cx.scene().push_glyph(scene::Glyph {
458                    //             font_id: run.font_id,
459                    //             font_size: self.layout.font_size,
460                    //             id: glyph.id,
461                    //             origin: glyph_bounds.origin() + baseline_offset,
462                    //             color,
463                    //         });
464                    //     }
465                    // }
466                    anyhow::Ok(())
467                })??;
468            }
469        }
470
471        if let Some((_underline_origin, _underline_style)) = underline.take() {
472            // let line_end_x = glyph_origin.x + self.layout.width - prev_position;
473            // cx.scene().push_underline(Underline {
474            //     origin: underline_origin,
475            //     width: line_end_x - underline_origin.x,
476            //     thickness: underline_style.thickness.into(),
477            //     color: underline_style.color,
478            //     squiggly: underline_style.squiggly,
479            // });
480        }
481
482        Ok(())
483    }
484}
485
486impl Run {
487    pub fn glyphs(&self) -> &[Glyph] {
488        &self.glyphs
489    }
490}