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 self.unwrapped_layout
421 .index_for_x(position_in_unwrapped_line.x)
422 .ok_or(wrapped_line_end_index)
423 }
424 }
425 }
426
427 pub fn position_for_index(&self, index: usize, line_height: Pixels) -> Option<Point<Pixels>> {
429 let mut line_start_ix = 0;
430 let mut line_end_indices = self
431 .wrap_boundaries
432 .iter()
433 .map(|wrap_boundary| {
434 let run = &self.unwrapped_layout.runs[wrap_boundary.run_ix];
435 let glyph = &run.glyphs[wrap_boundary.glyph_ix];
436 glyph.index
437 })
438 .chain([self.len()])
439 .enumerate();
440 for (ix, line_end_ix) in line_end_indices {
441 let line_y = ix as f32 * line_height;
442 if index < line_start_ix {
443 break;
444 } else if index > line_end_ix {
445 line_start_ix = line_end_ix;
446 continue;
447 } else {
448 let line_start_x = self.unwrapped_layout.x_for_index(line_start_ix);
449 let x = self.unwrapped_layout.x_for_index(index) - line_start_x;
450 return Some(point(x, line_y));
451 }
452 }
453
454 None
455 }
456}
457
458pub(crate) struct LineLayoutCache {
459 previous_frame: Mutex<FrameCache>,
460 current_frame: RwLock<FrameCache>,
461 platform_text_system: Arc<dyn PlatformTextSystem>,
462 font_generation: Arc<AtomicUsize>,
464 cached_font_generation: AtomicUsize,
466}
467
468#[derive(Default)]
469struct FrameCache {
470 lines: FxHashMap<Arc<CacheKey>, Arc<LineLayout>>,
471 wrapped_lines: FxHashMap<Arc<CacheKey>, Arc<WrappedLineLayout>>,
472 used_lines: Vec<Arc<CacheKey>>,
473 used_wrapped_lines: Vec<Arc<CacheKey>>,
474
475 lines_by_hash: FxHashMap<Arc<HashedCacheKey>, Arc<LineLayout>>,
482 wrapped_lines_by_hash: FxHashMap<Arc<HashedCacheKey>, Arc<WrappedLineLayout>>,
483 used_lines_by_hash: Vec<Arc<HashedCacheKey>>,
484 used_wrapped_lines_by_hash: Vec<Arc<HashedCacheKey>>,
485}
486
487#[derive(Clone, Default)]
488pub(crate) struct LineLayoutIndex {
489 font_generation: usize,
490 lines_index: usize,
491 wrapped_lines_index: usize,
492 lines_by_hash_index: usize,
493 wrapped_lines_by_hash_index: usize,
494}
495
496impl LineLayoutCache {
497 pub fn new(
498 platform_text_system: Arc<dyn PlatformTextSystem>,
499 font_generation: Arc<AtomicUsize>,
500 ) -> Self {
501 let cached_font_generation = font_generation.load(Ordering::Acquire);
502 Self {
503 previous_frame: Mutex::default(),
504 current_frame: RwLock::default(),
505 platform_text_system,
506 font_generation,
507 cached_font_generation: AtomicUsize::new(cached_font_generation),
508 }
509 }
510
511 pub fn layout_index(&self) -> LineLayoutIndex {
512 let font_generation = self.clear_if_font_generation_changed();
513 let frame = self.current_frame.read();
514 LineLayoutIndex {
515 font_generation,
516 lines_index: frame.used_lines.len(),
517 wrapped_lines_index: frame.used_wrapped_lines.len(),
518 lines_by_hash_index: frame.used_lines_by_hash.len(),
519 wrapped_lines_by_hash_index: frame.used_wrapped_lines_by_hash.len(),
520 }
521 }
522
523 pub fn reuse_layouts(&self, range: Range<LineLayoutIndex>) {
524 let font_generation = self.clear_if_font_generation_changed();
525 if range.start.font_generation != font_generation
526 || range.end.font_generation != font_generation
527 {
528 return;
529 }
530 let mut current_frame = &mut *self.current_frame.write();
531 let mut previous_frame = &mut *self.previous_frame.lock();
532
533 for key in &previous_frame.used_lines[range.start.lines_index..range.end.lines_index] {
534 if let Some((key, line)) = previous_frame.lines.remove_entry(key) {
535 current_frame.lines.insert(key, line);
536 }
537 current_frame.used_lines.push(key.clone());
538 }
539
540 for key in &previous_frame.used_wrapped_lines
541 [range.start.wrapped_lines_index..range.end.wrapped_lines_index]
542 {
543 if let Some((key, line)) = previous_frame.wrapped_lines.remove_entry(key) {
544 current_frame.wrapped_lines.insert(key, line);
545 }
546 current_frame.used_wrapped_lines.push(key.clone());
547 }
548
549 for key in &previous_frame.used_lines_by_hash
550 [range.start.lines_by_hash_index..range.end.lines_by_hash_index]
551 {
552 if let Some((key, line)) = previous_frame.lines_by_hash.remove_entry(key) {
553 current_frame.lines_by_hash.insert(key, line);
554 }
555 current_frame.used_lines_by_hash.push(key.clone());
556 }
557
558 for key in &previous_frame.used_wrapped_lines_by_hash
559 [range.start.wrapped_lines_by_hash_index..range.end.wrapped_lines_by_hash_index]
560 {
561 if let Some((key, line)) = previous_frame.wrapped_lines_by_hash.remove_entry(key) {
562 current_frame.wrapped_lines_by_hash.insert(key, line);
563 }
564 current_frame.used_wrapped_lines_by_hash.push(key.clone());
565 }
566 }
567
568 pub fn truncate_layouts(&self, index: LineLayoutIndex) {
569 let font_generation = self.clear_if_font_generation_changed();
570 if index.font_generation != font_generation {
571 return;
572 }
573 let mut current_frame = &mut *self.current_frame.write();
574 current_frame.used_lines.truncate(index.lines_index);
575 current_frame
576 .used_wrapped_lines
577 .truncate(index.wrapped_lines_index);
578 current_frame
579 .used_lines_by_hash
580 .truncate(index.lines_by_hash_index);
581 current_frame
582 .used_wrapped_lines_by_hash
583 .truncate(index.wrapped_lines_by_hash_index);
584 }
585
586 pub fn finish_frame(&self) {
587 let _font_generation = self.clear_if_font_generation_changed();
588 let mut curr_frame = self.current_frame.write();
589 let mut prev_frame = self.previous_frame.lock();
590 std::mem::swap(&mut *prev_frame, &mut *curr_frame);
591 curr_frame.lines.clear();
592 curr_frame.wrapped_lines.clear();
593 curr_frame.used_lines.clear();
594 curr_frame.used_wrapped_lines.clear();
595
596 curr_frame.lines_by_hash.clear();
597 curr_frame.wrapped_lines_by_hash.clear();
598 curr_frame.used_lines_by_hash.clear();
599 curr_frame.used_wrapped_lines_by_hash.clear();
600 }
601
602 pub fn layout_wrapped_line<Text>(
603 &self,
604 text: Text,
605 font_size: Pixels,
606 runs: &[FontRun],
607 wrap_width: Option<Pixels>,
608 max_lines: Option<usize>,
609 ) -> Arc<WrappedLineLayout>
610 where
611 Text: AsRef<str>,
612 SharedString: From<Text>,
613 {
614 let _font_generation = self.clear_if_font_generation_changed();
615 let key = &CacheKeyRef {
616 text: text.as_ref(),
617 font_size,
618 runs,
619 wrap_width,
620 force_width: None,
621 } as &dyn AsCacheKeyRef;
622
623 let current_frame = self.current_frame.upgradable_read();
624 if let Some(layout) = current_frame.wrapped_lines.get(key) {
625 return layout.clone();
626 }
627
628 let previous_frame_entry = self.previous_frame.lock().wrapped_lines.remove_entry(key);
629 if let Some((key, layout)) = previous_frame_entry {
630 let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
631 current_frame
632 .wrapped_lines
633 .insert(key.clone(), layout.clone());
634 current_frame.used_wrapped_lines.push(key);
635 layout
636 } else {
637 drop(current_frame);
638 let text = SharedString::from(text);
639 let unwrapped_layout = self.layout_line::<&SharedString>(&text, font_size, runs, None);
640 let wrap_boundaries = if let Some(wrap_width) = wrap_width {
641 unwrapped_layout.compute_wrap_boundaries(text.as_ref(), wrap_width, max_lines)
642 } else {
643 SmallVec::new()
644 };
645 let layout = Arc::new(WrappedLineLayout {
646 unwrapped_layout,
647 wrap_boundaries,
648 wrap_width,
649 });
650 let key = Arc::new(CacheKey {
651 text,
652 font_size,
653 runs: SmallVec::from(runs),
654 wrap_width,
655 force_width: None,
656 });
657
658 let mut current_frame = self.current_frame.write();
659 current_frame
660 .wrapped_lines
661 .insert(key.clone(), layout.clone());
662 current_frame.used_wrapped_lines.push(key);
663
664 layout
665 }
666 }
667
668 pub fn layout_line<Text>(
669 &self,
670 text: Text,
671 font_size: Pixels,
672 runs: &[FontRun],
673 force_width: Option<Pixels>,
674 ) -> Arc<LineLayout>
675 where
676 Text: AsRef<str>,
677 SharedString: From<Text>,
678 {
679 let _font_generation = self.clear_if_font_generation_changed();
680 let key = &CacheKeyRef {
681 text: text.as_ref(),
682 font_size,
683 runs,
684 wrap_width: None,
685 force_width,
686 } as &dyn AsCacheKeyRef;
687
688 let current_frame = self.current_frame.upgradable_read();
689 if let Some(layout) = current_frame.lines.get(key) {
690 return layout.clone();
691 }
692
693 let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
694 if let Some((key, layout)) = self.previous_frame.lock().lines.remove_entry(key) {
695 current_frame.lines.insert(key.clone(), layout.clone());
696 current_frame.used_lines.push(key);
697 layout
698 } else {
699 let text = SharedString::from(text);
700 let mut layout = self
701 .platform_text_system
702 .layout_line(&text, font_size, runs);
703
704 if let Some(force_width) = force_width {
705 apply_force_width_to_layout(&mut layout, force_width);
706 }
707
708 let key = Arc::new(CacheKey {
709 text,
710 font_size,
711 runs: SmallVec::from(runs),
712 wrap_width: None,
713 force_width,
714 });
715 let layout = Arc::new(layout);
716 current_frame.lines.insert(key.clone(), layout.clone());
717 current_frame.used_lines.push(key);
718 layout
719 }
720 }
721
722 pub fn try_layout_line_by_hash(
731 &self,
732 text_hash: u64,
733 text_len: usize,
734 font_size: Pixels,
735 runs: &[FontRun],
736 force_width: Option<Pixels>,
737 ) -> Option<Arc<LineLayout>> {
738 let _font_generation = self.clear_if_font_generation_changed();
739 let key_ref = HashedCacheKeyRef {
740 text_hash,
741 text_len,
742 font_size,
743 runs,
744 wrap_width: None,
745 force_width,
746 };
747
748 let current_frame = self.current_frame.read();
749 if let Some((_, layout)) = current_frame.lines_by_hash.iter().find(|(key, _)| {
750 HashedCacheKeyRef {
751 text_hash: key.text_hash,
752 text_len: key.text_len,
753 font_size: key.font_size,
754 runs: key.runs.as_slice(),
755 wrap_width: key.wrap_width,
756 force_width: key.force_width,
757 } == key_ref
758 }) {
759 return Some(layout.clone());
760 }
761
762 let previous_frame = self.previous_frame.lock();
763 if let Some((_, layout)) = previous_frame.lines_by_hash.iter().find(|(key, _)| {
764 HashedCacheKeyRef {
765 text_hash: key.text_hash,
766 text_len: key.text_len,
767 font_size: key.font_size,
768 runs: key.runs.as_slice(),
769 wrap_width: key.wrap_width,
770 force_width: key.force_width,
771 } == key_ref
772 }) {
773 return Some(layout.clone());
774 }
775
776 None
777 }
778
779 pub fn layout_line_by_hash(
788 &self,
789 text_hash: u64,
790 text_len: usize,
791 font_size: Pixels,
792 runs: &[FontRun],
793 force_width: Option<Pixels>,
794 materialize_text: impl FnOnce() -> SharedString,
795 ) -> Arc<LineLayout> {
796 let _font_generation = self.clear_if_font_generation_changed();
797 let key_ref = HashedCacheKeyRef {
798 text_hash,
799 text_len,
800 font_size,
801 runs,
802 wrap_width: None,
803 force_width,
804 };
805
806 let current_frame = self.current_frame.upgradable_read();
808 if let Some((_, layout)) = current_frame.lines_by_hash.iter().find(|(key, _)| {
809 HashedCacheKeyRef {
810 text_hash: key.text_hash,
811 text_len: key.text_len,
812 font_size: key.font_size,
813 runs: key.runs.as_slice(),
814 wrap_width: key.wrap_width,
815 force_width: key.force_width,
816 } == key_ref
817 }) {
818 return layout.clone();
819 }
820
821 let mut current_frame = RwLockUpgradableReadGuard::upgrade(current_frame);
822
823 let mut previous_frame = self.previous_frame.lock();
826 if let Some(existing_key) = previous_frame
827 .used_lines_by_hash
828 .iter()
829 .find(|key| {
830 HashedCacheKeyRef {
831 text_hash: key.text_hash,
832 text_len: key.text_len,
833 font_size: key.font_size,
834 runs: key.runs.as_slice(),
835 wrap_width: key.wrap_width,
836 force_width: key.force_width,
837 } == key_ref
838 })
839 .cloned()
840 {
841 if let Some((key, layout)) = previous_frame.lines_by_hash.remove_entry(&existing_key) {
842 current_frame
843 .lines_by_hash
844 .insert(key.clone(), layout.clone());
845 current_frame.used_lines_by_hash.push(key);
846 return layout;
847 }
848 }
849
850 let text = materialize_text();
851 let mut layout = self
852 .platform_text_system
853 .layout_line(&text, font_size, runs);
854
855 if let Some(force_width) = force_width {
856 apply_force_width_to_layout(&mut layout, force_width);
857 }
858
859 let key = Arc::new(HashedCacheKey {
860 text_hash,
861 text_len,
862 font_size,
863 runs: SmallVec::from(runs),
864 wrap_width: None,
865 force_width,
866 });
867 let layout = Arc::new(layout);
868 current_frame
869 .lines_by_hash
870 .insert(key.clone(), layout.clone());
871 current_frame.used_lines_by_hash.push(key);
872 layout
873 }
874
875 fn clear_if_font_generation_changed(&self) -> usize {
876 let font_generation = self.font_generation.load(Ordering::Acquire);
877 if self.cached_font_generation.load(Ordering::Acquire) == font_generation {
878 return font_generation;
879 }
880
881 let mut current_frame = self.current_frame.write();
882 if self.cached_font_generation.load(Ordering::Acquire) == font_generation {
883 return font_generation;
884 }
885
886 *current_frame = FrameCache::default();
887 *self.previous_frame.lock() = FrameCache::default();
888 self.cached_font_generation
889 .store(font_generation, Ordering::Release);
890 font_generation
891 }
892}
893
894fn apply_force_width_to_layout(layout: &mut LineLayout, force_width: Pixels) {
900 let mut glyph_pos: usize = 0;
901 let mut last_base_shaped_x = px(f32::NEG_INFINITY);
903 let mut last_base_actual_x = px(0.);
904
905 for run in layout.runs.iter_mut() {
906 for glyph in run.glyphs.iter_mut() {
907 let shaped_x = glyph.position.x;
908
909 if shaped_x > last_base_shaped_x + force_width * 0.5 {
910 let forced_x = glyph_pos * force_width;
911 if (shaped_x - forced_x).abs() > px(1.) {
912 glyph.position.x = forced_x;
913 }
914 last_base_shaped_x = shaped_x;
915 last_base_actual_x = glyph.position.x;
916 glyph_pos += 1;
917 } else {
918 glyph.position.x = last_base_actual_x + (shaped_x - last_base_shaped_x);
919 }
920 }
921 }
922}
923
924#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
926#[expect(missing_docs)]
927pub struct FontRun {
928 pub len: usize,
929 pub font_id: FontId,
930}
931
932trait AsCacheKeyRef {
933 fn as_cache_key_ref(&self) -> CacheKeyRef<'_>;
934}
935
936#[derive(Clone, Debug, Eq)]
937struct CacheKey {
938 text: SharedString,
939 font_size: Pixels,
940 runs: SmallVec<[FontRun; 1]>,
941 wrap_width: Option<Pixels>,
942 force_width: Option<Pixels>,
943}
944
945#[derive(Copy, Clone, PartialEq, Eq, Hash)]
946struct CacheKeyRef<'a> {
947 text: &'a str,
948 font_size: Pixels,
949 runs: &'a [FontRun],
950 wrap_width: Option<Pixels>,
951 force_width: Option<Pixels>,
952}
953
954#[derive(Clone, Debug)]
955struct HashedCacheKey {
956 text_hash: u64,
957 text_len: usize,
958 font_size: Pixels,
959 runs: SmallVec<[FontRun; 1]>,
960 wrap_width: Option<Pixels>,
961 force_width: Option<Pixels>,
962}
963
964#[derive(Copy, Clone)]
965struct HashedCacheKeyRef<'a> {
966 text_hash: u64,
967 text_len: usize,
968 font_size: Pixels,
969 runs: &'a [FontRun],
970 wrap_width: Option<Pixels>,
971 force_width: Option<Pixels>,
972}
973
974impl PartialEq for dyn AsCacheKeyRef + '_ {
975 fn eq(&self, other: &dyn AsCacheKeyRef) -> bool {
976 self.as_cache_key_ref() == other.as_cache_key_ref()
977 }
978}
979
980impl PartialEq for HashedCacheKey {
981 fn eq(&self, other: &Self) -> bool {
982 self.text_hash == other.text_hash
983 && self.text_len == other.text_len
984 && self.font_size == other.font_size
985 && self.runs.as_slice() == other.runs.as_slice()
986 && self.wrap_width == other.wrap_width
987 && self.force_width == other.force_width
988 }
989}
990
991impl Eq for HashedCacheKey {}
992
993impl Hash for HashedCacheKey {
994 fn hash<H: Hasher>(&self, state: &mut H) {
995 self.text_hash.hash(state);
996 self.text_len.hash(state);
997 self.font_size.hash(state);
998 self.runs.as_slice().hash(state);
999 self.wrap_width.hash(state);
1000 self.force_width.hash(state);
1001 }
1002}
1003
1004impl PartialEq for HashedCacheKeyRef<'_> {
1005 fn eq(&self, other: &Self) -> bool {
1006 self.text_hash == other.text_hash
1007 && self.text_len == other.text_len
1008 && self.font_size == other.font_size
1009 && self.runs == other.runs
1010 && self.wrap_width == other.wrap_width
1011 && self.force_width == other.force_width
1012 }
1013}
1014
1015impl Eq for HashedCacheKeyRef<'_> {}
1016
1017impl Hash for HashedCacheKeyRef<'_> {
1018 fn hash<H: Hasher>(&self, state: &mut H) {
1019 self.text_hash.hash(state);
1020 self.text_len.hash(state);
1021 self.font_size.hash(state);
1022 self.runs.hash(state);
1023 self.wrap_width.hash(state);
1024 self.force_width.hash(state);
1025 }
1026}
1027
1028impl Eq for dyn AsCacheKeyRef + '_ {}
1029
1030impl Hash for dyn AsCacheKeyRef + '_ {
1031 fn hash<H: Hasher>(&self, state: &mut H) {
1032 self.as_cache_key_ref().hash(state)
1033 }
1034}
1035
1036impl AsCacheKeyRef for CacheKey {
1037 fn as_cache_key_ref(&self) -> CacheKeyRef<'_> {
1038 CacheKeyRef {
1039 text: &self.text,
1040 font_size: self.font_size,
1041 runs: self.runs.as_slice(),
1042 wrap_width: self.wrap_width,
1043 force_width: self.force_width,
1044 }
1045 }
1046}
1047
1048impl PartialEq for CacheKey {
1049 fn eq(&self, other: &Self) -> bool {
1050 self.as_cache_key_ref().eq(&other.as_cache_key_ref())
1051 }
1052}
1053
1054impl Hash for CacheKey {
1055 fn hash<H: Hasher>(&self, state: &mut H) {
1056 self.as_cache_key_ref().hash(state);
1057 }
1058}
1059
1060impl<'a> Borrow<dyn AsCacheKeyRef + 'a> for Arc<CacheKey> {
1061 fn borrow(&self) -> &(dyn AsCacheKeyRef + 'a) {
1062 self.as_ref() as &dyn AsCacheKeyRef
1063 }
1064}
1065
1066impl AsCacheKeyRef for CacheKeyRef<'_> {
1067 fn as_cache_key_ref(&self) -> CacheKeyRef<'_> {
1068 *self
1069 }
1070}
1071
1072#[cfg(test)]
1073mod tests {
1074 use super::*;
1075 use crate::GlyphId;
1076
1077 fn glyph_at(x: f32, index: usize) -> ShapedGlyph {
1078 ShapedGlyph {
1079 id: GlyphId(0),
1080 position: point(px(x), px(0.)),
1081 index,
1082 is_emoji: false,
1083 }
1084 }
1085
1086 fn make_layout(glyphs: Vec<ShapedGlyph>) -> LineLayout {
1087 LineLayout {
1088 font_size: px(16.),
1089 width: px(100.),
1090 ascent: px(12.),
1091 descent: px(4.),
1092 runs: vec![ShapedRun {
1093 font_id: FontId(0),
1094 glyphs,
1095 }],
1096 len: 0,
1097 }
1098 }
1099
1100 fn glyph_x_positions(layout: &LineLayout) -> Vec<f32> {
1101 layout.runs[0]
1102 .glyphs
1103 .iter()
1104 .map(|g| f32::from(g.position.x))
1105 .collect()
1106 }
1107
1108 #[test]
1109 fn test_force_width_latin_unchanged() {
1110 let cell_width = px(8.);
1111 let mut layout = make_layout(vec![glyph_at(0., 0), glyph_at(8., 1), glyph_at(16., 2)]);
1112
1113 apply_force_width_to_layout(&mut layout, cell_width);
1114
1115 let positions = glyph_x_positions(&layout);
1116 assert_eq!(positions, vec![0., 8., 16.]);
1117 }
1118
1119 #[test]
1120 fn test_force_width_combining_marks_not_advanced() {
1121 let cell_width = px(8.);
1122 let mut layout = make_layout(vec![
1124 glyph_at(0., 0), glyph_at(0., 3), ]);
1127
1128 apply_force_width_to_layout(&mut layout, cell_width);
1129
1130 let positions = glyph_x_positions(&layout);
1131 assert_eq!(positions, vec![0., 0.]);
1132 }
1133
1134 #[test]
1135 fn test_force_width_base_after_combining_mark() {
1136 let cell_width = px(8.);
1137 let mut layout = make_layout(vec![glyph_at(0., 0), glyph_at(0., 3), glyph_at(8., 6)]);
1138
1139 apply_force_width_to_layout(&mut layout, cell_width);
1140
1141 let positions = glyph_x_positions(&layout);
1142 assert_eq!(positions, vec![0., 0., 8.]);
1143 }
1144
1145 #[test]
1146 fn test_force_width_multiple_combining_marks() {
1147 let cell_width = px(8.);
1148 let mut layout = make_layout(vec![
1150 glyph_at(0., 0), glyph_at(0., 3), glyph_at(0., 6), glyph_at(8., 9), ]);
1155
1156 apply_force_width_to_layout(&mut layout, cell_width);
1157
1158 let positions = glyph_x_positions(&layout);
1159 assert_eq!(positions, vec![0., 0., 0., 8.]);
1160 }
1161
1162 #[test]
1163 fn test_force_width_corrects_drifted_base_positions() {
1164 let cell_width = px(8.);
1165 let mut layout = make_layout(vec![
1167 glyph_at(0.5, 0), glyph_at(10.2, 1), glyph_at(19.8, 2), ]);
1171
1172 apply_force_width_to_layout(&mut layout, cell_width);
1173
1174 let positions = glyph_x_positions(&layout);
1175 assert_eq!(positions, vec![0.5, 8., 16.]);
1176 }
1177
1178 #[test]
1179 fn test_force_width_combining_mark_after_within_tolerance_base() {
1180 let cell_width = px(8.);
1181 let mut layout = make_layout(vec![glyph_at(0.5, 0), glyph_at(0.5, 3)]);
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, 0.5]);
1189 }
1190}