fix(compose): mix group coverage in premultiplied space (#108)
This commit is contained in:
parent
7100b0d617
commit
a65b5ca80f
|
|
@ -847,7 +847,16 @@ fn composite_layer_plain(layer: &Layer, clipped: &[Layer], backdrop: &mut Buffer
|
|||
for (i, (p, a)) in backdrop.px.iter_mut().zip(&before.px).enumerate() {
|
||||
let k = opacity * mask_k(&mv, i);
|
||||
let b = *p;
|
||||
*p = std::array::from_fn(|c| a[c] + (b[c] - a[c]) * k);
|
||||
// Coverage mixes premultiplied colour; the stored buffer remains straight alpha.
|
||||
let wa = a[3] * (1.0 - k);
|
||||
let wb = b[3] * k;
|
||||
let alpha = wa + wb;
|
||||
*p = if alpha > 0.0 {
|
||||
let c = |c: usize| (a[c] * wa + b[c] * wb) / alpha;
|
||||
[c(0), c(1), c(2), alpha]
|
||||
} else {
|
||||
[0.0; 4]
|
||||
};
|
||||
}
|
||||
}
|
||||
// Layers clipped to a pass-through group sit atop the group's
|
||||
|
|
|
|||
|
|
@ -173,6 +173,16 @@ fn pass_through_group_opacity_mixes() {
|
|||
g.opacity = 0.25;
|
||||
d.layers.push(g);
|
||||
assert!((px(&d, 0, 0)[0] - 0.75).abs() <= E);
|
||||
|
||||
// Opaque backdrops hide straight-alpha interpolation errors.
|
||||
let mut transparent = Document::new("coverage", Size::new(2, 2), ColorMode::Rgb, SampleType::F32);
|
||||
let mut red = Layer::raster("HDR red", transparent.pixel_format());
|
||||
red.surface_mut().unwrap().fill_rect(transparent.bounds(), &[2.0, 0.0, 0.0, 1.0]);
|
||||
let mut group = Layer::group("half", vec![red]);
|
||||
group.opacity = 0.5;
|
||||
transparent.layers.push(group);
|
||||
let actual = px(&transparent, 0, 0);
|
||||
assert!(close4(actual, [2.0, 0.0, 0.0, 0.5]), "{actual:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -687,7 +687,7 @@ fn fs_adjmix(in: VOut) -> @location(0) vec4<f32> {
|
|||
return vec4(b.rgb + (bl - b.rgb) * k, b.a);
|
||||
}
|
||||
|
||||
// Pass-through group opacity/mask: lerp(A, B, opacity × mask) on all channels.
|
||||
// Pass-through group coverage mixes premultiplied colour, then returns straight alpha.
|
||||
@fragment
|
||||
fn fs_lerp(in: VOut) -> @location(0) vec4<f32> {
|
||||
let p = local(in.pos);
|
||||
|
|
@ -708,7 +708,11 @@ fn fs_lerp(in: VOut) -> @location(0) vec4<f32> {
|
|||
return vec4(clamp(pm.rgb / al, vec3(0.0), vec3(1.0)), al);
|
||||
}
|
||||
let k = op.opacity * mask_value(doc_px(p));
|
||||
return a + (b - a) * k;
|
||||
let wa = a.a * (1.0 - k);
|
||||
let wb = b.a * k;
|
||||
let alpha = wa + wb;
|
||||
if (alpha <= 0.0) { return vec4(0.0); }
|
||||
return vec4((a.rgb * wa + b.rgb * wb) / alpha, alpha);
|
||||
}
|
||||
|
||||
// ---- layer effects (compose::effects::composite_with_effects) -------------------------------
|
||||
|
|
|
|||
Loading…
Reference in New Issue