Merge pull request #1383 from marcoarnulfo/fix/1381-generators-alpha

Effects: 4-Color Gradient keeps the layer's alpha with Blending Mode None (refs #1381)
This commit is contained in:
BFlat
2026-10-16 18:08:08 +07:00
committed by GitHub
2 changed files with 27 additions and 3 deletions
+23 -1
View File
@@ -138,7 +138,10 @@ fn four_color(ctx: &EffectCtx, mut b: Buf) -> Buf {
let n = (effectcraft_raster::hash_noise(x as u32, y as u32, seed) - 0.5) * jitter * (8.0 / 255.0);
c = c.map(|v| v + n);
}
*px = gen_blend(*px, [c[0] * op, c[1] * op, c[2] * op, op], mode);
// None keeps the layer's alpha (#1381), as the Gradient Ramp does: the gradient fills
// a shape or text, not its bounding box.
let a = if gen_mode(mode).is_none() { op * px[3] } else { op };
*px = gen_blend(*px, [c[0] * a, c[1] * a, c[2] * a, a], mode);
}
});
b
@@ -420,6 +423,25 @@ mod generator_tests {
}
}
#[test]
fn four_color_none_stays_inside_the_layer_alpha() {
// #1381: like the Gradient Ramp, the gradient fills a shape or text, not its bounding box.
let mut img = Image::filled(40, 24, [1.0, 0.0, 0.0, 1.0]);
for (i, p) in img.data.iter_mut().enumerate() {
let a = [0.0, 0.5, 1.0][i % 40 / 14];
*p = p.map(|c| c * a);
}
for op in [100.0, 50.0] {
let o = run_fx("ec.generate.fourcolor", &[("opacity", num(op))], img.clone(), 0.0, EffectEnv::default());
let k = op as f32 / 100.0;
for (src, out) in img.data.iter().zip(&o.img.data) {
assert!((src[3] * k - out[3]).abs() < 1e-6, "opacity {op}: alpha {} became {}", src[3], out[3]);
assert!(out[..3].iter().all(|&c| c <= out[3] + 1e-6), "opacity {op}: premultiplied colour above alpha");
}
assert!(o.img.data.iter().any(|p| p[1] > 0.1), "opacity {op}: the gradient replaces the layer's colour");
}
}
#[test]
fn checkerboard_blending_none_shows_pattern_only_and_size_from_width() {
let set = [("width", num(8.0)), ("height", num(2.0))];
+4 -2
View File
@@ -510,8 +510,10 @@ fn gen_fourcolor(@builtin(global_invocation_id) gid: vec3<u32>) {
let n = (hash_noise(u32(p.x), u32(p.y), P.u[0].y) - 0.5) * jitter * (8.0 / 255.0);
c += vec3<f32>(n);
}
let op = P.f[8].z;
textureStore(out, p, gen_blend(textureLoad(src, p, 0), vec4<f32>(c * op, op), P.u[0].x));
let o = textureLoad(src, p, 0);
// None keeps the layer's alpha, as the CPU path does (#1381).
let op = select(P.f[8].z, P.f[8].z * o.w, P.u[0].x == 255u);
textureStore(out, p, gen_blend(o, vec4<f32>(c * op, op), P.u[0].x));
}
// ---------------------------------------------------------------- Noise / Add Grain