i_slint_renderer_software/
path.rs1use super::PhysicalRect;
7use super::draw_functions::{PremultipliedRgbaColor, TargetPixel};
8use alloc::vec;
9use alloc::vec::Vec;
10use zeno::{Cap, Fill, Join, Mask, Stroke, Style};
11
12pub use zeno::Command;
13
14pub fn convert_path_data_to_zeno(
16 path_data: i_slint_core::graphics::PathDataIterator,
17 rotation: crate::RotationInfo,
18 scale_factor: i_slint_core::lengths::ScaleFactor,
19 offset: euclid::Vector2D<f32, i_slint_core::lengths::PhysicalPx>,
20) -> Vec<Command> {
21 use crate::Transform as _;
22 use i_slint_core::lengths::LogicalPoint;
23 use lyon_path::Event;
24 let mut commands = Vec::new();
25
26 let convert_point = |p| {
27 let p = (LogicalPoint::from_untyped(p) * scale_factor + offset).transformed(rotation);
28 zeno::Point::new(p.x, p.y)
29 };
30
31 for event in path_data.iter() {
32 match event {
33 Event::Begin { at } => {
34 commands.push(Command::MoveTo(convert_point(at)));
35 }
36 Event::Line { to, .. } => {
37 commands.push(Command::LineTo(convert_point(to)));
38 }
39 Event::Quadratic { ctrl, to, .. } => {
40 commands.push(Command::QuadTo(convert_point(ctrl), convert_point(to)));
41 }
42 Event::Cubic { ctrl1, ctrl2, to, .. } => {
43 commands.push(Command::CurveTo(
44 convert_point(ctrl1),
45 convert_point(ctrl2),
46 convert_point(to),
47 ));
48 }
49 Event::End { close, .. } => {
50 if close {
51 commands.push(Command::Close);
52 }
53 }
54 }
55 }
56
57 commands
58}
59
60fn render_path_with_style<T: TargetPixel>(
62 commands: &[Command],
63 path_geometry: &PhysicalRect,
64 clip_geometry: &PhysicalRect,
65 color: PremultipliedRgbaColor,
66 style: zeno::Style,
67 buffer: &mut impl crate::target_pixel_buffer::TargetPixelBuffer<TargetPixel = T>,
68) {
69 let path_width = path_geometry.size.width as usize;
71 let path_height = path_geometry.size.height as usize;
72
73 if path_width == 0 || path_height == 0 {
74 return;
75 }
76
77 let mut mask_buffer = vec![0u8; path_width * path_height];
79
80 Mask::new(commands)
82 .size(path_width as u32, path_height as u32)
83 .style(style)
84 .render_into(&mut mask_buffer, None);
85
86 let clip_x_start = clip_geometry.origin.x.max(0) as usize;
89 let clip_y_start = clip_geometry.origin.y.max(0) as usize;
90 let clip_x_end = (clip_geometry.max_x().max(0) as usize).min(buffer.line_slice(0).len());
91 let clip_y_end = (clip_geometry.max_y().max(0) as usize).min(buffer.num_lines());
92
93 let path_x_start = path_geometry.origin.x as isize;
94 let path_y_start = path_geometry.origin.y as isize;
95
96 for screen_y in clip_y_start..clip_y_end {
98 let line = buffer.line_slice(screen_y);
99
100 let mask_y = screen_y as isize - path_y_start;
102 if mask_y < 0 || mask_y >= path_height as isize {
103 continue;
104 }
105
106 let line_slice = &mut line[clip_x_start..clip_x_end];
108 for (i, pixel) in line_slice.iter_mut().enumerate() {
109 let screen_x = clip_x_start + i;
110
111 let mask_x = screen_x as isize - path_x_start;
113 if mask_x < 0 || mask_x >= path_width as isize {
114 continue;
115 }
116
117 let mask_idx = (mask_y as usize) * path_width + (mask_x as usize);
118 let coverage = mask_buffer[mask_idx];
119
120 if coverage > 0 {
121 let coverage_factor = coverage as u16;
123 let alpha_color = PremultipliedRgbaColor {
124 red: ((color.red as u16 * coverage_factor) / 255) as u8,
125 green: ((color.green as u16 * coverage_factor) / 255) as u8,
126 blue: ((color.blue as u16 * coverage_factor) / 255) as u8,
127 alpha: ((color.alpha as u16 * coverage_factor) / 255) as u8,
128 };
129 T::blend(pixel, alpha_color);
130 }
131 }
132 }
133}
134
135pub fn render_filled_path<T: TargetPixel>(
143 commands: &[Command],
144 path_geometry: &PhysicalRect,
145 clip_geometry: &PhysicalRect,
146 color: PremultipliedRgbaColor,
147 buffer: &mut impl crate::target_pixel_buffer::TargetPixelBuffer<TargetPixel = T>,
148) {
149 render_path_with_style(
150 commands,
151 path_geometry,
152 clip_geometry,
153 color,
154 zeno::Style::Fill(Fill::NonZero),
155 buffer,
156 );
157}
158
159pub fn render_stroked_path<T: TargetPixel>(
168 commands: &[Command],
169 path_geometry: &PhysicalRect,
170 clip_geometry: &PhysicalRect,
171 color: PremultipliedRgbaColor,
172 stroke_width: f32,
173 stroke_line_cap: i_slint_core::items::LineCap,
174 stroke_line_join: i_slint_core::items::LineJoin,
175 stroke_miter_limit: f32,
176 buffer: &mut impl crate::target_pixel_buffer::TargetPixelBuffer<TargetPixel = T>,
177) {
178 let mut stroke = Stroke::new(stroke_width);
179 stroke
180 .cap(match stroke_line_cap {
181 i_slint_core::items::LineCap::Round => Cap::Round,
182 i_slint_core::items::LineCap::Square => Cap::Square,
183 i_slint_core::items::LineCap::Butt | _ => Cap::Butt,
184 })
185 .join(match stroke_line_join {
186 i_slint_core::items::LineJoin::Round => Join::Round,
187 i_slint_core::items::LineJoin::Bevel => Join::Bevel,
188 i_slint_core::items::LineJoin::Miter | _ => Join::Miter,
189 })
190 .miter_limit(stroke_miter_limit);
191 let style = Style::Stroke(stroke);
192 render_path_with_style(commands, path_geometry, clip_geometry, color, style, buffer);
193}