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}