mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 20:27:05 -06:00
Glowing Edges: transparency beyond the right and bottom edges; rustfmt; as_chunks in tests
The edge filter read `y * w + x` for x = w (the next row's first pixel) and y = h at the raster's right and bottom edges; it now reads transparency there, as the module documents. A test checks a solid square glows alike on every side. Formatted with rustfmt, and the test uses as_chunks for clippy 1.99. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
11aaf816d0
commit
800baacbba
@@ -10,10 +10,15 @@ pub(super) fn glow(px: &mut [[u8; 4]], w: usize, h: usize, width: f64, brightnes
|
||||
}
|
||||
let src = px.to_vec();
|
||||
let signal = |x: isize, y: isize| -> f32 {
|
||||
if x < 0 || y < 0 { return 0.0; }
|
||||
let Some(p) = src.get(y as usize * w + x as usize) else { return 0.0 };
|
||||
// Beyond the raster's edges is transparency (not the next row's first pixel).
|
||||
let (Ok(x), Ok(y)) = (usize::try_from(x), usize::try_from(y)) else { return 0.0 };
|
||||
if x >= w || y >= h {
|
||||
return 0.0;
|
||||
}
|
||||
let Some(p) = src.get(y * w + x) else { return 0.0 };
|
||||
let alpha = f32::from(p[3]) / 255.0;
|
||||
let straight = if alpha > 0.0 { [f32::from(p[0]) / 255.0 / alpha, f32::from(p[1]) / 255.0 / alpha, f32::from(p[2]) / 255.0 / alpha] } else { [0.0; 3] };
|
||||
let straight =
|
||||
if alpha > 0.0 { [f32::from(p[0]) / 255.0 / alpha, f32::from(p[1]) / 255.0 / alpha, f32::from(p[2]) / 255.0 / alpha] } else { [0.0; 3] };
|
||||
(0.2126 * straight[0] + 0.7152 * straight[1] + 0.0722 * straight[2]) * alpha
|
||||
};
|
||||
let mut edges = vec![0.0f32; px.len()];
|
||||
@@ -21,18 +26,39 @@ pub(super) fn glow(px: &mut [[u8; 4]], w: usize, h: usize, width: f64, brightnes
|
||||
for x in 0..w {
|
||||
let x = x as isize;
|
||||
let y = y as isize;
|
||||
let gx = -signal(x - 1, y - 1) + signal(x + 1, y - 1) - 2.0 * signal(x - 1, y) + 2.0 * signal(x + 1, y) - signal(x - 1, y + 1) + signal(x + 1, y + 1);
|
||||
let gy = -signal(x - 1, y - 1) - 2.0 * signal(x, y - 1) - signal(x + 1, y - 1) + signal(x - 1, y + 1) + 2.0 * signal(x, y + 1) + signal(x + 1, y + 1);
|
||||
if let Some(edge) = edges.get_mut(y as usize * w + x as usize) { *edge = gx.hypot(gy).clamp(0.0, 1.0); }
|
||||
let gx = -signal(x - 1, y - 1) + signal(x + 1, y - 1) - 2.0 * signal(x - 1, y) + 2.0 * signal(x + 1, y) - signal(x - 1, y + 1)
|
||||
+ signal(x + 1, y + 1);
|
||||
let gy = -signal(x - 1, y - 1) - 2.0 * signal(x, y - 1) - signal(x + 1, y - 1)
|
||||
+ signal(x - 1, y + 1)
|
||||
+ 2.0 * signal(x, y + 1)
|
||||
+ signal(x + 1, y + 1);
|
||||
if let Some(edge) = edges.get_mut(y as usize * w + x as usize) {
|
||||
*edge = gx.hypot(gy).clamp(0.0, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
blur_plane(&mut edges, w, h, smoothness.max(0.0));
|
||||
if width > 0.5 { blur_plane(&mut edges, w, h, width * 0.5); }
|
||||
if width > 0.5 {
|
||||
blur_plane(&mut edges, w, h, width * 0.5);
|
||||
}
|
||||
for (out, (original, edge)) in px.iter_mut().zip(src.iter().zip(edges)) {
|
||||
let alpha = f32::from(original[3]) / 255.0;
|
||||
let glow = (edge * brightness as f32 / 6.0).clamp(0.0, 1.0);
|
||||
let straight = if original[3] > 0 { [f32::from(original[0]) / 255.0 / alpha.max(1e-6), f32::from(original[1]) / 255.0 / alpha.max(1e-6), f32::from(original[2]) / 255.0 / alpha.max(1e-6)] } else { [1.0, 1.0, 1.0] };
|
||||
let straight = if original[3] > 0 {
|
||||
[
|
||||
f32::from(original[0]) / 255.0 / alpha.max(1e-6),
|
||||
f32::from(original[1]) / 255.0 / alpha.max(1e-6),
|
||||
f32::from(original[2]) / 255.0 / alpha.max(1e-6),
|
||||
]
|
||||
} else {
|
||||
[1.0, 1.0, 1.0]
|
||||
};
|
||||
let a = alpha.max(glow);
|
||||
*out = [(straight[0] * glow * a * 255.0).round().clamp(0.0, 255.0) as u8, (straight[1] * glow * a * 255.0).round().clamp(0.0, 255.0) as u8, (straight[2] * glow * a * 255.0).round().clamp(0.0, 255.0) as u8, (a * 255.0).round() as u8];
|
||||
*out = [
|
||||
(straight[0] * glow * a * 255.0).round().clamp(0.0, 255.0) as u8,
|
||||
(straight[1] * glow * a * 255.0).round().clamp(0.0, 255.0) as u8,
|
||||
(straight[2] * glow * a * 255.0).round().clamp(0.0, 255.0) as u8,
|
||||
(a * 255.0).round() as u8,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,19 @@ fn glowing_edges_draw_a_bright_outline_and_preserve_premultiplication() {
|
||||
let mut out = src.clone();
|
||||
fx("stylize.glowingEdges", json!({"edgeWidth": 1, "smoothness": 1, "edgeBrightness": 20})).apply(&mut out, w, h, &space(w, h));
|
||||
assert!(at(&out, w, 3, 4)[0] > 0 || at(&out, w, 4, 4)[0] > 0);
|
||||
assert!(out.chunks_exact(4).all(|p| p[0] <= p[3] && p[1] <= p[3] && p[2] <= p[3]));
|
||||
assert!(out.as_chunks::<4>().0.iter().all(|p| p[0] <= p[3] && p[1] <= p[3] && p[2] <= p[3]));
|
||||
}
|
||||
|
||||
/// Glowing Edges sees transparency beyond every side of the raster: a solid square glows alike at
|
||||
/// its left and right edges (the right one doesn't read the next row's first pixel).
|
||||
#[test]
|
||||
fn glowing_edges_glow_alike_on_every_side() {
|
||||
let (w, h) = (9, 9);
|
||||
let mut out = image(w, h, |_, _| [180, 180, 180, 255]);
|
||||
fx("stylize.glowingEdges", json!({"edgeWidth": 1, "smoothness": 1, "edgeBrightness": 20})).apply(&mut out, w, h, &space(w, h));
|
||||
let (left, right, top, bottom) = (at(&out, w, 0, 4), at(&out, w, 8, 4), at(&out, w, 4, 0), at(&out, w, 4, 8));
|
||||
assert!(left[0] > 0 && left == right && top == bottom, "{left:?} {right:?} {top:?} {bottom:?}");
|
||||
assert!(at(&out, w, 4, 4)[0] < left[0], "the inside stays darker than the edges");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user