Commit Graph

2 Commits

Author SHA1 Message Date
Terry Jia
1c4953ff9d feat: LAYERS io type with a chainable Add Layer builder 2026-08-06 22:16:34 -04:00
Christian Byrne
d36bc61a3e test(compositor): pin blend behaviour to a golden fixture; fix luminosity (#15373)
Blending exists in two implementations - the numpy compositor and the
layerBlend.frag shader that drives the live preview - with nothing holding
them together. They have already diverged once (the safeDiv operand), and a
divergence only shows up to the user as 'the render does not match the
preview'.

Adds compositor_blend_golden.json: every mode, at both endpoints, the
midpoint and inside each epsilon guard. Any implementation of these 26 modes
must reproduce it. compositor_blend_test.py pins the numpy side to it and
additionally spells out the boundary rules by hand, so the guards cannot be
re-broken by regenerating the fixture.

Diffing the shader against the numpy implementation over that grid leaves
exactly one mismatch: luminosity. safe_div guards the denominator and
returns 0, so a luminosity layer over a black or near-black backdrop
disappears. The backdrop has no hue or saturation to preserve there, so the
result should be a neutral grey at the layer's luminance - which is also the
analytic limit of i * lum(l)/lum(i) as the backdrop approaches black. The
matching four-line shader change is proposed on the frontend PR; with both
applied all 26 modes agree.

Also clamps layer opacity to [0, 1]. The layer state round-trips through the
saved workflow and is accepted verbatim on /prompt, so it is untrusted
input; the canvas is only clamped once, after the last layer, so an
out-of-range coverage multiplier changes the blend of every layer above it.
_parse_background already clamps the same field.
2026-08-06 22:14:37 -04:00