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}