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 glyphs = &run.glyphs[span.glyph_range.clone()];
34 if glyphs.is_empty() {
35 return;
36 }
37
38 let deco = &span.data;
39 let td = &deco.text_decoration;
40 let font_size = span.font_size;
41
42 let mut x_min = f32::INFINITY;
45 let mut x_max = f32::NEG_INFINITY;
46 for g in glyphs {
47 x_min = x_min.min(g.x);
48 x_max = x_max.max(g.x + g.w);
49 }
50 let width = x_max - x_min;
51 if width <= 0.0 {
52 return;
53 }
54 let w = width as u32;
55 if w == 0 {
56 return;
57 }
58 let x_start = x_min;
59
60 match td.underline {
62 UnderlineStyle::None => {}
63 UnderlineStyle::Single => {
64 let color = td
65 .underline_color_opt
66 .or(span.color_opt)
67 .unwrap_or(default_color);
68 let thickness = (deco.underline_metrics.thickness * font_size)
69 .max(1.0)
70 .ceil();
71 let y = run.line_y - deco.underline_metrics.offset * font_size;
72 renderer.rectangle(x_start as i32, y as i32, w, thickness as u32, color);
73 }
74 UnderlineStyle::Double => {
75 let color = td
76 .underline_color_opt
77 .or(span.color_opt)
78 .unwrap_or(default_color);
79 let thickness = (deco.underline_metrics.thickness * font_size)
80 .max(1.0)
81 .ceil();
82 let gap = thickness;
83 let y = run.line_y - deco.underline_metrics.offset * font_size;
84 renderer.rectangle(x_start as i32, y as i32, w, thickness as u32, color);
85 renderer.rectangle(
86 x_start as i32,
87 (y + thickness + gap) as i32,
88 w,
89 thickness as u32,
90 color,
91 );
92 }
93 }
94
95 if td.strikethrough {
97 let color = td
98 .strikethrough_color_opt
99 .or(span.color_opt)
100 .unwrap_or(default_color);
101 let thickness = (deco.strikethrough_metrics.thickness * font_size)
102 .max(1.0)
103 .ceil();
104 let y = run.line_y - deco.strikethrough_metrics.offset * font_size;
105 renderer.rectangle(x_start as i32, y as i32, w, thickness as u32, color);
106 }
107
108 if td.overline {
110 let color = td
111 .overline_color_opt
112 .or(span.color_opt)
113 .unwrap_or(default_color);
114 let thickness = (deco.underline_metrics.thickness * font_size)
116 .max(1.0)
117 .ceil();
118 let y = (run.line_y - deco.ascent * font_size).max(run.line_top);
120 renderer.rectangle(x_start as i32, y as i32, w, thickness as u32, color);
121 }
122}
123
124#[cfg(feature = "swash")]
127#[derive(Debug)]
128pub struct LegacyRenderer<'a, F: FnMut(i32, i32, u32, u32, Color)> {
129 pub font_system: &'a mut FontSystem,
130 pub cache: &'a mut SwashCache,
131 pub callback: F,
132}
133
134#[cfg(feature = "swash")]
135impl<'a, F: FnMut(i32, i32, u32, u32, Color)> Renderer for LegacyRenderer<'a, F> {
136 fn rectangle(&mut self, x: i32, y: i32, w: u32, h: u32, color: Color) {
137 (self.callback)(x, y, w, h, color);
138 }
139
140 fn glyph(&mut self, physical_glyph: PhysicalGlyph, color: Color) {
141 self.cache.with_pixels(
142 self.font_system,
143 physical_glyph.cache_key,
144 color,
145 |x, y, pixel_color| {
146 (self.callback)(
147 physical_glyph.x + x,
148 physical_glyph.y + y,
149 1,
150 1,
151 pixel_color,
152 );
153 },
154 );
155 }
156}