1use crate::{
2 black, point, px, Bounds, FontId, Glyph, Hsla, LineLayout, Pixels, PlatformTextSystem, Point,
3 Run, RunStyle, UnderlineStyle, WindowContext,
4};
5use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
6use smallvec::SmallVec;
7use std::{
8 borrow::Borrow,
9 collections::HashMap,
10 hash::{Hash, Hasher},
11 sync::Arc,
12};
13
14pub(crate) struct TextLayoutCache {
15 prev_frame: Mutex<HashMap<CacheKeyValue, Arc<LineLayout>>>,
16 curr_frame: RwLock<HashMap<CacheKeyValue, Arc<LineLayout>>>,
17 fonts: Arc<dyn PlatformTextSystem>,
18}
19
20impl TextLayoutCache {
21 pub fn new(fonts: Arc<dyn PlatformTextSystem>) -> Self {
22 Self {
23 prev_frame: Mutex::new(HashMap::new()),
24 curr_frame: RwLock::new(HashMap::new()),
25 fonts,
26 }
27 }
28
29 pub fn finish_frame(&self) {
30 let mut prev_frame = self.prev_frame.lock();
31 let mut curr_frame = self.curr_frame.write();
32 std::mem::swap(&mut *prev_frame, &mut *curr_frame);
33 curr_frame.clear();
34 }
35
36 pub fn layout_str<'a>(
37 &'a self,
38 text: &'a str,
39 font_size: Pixels,
40 runs: &'a [(usize, RunStyle)],
41 ) -> Line {
42 let key = &CacheKeyRef {
43 text,
44 font_size,
45 runs,
46 } as &dyn CacheKey;
47 let curr_frame = self.curr_frame.upgradable_read();
48 if let Some(layout) = curr_frame.get(key) {
49 return Line::new(layout.clone(), runs);
50 }
51
52 let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
53 if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
54 curr_frame.insert(key, layout.clone());
55 Line::new(layout, runs)
56 } else {
57 let layout = Arc::new(self.fonts.layout_line(text, font_size, runs));
58 let key = CacheKeyValue {
59 text: text.into(),
60 font_size,
61 runs: SmallVec::from(runs),
62 };
63 curr_frame.insert(key, layout.clone());
64 Line::new(layout, runs)
65 }
66 }
67}
68
69trait CacheKey {
70 fn key(&self) -> CacheKeyRef;
71}
72
73impl<'a> PartialEq for (dyn CacheKey + 'a) {
74 fn eq(&self, other: &dyn CacheKey) -> bool {
75 self.key() == other.key()
76 }
77}
78
79impl<'a> Eq for (dyn CacheKey + 'a) {}
80
81impl<'a> Hash for (dyn CacheKey + 'a) {
82 fn hash<H: Hasher>(&self, state: &mut H) {
83 self.key().hash(state)
84 }
85}
86
87#[derive(Eq)]
88struct CacheKeyValue {
89 text: String,
90 font_size: Pixels,
91 runs: SmallVec<[(usize, RunStyle); 1]>,
92}
93
94impl CacheKey for CacheKeyValue {
95 fn key(&self) -> CacheKeyRef {
96 CacheKeyRef {
97 text: self.text.as_str(),
98 font_size: self.font_size,
99 runs: self.runs.as_slice(),
100 }
101 }
102}
103
104impl PartialEq for CacheKeyValue {
105 fn eq(&self, other: &Self) -> bool {
106 self.key().eq(&other.key())
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)]
123struct CacheKeyRef<'a> {
124 text: &'a str,
125 font_size: Pixels,
126 runs: &'a [(usize, RunStyle)],
127}
128
129impl<'a> CacheKey for CacheKeyRef<'a> {
130 fn key(&self) -> CacheKeyRef {
131 *self
132 }
133}
134
135impl<'a> PartialEq for CacheKeyRef<'a> {
136 fn eq(&self, other: &Self) -> bool {
137 self.text == other.text
138 && self.font_size == other.font_size
139 && self.runs.len() == other.runs.len()
140 && self.runs.iter().zip(other.runs.iter()).all(
141 |((len_a, style_a), (len_b, style_b))| {
142 len_a == len_b && style_a.font_id == style_b.font_id
143 },
144 )
145 }
146}
147
148impl<'a> Hash for CacheKeyRef<'a> {
149 fn hash<H: Hasher>(&self, state: &mut H) {
150 self.text.hash(state);
151 self.font_size.hash(state);
152 for (len, style_id) in self.runs {
153 len.hash(state);
154 style_id.font_id.hash(state);
155 }
156 }
157}
158
159#[derive(Default, Debug, Clone)]
160pub struct Line {
161 layout: Arc<LineLayout>,
162 style_runs: SmallVec<[StyleRun; 32]>,
163}
164
165#[derive(Debug, Clone)]
166struct StyleRun {
167 len: u32,
168 color: Hsla,
169 underline: UnderlineStyle,
170}
171
172#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
173pub struct ShapedBoundary {
174 pub run_ix: usize,
175 pub glyph_ix: usize,
176}
177
178impl Line {
179 pub fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self {
180 let mut style_runs = SmallVec::new();
181 for (len, style) in runs {
182 style_runs.push(StyleRun {
183 len: *len as u32,
184 color: style.color,
185 underline: style.underline.clone().unwrap_or_default(),
186 });
187 }
188 Self { layout, style_runs }
189 }
190
191 pub fn runs(&self) -> &[Run] {
192 &self.layout.runs
193 }
194
195 pub fn width(&self) -> Pixels {
196 self.layout.width
197 }
198
199 pub fn font_size(&self) -> Pixels {
200 self.layout.font_size
201 }
202
203 pub fn x_for_index(&self, index: usize) -> Pixels {
204 for run in &self.layout.runs {
205 for glyph in &run.glyphs {
206 if glyph.index >= index {
207 return glyph.position.x;
208 }
209 }
210 }
211 self.layout.width
212 }
213
214 pub fn font_for_index(&self, index: usize) -> Option<FontId> {
215 for run in &self.layout.runs {
216 for glyph in &run.glyphs {
217 if glyph.index >= index {
218 return Some(run.font_id);
219 }
220 }
221 }
222
223 None
224 }
225
226 pub fn len(&self) -> usize {
227 self.layout.len
228 }
229
230 pub fn is_empty(&self) -> bool {
231 self.layout.len == 0
232 }
233
234 pub fn index_for_x(&self, x: Pixels) -> Option<usize> {
235 if x >= self.layout.width {
236 None
237 } else {
238 for run in self.layout.runs.iter().rev() {
239 for glyph in run.glyphs.iter().rev() {
240 if glyph.position.x <= x {
241 return Some(glyph.index);
242 }
243 }
244 }
245 Some(0)
246 }
247 }
248
249 pub fn paint(
250 &self,
251 origin: Point<Pixels>,
252 visible_bounds: Bounds<Pixels>,
253 line_height: Pixels,
254 cx: &mut WindowContext,
255 ) {
256 let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
257 let baseline_offset = point(px(0.), padding_top + self.layout.ascent);
258
259 let mut style_runs = self.style_runs.iter();
260 let mut run_end = 0;
261 let mut color = black();
262 let mut underline = None;
263
264 for run in &self.layout.runs {
265 let max_glyph_width = cx
266 .text_system()
267 .bounding_box(run.font_id, self.layout.font_size)
268 .width;
269
270 for glyph in &run.glyphs {
271 let glyph_origin = origin + baseline_offset + glyph.position;
272 if glyph_origin.x > visible_bounds.upper_right().x {
273 break;
274 }
275
276 let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
277 if glyph.index >= run_end {
278 if let Some(style_run) = style_runs.next() {
279 if let Some((_, underline_style)) = &mut underline {
280 if style_run.underline != *underline_style {
281 finished_underline = underline.take();
282 }
283 }
284 if style_run.underline.thickness > px(0.) {
285 underline.get_or_insert((
286 point(
287 glyph_origin.x,
288 origin.y + baseline_offset.y + (self.layout.descent * 0.618),
289 ),
290 UnderlineStyle {
291 color: style_run.underline.color,
292 thickness: style_run.underline.thickness,
293 squiggly: style_run.underline.squiggly,
294 },
295 ));
296 }
297
298 run_end += style_run.len as usize;
299 color = style_run.color;
300 } else {
301 run_end = self.layout.len;
302 finished_underline = underline.take();
303 }
304 }
305
306 if glyph_origin.x + max_glyph_width < visible_bounds.origin.x {
307 continue;
308 }
309
310 if let Some((_underline_origin, _underline_style)) = finished_underline {
311 // cx.scene().insert(Underline {
312 // origin: underline_origin,
313 // width: glyph_origin.x - underline_origin.x,
314 // thickness: underline_style.thickness.into(),
315 // color: underline_style.color.unwrap(),
316 // squiggly: underline_style.squiggly,
317 // });
318 }
319
320 // todo!()
321 // if glyph.is_emoji {
322 // cx.scene().push_image_glyph(scene::ImageGlyph {
323 // font_id: run.font_id,
324 // font_size: self.layout.font_size,
325 // id: glyph.id,
326 // origin: glyph_origin,
327 // });
328 // } else {
329 // cx.scene().push_glyph(scene::Glyph {
330 // font_id: run.font_id,
331 // font_size: self.layout.font_size,
332 // id: glyph.id,
333 // origin: glyph_origin,
334 // color,
335 // });
336 // }
337 }
338 }
339
340 if let Some((_underline_start, _underline_style)) = underline.take() {
341 let _line_end_x = origin.x + self.layout.width;
342 // cx.scene().push_underline(Underline {
343 // origin: underline_start,
344 // width: line_end_x - underline_start.x,
345 // color: underline_style.color,
346 // thickness: underline_style.thickness.into(),
347 // squiggly: underline_style.squiggly,
348 // });
349 }
350 }
351
352 pub fn paint_wrapped(
353 &self,
354 origin: Point<Pixels>,
355 _visible_bounds: Bounds<Pixels>,
356 line_height: Pixels,
357 boundaries: &[ShapedBoundary],
358 cx: &mut WindowContext,
359 ) {
360 let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
361 let baseline_offset = point(px(0.), padding_top + self.layout.ascent);
362
363 let mut boundaries = boundaries.into_iter().peekable();
364 let mut color_runs = self.style_runs.iter();
365 let mut style_run_end = 0;
366 let mut color = black();
367 let mut underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
368
369 let mut glyph_origin = origin;
370 let mut prev_position = px(0.);
371 for (run_ix, run) in self.layout.runs.iter().enumerate() {
372 for (glyph_ix, glyph) in run.glyphs.iter().enumerate() {
373 glyph_origin.x += glyph.position.x - prev_position;
374
375 if boundaries
376 .peek()
377 .map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
378 {
379 boundaries.next();
380 if let Some((_underline_origin, _underline_style)) = underline.take() {
381 // cx.scene().push_underline(Underline {
382 // origin: underline_origin,
383 // width: glyph_origin.x - underline_origin.x,
384 // thickness: underline_style.thickness.into(),
385 // color: underline_style.color.unwrap(),
386 // squiggly: underline_style.squiggly,
387 // });
388 }
389
390 glyph_origin = point(origin.x, glyph_origin.y + line_height);
391 }
392 prev_position = glyph.position.x;
393
394 let mut finished_underline = None;
395 if glyph.index >= style_run_end {
396 if let Some(style_run) = color_runs.next() {
397 style_run_end += style_run.len as usize;
398 color = style_run.color;
399 if let Some((_, underline_style)) = &mut underline {
400 if style_run.underline != *underline_style {
401 finished_underline = underline.take();
402 }
403 }
404 if style_run.underline.thickness > px(0.) {
405 underline.get_or_insert((
406 glyph_origin
407 + point(
408 px(0.),
409 baseline_offset.y + (self.layout.descent * 0.618),
410 ),
411 UnderlineStyle {
412 color: Some(
413 style_run.underline.color.unwrap_or(style_run.color),
414 ),
415 thickness: style_run.underline.thickness,
416 squiggly: style_run.underline.squiggly,
417 },
418 ));
419 }
420 } else {
421 style_run_end = self.layout.len;
422 color = black();
423 finished_underline = underline.take();
424 }
425 }
426
427 if let Some((_underline_origin, _underline_style)) = finished_underline {
428 // cx.scene().push_underline(Underline {
429 // origin: underline_origin,
430 // width: glyph_origin.x - underline_origin.x,
431 // thickness: underline_style.thickness.into(),
432 // color: underline_style.color.unwrap(),
433 // squiggly: underline_style.squiggly,
434 // });
435 }
436
437 let _glyph_bounds = Bounds {
438 origin: glyph_origin,
439 size: cx
440 .text_system()
441 .bounding_box(run.font_id, self.layout.font_size),
442 };
443 // todo!()
444 // if glyph_bounds.intersects(visible_bounds) {
445 // if glyph.is_emoji {
446 // cx.scene().push_image_glyph(scene::ImageGlyph {
447 // font_id: run.font_id,
448 // font_size: self.layout.font_size,
449 // id: glyph.id,
450 // origin: glyph_bounds.origin() + baseline_offset,
451 // });
452 // } else {
453 // cx.scene().push_glyph(scene::Glyph {
454 // font_id: run.font_id,
455 // font_size: self.layout.font_size,
456 // id: glyph.id,
457 // origin: glyph_bounds.origin() + baseline_offset,
458 // color,
459 // });
460 // }
461 // }
462 }
463 }
464
465 if let Some((_underline_origin, _underline_style)) = underline.take() {
466 // let line_end_x = glyph_origin.x + self.layout.width - prev_position;
467 // cx.scene().push_underline(Underline {
468 // origin: underline_origin,
469 // width: line_end_x - underline_origin.x,
470 // thickness: underline_style.thickness.into(),
471 // color: underline_style.color,
472 // squiggly: underline_style.squiggly,
473 // });
474 }
475 }
476}
477
478impl Run {
479 pub fn glyphs(&self) -> &[Glyph] {
480 &self.glyphs
481 }
482}