1#[path = "paragraph_cache.rs"]
2mod paragraph_cache;
3
4use crate::{FontId, GlyphId, Pixels, PlatformTextSystem, Point, SharedString, Size, point, px};
5use collections::FxHashMap;
6use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
7use smallvec::SmallVec;
8use std::{
9 borrow::Borrow,
10 hash::{Hash, Hasher},
11 ops::Range,
12 sync::{
13 Arc,
14 atomic::{AtomicUsize, Ordering},
15 },
16};
17
18use super::LineWrapper;
19
20#[derive(Default, Debug)]
22pub struct LineLayout {
23 pub font_size: Pixels,
25 pub width: Pixels,
27 pub ascent: Pixels,
29 pub descent: Pixels,
31 pub runs: Vec<ShapedRun>,
33 pub len: usize,
35}
36
37#[derive(Debug, Clone)]
39pub struct ShapedRun {
40 pub font_id: FontId,
42 pub glyphs: Vec<ShapedGlyph>,
44}
45
46#[derive(Clone, Debug)]
48pub struct ShapedGlyph {
49 pub id: GlyphId,
51
52 pub position: Point<Pixels>,
54
55 pub index: usize,
57
58 pub is_emoji: bool,
60}
61
62impl LineLayout {
63 pub fn index_for_x(&self, x: Pixels) -> Option<usize> {
65 if x >= self.width {
66 None
67 } else {
68 for run in self.runs.iter().rev() {
69 for glyph in run.glyphs.iter().rev() {
70 if glyph.position.x <= x {
71 return Some(glyph.index);
72 }
73 }
74 }
75 Some(0)
76 }
77 }
78
79 pub fn closest_index_for_x(&self, x: Pixels) -> usize {
82 let mut prev_index = 0;
83 let mut prev_x = px(0.);
84
85 for run in self.runs.iter() {
86 for glyph in run.glyphs.iter() {
87 if glyph.position.x >= x {
88 if glyph.position.x - x < x - prev_x {
89 return glyph.index;
90 } else {
91 return prev_index;
92 }
93 }
94 prev_index = glyph.index;
95 prev_x = glyph.position.x;
96 }
97 }
98
99 if self.len == 1 {
100 if x > self.width / 2. {
101 return 1;
102 } else {
103 return 0;
104 }
105 }
106
107 self.len
108 }
109
110 pub fn x_for_index(&self, index: usize) -> Pixels {
112 for run in &self.runs {
113 for glyph in &run.glyphs {
114 if glyph.index >= index {
115 return glyph.position.x;
116 }
117 }
118 }
119 self.width
120 }
121
122 pub fn font_id_for_index(&self, index: usize) -> Option<FontId> {
124 for run in &self.runs {
125 for glyph in &run.glyphs {
126 if glyph.index >= index {
127 return Some(run.font_id);
128 }
129 }
130 }
131 None
132 }
133
134 pub fn split_at(&self, byte_index: usize) -> (LineLayout, LineLayout) {
142 let x_offset = self.x_for_index(byte_index);
143
144 let mut left_runs = Vec::new();
146 let mut right_runs = Vec::new();
147
148 for run in &self.runs {
149 let split_pos = run.glyphs.partition_point(|g| g.index < byte_index);
150
151 if split_pos > 0 {
152 left_runs.push(ShapedRun {
153 font_id: run.font_id,
154 glyphs: run.glyphs[..split_pos].to_vec(),
155 });
156 }
157
158 if split_pos < run.glyphs.len() {
159 let right_glyphs = run.glyphs[split_pos..]
160 .iter()
161 .map(|g| ShapedGlyph {
162 id: g.id,
163 position: point(g.position.x - x_offset, g.position.y),
164 index: g.index - byte_index,
165 is_emoji: g.is_emoji,
166 })
167 .collect();
168 right_runs.push(ShapedRun {
169 font_id: run.font_id,
170 glyphs: right_glyphs,
171 });
172 }
173 }
174
175 let left = LineLayout {
176 font_size: self.font_size,
177 width: x_offset,
178 ascent: self.ascent,
179 descent: self.descent,
180 runs: left_runs,
181 len: byte_index,
182 };
183
184 let right = LineLayout {
185 font_size: self.font_size,
186 width: self.width - x_offset,
187 ascent: self.ascent,
188 descent: self.descent,
189 runs: right_runs,
190 len: self.len - byte_index,
191 };
192
193 (left, right)
194 }
195
196 fn compute_wrap_boundaries(
197 &self,
198 text: &str,
199 wrap_width: Pixels,
200 max_lines: Option<usize>,
201 ) -> SmallVec<[WrapBoundary; 1]> {
202 let mut boundaries = SmallVec::new();
203 let mut first_non_whitespace_ix = None;
204 let mut last_candidate_ix = None;
205 let mut last_candidate_x = px(0.);
206 let mut last_boundary = WrapBoundary {
207 run_ix: 0,
208 glyph_ix: 0,
209 };
210 let mut last_boundary_x = px(0.);
211 let mut prev_ch = '\0';
212 let mut glyphs = self
213 .runs
214 .iter()
215 .enumerate()
216 .flat_map(move |(run_ix, run)| {
217 run.glyphs.iter().enumerate().map(move |(glyph_ix, glyph)| {
218 let character = text[glyph.index..].chars().next().unwrap();
219 (
220 WrapBoundary { run_ix, glyph_ix },
221 character,
222 glyph.position.x,
223 )
224 })
225 })
226 .peekable();
227
228 while let Some((boundary, ch, x)) = glyphs.next() {
229 if ch == '\n' {
230 continue;
231 }
232
233 if LineWrapper::is_word_char(ch) {
236 if prev_ch == ' ' && ch != ' ' && first_non_whitespace_ix.is_some() {
237 last_candidate_ix = Some(boundary);
238 last_candidate_x = x;
239 }
240 } else {
241 if ch != ' ' && first_non_whitespace_ix.is_some() {
242 last_candidate_ix = Some(boundary);
243 last_candidate_x = x;
244 }
245 }
246
247 if ch != ' ' && first_non_whitespace_ix.is_none() {
248 first_non_whitespace_ix = Some(boundary);
249 }
250
251 let next_x = glyphs.peek().map_or(self.width, |(_, _, x)| *x);
252 let width = next_x - last_boundary_x;
253
254 if width > wrap_width && boundary > last_boundary {
255 if let Some(max_lines) = max_lines
257 && boundaries.len() >= max_lines.saturating_sub(1)
258 {
259 break;
260 }
261
262 if let Some(last_candidate_ix) = last_candidate_ix.take() {
263 last_boundary = last_candidate_ix;
264 last_boundary_x = last_candidate_x;
265 } else {
266 last_boundary = boundary;
267 last_boundary_x = x;
268 }
269 boundaries.push(last_boundary);
270 }
271 prev_ch = ch;
272 }
273
274 boundaries
275 }
276}
277
278#[derive(Default, Debug)]
280pub struct WrappedLineLayout {
281 pub unwrapped_layout: Arc<LineLayout>,
283
284 pub wrap_boundaries: SmallVec<[WrapBoundary; 1]>,
286
287 pub wrap_width: Option<Pixels>,
289}
290
291#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
293pub struct WrapBoundary {
294 pub run_ix: usize,
296 pub glyph_ix: usize,
298}
299
300impl WrappedLineLayout {
301 #[allow(clippy::len_without_is_empty)]
303 pub fn len(&self) -> usize {
304 self.unwrapped_layout.len
305 }
306
307 pub fn width(&self) -> Pixels {
309 self.wrap_width
310 .unwrap_or(Pixels::MAX)
311 .min(self.unwrapped_layout.width)
312 }
313
314 pub fn size(&self, line_height: Pixels) -> Size<Pixels> {
317 Size {
318 width: self.width(),
319 height: line_height * (self.wrap_boundaries.len() + 1),
320 }
321 }
322
323 pub fn ascent(&self) -> Pixels {
325 self.unwrapped_layout.ascent
326 }
327
328 pub fn descent(&self) -> Pixels {
330 self.unwrapped_layout.descent
331 }
332
333 pub fn wrap_boundaries(&self) -> &[WrapBoundary] {
335 &self.wrap_boundaries
336 }
337
338 pub fn font_size(&self) -> Pixels {
340 self.unwrapped_layout.font_size
341 }
342
343 pub fn runs(&self) -> &[ShapedRun] {
345 &self.unwrapped_layout.runs
346 }
347
348 pub fn index_for_position(
352 &self,
353 position: Point<Pixels>,
354 line_height: Pixels,
355 ) -> Result<usize, usize> {
356 self._index_for_position(position, line_height, false)
357 }
358
359 pub fn closest_index_for_position(
365 &self,
366 position: Point<Pixels>,
367 line_height: Pixels,
368 ) -> Result<usize, usize> {
369 self._index_for_position(position, line_height, true)
370 }
371
372 fn _index_for_position(
373 &self,
374 mut position: Point<Pixels>,
375 line_height: Pixels,
376 closest: bool,
377 ) -> Result<usize, usize> {
378 let wrapped_line_ix = (position.y / line_height) as usize;
379
380 let wrapped_line_start_index;
381 let wrapped_line_start_x;
382 if wrapped_line_ix > 0 {
383 let Some(line_start_boundary) = self.wrap_boundaries.get(wrapped_line_ix - 1) else {
384 return Err(0);
385 };
386 let run = &self.unwrapped_layout.runs[line_start_boundary.run_ix];
387 let glyph = &run.glyphs[line_start_boundary.glyph_ix];
388 wrapped_line_start_index = glyph.index;
389 wrapped_line_start_x = glyph.position.x;
390 } else {
391 wrapped_line_start_index = 0;
392 wrapped_line_start_x = Pixels::ZERO;
393 };
394
395 let wrapped_line_end_index;
396 let wrapped_line_end_x;
397 if wrapped_line_ix < self.wrap_boundaries.len() {
398 let next_wrap_boundary_ix = wrapped_line_ix;
399 let next_wrap_boundary = self.wrap_boundaries[next_wrap_boundary_ix];
400 let run = &self.unwrapped_layout.runs[next_wrap_boundary.run_ix];
401 let glyph = &run.glyphs[next_wrap_boundary.glyph_ix];
402 wrapped_line_end_index = glyph.index;
403 wrapped_line_end_x = glyph.position.x;
404 } else {
405 wrapped_line_end_index = self.unwrapped_layout.len;
406 wrapped_line_end_x = self.unwrapped_layout.width;
407 };
408
409 let mut position_in_unwrapped_line = position;
410 position_in_unwrapped_line.x += wrapped_line_start_x;
411 if position_in_unwrapped_line.x < wrapped_line_start_x {
412 Err(wrapped_line_start_index)
413 } else if position_in_unwrapped_line.x >= wrapped_line_end_x {
414 Err(wrapped_line_end_index)
415 } else {
416 if closest {
417 Ok(self
418 .unwrapped_layout
419 .closest_index_for_x(position_in_unwrapped_line.x))
420 } else {
421 self.unwrapped_layout
424 .index_for_x(position_in_unwrapped_line.x)
425 .ok_or(wrapped_line_end_index)
426 }
427 }
428 }
429
430 pub fn position_for_index(&self, index: usize, line_height: Pixels) -> Option<Point<Pixels>> {
432 let mut line_start_ix = 0;
433 let mut line_end_indices = self
434 .wrap_boundaries
435 .iter()
436 .map(|wrap_boundary| {
437 let run = &self.unwrapped_layout.runs[wrap_boundary.run_ix];
438 let glyph = &run.glyphs[wrap_boundary.glyph_ix];
439 glyph.index
440 })
441 .chain([self.len()])
442 .enumerate();
443 for (ix, line_end_ix) in line_end_indices {
444 let line_y = ix as f32 * line_height;
445 if index < line_start_ix {
446 break;
447 } else if index > line_end_ix {
448 line_start_ix = line_end_ix;
449 continue;
450 } else {
451 let line_start_x = self.unwrapped_layout.x_for_index(line_start_ix);
452 let x = self.unwrapped_layout.x_for_index(index) - line_start_x;
453 return Some(point(x, line_y));
454 }
455 }
456
457 None
458 }
459}
460
461pub(crate) struct LineLayoutCache {
462 previous_frame: Mutex<FrameCache>,
463 current_frame: RwLock<FrameCache>,
464 platform_text_system: Arc<dyn PlatformTextSystem>,
465 font_generation: Arc<AtomicUsize>,
467 cached_font_generation: AtomicUsize,
469}
470
471#[derive(Default)]
472struct FrameCache {
473 paragraphs: paragraph_cache::ParagraphFrameCache,
474 lines: FxHashMap<Arc<CacheKey>, Arc<LineLayout>>,
475 wrapped_lines: FxHashMap<Arc<CacheKey>, Arc<WrappedLineLayout>>,
476 used_lines: Vec<Arc<CacheKey>>,
477 used_wrapped_lines: Vec<Arc<CacheKey>>,
478
479 lines_by_hash: FxHashMap<Arc<HashedCacheKey>, Arc<LineLayout>>,
486 wrapped_lines_by_hash: FxHashMap<Arc<HashedCacheKey>, Arc<WrappedLineLayout>>,
487 used_lines_by_hash: Vec<Arc<HashedCacheKey>>,
488 used_wrapped_lines_by_hash: Vec<Arc<HashedCacheKey>>,
489}
490
491#[derive(Clone, Default)]
492pub(crate) struct LineLayoutIndex {
493 font_generation: usize,
494 paragraphs: paragraph_cache::ParagraphIndex,
495 lines_index: usize,
496 wrapped_lines_index: usize,
497 lines_by_hash_index: usize,
498 wrapped_lines_by_hash_index: usize,
499}
500
501impl LineLayoutCache {
502 pub fn new(
503 platform_text_system: Arc<dyn PlatformTextSystem>,
504 font_generation: Arc<AtomicUsize>,
505 ) -> Self {
506 let cached_font_generation = font_generation.load(Ordering::Acquire);
507 Self {
508 previous_frame: Mutex::default(),
509 current_frame: RwLock::default(),
510 platform_text_system,
511 font_generation,
512 cached_font_generation: AtomicUsize::new(cached_font_generation),
513 }
514 }
515
516 pub fn layout_index(&self) -> LineLayoutIndex {
517 let font_generation = self.clear_if_font_generation_changed();
518 let frame = self.current_frame.read();
519 LineLayoutIndex {
520 font_generation,
521 paragraphs: frame.paragraphs.index(),
522 lines_index: frame.used_lines.len(),
523 wrapped_lines_index: frame.used_wrapped_lines.len(),
524 lines_by_hash_index: frame.used_lines_by_hash.len(),
525 wrapped_lines_by_hash_index: frame.used_wrapped_lines_by_hash.len(),
526 }
527 }
528
529 pub fn reuse_layouts(&self, range: Range<LineLayoutIndex>) {
530 let font_generation = self.clear_if_font_generation_changed();
531 if range.start.font_generation != font_generation
532 || range.end.font_generation != font_generation
533 {
534 return;
535 }
536 let mut current_frame = &mut *self.current_frame.write();
537 let mut previous_frame = &mut *self.previous_frame.lock();
538
539 current_frame.paragraphs.reuse_from(
540 &mut previous_frame.paragraphs,
541 range.start.paragraphs..range.end.paragraphs,
542 );
543
544 for key in &previous_frame.used_lines[range.start.lines_index..range.end.lines_index] {
545 if let Some((key, line)) = previous_frame.lines.remove_entry(key) {
546 current_frame.lines.insert(key, line);
547 }
548 current_frame.used_lines.push(key.clone());
549 }
550
551 for key in &previous_frame.used_wrapped_lines
552 [range.start.wrapped_lines_index..range.end.wrapped_lines_index]
553 {
554 if let Some((key, line)) = previous_frame.wrapped_lines.remove_entry(key) {
555 current_frame.wrapped_lines.insert(key, line);
556 }
557 current_frame.used_wrapped_lines.push(key.clone());
558 }
559
560 for key in &previous_frame.used_lines_by_hash
561 [range.start.lines_by_hash_index..range.end.lines_by_hash_index]
562 {
563 if let Some((key, line)) = previous_frame.lines_by_hash.remove_entry(key) {
564 current_frame.lines_by_hash.insert(key, line);
565 }
566 current_frame.used_lines_by_hash.push(key.clone());
567 }
568
569 for key in &previous_frame.used_wrapped_lines_by_hash
570 [range.start.wrapped_lines_by_hash_index..range.end.wrapped_lines_by_hash_index]
571 {
572 if let Some((key, line)) = previous_frame.wrapped_lines_by_hash.remove_entry(key) {
573 current_frame.wrapped_lines_by_hash.insert(key, line);
574 }
575 current_frame.used_wrapped_lines_by_hash.push(key.clone());
576 }
577 }
578
579 pub fn truncate_layouts(&self, index: LineLayoutIndex) {
580 let font_generation = self.clear_if_font_generation_changed();
581 if index.font_generation != font_generation {
582 return;
583 }
584 let mut current_frame = &mut *self.current_frame.write();
585 current_frame.paragraphs.truncate(index.paragraphs);
586 current_frame.used_lines.truncate(index.lines_index);
587 current_frame
588 .used_wrapped_lines
589 .truncate(index.wrapped_lines_index);
590 current_frame
591 .used_lines_by_hash
592 .truncate(index.lines_by_hash_index);
593 current_frame
594 .used_wrapped_lines_by_hash
595 .truncate(index.wrapped_lines_by_hash_index);
596 }
597
598 pub fn finish_frame(&self) {
599 let _font_generation = self.clear_if_font_generation_changed();
600 let mut curr_frame = self.current_frame.write();
601 let mut prev_frame = self.previous_frame.lock();
602 std::mem::swap(&mut *prev_frame, &mut *curr_frame);
603 curr_frame.paragraphs.clear();
604 curr_frame.lines.clear();
605 curr_frame.wrapped_lines.clear();
606 curr_frame.used_lines.clear();
607 curr_frame.used_wrapped_lines.clear();
608
609 curr_frame.lines_by_hash.clear();
610 curr_frame.wrapped_lines_by_hash.clear();
611 curr_frame.used_lines_by_hash.clear();
612 curr_frame.used_wrapped_lines_by_hash.clear();
613 }
614
615 pub fn layout_wrapped_line<Text>(
616 &self,
617 text: Text,
618 font_size: Pixels,
619 runs: &[FontRun],
620 wrap_width: Option<Pixels>,
621 max_lines: Option<usize>,
622 ) -> Arc<WrappedLineLayout>
623 where
624 Text: AsRef<str>,
625 SharedString: From<Text>,
626 {
627 let _font_generation = self.clear_if_font_generation_changed();
628 let key = &CacheKeyRef {
629 text: text.as_ref(),
630 font_size,
631 runs,
632 wrap_width,
633 force_width: None,
634 } as &dyn AsCacheKeyRef;
635
636 let current_frame = self.current_frame.upgradable_read();
637 if let Some(layout) = current_frame.wrapped_lines.get(key) {
638 return layout.clone();
639 }
640
641 let previous_frame_entry = self.previous_frame.lock().wrapped_lines.remove_entry(key);
642 if let Some((key, layout)) = previous_frame_entry {
643 let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
644 current_frame
645 .wrapped_lines
646 .insert(key.clone(), layout.clone());
647 current_frame.used_wrapped_lines.push(key);
648 layout
649 } else {
650 drop(current_frame);
651 let text = SharedString::from(text);
652 let unwrapped_layout = self.layout_line::<&SharedString>(&text, font_size, runs, None);
653 let wrap_boundaries = if let Some(wrap_width) = wrap_width {
654 unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width, max_lines)
655 } else {
656 SmallVec::new()
657 };
658 let layout = Arc::new(WrappedLineLayout {
659 unwrapped_layout,
660 wrap_boundaries,
661 wrap_width,
662 });
663 let key = Arc::new(CacheKey {
664 text,
665 font_size,
666 runs: SmallVec::from(runs),
667 wrap_width,
668 force_width: None,
669 });
670
671 let mut current_frame = self.current_frame.write();
672 current_frame
673 .wrapped_lines
674 .insert(key.clone(), layout.clone());
675 current_frame.used_wrapped_lines.push(key);
676
677 layout
678 }
679 }
680
681 pub fn layout_line<Text>(
682 &self,
683 text: Text,
684 font_size: Pixels,
685 runs: &[FontRun],
686 force_width: Option<Pixels>,
687 ) -> Arc<LineLayout>
688 where
689 Text: AsRef<str>,
690 SharedString: From<Text>,
691 {
692 let _font_generation = self.clear_if_font_generation_changed();
693 let key = &CacheKeyRef {
694 text: text.as_ref(),
695 font_size,
696 runs,
697 wrap_width: None,
698 force_width,
699 } as &dyn AsCacheKeyRef;
700
701 let current_frame = self.current_frame.upgradable_read();
702 if let Some(layout) = current_frame.lines.get(key) {
703 return layout.clone();
704 }
705
706 let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
707 if let Some((key, layout)) = self.previous_frame.lock().lines.remove_entry(key) {
708 current_frame.lines.insert(key.clone(), layout.clone());
709 current_frame.used_lines.push(key);
710 layout
711 } else {
712 let text = SharedString::from(text);
713 let mut layout = self
714 .platform_text_system
715 .layout_line(&text, font_size, runs);
716
717 if let Some(force_width) = force_width {
718 apply_force_width_to_layout(&mut layout, force_width);
719 }
720
721 let key = Arc::new(CacheKey {
722 text,
723 font_size,
724 runs: SmallVec::from(runs),
725 wrap_width: None,
726 force_width,
727 });
728 let layout = Arc::new(layout);
729 current_frame.lines.insert(key.clone(), layout.clone());
730 current_frame.used_lines.push(key);
731 layout
732 }
733 }
734
735 pub fn try_layout_line_by_hash(
744 &self,
745 text_hash: u64,
746 text_len: usize,
747 font_size: Pixels,
748 runs: &[FontRun],
749 force_width: Option<Pixels>,
750 ) -> Option<Arc<LineLayout>> {
751 let _font_generation = self.clear_if_font_generation_changed();
752 let key_ref = HashedCacheKeyRef {
753 text_hash,
754 text_len,
755 font_size,
756 runs,
757 wrap_width: None,
758 force_width,
759 };
760
761 let current_frame = self.current_frame.read();
762 if let Some((_, layout)) = current_frame.lines_by_hash.iter().find(|(key, _)| {
763 HashedCacheKeyRef {
764 text_hash: key.text_hash,
765 text_len: key.text_len,
766 font_size: key.font_size,
767 runs: key.runs.as_slice(),
768 wrap_width: key.wrap_width,
769 force_width: key.force_width,
770 } == key_ref
771 }) {
772 return Some(layout.clone());
773 }
774
775 let previous_frame = self.previous_frame.lock();
776 if let Some((_, layout)) = previous_frame.lines_by_hash.iter().find(|(key, _)| {
777 HashedCacheKeyRef {
778 text_hash: key.text_hash,
779 text_len: key.text_len,
780 font_size: key.font_size,
781 runs: key.runs.as_slice(),
782 wrap_width: key.wrap_width,
783 force_width: key.force_width,
784 } == key_ref
785 }) {
786 return Some(layout.clone());
787 }
788
789 None
790 }
791
792 pub fn layout_line_by_hash(
801 &self,
802 text_hash: u64,
803 text_len: usize,
804 font_size: Pixels,
805 runs: &[FontRun],
806 force_width: Option<Pixels>,
807 materialize_text: impl FnOnce() -> SharedString,
808 ) -> Arc<LineLayout> {
809 let _font_generation = self.clear_if_font_generation_changed();
810 let key_ref = HashedCacheKeyRef {
811 text_hash,
812 text_len,
813 font_size,
814 runs,
815 wrap_width: None,
816 force_width,
817 };
818
819 let current_frame = self.current_frame.upgradable_read();
821 if let Some((_, layout)) = current_frame.lines_by_hash.iter().find(|(key, _)| {
822 HashedCacheKeyRef {
823 text_hash: key.text_hash,
824 text_len: key.text_len,
825 font_size: key.font_size,
826 runs: key.runs.as_slice(),
827 wrap_width: key.wrap_width,
828 force_width: key.force_width,
829 } == key_ref
830 }) {
831 return layout.clone();
832 }
833
834 let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
835
836 let mut previous_frame = self.previous_frame.lock();
839 if let Some(existing_key) = previous_frame
840 .used_lines_by_hash
841 .iter()
842 .find(|key| {
843 HashedCacheKeyRef {
844 text_hash: key.text_hash,
845 text_len: key.text_len,
846 font_size: key.font_size,
847 runs: key.runs.as_slice(),
848 wrap_width: key.wrap_width,
849 force_width: key.force_width,
850 } == key_ref
851 })
852 .cloned()
853 {
854 if let Some((key, layout)) = previous_frame.lines_by_hash.remove_entry(&existing_key) {
855 current_frame
856 .lines_by_hash
857 .insert(key.clone(), layout.clone());
858 current_frame.used_lines_by_hash.push(key);
859 return layout;
860 }
861 }
862
863 let text = materialize_text();
864 let mut layout = self
865 .platform_text_system
866 .layout_line(&text, font_size, runs);
867
868 if let Some(force_width) = force_width {
869 apply_force_width_to_layout(&mut layout, force_width);
870 }
871
872 let key = Arc::new(HashedCacheKey {
873 text_hash,
874 text_len,
875 font_size,
876 runs: SmallVec::from(runs),
877 wrap_width: None,
878 force_width,
879 });
880 let layout = Arc::new(layout);
881 current_frame
882 .lines_by_hash
883 .insert(key.clone(), layout.clone());
884 current_frame.used_lines_by_hash.push(key);
885 layout
886 }
887
888 fn clear_if_font_generation_changed(&self) -> usize {
889 let font_generation = self.font_generation.load(Ordering::Acquire);
890 if self.cached_font_generation.load(Ordering::Acquire) == font_generation {
891 return font_generation;
892 }
893
894 let mut current_frame = self.current_frame.write();
895 if self.cached_font_generation.load(Ordering::Acquire) == font_generation {
896 return font_generation;
897 }
898
899 *current_frame = FrameCache::default();
900 *self.previous_frame.lock() = FrameCache::default();
901 self.cached_font_generation
902 .store(font_generation, Ordering::Release);
903 font_generation
904 }
905}
906
907fn apply_force_width_to_layout(layout: &mut LineLayout, force_width: Pixels) {
913 let mut glyph_pos: usize = 0;
914 let mut last_base_shaped_x = px(f32::NEG_INFINITY);
916 let mut last_base_actual_x = px(0.);
917
918 for run in layout.runs.iter_mut() {
919 for glyph in run.glyphs.iter_mut() {
920 let shaped_x = glyph.position.x;
921
922 if shaped_x > last_base_shaped_x + force_width * 0.5 {
923 let forced_x = glyph_pos * force_width;
924 if (shaped_x - forced_x).abs() > px(1.) {
925 glyph.position.x = forced_x;
926 }
927 last_base_shaped_x = shaped_x;
928 last_base_actual_x = glyph.position.x;
929 glyph_pos += 1;
930 } else {
931 glyph.position.x = last_base_actual_x + (shaped_x - last_base_shaped_x);
932 }
933 }
934 }
935}
936
937#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
939#[expect(missing_docs)]
940pub struct FontRun {
941 pub len: usize,
942 pub font_id: FontId,
943}
944
945trait AsCacheKeyRef {
946 fn as_cache_key_ref(&self) -> CacheKeyRef<'_>;
947}
948
949#[derive(Clone, Debug, Eq)]
950struct CacheKey {
951 text: SharedString,
952 font_size: Pixels,
953 runs: SmallVec<[FontRun; 1]>,
954 wrap_width: Option<Pixels>,
955 force_width: Option<Pixels>,
956}
957
958#[derive(Copy, Clone, PartialEq, Eq, Hash)]
959struct CacheKeyRef<'a> {
960 text: &'a str,
961 font_size: Pixels,
962 runs: &'a [FontRun],
963 wrap_width: Option<Pixels>,
964 force_width: Option<Pixels>,
965}
966
967#[derive(Clone, Debug)]
968struct HashedCacheKey {
969 text_hash: u64,
970 text_len: usize,
971 font_size: Pixels,
972 runs: SmallVec<[FontRun; 1]>,
973 wrap_width: Option<Pixels>,
974 force_width: Option<Pixels>,
975}
976
977#[derive(Copy, Clone)]
978struct HashedCacheKeyRef<'a> {
979 text_hash: u64,
980 text_len: usize,
981 font_size: Pixels,
982 runs: &'a [FontRun],
983 wrap_width: Option<Pixels>,
984 force_width: Option<Pixels>,
985}
986
987impl PartialEq for dyn AsCacheKeyRef + '_ {
988 fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
989 self.as_cache_key_ref() == other.as_cache_key_ref()
990 }
991}
992
993impl PartialEq for HashedCacheKey {
994 fn eq(&self, other: &Self) -> bool {
995 self.text_hash == other.text_hash
996 && self.text_len == other.text_len
997 && self.font_size == other.font_size
998 && self.runs.as_slice() == other.runs.as_slice()
999 && self.wrap_width == other.wrap_width
1000 && self.force_width == other.force_width
1001 }
1002}
1003
1004impl Eq for HashedCacheKey {}
1005
1006impl Hash for HashedCacheKey {
1007 fn hash<H: Hasher>(&self, state: &mut H) {
1008 self.text_hash.hash(state);
1009 self.text_len.hash(state);
1010 self.font_size.hash(state);
1011 self.runs.as_slice().hash(state);
1012 self.wrap_width.hash(state);
1013 self.force_width.hash(state);
1014 }
1015}
1016
1017impl PartialEq for HashedCacheKeyRef<'_> {
1018 fn eq(&self, other: &Self) -> bool {
1019 self.text_hash == other.text_hash
1020 && self.text_len == other.text_len
1021 && self.font_size == other.font_size
1022 && self.runs == other.runs
1023 && self.wrap_width == other.wrap_width
1024 && self.force_width == other.force_width
1025 }
1026}
1027
1028impl Eq for HashedCacheKeyRef<'_> {}
1029
1030impl Hash for HashedCacheKeyRef<'_> {
1031 fn hash<H: Hasher>(&self, state: &mut H) {
1032 self.text_hash.hash(state);
1033 self.text_len.hash(state);
1034 self.font_size.hash(state);
1035 self.runs.hash(state);
1036 self.wrap_width.hash(state);
1037 self.force_width.hash(state);
1038 }
1039}
1040
1041impl Eq for dyn AsCacheKeyRef + '_ {}
1042
1043impl Hash for dyn AsCacheKeyRef + '_ {
1044 fn hash<H: Hasher>(&self, state: &mut H) {
1045 self.as_cache_key_ref().hash(state)
1046 }
1047}
1048
1049impl AsCacheKeyRef for CacheKey {
1050 fn as_cache_key_ref(&self) -> CacheKeyRef<'_> {
1051 CacheKeyRef {
1052 text: &self.text,
1053 font_size: self.font_size,
1054 runs: self.runs.as_slice(),
1055 wrap_width: self.wrap_width,
1056 force_width: self.force_width,
1057 }
1058 }
1059}
1060
1061impl PartialEq for CacheKey {
1062 fn eq(&self, other: &Self) -> bool {
1063 self.as_cache_key_ref().eq(&other.as_cache_key_ref())
1064 }
1065}
1066
1067impl Hash for CacheKey {
1068 fn hash<H: Hasher>(&self, state: &mut H) {
1069 self.as_cache_key_ref().hash(state);
1070 }
1071}
1072
1073impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for Arc<CacheKey> {
1074 fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
1075 self.as_ref() as &dyn AsCacheKeyRef
1076 }
1077}
1078
1079impl AsCacheKeyRef for CacheKeyRef<'_> {
1080 fn as_cache_key_ref(&self) -> CacheKeyRef<'_> {
1081 *self
1082 }
1083}
1084
1085#[cfg(test)]
1086mod tests {
1087 use super::*;
1088 use crate::GlyphId;
1089
1090 fn glyph_at(x: f32, index: usize) -> ShapedGlyph {
1091 ShapedGlyph {
1092 id: GlyphId(0),
1093 position: point(px(x), px(0.)),
1094 index,
1095 is_emoji: false,
1096 }
1097 }
1098
1099 fn make_layout(glyphs: Vec<ShapedGlyph>) -> LineLayout {
1100 LineLayout {
1101 font_size: px(16.),
1102 width: px(100.),
1103 ascent: px(12.),
1104 descent: px(4.),
1105 runs: vec![ShapedRun {
1106 font_id: FontId(0),
1107 glyphs,
1108 }],
1109 len: 0,
1110 }
1111 }
1112
1113 fn glyph_x_positions(layout: &LineLayout) -> Vec<f32> {
1114 layout.runs[0]
1115 .glyphs
1116 .iter()
1117 .map(|g| f32::from(g.position.x))
1118 .collect()
1119 }
1120
1121 #[test]
1122 fn test_force_width_latin_unchanged() {
1123 let cell_width = px(8.);
1124 let mut layout = make_layout(vec![glyph_at(0., 0), glyph_at(8., 1), glyph_at(16., 2)]);
1125
1126 apply_force_width_to_layout(&mut layout, cell_width);
1127
1128 let positions = glyph_x_positions(&layout);
1129 assert_eq!(positions, vec![0., 8., 16.]);
1130 }
1131
1132 #[test]
1133 fn test_force_width_combining_marks_not_advanced() {
1134 let cell_width = px(8.);
1135 let mut layout = make_layout(vec![
1137 glyph_at(0., 0), glyph_at(0., 3), ]);
1140
1141 apply_force_width_to_layout(&mut layout, cell_width);
1142
1143 let positions = glyph_x_positions(&layout);
1144 assert_eq!(positions, vec![0., 0.]);
1145 }
1146
1147 #[test]
1148 fn test_force_width_base_after_combining_mark() {
1149 let cell_width = px(8.);
1150 let mut layout = make_layout(vec![glyph_at(0., 0), glyph_at(0., 3), glyph_at(8., 6)]);
1151
1152 apply_force_width_to_layout(&mut layout, cell_width);
1153
1154 let positions = glyph_x_positions(&layout);
1155 assert_eq!(positions, vec![0., 0., 8.]);
1156 }
1157
1158 #[test]
1159 fn test_force_width_multiple_combining_marks() {
1160 let cell_width = px(8.);
1161 let mut layout = make_layout(vec![
1163 glyph_at(0., 0), glyph_at(0., 3), glyph_at(0., 6), glyph_at(8., 9), ]);
1168
1169 apply_force_width_to_layout(&mut layout, cell_width);
1170
1171 let positions = glyph_x_positions(&layout);
1172 assert_eq!(positions, vec![0., 0., 0., 8.]);
1173 }
1174
1175 #[test]
1176 fn test_force_width_corrects_drifted_base_positions() {
1177 let cell_width = px(8.);
1178 let mut layout = make_layout(vec![
1180 glyph_at(0.5, 0), glyph_at(10.2, 1), glyph_at(19.8, 2), ]);
1184
1185 apply_force_width_to_layout(&mut layout, cell_width);
1186
1187 let positions = glyph_x_positions(&layout);
1188 assert_eq!(positions, vec![0.5, 8., 16.]);
1189 }
1190
1191 #[test]
1192 fn test_force_width_combining_mark_after_within_tolerance_base() {
1193 let cell_width = px(8.);
1194 let mut layout = make_layout(vec![glyph_at(0.5, 0), glyph_at(0.5, 3)]);
1197
1198 apply_force_width_to_layout(&mut layout, cell_width);
1199
1200 let positions = glyph_x_positions(&layout);
1201 assert_eq!(positions, vec![0.5, 0.5]);
1202 }
1203}