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    pub fn paint(
251        &self,
252        origin: Point<Pixels>,
253        visible_bounds: Bounds<Pixels>,
254        line_height: Pixels,
255        cx: &mut WindowContext,
256    ) {
257        let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
258        let baseline_offset = point(px(0.), padding_top + self.layout.ascent);
259
260        let mut style_runs = self.style_runs.iter();
261        let mut run_end = 0;
262        let mut color = black();
263        let mut underline = None;
264
265        for run in &self.layout.runs {
266            cx.text_system().with_font(run.font_id, |system, font| {
267                let max_glyph_width = cx
268                    .text_system()
269                    .bounding_box(font, self.layout.font_size)?
270                    .size
271                    .width;
272
273                for glyph in &run.glyphs {
274                    let glyph_origin = origin + baseline_offset + glyph.position;
275                    if glyph_origin.x > visible_bounds.upper_right().x {
276                        break;
277                    }
278
279                    let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
280                    if glyph.index >= run_end {
281                        if let Some(style_run) = style_runs.next() {
282                            if let Some((_, underline_style)) = &mut underline {
283                                if style_run.underline != *underline_style {
284                                    finished_underline = underline.take();
285                                }
286                            }
287                            if style_run.underline.thickness > px(0.) {
288                                underline.get_or_insert((
289                                    point(
290                                        glyph_origin.x,
291                                        origin.y
292                                            + baseline_offset.y
293                                            + (self.layout.descent * 0.618),
294                                    ),
295                                    UnderlineStyle {
296                                        color: style_run.underline.color,
297                                        thickness: style_run.underline.thickness,
298                                        squiggly: style_run.underline.squiggly,
299                                    },
300                                ));
301                            }
302
303                            run_end += style_run.len as usize;
304                            color = style_run.color;
305                        } else {
306                            run_end = self.layout.len;
307                            finished_underline = underline.take();
308                        }
309                    }
310
311                    if glyph_origin.x + max_glyph_width < visible_bounds.origin.x {
312                        continue;
313                    }
314
315                    if let Some((_underline_origin, _underline_style)) = finished_underline {
316                        // cx.scene().insert(Underline {
317                        //     origin: underline_origin,
318                        //     width: glyph_origin.x - underline_origin.x,
319                        //     thickness: underline_style.thickness.into(),
320                        //     color: underline_style.color.unwrap(),
321                        //     squiggly: underline_style.squiggly,
322                        // });
323                    }
324
325                    // todo!()
326                    // if glyph.is_emoji {
327                    //     cx.scene().push_image_glyph(scene::ImageGlyph {
328                    //         font_id: run.font_id,
329                    //         font_size: self.layout.font_size,
330                    //         id: glyph.id,
331                    //         origin: glyph_origin,
332                    //     });
333                    // } else {
334                    //     cx.scene().push_glyph(scene::Glyph {
335                    //         font_id: run.font_id,
336                    //         font_size: self.layout.font_size,
337                    //         id: glyph.id,
338                    //         origin: glyph_origin,
339                    //         color,
340                    //     });
341                    // }
342                }
343
344                anyhow::Ok(())
345            });
346        }
347
348        if let Some((_underline_start, _underline_style)) = underline.take() {
349            let _line_end_x = origin.x + self.layout.width;
350            // cx.scene().push_underline(Underline {
351            //     origin: underline_start,
352            //     width: line_end_x - underline_start.x,
353            //     color: underline_style.color,
354            //     thickness: underline_style.thickness.into(),
355            //     squiggly: underline_style.squiggly,
356            // });
357        }
358    }
359
360    pub fn paint_wrapped(
361        &self,
362        origin: Point<Pixels>,
363        _visible_bounds: Bounds<Pixels>,
364        line_height: Pixels,
365        boundaries: &[ShapedBoundary],
366        cx: &mut WindowContext,
367    ) -> Result<()> {
368        let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
369        let baseline_offset = point(px(0.), padding_top + self.layout.ascent);
370
371        let mut boundaries = boundaries.into_iter().peekable();
372        let mut color_runs = self.style_runs.iter();
373        let mut style_run_end = 0;
374        let mut color = black();
375        let mut underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
376
377        let mut glyph_origin = origin;
378        let mut prev_position = px(0.);
379        for (run_ix, run) in self.layout.runs.iter().enumerate() {
380            for (glyph_ix, glyph) in run.glyphs.iter().enumerate() {
381                glyph_origin.x += glyph.position.x - prev_position;
382
383                if boundaries
384                    .peek()
385                    .map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
386                {
387                    boundaries.next();
388                    if let Some((_underline_origin, _underline_style)) = underline.take() {
389                        // cx.scene().push_underline(Underline {
390                        //     origin: underline_origin,
391                        //     width: glyph_origin.x - underline_origin.x,
392                        //     thickness: underline_style.thickness.into(),
393                        //     color: underline_style.color.unwrap(),
394                        //     squiggly: underline_style.squiggly,
395                        // });
396                    }
397
398                    glyph_origin = point(origin.x, glyph_origin.y + line_height);
399                }
400                prev_position = glyph.position.x;
401
402                let mut finished_underline = None;
403                if glyph.index >= style_run_end {
404                    if let Some(style_run) = color_runs.next() {
405                        style_run_end += style_run.len as usize;
406                        color = style_run.color;
407                        if let Some((_, underline_style)) = &mut underline {
408                            if style_run.underline != *underline_style {
409                                finished_underline = underline.take();
410                            }
411                        }
412                        if style_run.underline.thickness > px(0.) {
413                            underline.get_or_insert((
414                                glyph_origin
415                                    + point(
416                                        px(0.),
417                                        baseline_offset.y + (self.layout.descent * 0.618),
418                                    ),
419                                UnderlineStyle {
420                                    color: Some(
421                                        style_run.underline.color.unwrap_or(style_run.color),
422                                    ),
423                                    thickness: style_run.underline.thickness,
424                                    squiggly: style_run.underline.squiggly,
425                                },
426                            ));
427                        }
428                    } else {
429                        style_run_end = self.layout.len;
430                        color = black();
431                        finished_underline = underline.take();
432                    }
433                }
434
435                if let Some((_underline_origin, _underline_style)) = finished_underline {
436                    // cx.scene().push_underline(Underline {
437                    //     origin: underline_origin,
438                    //     width: glyph_origin.x - underline_origin.x,
439                    //     thickness: underline_style.thickness.into(),
440                    //     color: underline_style.color.unwrap(),
441                    //     squiggly: underline_style.squiggly,
442                    // });
443                }
444
445                cx.text_system().with_font(run.font_id, |system, font| {
446                    let _glyph_bounds = Bounds {
447                        origin: glyph_origin,
448                        size: system.bounding_box(font, self.layout.font_size)?.size,
449                    };
450                    // todo!()
451                    // if glyph_bounds.intersects(visible_bounds) {
452                    //     if glyph.is_emoji {
453                    //         cx.scene().push_image_glyph(scene::ImageGlyph {
454                    //             font_id: run.font_id,
455                    //             font_size: self.layout.font_size,
456                    //             id: glyph.id,
457                    //             origin: glyph_bounds.origin() + baseline_offset,
458                    //         });
459                    //     } else {
460                    //         cx.scene().push_glyph(scene::Glyph {
461                    //             font_id: run.font_id,
462                    //             font_size: self.layout.font_size,
463                    //             id: glyph.id,
464                    //             origin: glyph_bounds.origin() + baseline_offset,
465                    //             color,
466                    //         });
467                    //     }
468                    // }
469                    anyhow::Ok(())
470                })?;
471            }
472        }
473
474        if let Some((_underline_origin, _underline_style)) = underline.take() {
475            // let line_end_x = glyph_origin.x + self.layout.width - prev_position;
476            // cx.scene().push_underline(Underline {
477            //     origin: underline_origin,
478            //     width: line_end_x - underline_origin.x,
479            //     thickness: underline_style.thickness.into(),
480            //     color: underline_style.color,
481            //     squiggly: underline_style.squiggly,
482            // });
483        }
484
485        Ok(())
486    }
487}
488
489impl Run {
490    pub fn glyphs(&self) -> &[Glyph] {
491        &self.glyphs
492    }
493}