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