1use crate::{
2 color::ColorU,
3 fonts::{FontCache, FontId, GlyphId},
4 geometry::rect::RectF,
5 platform, scene, PaintContext,
6};
7use core_foundation::{
8 attributed_string::CFMutableAttributedString,
9 base::{CFRange, TCFType},
10 string::CFString,
11};
12use core_text::{font::CTFont, line::CTLine, string_attributes::kCTFontAttributeName};
13use ordered_float::OrderedFloat;
14use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
15use pathfinder_geometry::vector::{vec2f, Vector2F};
16use smallvec::SmallVec;
17use std::{
18 borrow::Borrow,
19 char,
20 collections::HashMap,
21 convert::TryFrom,
22 hash::{Hash, Hasher},
23 ops::Range,
24 sync::Arc,
25};
26
27pub struct TextLayoutCache {
28 prev_frame: Mutex<HashMap<CacheKeyValue, Arc<Line>>>,
29 curr_frame: RwLock<HashMap<CacheKeyValue, Arc<Line>>>,
30 fonts: Arc<dyn platform::FontSystem>,
31}
32
33impl TextLayoutCache {
34 pub fn new(fonts: Arc<dyn platform::FontSystem>) -> Self {
35 Self {
36 prev_frame: Mutex::new(HashMap::new()),
37 curr_frame: RwLock::new(HashMap::new()),
38 fonts,
39 }
40 }
41
42 pub fn finish_frame(&self) {
43 let mut prev_frame = self.prev_frame.lock();
44 let mut curr_frame = self.curr_frame.write();
45 std::mem::swap(&mut *prev_frame, &mut *curr_frame);
46 curr_frame.clear();
47 }
48
49 pub fn layout_str<'a>(
50 &'a self,
51 text: &'a str,
52 font_size: f32,
53 runs: &'a [(Range<usize>, FontId)],
54 ) -> Arc<Line> {
55 let key = &CacheKeyRef {
56 text,
57 font_size: OrderedFloat(font_size),
58 runs,
59 } as &dyn CacheKey;
60 let curr_frame = self.curr_frame.upgradable_read();
61 if let Some(line) = curr_frame.get(key) {
62 return line.clone();
63 }
64
65 let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
66 if let Some((key, line)) = self.prev_frame.lock().remove_entry(key) {
67 curr_frame.insert(key, line.clone());
68 line.clone()
69 } else {
70 let line = Arc::new(self.fonts.layout_str(text, font_size, runs));
71 let key = CacheKeyValue {
72 text: text.into(),
73 font_size: OrderedFloat(font_size),
74 runs: SmallVec::from(runs),
75 };
76 curr_frame.insert(key, line.clone());
77 line
78 }
79 }
80}
81
82trait CacheKey {
83 fn key<'a>(&'a self) -> CacheKeyRef<'a>;
84}
85
86impl<'a> PartialEq for (dyn CacheKey + 'a) {
87 fn eq(&self, other: &dyn CacheKey) -> bool {
88 self.key() == other.key()
89 }
90}
91
92impl<'a> Eq for (dyn CacheKey + 'a) {}
93
94impl<'a> Hash for (dyn CacheKey + 'a) {
95 fn hash<H: Hasher>(&self, state: &mut H) {
96 self.key().hash(state)
97 }
98}
99
100#[derive(Eq, PartialEq)]
101struct CacheKeyValue {
102 text: String,
103 font_size: OrderedFloat<f32>,
104 runs: SmallVec<[(Range<usize>, FontId); 1]>,
105}
106
107impl CacheKey for CacheKeyValue {
108 fn key<'a>(&'a self) -> CacheKeyRef<'a> {
109 CacheKeyRef {
110 text: &self.text.as_str(),
111 font_size: self.font_size,
112 runs: self.runs.as_slice(),
113 }
114 }
115}
116
117impl Hash for CacheKeyValue {
118 fn hash<H: Hasher>(&self, state: &mut H) {
119 self.key().hash(state);
120 }
121}
122
123impl<'a> Borrow<dyn CacheKey + 'a> for CacheKeyValue {
124 fn borrow(&self) -> &(dyn CacheKey + 'a) {
125 self as &dyn CacheKey
126 }
127}
128
129#[derive(Copy, Clone, PartialEq, Eq, Hash)]
130struct CacheKeyRef<'a> {
131 text: &'a str,
132 font_size: OrderedFloat<f32>,
133 runs: &'a [(Range<usize>, FontId)],
134}
135
136impl<'a> CacheKey for CacheKeyRef<'a> {
137 fn key<'b>(&'b self) -> CacheKeyRef<'b> {
138 *self
139 }
140}
141
142#[derive(Default, Debug)]
143pub struct Line {
144 pub width: f32,
145 pub runs: Vec<Run>,
146 pub len: usize,
147 pub font_size: f32,
148}
149
150#[derive(Debug)]
151pub struct Run {
152 pub font_id: FontId,
153 pub glyphs: Vec<Glyph>,
154}
155
156#[derive(Debug)]
157pub struct Glyph {
158 pub id: GlyphId,
159 pub position: Vector2F,
160 pub index: usize,
161}
162
163impl Line {
164 pub fn x_for_index(&self, index: usize) -> f32 {
165 for run in &self.runs {
166 for glyph in &run.glyphs {
167 if glyph.index == index {
168 return glyph.position.x();
169 }
170 }
171 }
172 self.width
173 }
174
175 pub fn index_for_x(&self, x: f32) -> Option<usize> {
176 if x >= self.width {
177 None
178 } else {
179 for run in self.runs.iter().rev() {
180 for glyph in run.glyphs.iter().rev() {
181 if glyph.position.x() <= x {
182 return Some(glyph.index);
183 }
184 }
185 }
186 Some(0)
187 }
188 }
189
190 pub fn paint(&self, bounds: RectF, colors: &[(Range<usize>, ColorU)], ctx: &mut PaintContext) {
191 let mut colors = colors.iter().peekable();
192 let mut color = ColorU::black();
193
194 for run in &self.runs {
195 let bounding_box = ctx.font_cache.bounding_box(run.font_id, self.font_size);
196 let max_glyph_width = bounding_box.x();
197 for glyph in &run.glyphs {
198 let glyph_origin = bounds.origin() + glyph.position;
199 if glyph_origin.x() + max_glyph_width < bounds.origin().x() {
200 continue;
201 }
202 if glyph_origin.x() > bounds.upper_right().x() {
203 break;
204 }
205
206 while let Some((range, next_color)) = colors.peek() {
207 if glyph.index >= range.end {
208 colors.next();
209 } else {
210 color = *next_color;
211 break;
212 }
213 }
214
215 ctx.scene.push_glyph(scene::Glyph {
216 font_id: run.font_id,
217 font_size: self.font_size,
218 id: glyph.id,
219 origin: glyph_origin,
220 color,
221 });
222 }
223 }
224 }
225}