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