text_layout.rs

  1use crate::{
  2    color::Color,
  3    fonts::{FontId, GlyphId},
  4    geometry::{
  5        rect::RectF,
  6        vector::{vec2f, Vector2F},
  7    },
  8    platform, scene, FontSystem, PaintContext,
  9};
 10use lazy_static::lazy_static;
 11use ordered_float::OrderedFloat;
 12use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
 13use smallvec::SmallVec;
 14use std::{
 15    borrow::Borrow,
 16    collections::HashMap,
 17    hash::{Hash, Hasher},
 18    iter,
 19    ops::{Deref, DerefMut},
 20    sync::Arc,
 21};
 22
 23pub struct TextLayoutCache {
 24    prev_frame: Mutex<HashMap<CacheKeyValue, Arc<LineLayout>>>,
 25    curr_frame: RwLock<HashMap<CacheKeyValue, Arc<LineLayout>>>,
 26    fonts: Arc<dyn platform::FontSystem>,
 27}
 28
 29impl TextLayoutCache {
 30    pub fn new(fonts: Arc<dyn platform::FontSystem>) -> Self {
 31        Self {
 32            prev_frame: Mutex::new(HashMap::new()),
 33            curr_frame: RwLock::new(HashMap::new()),
 34            fonts,
 35        }
 36    }
 37
 38    pub fn finish_frame(&self) {
 39        let mut prev_frame = self.prev_frame.lock();
 40        let mut curr_frame = self.curr_frame.write();
 41        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
 42        curr_frame.clear();
 43    }
 44
 45    pub fn layout_str<'a>(
 46        &'a self,
 47        text: &'a str,
 48        font_size: f32,
 49        runs: &'a [(usize, FontId, Color)],
 50    ) -> Line {
 51        let key = &CacheKeyRef {
 52            text,
 53            font_size: OrderedFloat(font_size),
 54            runs,
 55        } as &dyn CacheKey;
 56        let curr_frame = self.curr_frame.upgradable_read();
 57        if let Some(layout) = curr_frame.get(key) {
 58            return Line::new(layout.clone(), runs);
 59        }
 60
 61        let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
 62        if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
 63            curr_frame.insert(key, layout.clone());
 64            Line::new(layout.clone(), runs)
 65        } else {
 66            let layout = Arc::new(self.fonts.layout_line(text, font_size, runs));
 67            let key = CacheKeyValue {
 68                text: text.into(),
 69                font_size: OrderedFloat(font_size),
 70                runs: SmallVec::from(runs),
 71            };
 72            curr_frame.insert(key, layout.clone());
 73            Line::new(layout, runs)
 74        }
 75    }
 76}
 77
 78trait CacheKey {
 79    fn key<'a>(&'a self) -> CacheKeyRef<'a>;
 80}
 81
 82impl<'a> PartialEq for (dyn CacheKey + 'a) {
 83    fn eq(&self, other: &dyn CacheKey) -> bool {
 84        self.key() == other.key()
 85    }
 86}
 87
 88impl<'a> Eq for (dyn CacheKey + 'a) {}
 89
 90impl<'a> Hash for (dyn CacheKey + 'a) {
 91    fn hash<H: Hasher>(&self, state: &mut H) {
 92        self.key().hash(state)
 93    }
 94}
 95
 96#[derive(Eq, PartialEq)]
 97struct CacheKeyValue {
 98    text: String,
 99    font_size: OrderedFloat<f32>,
100    runs: SmallVec<[(usize, FontId, Color); 1]>,
101}
102
103impl CacheKey for CacheKeyValue {
104    fn key<'a>(&'a self) -> CacheKeyRef<'a> {
105        CacheKeyRef {
106            text: &self.text.as_str(),
107            font_size: self.font_size,
108            runs: self.runs.as_slice(),
109        }
110    }
111}
112
113impl Hash for CacheKeyValue {
114    fn hash<H: Hasher>(&self, state: &mut H) {
115        self.key().hash(state);
116    }
117}
118
119impl<'a> Borrow<dyn CacheKey + 'a> for CacheKeyValue {
120    fn borrow(&self) -> &(dyn CacheKey + 'a) {
121        self as &dyn CacheKey
122    }
123}
124
125#[derive(Copy, Clone, PartialEq, Eq, Hash)]
126struct CacheKeyRef<'a> {
127    text: &'a str,
128    font_size: OrderedFloat<f32>,
129    runs: &'a [(usize, FontId, Color)],
130}
131
132impl<'a> CacheKey for CacheKeyRef<'a> {
133    fn key<'b>(&'b self) -> CacheKeyRef<'b> {
134        *self
135    }
136}
137
138#[derive(Default, Debug)]
139pub struct Line {
140    layout: Arc<LineLayout>,
141    color_runs: SmallVec<[(u32, Color); 32]>,
142}
143
144#[derive(Default, Debug)]
145pub struct LineLayout {
146    pub width: f32,
147    pub ascent: f32,
148    pub descent: f32,
149    pub runs: Vec<Run>,
150    pub len: usize,
151    pub font_size: f32,
152}
153
154#[derive(Debug)]
155pub struct Run {
156    pub font_id: FontId,
157    pub glyphs: Vec<Glyph>,
158}
159
160#[derive(Debug)]
161pub struct Glyph {
162    pub id: GlyphId,
163    pub position: Vector2F,
164    pub index: usize,
165}
166
167impl Line {
168    fn new(layout: Arc<LineLayout>, runs: &[(usize, FontId, Color)]) -> Self {
169        let mut color_runs = SmallVec::new();
170        for (len, _, color) in runs {
171            color_runs.push((*len as u32, *color));
172        }
173        Self { layout, color_runs }
174    }
175
176    pub fn runs(&self) -> &[Run] {
177        &self.layout.runs
178    }
179
180    pub fn width(&self) -> f32 {
181        self.layout.width
182    }
183
184    pub fn x_for_index(&self, index: usize) -> f32 {
185        for run in &self.layout.runs {
186            for glyph in &run.glyphs {
187                if glyph.index == index {
188                    return glyph.position.x();
189                }
190            }
191        }
192        self.layout.width
193    }
194
195    pub fn index_for_x(&self, x: f32) -> Option<usize> {
196        if x >= self.layout.width {
197            None
198        } else {
199            for run in self.layout.runs.iter().rev() {
200                for glyph in run.glyphs.iter().rev() {
201                    if glyph.position.x() <= x {
202                        return Some(glyph.index);
203                    }
204                }
205            }
206            Some(0)
207        }
208    }
209
210    pub fn paint(&self, origin: Vector2F, visible_bounds: RectF, cx: &mut PaintContext) {
211        let padding_top = (visible_bounds.height() - self.layout.ascent - self.layout.descent) / 2.;
212        let baseline_origin = vec2f(0., padding_top + self.layout.ascent);
213
214        let mut color_runs = self.color_runs.iter();
215        let mut color_end = 0;
216        let mut color = Color::black();
217
218        for run in &self.layout.runs {
219            let max_glyph_width = cx
220                .font_cache
221                .bounding_box(run.font_id, self.layout.font_size)
222                .x();
223
224            for glyph in &run.glyphs {
225                let glyph_origin = baseline_origin + glyph.position;
226
227                if glyph_origin.x() + max_glyph_width < visible_bounds.origin().x() {
228                    continue;
229                }
230                if glyph_origin.x() > visible_bounds.upper_right().x() {
231                    break;
232                }
233
234                if glyph.index >= color_end {
235                    if let Some(next_run) = color_runs.next() {
236                        color_end += next_run.0 as usize;
237                        color = next_run.1;
238                    } else {
239                        color_end = self.layout.len;
240                        color = Color::black();
241                    }
242                }
243
244                cx.scene.push_glyph(scene::Glyph {
245                    font_id: run.font_id,
246                    font_size: self.layout.font_size,
247                    id: glyph.id,
248                    origin: origin + glyph_origin,
249                    color,
250                });
251            }
252        }
253    }
254
255    pub fn paint_wrapped(
256        &self,
257        origin: Vector2F,
258        line_height: f32,
259        boundaries: impl IntoIterator<Item = ShapedBoundary>,
260        cx: &mut PaintContext,
261    ) {
262        let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
263        let baseline_origin = vec2f(0., padding_top + self.layout.ascent);
264
265        let mut boundaries = boundaries.into_iter().peekable();
266        let mut color_runs = self.color_runs.iter();
267        let mut color_end = 0;
268        let mut color = Color::black();
269
270        let mut glyph_origin = baseline_origin;
271        let mut prev_position = 0.;
272        for run in &self.layout.runs {
273            for (glyph_ix, glyph) in run.glyphs.iter().enumerate() {
274                if boundaries.peek().map_or(false, |b| b.glyph_ix == glyph_ix) {
275                    boundaries.next();
276                    glyph_origin = vec2f(0., glyph_origin.y() + line_height);
277                } else {
278                    glyph_origin.set_x(glyph_origin.x() + glyph.position.x() - prev_position);
279                }
280                prev_position = glyph.position.x();
281
282                if glyph.index >= color_end {
283                    if let Some(next_run) = color_runs.next() {
284                        color_end += next_run.0 as usize;
285                        color = next_run.1;
286                    } else {
287                        color_end = self.layout.len;
288                        color = Color::black();
289                    }
290                }
291
292                cx.scene.push_glyph(scene::Glyph {
293                    font_id: run.font_id,
294                    font_size: self.layout.font_size,
295                    id: glyph.id,
296                    origin: origin + glyph_origin,
297                    color,
298                });
299            }
300        }
301    }
302}
303
304impl Run {
305    pub fn glyphs(&self) -> &[Glyph] {
306        &self.glyphs
307    }
308}
309
310lazy_static! {
311    static ref WRAPPER_POOL: Mutex<Vec<LineWrapper>> = Default::default();
312}
313
314#[derive(Copy, Clone, Debug, PartialEq, Eq)]
315pub struct Boundary {
316    pub ix: usize,
317    pub next_indent: u32,
318}
319
320#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
321pub struct ShapedBoundary {
322    pub run_ix: usize,
323    pub glyph_ix: usize,
324}
325
326impl Boundary {
327    fn new(ix: usize, next_indent: u32) -> Self {
328        Self { ix, next_indent }
329    }
330}
331
332pub struct LineWrapper {
333    font_system: Arc<dyn FontSystem>,
334    font_id: FontId,
335    font_size: f32,
336    cached_ascii_char_widths: [f32; 128],
337    cached_other_char_widths: HashMap<char, f32>,
338}
339
340impl LineWrapper {
341    pub const MAX_INDENT: u32 = 256;
342
343    pub fn acquire(
344        font_id: FontId,
345        font_size: f32,
346        font_system: Arc<dyn FontSystem>,
347    ) -> LineWrapperHandle {
348        let wrapper = if let Some(mut wrapper) = WRAPPER_POOL.lock().pop() {
349            if wrapper.font_id != font_id || wrapper.font_size != font_size {
350                wrapper.cached_ascii_char_widths = [f32::NAN; 128];
351                wrapper.cached_other_char_widths.clear();
352            }
353            wrapper
354        } else {
355            LineWrapper::new(font_id, font_size, font_system)
356        };
357        LineWrapperHandle(Some(wrapper))
358    }
359
360    pub fn new(font_id: FontId, font_size: f32, font_system: Arc<dyn FontSystem>) -> Self {
361        Self {
362            font_system,
363            font_id,
364            font_size,
365            cached_ascii_char_widths: [f32::NAN; 128],
366            cached_other_char_widths: HashMap::new(),
367        }
368    }
369
370    pub fn wrap_line<'a>(
371        &'a mut self,
372        line: &'a str,
373        wrap_width: f32,
374    ) -> impl Iterator<Item = Boundary> + 'a {
375        let mut width = 0.0;
376        let mut first_non_whitespace_ix = None;
377        let mut indent = None;
378        let mut last_candidate_ix = 0;
379        let mut last_candidate_width = 0.0;
380        let mut last_wrap_ix = 0;
381        let mut prev_c = '\0';
382        let mut char_indices = line.char_indices();
383        iter::from_fn(move || {
384            while let Some((ix, c)) = char_indices.next() {
385                if c == '\n' {
386                    continue;
387                }
388
389                if self.is_boundary(prev_c, c) && first_non_whitespace_ix.is_some() {
390                    last_candidate_ix = ix;
391                    last_candidate_width = width;
392                }
393
394                if c != ' ' && first_non_whitespace_ix.is_none() {
395                    first_non_whitespace_ix = Some(ix);
396                }
397
398                let char_width = self.width_for_char(c);
399                width += char_width;
400                if width > wrap_width && ix > last_wrap_ix {
401                    if let (None, Some(first_non_whitespace_ix)) = (indent, first_non_whitespace_ix)
402                    {
403                        indent = Some(
404                            Self::MAX_INDENT.min((first_non_whitespace_ix - last_wrap_ix) as u32),
405                        );
406                    }
407
408                    if last_candidate_ix > 0 {
409                        last_wrap_ix = last_candidate_ix;
410                        width -= last_candidate_width;
411                        last_candidate_ix = 0;
412                    } else {
413                        last_wrap_ix = ix;
414                        width = char_width;
415                    }
416
417                    let indent_width =
418                        indent.map(|indent| indent as f32 * self.width_for_char(' '));
419                    width += indent_width.unwrap_or(0.);
420
421                    return Some(Boundary::new(last_wrap_ix, indent.unwrap_or(0)));
422                }
423                prev_c = c;
424            }
425
426            None
427        })
428    }
429
430    pub fn wrap_shaped_line<'a>(
431        &'a mut self,
432        str: &'a str,
433        line: &'a Line,
434        wrap_width: f32,
435    ) -> impl Iterator<Item = ShapedBoundary> + 'a {
436        let mut first_non_whitespace_ix = None;
437        let mut last_candidate_ix = None;
438        let mut last_candidate_x = 0.0;
439        let mut last_wrap_ix = ShapedBoundary {
440            run_ix: 0,
441            glyph_ix: 0,
442        };
443        let mut last_wrap_x = 0.;
444        let mut prev_c = '\0';
445        let mut glyphs = line
446            .runs()
447            .iter()
448            .enumerate()
449            .flat_map(move |(run_ix, run)| {
450                run.glyphs()
451                    .iter()
452                    .enumerate()
453                    .map(move |(glyph_ix, glyph)| {
454                        let character = str[glyph.index..].chars().next().unwrap();
455                        (
456                            ShapedBoundary { run_ix, glyph_ix },
457                            character,
458                            glyph.position.x(),
459                        )
460                    })
461            })
462            .peekable();
463
464        iter::from_fn(move || {
465            while let Some((ix, c, x)) = glyphs.next() {
466                if c == '\n' {
467                    continue;
468                }
469
470                if self.is_boundary(prev_c, c) && first_non_whitespace_ix.is_some() {
471                    last_candidate_ix = Some(ix);
472                    last_candidate_x = x;
473                }
474
475                if c != ' ' && first_non_whitespace_ix.is_none() {
476                    first_non_whitespace_ix = Some(ix);
477                }
478
479                let next_x = glyphs.peek().map_or(line.width(), |(_, _, x)| *x);
480                let width = next_x - last_wrap_x;
481                if width > wrap_width && ix > last_wrap_ix {
482                    if let Some(last_candidate_ix) = last_candidate_ix.take() {
483                        last_wrap_ix = last_candidate_ix;
484                        last_wrap_x = last_candidate_x;
485                    } else {
486                        last_wrap_ix = ix;
487                        last_wrap_x = x;
488                    }
489
490                    return Some(last_wrap_ix);
491                }
492                prev_c = c;
493            }
494
495            None
496        })
497    }
498
499    fn is_boundary(&self, prev: char, next: char) -> bool {
500        (prev == ' ') && (next != ' ')
501    }
502
503    #[inline(always)]
504    fn width_for_char(&mut self, c: char) -> f32 {
505        if (c as u32) < 128 {
506            let mut width = self.cached_ascii_char_widths[c as usize];
507            if width.is_nan() {
508                width = self.compute_width_for_char(c);
509                self.cached_ascii_char_widths[c as usize] = width;
510            }
511            width
512        } else {
513            let mut width = self
514                .cached_other_char_widths
515                .get(&c)
516                .copied()
517                .unwrap_or(f32::NAN);
518            if width.is_nan() {
519                width = self.compute_width_for_char(c);
520                self.cached_other_char_widths.insert(c, width);
521            }
522            width
523        }
524    }
525
526    fn compute_width_for_char(&self, c: char) -> f32 {
527        self.font_system
528            .layout_line(
529                &c.to_string(),
530                self.font_size,
531                &[(1, self.font_id, Default::default())],
532            )
533            .width
534    }
535}
536
537pub struct LineWrapperHandle(Option<LineWrapper>);
538
539impl Drop for LineWrapperHandle {
540    fn drop(&mut self) {
541        let wrapper = self.0.take().unwrap();
542        WRAPPER_POOL.lock().push(wrapper)
543    }
544}
545
546impl Deref for LineWrapperHandle {
547    type Target = LineWrapper;
548
549    fn deref(&self) -> &Self::Target {
550        self.0.as_ref().unwrap()
551    }
552}
553
554impl DerefMut for LineWrapperHandle {
555    fn deref_mut(&mut self) -> &mut Self::Target {
556        self.0.as_mut().unwrap()
557    }
558}
559
560#[cfg(test)]
561mod tests {
562    use super::*;
563    use crate::{
564        color::Color,
565        fonts::{Properties, Weight},
566    };
567
568    #[crate::test(self)]
569    fn test_wrap_line(cx: &mut crate::MutableAppContext) {
570        let font_cache = cx.font_cache().clone();
571        let font_system = cx.platform().fonts();
572        let family = font_cache.load_family(&["Courier"]).unwrap();
573        let font_id = font_cache.select_font(family, &Default::default()).unwrap();
574
575        let mut wrapper = LineWrapper::new(font_id, 16., font_system);
576        assert_eq!(
577            wrapper
578                .wrap_line("aa bbb cccc ddddd eeee", 72.0)
579                .collect::<Vec<_>>(),
580            &[
581                Boundary::new(7, 0),
582                Boundary::new(12, 0),
583                Boundary::new(18, 0)
584            ],
585        );
586        assert_eq!(
587            wrapper
588                .wrap_line("aaa aaaaaaaaaaaaaaaaaa", 72.0)
589                .collect::<Vec<_>>(),
590            &[
591                Boundary::new(4, 0),
592                Boundary::new(11, 0),
593                Boundary::new(18, 0)
594            ],
595        );
596        assert_eq!(
597            wrapper.wrap_line("     aaaaaaa", 72.).collect::<Vec<_>>(),
598            &[
599                Boundary::new(7, 5),
600                Boundary::new(9, 5),
601                Boundary::new(11, 5),
602            ]
603        );
604        assert_eq!(
605            wrapper
606                .wrap_line("                            ", 72.)
607                .collect::<Vec<_>>(),
608            &[
609                Boundary::new(7, 0),
610                Boundary::new(14, 0),
611                Boundary::new(21, 0)
612            ]
613        );
614        assert_eq!(
615            wrapper
616                .wrap_line("          aaaaaaaaaaaaaa", 72.)
617                .collect::<Vec<_>>(),
618            &[
619                Boundary::new(7, 0),
620                Boundary::new(14, 3),
621                Boundary::new(18, 3),
622                Boundary::new(22, 3),
623            ]
624        );
625    }
626
627    #[crate::test(self)]
628    fn test_wrap_shaped_line(cx: &mut crate::MutableAppContext) {
629        let font_cache = cx.font_cache().clone();
630        let font_system = cx.platform().fonts();
631        let text_layout_cache = TextLayoutCache::new(font_system.clone());
632
633        let family = font_cache.load_family(&["Helvetica"]).unwrap();
634        let font_id = font_cache.select_font(family, &Default::default()).unwrap();
635        let normal = font_cache.select_font(family, &Default::default()).unwrap();
636        let bold = font_cache
637            .select_font(
638                family,
639                &Properties {
640                    weight: Weight::BOLD,
641                    ..Default::default()
642                },
643            )
644            .unwrap();
645
646        let text = "aa bbb cccc ddddd eeee";
647        let line = text_layout_cache.layout_str(
648            text,
649            16.0,
650            &[
651                (4, normal, Color::default()),
652                (5, bold, Color::default()),
653                (6, normal, Color::default()),
654                (1, bold, Color::default()),
655                (7, normal, Color::default()),
656            ],
657        );
658
659        let mut wrapper = LineWrapper::new(font_id, 16., font_system);
660        assert_eq!(
661            wrapper
662                .wrap_shaped_line(&text, &line, 72.0)
663                .collect::<Vec<_>>(),
664            &[
665                ShapedBoundary {
666                    run_ix: 1,
667                    glyph_ix: 3
668                },
669                ShapedBoundary {
670                    run_ix: 2,
671                    glyph_ix: 3
672                },
673                ShapedBoundary {
674                    run_ix: 4,
675                    glyph_ix: 2
676                }
677            ],
678        );
679    }
680}