1use crate::{
2 black, point, px, Bounds, FontId, Glyph, Hsla, LineLayout, Pixels, PlatformTextSystem, Point,
3 Run, RunStyle, UnderlineStyle, WindowContext,
4};
5use anyhow::Result;
6use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
7use smallvec::SmallVec;
8use std::{
9 borrow::Borrow,
10 collections::HashMap,
11 hash::{Hash, Hasher},
12 sync::Arc,
13};
14
15pub(crate) struct TextLayoutCache {
16 prev_frame: Mutex<HashMap<CacheKeyValue, Arc<LineLayout>>>,
17 curr_frame: RwLock<HashMap<CacheKeyValue, Arc<LineLayout>>>,
18 platform_text_system: Arc<dyn PlatformTextSystem>,
19}
20
21impl TextLayoutCache {
22 pub fn new(fonts: Arc<dyn PlatformTextSystem>) -> Self {
23 Self {
24 prev_frame: Mutex::new(HashMap::new()),
25 curr_frame: RwLock::new(HashMap::new()),
26 platform_text_system: fonts,
27 }
28 }
29
30 pub fn finish_frame(&self) {
31 let mut prev_frame = self.prev_frame.lock();
32 let mut curr_frame = self.curr_frame.write();
33 std::mem::swap(&mut *prev_frame, &mut *curr_frame);
34 curr_frame.clear();
35 }
36
37 pub fn layout_line<'a>(
38 &'a self,
39 text: &'a str,
40 font_size: Pixels,
41 runs: &[(usize, FontId)],
42 ) -> Arc<LineLayout> {
43 let key = &CacheKeyRef {
44 text,
45 font_size,
46 runs,
47 } as &dyn CacheKey;
48 let curr_frame = self.curr_frame.upgradable_read();
49 if let Some(layout) = curr_frame.get(key) {
50 return layout.clone();
51 }
52
53 let mut curr_frame = RwLockUpgradableReadGuard::upgrade(curr_frame);
54 if let Some((key, layout)) = self.prev_frame.lock().remove_entry(key) {
55 curr_frame.insert(key, layout.clone());
56 layout
57 } else {
58 let layout = Arc::new(self.platform_text_system.layout_line(text, font_size, runs));
59 let key = CacheKeyValue {
60 text: text.into(),
61 font_size,
62 runs: SmallVec::from(runs),
63 };
64 curr_frame.insert(key, layout.clone());
65 layout
66 }
67 }
68}
69
70trait CacheKey {
71 fn key(&self) -> CacheKeyRef;
72}
73
74impl<'a> PartialEq for (dyn CacheKey + 'a) {
75 fn eq(&self, other: &dyn CacheKey) -> bool {
76 self.key() == other.key()
77 }
78}
79
80impl<'a> Eq for (dyn CacheKey + 'a) {}
81
82impl<'a> Hash for (dyn CacheKey + 'a) {
83 fn hash<H: Hasher>(&self, state: &mut H) {
84 self.key().hash(state)
85 }
86}
87
88#[derive(Eq)]
89struct CacheKeyValue {
90 text: String,
91 font_size: Pixels,
92 runs: SmallVec<[(usize, FontId); 1]>,
93}
94
95impl CacheKey for CacheKeyValue {
96 fn key(&self) -> CacheKeyRef {
97 CacheKeyRef {
98 text: self.text.as_str(),
99 font_size: self.font_size,
100 runs: self.runs.as_slice(),
101 }
102 }
103}
104
105impl PartialEq for CacheKeyValue {
106 fn eq(&self, other: &Self) -> bool {
107 self.key().eq(&other.key())
108 }
109}
110
111impl Hash for CacheKeyValue {
112 fn hash<H: Hasher>(&self, state: &mut H) {
113 self.key().hash(state);
114 }
115}
116
117impl<'a> Borrow<dyn CacheKey + 'a> for CacheKeyValue {
118 fn borrow(&self) -> &(dyn CacheKey + 'a) {
119 self as &dyn CacheKey
120 }
121}
122
123#[derive(Copy, Clone, PartialEq, Eq)]
124struct CacheKeyRef<'a> {
125 text: &'a str,
126 font_size: Pixels,
127 runs: &'a [(usize, FontId)],
128}
129
130impl<'a> CacheKey for CacheKeyRef<'a> {
131 fn key(&self) -> CacheKeyRef {
132 *self
133 }
134}
135
136impl<'a> Hash for CacheKeyRef<'a> {
137 fn hash<H: Hasher>(&self, state: &mut H) {
138 self.text.hash(state);
139 self.font_size.hash(state);
140 for (len, font_id) in self.runs {
141 len.hash(state);
142 font_id.hash(state);
143 }
144 }
145}
146
147#[derive(Default, Debug, Clone)]
148pub struct Line {
149 layout: Arc<LineLayout>,
150 style_runs: SmallVec<[StyleRun; 32]>,
151}
152
153#[derive(Debug, Clone)]
154struct StyleRun {
155 len: u32,
156 color: Hsla,
157 underline: UnderlineStyle,
158}
159
160#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
161pub struct ShapedBoundary {
162 pub run_ix: usize,
163 pub glyph_ix: usize,
164}
165
166impl Line {
167 pub fn new(layout: Arc<LineLayout>, runs: &[(usize, RunStyle)]) -> Self {
168 let mut style_runs = SmallVec::new();
169 for (len, style) in runs {
170 style_runs.push(StyleRun {
171 len: *len as u32,
172 color: style.color,
173 underline: style.underline.clone().unwrap_or_default(),
174 });
175 }
176 Self { layout, style_runs }
177 }
178
179 pub fn runs(&self) -> &[Run] {
180 &self.layout.runs
181 }
182
183 pub fn width(&self) -> Pixels {
184 self.layout.width
185 }
186
187 pub fn font_size(&self) -> Pixels {
188 self.layout.font_size
189 }
190
191 pub fn x_for_index(&self, index: usize) -> Pixels {
192 for run in &self.layout.runs {
193 for glyph in &run.glyphs {
194 if glyph.index >= index {
195 return glyph.position.x;
196 }
197 }
198 }
199 self.layout.width
200 }
201
202 pub fn font_for_index(&self, index: usize) -> Option<FontId> {
203 for run in &self.layout.runs {
204 for glyph in &run.glyphs {
205 if glyph.index >= index {
206 return Some(run.font_id);
207 }
208 }
209 }
210
211 None
212 }
213
214 pub fn len(&self) -> usize {
215 self.layout.len
216 }
217
218 pub fn is_empty(&self) -> bool {
219 self.layout.len == 0
220 }
221
222 pub fn index_for_x(&self, x: Pixels) -> Option<usize> {
223 if x >= self.layout.width {
224 None
225 } else {
226 for run in self.layout.runs.iter().rev() {
227 for glyph in run.glyphs.iter().rev() {
228 if glyph.position.x <= x {
229 return Some(glyph.index);
230 }
231 }
232 }
233 Some(0)
234 }
235 }
236
237 // todo!
238 pub fn paint(
239 &self,
240 origin: Point<Pixels>,
241 visible_bounds: Bounds<Pixels>,
242 line_height: Pixels,
243 cx: &mut WindowContext,
244 ) -> Result<()> {
245 let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
246 let baseline_offset = point(px(0.), padding_top + self.layout.ascent);
247
248 let mut style_runs = self.style_runs.iter();
249 let mut run_end = 0;
250 let mut color = black();
251 let mut underline = None;
252
253 for run in &self.layout.runs {
254 cx.text_system().with_font(run.font_id, |system, font| {
255 let max_glyph_width = system.bounding_box(font, self.layout.font_size)?.size.width;
256
257 for glyph in &run.glyphs {
258 let glyph_origin = origin + baseline_offset + glyph.position;
259 if glyph_origin.x > visible_bounds.upper_right().x {
260 break;
261 }
262
263 let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
264 if glyph.index >= run_end {
265 if let Some(style_run) = style_runs.next() {
266 if let Some((_, underline_style)) = &mut underline {
267 if style_run.underline != *underline_style {
268 finished_underline = underline.take();
269 }
270 }
271 if style_run.underline.thickness > px(0.) {
272 underline.get_or_insert((
273 point(
274 glyph_origin.x,
275 origin.y
276 + baseline_offset.y
277 + (self.layout.descent * 0.618),
278 ),
279 UnderlineStyle {
280 color: style_run.underline.color,
281 thickness: style_run.underline.thickness,
282 squiggly: style_run.underline.squiggly,
283 },
284 ));
285 }
286
287 run_end += style_run.len as usize;
288 color = style_run.color;
289 } else {
290 run_end = self.layout.len;
291 finished_underline = underline.take();
292 }
293 }
294
295 if glyph_origin.x + max_glyph_width < visible_bounds.origin.x {
296 continue;
297 }
298
299 if let Some((_underline_origin, _underline_style)) = finished_underline {
300 // cx.scene().insert(Underline {
301 // origin: underline_origin,
302 // width: glyph_origin.x - underline_origin.x,
303 // thickness: underline_style.thickness.into(),
304 // color: underline_style.color.unwrap(),
305 // squiggly: underline_style.squiggly,
306 // });
307 }
308
309 // if glyph.is_emoji {
310 // cx.scene().push_image_glyph(scene::ImageGlyph {
311 // font_id: run.font_id,
312 // font_size: self.layout.font_size,
313 // id: glyph.id,
314 // origin: glyph_origin,
315 // });
316 // } else {
317 // cx.scene().push_glyph(scene::Glyph {
318 // font_id: run.font_id,
319 // font_size: self.layout.font_size,
320 // id: glyph.id,
321 // origin: glyph_origin,
322 // color,
323 // });
324 // }
325 }
326
327 anyhow::Ok(())
328 })??;
329 }
330
331 if let Some((_underline_start, _underline_style)) = underline.take() {
332 let _line_end_x = origin.x + self.layout.width;
333 // cx.scene().push_underline(Underline {
334 // origin: underline_start,
335 // width: line_end_x - underline_start.x,
336 // color: underline_style.color,
337 // thickness: underline_style.thickness.into(),
338 // squiggly: underline_style.squiggly,
339 // });
340 }
341
342 Ok(())
343 }
344
345 pub fn paint_wrapped(
346 &self,
347 origin: Point<Pixels>,
348 _visible_bounds: Bounds<Pixels>,
349 line_height: Pixels,
350 boundaries: &[ShapedBoundary],
351 cx: &mut WindowContext,
352 ) -> Result<()> {
353 let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.;
354 let baseline_offset = point(px(0.), padding_top + self.layout.ascent);
355
356 let mut boundaries = boundaries.into_iter().peekable();
357 let mut color_runs = self.style_runs.iter();
358 let mut style_run_end = 0;
359 let mut _color = black(); // todo!
360 let mut underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
361
362 let mut glyph_origin = origin;
363 let mut prev_position = px(0.);
364 for (run_ix, run) in self.layout.runs.iter().enumerate() {
365 for (glyph_ix, glyph) in run.glyphs.iter().enumerate() {
366 glyph_origin.x += glyph.position.x - prev_position;
367
368 if boundaries
369 .peek()
370 .map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix)
371 {
372 boundaries.next();
373 if let Some((_underline_origin, _underline_style)) = underline.take() {
374 // cx.scene().push_underline(Underline {
375 // origin: underline_origin,
376 // width: glyph_origin.x - underline_origin.x,
377 // thickness: underline_style.thickness.into(),
378 // color: underline_style.color.unwrap(),
379 // squiggly: underline_style.squiggly,
380 // });
381 }
382
383 glyph_origin = point(origin.x, glyph_origin.y + line_height);
384 }
385 prev_position = glyph.position.x;
386
387 let mut finished_underline = None;
388 if glyph.index >= style_run_end {
389 if let Some(style_run) = color_runs.next() {
390 style_run_end += style_run.len as usize;
391 _color = style_run.color;
392 if let Some((_, underline_style)) = &mut underline {
393 if style_run.underline != *underline_style {
394 finished_underline = underline.take();
395 }
396 }
397 if style_run.underline.thickness > px(0.) {
398 underline.get_or_insert((
399 glyph_origin
400 + point(
401 px(0.),
402 baseline_offset.y + (self.layout.descent * 0.618),
403 ),
404 UnderlineStyle {
405 color: Some(
406 style_run.underline.color.unwrap_or(style_run.color),
407 ),
408 thickness: style_run.underline.thickness,
409 squiggly: style_run.underline.squiggly,
410 },
411 ));
412 }
413 } else {
414 style_run_end = self.layout.len;
415 _color = black();
416 finished_underline = underline.take();
417 }
418 }
419
420 if let Some((_underline_origin, _underline_style)) = finished_underline {
421 // cx.scene().push_underline(Underline {
422 // origin: underline_origin,
423 // width: glyph_origin.x - underline_origin.x,
424 // thickness: underline_style.thickness.into(),
425 // color: underline_style.color.unwrap(),
426 // squiggly: underline_style.squiggly,
427 // });
428 }
429
430 cx.text_system().with_font(run.font_id, |system, font| {
431 let _glyph_bounds = Bounds {
432 origin: glyph_origin,
433 size: system.bounding_box(font, self.layout.font_size)?.size,
434 };
435 // if glyph_bounds.intersects(visible_bounds) {
436 // if glyph.is_emoji {
437 // cx.scene().push_image_glyph(scene::ImageGlyph {
438 // font_id: run.font_id,
439 // font_size: self.layout.font_size,
440 // id: glyph.id,
441 // origin: glyph_bounds.origin() + baseline_offset,
442 // });
443 // } else {
444 // cx.scene().push_glyph(scene::Glyph {
445 // font_id: run.font_id,
446 // font_size: self.layout.font_size,
447 // id: glyph.id,
448 // origin: glyph_bounds.origin() + baseline_offset,
449 // color,
450 // });
451 // }
452 // }
453 anyhow::Ok(())
454 })??;
455 }
456 }
457
458 if let Some((_underline_origin, _underline_style)) = underline.take() {
459 // let line_end_x = glyph_origin.x + self.layout.width - prev_position;
460 // cx.scene().push_underline(Underline {
461 // origin: underline_origin,
462 // width: line_end_x - underline_origin.x,
463 // thickness: underline_style.thickness.into(),
464 // color: underline_style.color,
465 // squiggly: underline_style.squiggly,
466 // });
467 }
468
469 Ok(())
470 }
471}
472
473impl Run {
474 pub fn glyphs(&self) -> &[Glyph] {
475 &self.glyphs
476 }
477}