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}