Merge pull request #2740 from dotCooCoo/fix/clippy-1-95-artistic-filters

Effects: vectorcraft-effects passes clippy on Rust 1.95, the minimum toolchain
This commit is contained in:
BFlat
2026-10-18 01:54:16 +07:00
committed by GitHub
4 changed files with 91 additions and 4 deletions
+3 -3
View File
@@ -165,7 +165,7 @@ fn planes(px: &[[u8; 4]]) -> ([Vec<f32>; 3], Vec<f32>) {
/// colours keep their hue. Slivers (the blends along softened edges) take the nearest large
/// piece's colour, so the pieces meet with crisp edges. Coverage is kept.
pub(super) fn cutout(px: &mut [[u8; 4]], w: usize, h: usize, space: &PixelSpace, levels: f64, simplicity: f64, fidelity: f64) {
if ![levels, simplicity, fidelity].iter().all(|v| v.is_finite()) || !(space.px.is_finite() && space.px > 0.0) {
if !([levels, simplicity, fidelity].iter().all(|v| v.is_finite()) && space.px.is_finite() && space.px > 0.0) {
return;
}
let levels = levels.round().clamp(2.0, 8.0) as usize;
@@ -212,7 +212,7 @@ pub(super) fn cutout(px: &mut [[u8; 4]], w: usize, h: usize, space: &PixelSpace,
/// channel, with black lines drawn on the edges of any of its colour channels and of its outline. `thickness` (0..10) widens the lines and
/// `intensity` (0..10) darkens them and lets fainter edges take a line. Coverage is kept.
pub(super) fn poster_edges(px: &mut [[u8; 4]], w: usize, h: usize, space: &PixelSpace, thickness: f64, intensity: f64, posterization: f64) {
if ![thickness, intensity, posterization].iter().all(|v| v.is_finite()) || !(space.px.is_finite() && space.px > 0.0) {
if !([thickness, intensity, posterization].iter().all(|v| v.is_finite()) && space.px.is_finite() && space.px > 0.0) {
return;
}
let steps = (posterization.round().clamp(0.0, 6.0) + 1.0) as f32;
@@ -248,7 +248,7 @@ pub(super) fn glow_spread(size: f64) -> f64 {
/// above 0) or from its dark areas (below 0), spread as far as |`size`| (0..24) says and as
/// strong as `brightness` (0..50). Coverage is kept.
pub(super) fn neon_glow(px: &mut [[u8; 4]], w: usize, h: usize, space: &PixelSpace, size: f64, brightness: f64, colour: [f32; 3]) {
if ![size, brightness].iter().all(|v| v.is_finite()) || !(space.px.is_finite() && space.px > 0.0) {
if !([size, brightness].iter().all(|v| v.is_finite()) && space.px.is_finite() && space.px > 0.0) {
return;
}
let strength = (brightness / 50.0).clamp(0.0, 1.0) as f32;
+43
View File
@@ -2133,6 +2133,49 @@ fn artistic_params_take_defaults_and_stay_in_range() {
}
}
#[test]
fn artistic_filters_leave_invalid_coordinate_spaces_alone() {
let (w, h) = (16, 12);
let src = ramp(w, h);
let run = |f: &PixelFx, s: &PixelSpace| {
let mut out = src.clone();
f.apply(&mut out, w, h, s);
out
};
let colour = [0.3, 0.65, 1.0];
let filters = [
PixelFx::Cutout { levels: 4.0, simplicity: 4.0, fidelity: 2.0 },
PixelFx::NeonGlow { size: 5.0, brightness: 15.0, colour },
PixelFx::PosterEdges { thickness: 2.0, intensity: 1.0, posterization: 2.0 },
];
for f in &filters {
assert_ne!(run(f, &space(w, h)), src, "{f:?} changes the art in a valid space");
}
let mut changed = Vec::new();
for f in &filters {
for px in [0.0, -1.0, f64::NAN, f64::INFINITY] {
if run(f, &PixelSpace { px, ..space(w, h) }) != src {
changed.push(format!("{f:?} at px {px}"));
}
}
}
for f in [
PixelFx::Cutout { levels: f64::NAN, simplicity: 4.0, fidelity: 2.0 },
PixelFx::Cutout { levels: 4.0, simplicity: f64::INFINITY, fidelity: 2.0 },
PixelFx::Cutout { levels: 4.0, simplicity: 4.0, fidelity: f64::NEG_INFINITY },
PixelFx::NeonGlow { size: f64::NAN, brightness: 15.0, colour },
PixelFx::NeonGlow { size: 5.0, brightness: f64::INFINITY, colour },
PixelFx::PosterEdges { thickness: f64::NAN, intensity: 1.0, posterization: 2.0 },
PixelFx::PosterEdges { thickness: 2.0, intensity: f64::INFINITY, posterization: 2.0 },
PixelFx::PosterEdges { thickness: 2.0, intensity: 1.0, posterization: f64::NEG_INFINITY },
] {
if run(&f, &space(w, h)) != src {
changed.push(format!("{f:?}"));
}
}
assert!(changed.is_empty(), "directly constructed non-finite parameters and invalid spaces are harmless: {changed:?}");
}
#[test]
fn cutout_paints_a_few_flat_colours_and_keeps_coverage() {
let (w, h) = (64, 24);
+1 -1
View File
@@ -197,7 +197,7 @@ impl Rasterize {
let (px, rest) = data.as_chunks_mut::<4>();
let (src, src_rest) = image.as_chunks::<4>();
let fits = |n: usize, w: usize, h: usize, rest: usize| w > 0 && h > 0 && rest == 0 && w.checked_mul(h) == Some(n);
if !fits(px.len(), w, h, rest.len()) || !fits(src.len(), cols, rows, src_rest.len()) || !(rect.width() > 0.0 && rect.height() > 0.0) {
if !(fits(px.len(), w, h, rest.len()) && fits(src.len(), cols, rows, src_rest.len()) && rect.width() > 0.0 && rect.height() > 0.0) {
return;
}
let (sx, sy) = (cols as f64 / rect.width(), rows as f64 / rect.height());
+44
View File
@@ -834,6 +834,50 @@ fn drop_shadow_darkness_replaces_its_color_when_chosen() {
assert_eq!(darkness(json!({})), None);
}
#[test]
fn rasterize_compose_leaves_the_raster_alone_when_the_sizes_do_not_fit() {
let r = match raster_effects(&[fx(RASTERIZE, json!({}))]).as_slice() {
[RasterFx::Rasterize(r)] => *r,
other => panic!("{other:?}"),
};
let space = PixelSpace {
to_doc: Affine::IDENTITY,
px: 1.0,
center: Point::new(1.0, 1.0),
channels: pixel::Channels::Rgb,
line: 1.0,
extent: std::f64::consts::SQRT_2,
};
let compose = |len: usize, w: usize, h: usize, image: &[u8], cols: usize, rows: usize, rect: Rect| {
let mut data = vec![7; len];
r.compose(&mut data, w, h, &space, image, cols, rows, rect);
data
};
// A 2 × 2 red image over a 2 × 2 raster of one-point pixels: each pixel takes the image's color.
let red = [255, 0, 0, 255].repeat(4);
let mut long = red.clone();
long.extend([255, 0]);
let (img, long, area) = (red.as_slice(), long.as_slice(), Rect::new(0.0, 0.0, 2.0, 2.0));
assert_eq!(compose(16, 2, 2, img, 2, 2, area), red);
let cases = [
("w × h is not the raster's pixel count", 16, 1, 2, img, 2, 2, area),
("the raster's length is not a multiple of 4", 18, 2, 2, img, 2, 2, area),
("cols × rows is not the image's pixel count", 16, 2, 2, img, 1, 2, area),
("the image's length is not a multiple of 4", 16, 2, 2, long, 2, 2, area),
("the image has no width", 16, 2, 2, img, 2, 2, Rect::new(0.0, 0.0, 0.0, 2.0)),
("the image has no height", 16, 2, 2, img, 2, 2, Rect::new(0.0, 0.0, 2.0, 0.0)),
("the image has a negative width", 16, 2, 2, img, 2, 2, Rect::new(2.0, 0.0, 0.0, 2.0)),
("the image has a NaN width", 16, 2, 2, img, 2, 2, Rect::new(0.0, 0.0, f64::NAN, 2.0)),
("the image has a NaN height", 16, 2, 2, img, 2, 2, Rect::new(0.0, 0.0, 2.0, f64::NAN)),
];
let failures: Vec<_> = cases
.iter()
.filter(|&&(_, len, w, h, image, cols, rows, rect)| compose(len, w, h, image, cols, rows, rect) != vec![7; len])
.map(|c| c.0)
.collect();
assert!(failures.is_empty(), "{failures:?}");
}
#[test]
fn blend_mode_names() {
use vectorcraft_doc::color::BlendMode;