text_layout.rs

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