mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-24 09:57:08 -06:00
Scale layer-effect noise by coverage so it follows the glow
Fixes #7833 Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 5.5
parent
3a896a5efc
commit
8591ca1904
@@ -997,7 +997,9 @@ pub fn hash_noise(x: i64, y: i64) -> f32 {
|
||||
}
|
||||
|
||||
/// Effect noise (Photoshop's Noise slider): monochrome speckle in the coverage,
|
||||
/// applied after the contour. `amount` is 0..=1; 0 leaves the map untouched.
|
||||
/// applied after the contour. `amount` is 0..=1; 0 leaves the map untouched. The speckle is
|
||||
/// scaled by the coverage, so it fades out with the effect instead of lighting up the blur's
|
||||
/// near-zero tail out to the edge of its rectangular reach (#7833).
|
||||
fn noise(m: &mut Map, amount: f32, x0: i32, y0: i32) {
|
||||
if !amount.is_finite() || amount <= 0.0 {
|
||||
return;
|
||||
@@ -1009,7 +1011,7 @@ fn noise(m: &mut Map, amount: f32, x0: i32, y0: i32) {
|
||||
continue;
|
||||
}
|
||||
let n = hash_noise(i64::from(x0 + x as i32), i64::from(y0 + y as i32));
|
||||
*v = (*v + (n - 0.5) * 2.0 * amount).clamp(0.0, 1.0);
|
||||
*v = (*v * (1.0 + (n - 0.5) * 2.0 * amount)).clamp(0.0, 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3233,6 +3235,56 @@ mod tests {
|
||||
assert!(diff > 5.0, "glow noise changes the coverage: {diff}");
|
||||
}
|
||||
|
||||
/// Issue #7833: the speckle stays inside the glow's visible falloff instead of filling the
|
||||
/// blur's whole rectangular reach out to the map edge.
|
||||
#[test]
|
||||
fn effect_noise_stays_inside_the_visible_falloff() {
|
||||
let mut shape = Map::new(80, 80, 0.0);
|
||||
for y in 35..45 {
|
||||
for x in 35..45 {
|
||||
shape.v[y * 80 + x] = 1.0;
|
||||
}
|
||||
}
|
||||
let g = |noise: f32| Glow {
|
||||
common: photocraft_doc::FxCommon::new(BlendMode::Screen, 1.0),
|
||||
paint: FxPaint::Color(photocraft_color::Color::WHITE),
|
||||
technique: GlowTechnique::Softer,
|
||||
spread: 0.0,
|
||||
size: 10.0,
|
||||
contour: Contour::Linear,
|
||||
anti_alias: false,
|
||||
range: 0.5,
|
||||
jitter: 0.0,
|
||||
noise,
|
||||
source: GlowSource::Edge,
|
||||
};
|
||||
let s = |noise: f32| Shadow {
|
||||
common: photocraft_doc::FxCommon::new(BlendMode::Multiply, 1.0),
|
||||
color: photocraft_color::Color::BLACK,
|
||||
angle: 0.0,
|
||||
use_global_light: false,
|
||||
distance: 0.0,
|
||||
spread: 0.0,
|
||||
size: 10.0,
|
||||
contour: Contour::Linear,
|
||||
anti_alias: false,
|
||||
noise,
|
||||
knocks_out: true,
|
||||
};
|
||||
let light = GlobalLight::default();
|
||||
let pairs = [
|
||||
("glow", glow_map(&shape, &g(0.0), false, (0, 0)), glow_map(&shape, &g(0.5), false, (0, 0))),
|
||||
("shadow", shadow_map(&shape, &s(0.0), &light, false, (0, 0)), shadow_map(&shape, &s(0.5), &light, false, (0, 0))),
|
||||
];
|
||||
for (name, plain, noisy) in pairs {
|
||||
for (i, (p, n)) in plain.v.iter().zip(&noisy.v).enumerate() {
|
||||
if *p < 1.0 / 255.0 {
|
||||
assert!(*n <= 2.0 / 255.0, "{name}: pixel ({}, {}) invisible without noise: {p} -> {n}", i % 80, i / 80);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shifting_by_hostile_distances_does_not_overflow() {
|
||||
let m = square_shape(12);
|
||||
|
||||
Reference in New Issue
Block a user