pub const COMPOSITE: &str = r#"
uniform sampler2D u_dst;
uniform vec2 u_layer;
uniform mat3 u_inv;
uniform int u_scaler;
uniform int u_mode;
uniform float u_opacity;
vec4 tap(vec2 ip) {
return texelFetch(tex, ivec2(clamp(ip, vec2(0.0), u_layer - 1.0)), 0);
}
vec4 catmull_w(float t) {
float t2 = t * t;
float t3 = t2 * t;
return 0.5 * vec4(-t3 + 2.0 * t2 - t, 3.0 * t3 - 5.0 * t2 + 2.0, -3.0 * t3 + 4.0 * t2 + t, t3 - t2);
}
// Alpha-weighted accumulation (matches the CPU compositor: transparent texels must not darken edges).
vec4 sample_layer(vec2 uv) {
vec2 sp = uv * u_layer - 0.5;
if (u_scaler == 0) { return tap(floor(sp + 0.5)); }
vec4 acc = vec4(0.0);
if (u_scaler == 1) {
vec2 f = floor(sp);
vec2 t = sp - f;
for (int j = 0; j < 2; j++) {
for (int i = 0; i < 2; i++) {
float w = (i == 0 ? 1.0 - t.x : t.x) * (j == 0 ? 1.0 - t.y : t.y);
vec4 c = tap(f + vec2(float(i), float(j)));
acc += vec4(c.rgb * c.a * w, c.a * w);
}
}
} else {
vec2 f = floor(sp);
vec4 wx = catmull_w(sp.x - f.x);
vec4 wy = catmull_w(sp.y - f.y);
for (int j = 0; j < 4; j++) {
for (int i = 0; i < 4; i++) {
float w = wx[i] * wy[j];
vec4 c = tap(f + vec2(float(i - 1), float(j - 1)));
acc += vec4(c.rgb * c.a * w, c.a * w);
}
}
}
if (acc.a <= 0.0) { return vec4(0.0); }
return vec4(clamp(acc.rgb / acc.a, 0.0, 1.0), clamp(acc.a, 0.0, 1.0));
}
void main() {
vec3 h = u_inv * vec3(v_uv * u_res, 1.0);
float z = (abs(h.z) < 1e-9) ? 1e-9 : h.z;
vec2 uv = h.xy / z;
// derivatives must be taken outside any branch
vec2 e = max(fwidth(uv), vec2(1e-8));
vec2 dd = min(uv, 1.0 - uv);
float cov = clamp(min(dd.x / e.x, dd.y / e.y) + 0.5, 0.0, 1.0);
vec4 d = texture(u_dst, v_uv);
if (cov <= 0.0 || h.z <= 0.0) { out_color = d; return; }
vec4 s = sample_layer(uv);
float a = s.a * u_opacity * cov * mask_at(v_uv);
if (a <= 0.0) { out_color = d; return; }
float ao = a + d.a * (1.0 - a);
if (ao <= 0.0) { out_color = vec4(0.0); return; }
vec3 b = blend(u_mode, s.rgb, d.rgb);
vec3 co = ((1.0 - d.a) * a * s.rgb + (1.0 - a) * d.a * d.rgb + a * d.a * b) / ao;
out_color = vec4(co, ao);
}
"#;Expand description
Composite/transform shader: samples a layer through an inverse 3x3 (perspective-capable) matrix with
the chosen Scaler (0 = nearest, 1 = bilinear, 2 = bicubic Catmull-Rom) and blends it onto the canvas.
Appended to PRELUDE + BLEND. u_inv maps canvas pixels (y down) to layer uv.