1use crate::{FontId, Pixels, PlatformTextSystem, ShapedGlyph, ShapedLine, ShapedRun};
2use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
3use smallvec::SmallVec;
4use std::{
5 borrow::Borrow,
6 collections::HashMap,
7 hash::{Hash, Hasher},
8 sync::Arc,
9};
10
11pub(crate) struct TextLayoutCache {
12 prev_frame: Mutex<HashMap<CacheKeyValue, Arc<ShapedLine>>>,
13 curr_frame: RwLock<HashMap<CacheKeyValue, Arc<ShapedLine>>>,
14 platform_text_system: Arc<dyn PlatformTextSystem>,
15}
16
17impl TextLayoutCache {
18 pub fn new(fonts: Arc<dyn PlatformTextSystem>) -> Self {
19 Self {
20 prev_frame: Mutex::new(HashMap::new()),
21 curr_frame: RwLock::new(HashMap::new()),
22 platform_text_system: fonts,
23 }
24 }
25
26 pub fn end_frame(&self) {
27 let mut prev_frame = self.prev_frame.lock();
28 let mut curr_frame = self.curr_frame.write();
29 std::mem::swap(&mut *prev_frame, &mut *curr_frame);
30 curr_frame.clear();
31 }
32
33 pub fn layout_line<'a>(
34 &'a self,
35 text: &'a str,
36 font_size: Pixels,
37 runs: &[(usize, FontId)],
38 ) -> Arc<ShapedLine> {
39 let key = &CacheKeyRef {
40 text,
41 font_size,
42 runs,
43 } as &dyn CacheKey;
44 let curr_frame = self.curr_frame.upgradable_read();
45 if let Some(layout) = curr_frame.get(key) {
46 return layout.clone();
47 }
48
49 let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
50 if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
51 curr_frame.insert(key, layout.clone());
52 layout
53 } else {
54 let layout = Arc::new(self.platform_text_system.layout_line(text, font_size, runs));
55 let key = CacheKeyValue {
56 text: text.into(),
57 font_size,
58 runs: SmallVec::from(runs),
59 };
60 curr_frame.insert(key, layout.clone());
61 layout
62 }
63 }
64}
65
66trait CacheKey {
67 fn key(&self) -> CacheKeyRef;
68}
69
70impl<'a> PartialEq for (dyn CacheKey + 'a) {
71 fn eq(&self, other: &dyn CacheKey) -> bool {
72 self.key() == other.key()
73 }
74}
75
76impl<'a> Eq for (dyn CacheKey + 'a) {}
77
78impl<'a> Hash for (dyn CacheKey + 'a) {
79 fn hash<H: Hasher>(&self, state: &mut H) {
80 self.key().hash(state)
81 }
82}
83
84#[derive(Eq)]
85struct CacheKeyValue {
86 text: String,
87 font_size: Pixels,
88 runs: SmallVec<[(usize, FontId); 1]>,
89}
90
91impl CacheKey for CacheKeyValue {
92 fn key(&self) -> CacheKeyRef {
93 CacheKeyRef {
94 text: self.text.as_str(),
95 font_size: self.font_size,
96 runs: self.runs.as_slice(),
97 }
98 }
99}
100
101impl PartialEq for CacheKeyValue {
102 fn eq(&self, other: &Self) -> bool {
103 self.key().eq(&other.key())
104 }
105}
106
107impl Hash for CacheKeyValue {
108 fn hash<H: Hasher>(&self, state: &mut H) {
109 self.key().hash(state);
110 }
111}
112
113impl<'a> Borrow<dyn CacheKey + 'a> for CacheKeyValue {
114 fn borrow(&self) -> &(dyn CacheKey + 'a) {
115 self as &dyn CacheKey
116 }
117}
118
119#[derive(Copy, Clone, PartialEq, Eq)]
120struct CacheKeyRef<'a> {
121 text: &'a str,
122 font_size: Pixels,
123 runs: &'a [(usize, FontId)],
124}
125
126impl<'a> CacheKey for CacheKeyRef<'a> {
127 fn key(&self) -> CacheKeyRef {
128 *self
129 }
130}
131
132impl<'a> Hash for CacheKeyRef<'a> {
133 fn hash<H: Hasher>(&self, state: &mut H) {
134 self.text.hash(state);
135 self.font_size.hash(state);
136 for (len, font_id) in self.runs {
137 len.hash(state);
138 font_id.hash(state);
139 }
140 }
141}
142
143#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
144pub struct ShapedBoundary {
145 pub run_ix: usize,
146 pub glyph_ix: usize,
147}
148
149impl ShapedRun {
150 pub fn glyphs(&self) -> &[ShapedGlyph] {
151 &self.glyphs
152 }
153}