use super::super::path::{Path, PathCmd};
use super::super::raster;
use super::SwRenderer;
use crate::types::{Color, Fixed, Point, Rect};
impl SwRenderer<'_> {
pub(super) fn scale_path(&self, path: &Path) -> Path {
let s = self.viewport.scale();
let cmds = path
.cmds
.iter()
.map(|c| match c {
PathCmd::MoveTo(p) => PathCmd::MoveTo(Point {
x: p.x * s,
y: p.y * s,
}),
PathCmd::LineTo(p) => PathCmd::LineTo(Point {
x: p.x * s,
y: p.y * s,
}),
PathCmd::QuadTo { ctrl, end } => PathCmd::QuadTo {
ctrl: Point {
x: ctrl.x * s,
y: ctrl.y * s,
},
end: Point {
x: end.x * s,
y: end.y * s,
},
},
PathCmd::CubicTo { ctrl1, ctrl2, end } => PathCmd::CubicTo {
ctrl1: Point {
x: ctrl1.x * s,
y: ctrl1.y * s,
},
ctrl2: Point {
x: ctrl2.x * s,
y: ctrl2.y * s,
},
end: Point {
x: end.x * s,
y: end.y * s,
},
},
PathCmd::Close => PathCmd::Close,
})
.collect();
Path { cmds }
}
pub(super) fn fill_path_inner(&mut self, path: &Path, clip: &Rect, color: &Color, opa: u8) {
if opa == 0 {
return;
}
let phys_path = self.scale_path(path);
self.fill_physical_path(&phys_path, clip, color, opa);
}
pub(super) fn stroke_path_inner(
&mut self,
path: &Path,
clip: &Rect,
width: Fixed,
color: &Color,
opa: u8,
) {
if opa == 0 || width <= Fixed::ZERO {
return;
}
let phys_path = self.scale_path(path);
let phys_width = width * self.viewport.scale();
let outline = raster::offset_polygon(&phys_path, phys_width);
self.fill_physical_path(&outline, clip, color, opa);
}
pub(super) fn fill_physical_path(
&mut self,
phys_path: &Path,
clip: &Rect,
color: &Color,
opa: u8,
) {
if opa == 0 {
return;
}
let phys_clip = self.viewport.rect_to_physical(*clip);
let segs = raster::flatten(phys_path);
if segs.is_empty() {
return;
}
let Some(bbox) = phys_path.bbox() else { return };
let screen = Rect::new(0, 0, self.target.width, self.target.height);
let Some(draw_area) = bbox
.intersect(&phys_clip)
.and_then(|r| r.intersect(&screen))
else {
return;
};
let (px_x0, px_y0, px_x1, px_y1) = draw_area.pixel_bounds();
let opa_norm = Fixed::from_int(opa as i32).map_range((0, 255), (Fixed::ZERO, Fixed::ONE));
let color_a_norm =
Fixed::from_int(color.a as i32).map_range((0, 255), (Fixed::ZERO, Fixed::ONE));
let combined_alpha = opa_norm * color_a_norm;
raster::scanline_fill(&segs, px_x0, px_y0, px_x1, px_y1, |px, py, cov| {
let final_alpha = (cov * combined_alpha).map01(255).to_int() as u8;
if final_alpha > 0 {
self.target.blend_pixel_int(px, py, color, final_alpha);
}
});
}
}