pub mod axial;
pub mod function;
pub mod gouraud;
pub mod radial;
use crate::bitmap::Bitmap;
use crate::clip::Clip;
use crate::fill;
use crate::path::Path;
use crate::pipe::{Pattern, PipeSrc, PipeState};
use color::Pixel;
use color::convert::lerp_u8;
#[inline]
pub(super) fn lerp_color(a: [u8; 3], b: [u8; 3], frac: u32, out: &mut [u8]) {
debug_assert_eq!(out.len(), 3, "lerp_color: out must be exactly 3 bytes");
out[0] = lerp_u8(a[0], b[0], frac);
out[1] = lerp_u8(a[1], b[1], frac);
out[2] = lerp_u8(a[2], b[2], frac);
}
#[expect(
clippy::too_many_arguments,
reason = "mirrors shadedFill signature: bitmap+clip+path+pipe+pattern+matrix+flatness+aa+eo"
)]
pub fn shaded_fill<P: Pixel>(
bitmap: &mut Bitmap<P>,
clip: &Clip,
path: &Path,
pipe: &PipeState<'_>,
pattern: &dyn Pattern,
matrix: &[f64; 6],
flatness: f64,
vector_antialias: bool,
eo: bool,
) {
let src = PipeSrc::Pattern(pattern);
if eo {
fill::eo_fill::<P>(
bitmap,
clip,
path,
pipe,
&src,
matrix,
flatness,
vector_antialias,
);
} else {
fill::fill::<P>(
bitmap,
clip,
path,
pipe,
&src,
matrix,
flatness,
vector_antialias,
);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bitmap::Bitmap;
use crate::clip::Clip;
use crate::path::PathBuilder;
use crate::shading::axial::AxialPattern;
use crate::testutil::{identity_matrix, rect_path, simple_pipe};
use color::Rgb8;
#[test]
fn shaded_fill_axial_paints_interior() {
let mut bmp: Bitmap<Rgb8> = Bitmap::new(8, 8, 4, false);
let clip = Clip::new(0.0, 0.0, 7.999, 7.999, false);
let pipe = simple_pipe();
let path = rect_path(1.0, 1.0, 6.0, 6.0);
let pattern = AxialPattern::new(
[0u8, 0, 0],
[255u8, 255, 255],
1.0,
3.5,
6.0,
3.5,
0.0,
1.0,
false,
false,
);
shaded_fill::<Rgb8>(
&mut bmp,
&clip,
&path,
&pipe,
&pattern,
&identity_matrix(),
1.0,
false,
false,
);
let r3 = bmp.row(3);
assert!(r3[1].r < 60, "x=1 should be near-black (got {})", r3[1].r);
assert!(r3[5].r > 180, "x=5 should be near-white (got {})", r3[5].r);
}
#[test]
fn shaded_fill_eo_and_nonzero_both_work() {
let mut bmp_nz: Bitmap<Rgb8> = Bitmap::new(8, 8, 4, false);
let mut bmp_eo: Bitmap<Rgb8> = Bitmap::new(8, 8, 4, false);
let clip = Clip::new(0.0, 0.0, 7.999, 7.999, false);
let pipe = simple_pipe();
let path = rect_path(1.0, 1.0, 6.0, 6.0);
let pattern = AxialPattern::new(
[200u8, 0, 0],
[200u8, 0, 0],
0.0,
0.0,
1.0,
0.0,
0.0,
1.0,
true,
true,
);
shaded_fill::<Rgb8>(
&mut bmp_nz,
&clip,
&path,
&pipe,
&pattern,
&identity_matrix(),
1.0,
false,
false,
);
shaded_fill::<Rgb8>(
&mut bmp_eo,
&clip,
&path,
&pipe,
&pattern,
&identity_matrix(),
1.0,
false,
true,
);
assert_eq!(
bmp_nz.row(3)[3].r,
bmp_eo.row(3)[3].r,
"non-zero and eo must agree for a simple convex path"
);
}
#[test]
fn shaded_fill_empty_path_is_noop() {
let mut bmp: Bitmap<Rgb8> = Bitmap::new(8, 8, 4, false);
let clip = Clip::new(0.0, 0.0, 7.999, 7.999, false);
let pipe = simple_pipe();
let path = PathBuilder::new().build();
let pattern = AxialPattern::new(
[255u8, 0, 0],
[0u8, 255, 0],
0.0,
0.0,
8.0,
0.0,
0.0,
1.0,
false,
false,
);
shaded_fill::<Rgb8>(
&mut bmp,
&clip,
&path,
&pipe,
&pattern,
&identity_matrix(),
1.0,
false,
false,
);
assert_eq!(bmp.row(4)[4].r, 0, "empty path must not paint");
}
}