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