1#[cfg(not(feature = "std"))]
4use core_maths::CoreFloat;
5
6use crate::{Color, DecorationSpan, LayoutRun, PhysicalGlyph, UnderlineStyle};
7#[cfg(feature = "swash")]
8use crate::{FontSystem, SwashCache};
9
10pub trait Renderer {
12 fn rectangle(&mut self, x: i32, y: i32, w: u32, h: u32, color: Color);
14
15 fn glyph(&mut self, physical_glyph: PhysicalGlyph, color: Color);
18}
19
20pub fn render_decoration<R: Renderer>(renderer: &mut R, run: &LayoutRun, default_color: Color) {
22 for span in run.decorations {
23 draw_decoration_span(renderer, run, span, default_color);
24 }
25}
26
27fn draw_decoration_span<R: Renderer>(
28 renderer: &mut R,
29 run: &LayoutRun,
30 span: &DecorationSpan,
31 default_color: Color,
32) {
33 let deco = &span.data;
34 let td = &deco.text_decoration;
35 let font_size = span.font_size;
36
37 let x_range = span.x_range(run);
38 let w = (x_range.end - x_range.start) as u32;
39 if w == 0 {
40 return;
41 }
42 let x_start = x_range.start;
43
44 match td.underline {
46 UnderlineStyle::None => {}
47 UnderlineStyle::Single => {
48 let color = td
49 .underline_color_opt
50 .or(span.color_opt)
51 .unwrap_or(default_color);
52 let thickness = (deco.underline_metrics.thickness * font_size)
53 .max(1.0)
54 .ceil();
55 let y = run.line_y - deco.underline_metrics.offset * font_size;
56 renderer.rectangle(x_start as i32, y as i32, w, thickness as u32, color);
57 }
58 UnderlineStyle::Double => {
59 let color = td
60 .underline_color_opt
61 .or(span.color_opt)
62 .unwrap_or(default_color);
63 let thickness = (deco.underline_metrics.thickness * font_size)
64 .max(1.0)
65 .ceil();
66 let gap = thickness;
67 let y = run.line_y - deco.underline_metrics.offset * font_size;
68 renderer.rectangle(x_start as i32, y as i32, w, thickness as u32, color);
69 renderer.rectangle(
70 x_start as i32,
71 (y + thickness + gap) as i32,
72 w,
73 thickness as u32,
74 color,
75 );
76 }
77 }
78
79 if td.strikethrough {
81 let color = td
82 .strikethrough_color_opt
83 .or(span.color_opt)
84 .unwrap_or(default_color);
85 let thickness = (deco.strikethrough_metrics.thickness * font_size)
86 .max(1.0)
87 .ceil();
88 let y = run.line_y - deco.strikethrough_metrics.offset * font_size;
89 renderer.rectangle(x_start as i32, y as i32, w, thickness as u32, color);
90 }
91
92 if td.overline {
94 let color = td
95 .overline_color_opt
96 .or(span.color_opt)
97 .unwrap_or(default_color);
98 let thickness = (deco.underline_metrics.thickness * font_size)
100 .max(1.0)
101 .ceil();
102 let y = (run.line_y - deco.ascent * font_size).max(run.line_top);
104 renderer.rectangle(x_start as i32, y as i32, w, thickness as u32, color);
105 }
106}
107
108#[cfg(feature = "swash")]
111#[derive(Debug)]
112pub struct LegacyRenderer<'a, F: FnMut(i32, i32, u32, u32, Color)> {
113 pub font_system: &'a mut FontSystem,
114 pub cache: &'a mut SwashCache,
115 pub callback: F,
116}
117
118#[cfg(feature = "swash")]
119impl<'a, F: FnMut(i32, i32, u32, u32, Color)> Renderer for LegacyRenderer<'a, F> {
120 fn rectangle(&mut self, x: i32, y: i32, w: u32, h: u32, color: Color) {
121 (self.callback)(x, y, w, h, color);
122 }
123
124 fn glyph(&mut self, physical_glyph: PhysicalGlyph, color: Color) {
125 self.cache.with_pixels(
126 self.font_system,
127 physical_glyph.cache_key,
128 color,
129 |x, y, pixel_color| {
130 (self.callback)(
131 physical_glyph.x + x,
132 physical_glyph.y + y,
133 1,
134 1,
135 pixel_color,
136 );
137 },
138 );
139 }
140}