1use crate::{
2 color::Color,
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, Color)],
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, Color); 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, Color)],
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, Color); 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, Color)]) -> 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 runs(&self) -> &[Run] {
174 &self.layout.runs
175 }
176
177 pub fn width(&self) -> f32 {
178 self.layout.width
179 }
180
181 pub fn x_for_index(&self, index: usize) -> f32 {
182 for run in &self.layout.runs {
183 for glyph in &run.glyphs {
184 if glyph.index == index {
185 return glyph.position.x();
186 }
187 }
188 }
189 self.layout.width
190 }
191
192 pub fn index_for_x(&self, x: f32) -> Option<usize> {
193 if x >= self.layout.width {
194 None
195 } else {
196 for run in self.layout.runs.iter().rev() {
197 for glyph in run.glyphs.iter().rev() {
198 if glyph.position.x() <= x {
199 return Some(glyph.index);
200 }
201 }
202 }
203 Some(0)
204 }
205 }
206
207 pub fn paint(&self, origin: Vector2F, visible_bounds: RectF, cx: &mut PaintContext) {
208 let padding_top = (visible_bounds.height() - self.layout.ascent - self.layout.descent) / 2.;
209 let baseline_origin = vec2f(0., padding_top + self.layout.ascent);
210
211 let mut color_runs = self.color_runs.iter();
212 let mut color_end = 0;
213 let mut color = Color::black();
214
215 for run in &self.layout.runs {
216 let max_glyph_width = cx
217 .font_cache
218 .bounding_box(run.font_id, self.layout.font_size)
219 .x();
220
221 for glyph in &run.glyphs {
222 let glyph_origin = baseline_origin + glyph.position;
223
224 if glyph_origin.x() + max_glyph_width < visible_bounds.origin().x() {
225 continue;
226 }
227 if glyph_origin.x() > visible_bounds.upper_right().x() {
228 break;
229 }
230
231 if glyph.index >= color_end {
232 if let Some(next_run) = color_runs.next() {
233 color_end += next_run.0 as usize;
234 color = next_run.1;
235 } else {
236 color_end = self.layout.len;
237 color = Color::black();
238 }
239 }
240
241 cx.scene.push_glyph(scene::Glyph {
242 font_id: run.font_id,
243 font_size: self.layout.font_size,
244 id: glyph.id,
245 origin: origin + glyph_origin,
246 color,
247 });
248 }
249 }
250 }
251}
252
253impl Run {
254 pub fn glyphs(&self) -> &[Glyph] {
255 &self.glyphs
256 }
257}