use super::PhysicalRect;
use super::draw_functions::{PremultipliedRgbaColor, TargetPixel};
use alloc::vec;
use alloc::vec::Vec;
use zeno::{Cap, Fill, Join, Mask, Stroke, Style};
pub use zeno::Command;
pub fn convert_path_data_to_zeno(
path_data: i_slint_core::graphics::PathDataIterator,
rotation: crate::RotationInfo,
scale_factor: i_slint_core::lengths::ScaleFactor,
offset: euclid::Vector2D<f32, i_slint_core::lengths::PhysicalPx>,
) -> Vec<Command> {
use crate::Transform as _;
use i_slint_core::lengths::LogicalPoint;
use lyon_path::Event;
let mut commands = Vec::new();
let convert_point = |p| {
let p = (LogicalPoint::from_untyped(p) * scale_factor + offset).transformed(rotation);
zeno::Point::new(p.x, p.y)
};
for event in path_data.iter() {
match event {
Event::Begin { at } => {
commands.push(Command::MoveTo(convert_point(at)));
}
Event::Line { to, .. } => {
commands.push(Command::LineTo(convert_point(to)));
}
Event::Quadratic { ctrl, to, .. } => {
commands.push(Command::QuadTo(convert_point(ctrl), convert_point(to)));
}
Event::Cubic { ctrl1, ctrl2, to, .. } => {
commands.push(Command::CurveTo(
convert_point(ctrl1),
convert_point(ctrl2),
convert_point(to),
));
}
Event::End { close, .. } => {
if close {
commands.push(Command::Close);
}
}
}
}
commands
}
fn render_path_with_style<T: TargetPixel>(
commands: &[Command],
path_geometry: &PhysicalRect,
clip_geometry: &PhysicalRect,
color: PremultipliedRgbaColor,
style: zeno::Style,
buffer: &mut impl crate::target_pixel_buffer::TargetPixelBuffer<TargetPixel = T>,
) {
let path_width = path_geometry.size.width as usize;
let path_height = path_geometry.size.height as usize;
if path_width == 0 || path_height == 0 {
return;
}
let mut mask_buffer = vec![0u8; path_width * path_height];
Mask::new(commands)
.size(path_width as u32, path_height as u32)
.style(style)
.render_into(&mut mask_buffer, None);
let clip_x_start = clip_geometry.origin.x.max(0) as usize;
let clip_y_start = clip_geometry.origin.y.max(0) as usize;
let clip_x_end = (clip_geometry.max_x().max(0) as usize).min(buffer.line_slice(0).len());
let clip_y_end = (clip_geometry.max_y().max(0) as usize).min(buffer.num_lines());
let path_x_start = path_geometry.origin.x as isize;
let path_y_start = path_geometry.origin.y as isize;
for screen_y in clip_y_start..clip_y_end {
let line = buffer.line_slice(screen_y);
let mask_y = screen_y as isize - path_y_start;
if mask_y < 0 || mask_y >= path_height as isize {
continue;
}
let line_slice = &mut line[clip_x_start..clip_x_end];
for (i, pixel) in line_slice.iter_mut().enumerate() {
let screen_x = clip_x_start + i;
let mask_x = screen_x as isize - path_x_start;
if mask_x < 0 || mask_x >= path_width as isize {
continue;
}
let mask_idx = (mask_y as usize) * path_width + (mask_x as usize);
let coverage = mask_buffer[mask_idx];
if coverage > 0 {
let coverage_factor = coverage as u16;
let alpha_color = PremultipliedRgbaColor {
red: ((color.red as u16 * coverage_factor) / 255) as u8,
green: ((color.green as u16 * coverage_factor) / 255) as u8,
blue: ((color.blue as u16 * coverage_factor) / 255) as u8,
alpha: ((color.alpha as u16 * coverage_factor) / 255) as u8,
};
T::blend(pixel, alpha_color);
}
}
}
}
pub fn render_filled_path<T: TargetPixel>(
commands: &[Command],
path_geometry: &PhysicalRect,
clip_geometry: &PhysicalRect,
color: PremultipliedRgbaColor,
buffer: &mut impl crate::target_pixel_buffer::TargetPixelBuffer<TargetPixel = T>,
) {
render_path_with_style(
commands,
path_geometry,
clip_geometry,
color,
zeno::Style::Fill(Fill::NonZero),
buffer,
);
}
pub fn render_stroked_path<T: TargetPixel>(
commands: &[Command],
path_geometry: &PhysicalRect,
clip_geometry: &PhysicalRect,
color: PremultipliedRgbaColor,
stroke_width: f32,
stroke_line_cap: i_slint_core::items::LineCap,
stroke_line_join: i_slint_core::items::LineJoin,
stroke_miter_limit: f32,
buffer: &mut impl crate::target_pixel_buffer::TargetPixelBuffer<TargetPixel = T>,
) {
let mut stroke = Stroke::new(stroke_width);
stroke
.cap(match stroke_line_cap {
i_slint_core::items::LineCap::Round => Cap::Round,
i_slint_core::items::LineCap::Square => Cap::Square,
i_slint_core::items::LineCap::Butt | _ => Cap::Butt,
})
.join(match stroke_line_join {
i_slint_core::items::LineJoin::Round => Join::Round,
i_slint_core::items::LineJoin::Bevel => Join::Bevel,
i_slint_core::items::LineJoin::Miter | _ => Join::Miter,
})
.miter_limit(stroke_miter_limit);
let style = Style::Stroke(stroke);
render_path_with_style(commands, path_geometry, clip_geometry, color, style, buffer);
}