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 // todo!
251 pub fn paint(
252 &self,
253 origin: Point<Pixels>,
254 visible_bounds: Bounds<Pixels>,
255 line_height: Pixels,
256 cx: &mut WindowContext,
257 ) -> Result<()> {
258 let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
259 let baseline_offset = point(px(0.), padding_top + self.layout.ascent);
260
261 let mut style_runs = self.style_runs.iter();
262 let mut run_end = 0;
263 let mut color = black();
264 let mut underline = None;
265
266 for run in &self.layout.runs {
267 cx.text_system().with_font(run.font_id, |system, font| {
268 let max_glyph_width = system.bounding_box(font, self.layout.font_size)?.size.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
289 + baseline_offset.y
290 + (self.layout.descent * 0.618),
291 ),
292 UnderlineStyle {
293 color: style_run.underline.color,
294 thickness: style_run.underline.thickness,
295 squiggly: style_run.underline.squiggly,
296 },
297 ));
298 }
299
300 run_end += style_run.len as usize;
301 color = style_run.color;
302 } else {
303 run_end = self.layout.len;
304 finished_underline = underline.take();
305 }
306 }
307
308 if glyph_origin.x + max_glyph_width < visible_bounds.origin.x {
309 continue;
310 }
311
312 if let Some((_underline_origin, _underline_style)) = finished_underline {
313 // cx.scene().insert(Underline {
314 // origin: underline_origin,
315 // width: glyph_origin.x - underline_origin.x,
316 // thickness: underline_style.thickness.into(),
317 // color: underline_style.color.unwrap(),
318 // squiggly: underline_style.squiggly,
319 // });
320 }
321
322 // if glyph.is_emoji {
323 // cx.scene().push_image_glyph(scene::ImageGlyph {
324 // font_id: run.font_id,
325 // font_size: self.layout.font_size,
326 // id: glyph.id,
327 // origin: glyph_origin,
328 // });
329 // } else {
330 // cx.scene().push_glyph(scene::Glyph {
331 // font_id: run.font_id,
332 // font_size: self.layout.font_size,
333 // id: glyph.id,
334 // origin: glyph_origin,
335 // color,
336 // });
337 // }
338 }
339
340 anyhow::Ok(())
341 })??;
342 }
343
344 if let Some((_underline_start, _underline_style)) = underline.take() {
345 let _line_end_x = origin.x + self.layout.width;
346 // cx.scene().push_underline(Underline {
347 // origin: underline_start,
348 // width: line_end_x - underline_start.x,
349 // color: underline_style.color,
350 // thickness: underline_style.thickness.into(),
351 // squiggly: underline_style.squiggly,
352 // });
353 }
354
355 Ok(())
356 }
357
358 pub fn paint_wrapped(
359 &self,
360 origin: Point<Pixels>,
361 _visible_bounds: Bounds<Pixels>,
362 line_height: Pixels,
363 boundaries: &[ShapedBoundary],
364 cx: &mut WindowContext,
365 ) -> Result<()> {
366 let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
367 let baseline_offset = point(px(0.), padding_top + self.layout.ascent);
368
369 let mut boundaries = boundaries.into_iter().peekable();
370 let mut color_runs = self.style_runs.iter();
371 let mut style_run_end = 0;
372 let mut _color = black(); // todo!
373 let mut underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
374
375 let mut glyph_origin = origin;
376 let mut prev_position = px(0.);
377 for (run_ix, run) in self.layout.runs.iter().enumerate() {
378 for (glyph_ix, glyph) in run.glyphs.iter().enumerate() {
379 glyph_origin.x += glyph.position.x - prev_position;
380
381 if boundaries
382 .peek()
383 .map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
384 {
385 boundaries.next();
386 if let Some((_underline_origin, _underline_style)) = underline.take() {
387 // cx.scene().push_underline(Underline {
388 // origin: underline_origin,
389 // width: glyph_origin.x - underline_origin.x,
390 // thickness: underline_style.thickness.into(),
391 // color: underline_style.color.unwrap(),
392 // squiggly: underline_style.squiggly,
393 // });
394 }
395
396 glyph_origin = point(origin.x, glyph_origin.y + line_height);
397 }
398 prev_position = glyph.position.x;
399
400 let mut finished_underline = None;
401 if glyph.index >= style_run_end {
402 if let Some(style_run) = color_runs.next() {
403 style_run_end += style_run.len as usize;
404 _color = style_run.color;
405 if let Some((_, underline_style)) = &mut underline {
406 if style_run.underline != *underline_style {
407 finished_underline = underline.take();
408 }
409 }
410 if style_run.underline.thickness > px(0.) {
411 underline.get_or_insert((
412 glyph_origin
413 + point(
414 px(0.),
415 baseline_offset.y + (self.layout.descent * 0.618),
416 ),
417 UnderlineStyle {
418 color: Some(
419 style_run.underline.color.unwrap_or(style_run.color),
420 ),
421 thickness: style_run.underline.thickness,
422 squiggly: style_run.underline.squiggly,
423 },
424 ));
425 }
426 } else {
427 style_run_end = self.layout.len;
428 _color = black();
429 finished_underline = underline.take();
430 }
431 }
432
433 if let Some((_underline_origin, _underline_style)) = finished_underline {
434 // cx.scene().push_underline(Underline {
435 // origin: underline_origin,
436 // width: glyph_origin.x - underline_origin.x,
437 // thickness: underline_style.thickness.into(),
438 // color: underline_style.color.unwrap(),
439 // squiggly: underline_style.squiggly,
440 // });
441 }
442
443 cx.text_system().with_font(run.font_id, |system, font| {
444 let _glyph_bounds = Bounds {
445 origin: glyph_origin,
446 size: system.bounding_box(font, self.layout.font_size)?.size,
447 };
448 // if glyph_bounds.intersects(visible_bounds) {
449 // if glyph.is_emoji {
450 // cx.scene().push_image_glyph(scene::ImageGlyph {
451 // font_id: run.font_id,
452 // font_size: self.layout.font_size,
453 // id: glyph.id,
454 // origin: glyph_bounds.origin() + baseline_offset,
455 // });
456 // } else {
457 // cx.scene().push_glyph(scene::Glyph {
458 // font_id: run.font_id,
459 // font_size: self.layout.font_size,
460 // id: glyph.id,
461 // origin: glyph_bounds.origin() + baseline_offset,
462 // color,
463 // });
464 // }
465 // }
466 anyhow::Ok(())
467 })??;
468 }
469 }
470
471 if let Some((_underline_origin, _underline_style)) = underline.take() {
472 // let line_end_x = glyph_origin.x + self.layout.width - prev_position;
473 // cx.scene().push_underline(Underline {
474 // origin: underline_origin,
475 // width: line_end_x - underline_origin.x,
476 // thickness: underline_style.thickness.into(),
477 // color: underline_style.color,
478 // squiggly: underline_style.squiggly,
479 // });
480 }
481
482 Ok(())
483 }
484}
485
486impl Run {
487 pub fn glyphs(&self) -> &[Glyph] {
488 &self.glyphs
489 }
490}