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