text_layout.rs

  1use crate::{
  2    color::ColorU,
  3    fonts::{FontId, GlyphId},
  4    geometry::{
  5        rect::RectF,
  6        vector::{vec2f, Vector2F},
  7    },
  8    platform, scene, PaintContext,
  9};
 10use ordered_float::OrderedFloat;
 11use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
 12use smallvec::SmallVec;
 13use std::{
 14    borrow::Borrow,
 15    collections::HashMap,
 16    hash::{Hash, Hasher},
 17    sync::Arc,
 18};
 19
 20pub struct TextLayoutCache {
 21    prev_frame: Mutex<HashMap<CacheKeyValue, Arc<LineLayout>>>,
 22    curr_frame: RwLock<HashMap<CacheKeyValue, Arc<LineLayout>>>,
 23    fonts: Arc<dyn platform::FontSystem>,
 24}
 25
 26impl TextLayoutCache {
 27    pub fn new(fonts: Arc<dyn platform::FontSystem>) -> Self {
 28        Self {
 29            prev_frame: Mutex::new(HashMap::new()),
 30            curr_frame: RwLock::new(HashMap::new()),
 31            fonts,
 32        }
 33    }
 34
 35    pub fn finish_frame(&self) {
 36        let mut prev_frame = self.prev_frame.lock();
 37        let mut curr_frame = self.curr_frame.write();
 38        std::mem::swap(&mut *prev_frame, &mut *curr_frame);
 39        curr_frame.clear();
 40    }
 41
 42    pub fn layout_str<'a>(
 43        &'a self,
 44        text: &'a str,
 45        font_size: f32,
 46        runs: &'a [(usize, FontId, ColorU)],
 47    ) -> Line {
 48        let key = &CacheKeyRef {
 49            text,
 50            font_size: OrderedFloat(font_size),
 51            runs,
 52        } as &dyn CacheKey;
 53        let curr_frame = self.curr_frame.upgradable_read();
 54        if let Some(layout) = curr_frame.get(key) {
 55            return Line::new(layout.clone(), runs);
 56        }
 57
 58        let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
 59        if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
 60            curr_frame.insert(key, layout.clone());
 61            Line::new(layout.clone(), runs)
 62        } else {
 63            let layout = Arc::new(self.fonts.layout_line(text, font_size, runs));
 64            let key = CacheKeyValue {
 65                text: text.into(),
 66                font_size: OrderedFloat(font_size),
 67                runs: SmallVec::from(runs),
 68            };
 69            curr_frame.insert(key, layout.clone());
 70            Line::new(layout, runs)
 71        }
 72    }
 73}
 74
 75trait CacheKey {
 76    fn key<'a>(&'a self) -> CacheKeyRef<'a>;
 77}
 78
 79impl<'a> PartialEq for (dyn CacheKey + 'a) {
 80    fn eq(&self, other: &dyn CacheKey) -> bool {
 81        self.key() == other.key()
 82    }
 83}
 84
 85impl<'a> Eq for (dyn CacheKey + 'a) {}
 86
 87impl<'a> Hash for (dyn CacheKey + 'a) {
 88    fn hash<H: Hasher>(&self, state: &mut H) {
 89        self.key().hash(state)
 90    }
 91}
 92
 93#[derive(Eq, PartialEq)]
 94struct CacheKeyValue {
 95    text: String,
 96    font_size: OrderedFloat<f32>,
 97    runs: SmallVec<[(usize, FontId, ColorU); 1]>,
 98}
 99
100impl CacheKey for CacheKeyValue {
101    fn key<'a>(&'a self) -> CacheKeyRef<'a> {
102        CacheKeyRef {
103            text: &self.text.as_str(),
104            font_size: self.font_size,
105            runs: self.runs.as_slice(),
106        }
107    }
108}
109
110impl Hash for CacheKeyValue {
111    fn hash<H: Hasher>(&self, state: &mut H) {
112        self.key().hash(state);
113    }
114}
115
116impl<'a> Borrow<dyn CacheKey + 'a> for CacheKeyValue {
117    fn borrow(&self) -> &(dyn CacheKey + 'a) {
118        self as &dyn CacheKey
119    }
120}
121
122#[derive(Copy, Clone, PartialEq, Eq, Hash)]
123struct CacheKeyRef<'a> {
124    text: &'a str,
125    font_size: OrderedFloat<f32>,
126    runs: &'a [(usize, FontId, ColorU)],
127}
128
129impl<'a> CacheKey for CacheKeyRef<'a> {
130    fn key<'b>(&'b self) -> CacheKeyRef<'b> {
131        *self
132    }
133}
134
135#[derive(Default, Debug)]
136pub struct Line {
137    layout: Arc<LineLayout>,
138    color_runs: SmallVec<[(u32, ColorU); 32]>,
139}
140
141#[derive(Default, Debug)]
142pub struct LineLayout {
143    pub width: f32,
144    pub ascent: f32,
145    pub descent: f32,
146    pub runs: Vec<Run>,
147    pub len: usize,
148    pub font_size: f32,
149}
150
151#[derive(Debug)]
152pub struct Run {
153    pub font_id: FontId,
154    pub glyphs: Vec<Glyph>,
155}
156
157#[derive(Debug)]
158pub struct Glyph {
159    pub id: GlyphId,
160    pub position: Vector2F,
161    pub index: usize,
162}
163
164impl Line {
165    fn new(layout: Arc<LineLayout>, runs: &[(usize, FontId, ColorU)]) -> Self {
166        let mut color_runs = SmallVec::new();
167        for (len, _, color) in runs {
168            color_runs.push((*len as u32, *color));
169        }
170        Self { layout, color_runs }
171    }
172
173    pub fn width(&self) -> f32 {
174        self.layout.width
175    }
176
177    pub fn x_for_index(&self, index: usize) -> f32 {
178        for run in &self.layout.runs {
179            for glyph in &run.glyphs {
180                if glyph.index == index {
181                    return glyph.position.x();
182                }
183            }
184        }
185        self.layout.width
186    }
187
188    pub fn index_for_x(&self, x: f32) -> Option<usize> {
189        if x >= self.layout.width {
190            None
191        } else {
192            for run in self.layout.runs.iter().rev() {
193                for glyph in run.glyphs.iter().rev() {
194                    if glyph.position.x() <= x {
195                        return Some(glyph.index);
196                    }
197                }
198            }
199            Some(0)
200        }
201    }
202
203    pub fn paint(&self, origin: Vector2F, visible_bounds: RectF, cx: &mut PaintContext) {
204        let padding_top = (visible_bounds.height() - self.layout.ascent - self.layout.descent) / 2.;
205        let baseline_origin = vec2f(0., padding_top + self.layout.ascent);
206
207        let mut color_runs = self.color_runs.iter();
208        let mut color_end = 0;
209        let mut color = ColorU::black();
210
211        for run in &self.layout.runs {
212            let max_glyph_width = cx
213                .font_cache
214                .bounding_box(run.font_id, self.layout.font_size)
215                .x();
216
217            for glyph in &run.glyphs {
218                let glyph_origin = baseline_origin + glyph.position;
219
220                if glyph_origin.x() + max_glyph_width < visible_bounds.origin().x() {
221                    continue;
222                }
223                if glyph_origin.x() > visible_bounds.upper_right().x() {
224                    break;
225                }
226
227                if glyph.index >= color_end {
228                    if let Some(next_run) = color_runs.next() {
229                        color_end += next_run.0 as usize;
230                        color = next_run.1;
231                    } else {
232                        color_end = self.layout.len;
233                        color = ColorU::black();
234                    }
235                }
236
237                cx.scene.push_glyph(scene::Glyph {
238                    font_id: run.font_id,
239                    font_size: self.layout.font_size,
240                    id: glyph.id,
241                    origin: origin + glyph_origin,
242                    color,
243                });
244            }
245        }
246    }
247}