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