Constant GRADE_OPS
Source pub const GRADE_OPS: &str = r#"
fn as_half(c: vec4<f32>) -> vec4<f32> {
return vec4<f32>(unpack2x16float(pack2x16float(c.xy)), unpack2x16float(pack2x16float(c.zw)));
}
fn grade_exposure(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
return vec4<f32>(src.rgb * q0.x, src.a);
}
fn grade_black(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
let denom = 1.0 - q0.x;
if (abs(denom) < 1e-6) { return vec4<f32>(0.0, 0.0, 0.0, src.a); }
return vec4<f32>(max((src.rgb - vec3<f32>(q0.x)) / denom, vec3<f32>(0.0)), src.a);
}
fn grade_saturation(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
let l = dot(src.rgb, vec3<f32>(q0.y, q0.z, q0.w));
return vec4<f32>(max(vec3<f32>(l) + (src.rgb - vec3<f32>(l)) * q0.x, vec3<f32>(0.0)), src.a);
}
fn grade_contrast(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
if (q0.z > 0.5) {
return vec4<f32>(clamp(vec3<f32>(
safe_pow(max(src.r, 0.0), q0.x),
safe_pow(max(src.g, 0.0), q0.x),
safe_pow(max(src.b, 0.0), q0.x)
) * q0.y, vec3<f32>(0.0), vec3<f32>(1.0)), src.a);
}
return vec4<f32>((src.rgb - vec3<f32>(q0.y)) * q0.x + vec3<f32>(q0.y), src.a);
}
fn grade_warmth(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
if (q0.y > 0.5) {
let l = clamp(luma(src.rgb), 0.0, 1.0);
let t = edge_step(q0.z, q0.w, l);
let warm = mix(q1.xy, q1.zw, t);
let scale = mix(vec2<f32>(1.0), warm, q0.x);
return vec4<f32>(max(vec3<f32>(src.r * scale.x, src.g, src.b * scale.y), vec3<f32>(0.0)), src.a);
}
return vec4<f32>(src.r * (1.0 + q0.x), src.g, src.b * (1.0 - q0.x), src.a);
}
fn grade_tone(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
if (q0.x < 0.5) {
return vec4<f32>(aces_channel(src.r * q0.y), aces_channel(src.g * q0.y), aces_channel(src.b * q0.y), src.a);
}
if (q0.x < 1.5) {
return vec4<f32>(neutral_map(src.rgb, q0.y, q0.z, q0.w, q1.x > 0.5), src.a);
}
return vec4<f32>(agx(src.rgb), src.a);
}
fn grade_vignette(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
if (abs(q0.x) < 1e-8) { return src; }
let dim = max(vec2<f32>(src_dim()), vec2<f32>(1.0));
let st = (vec2<f32>(p) + vec2<f32>(0.5)) / dim;
var factor = 1.0;
if (q0.y > 0.5) {
let d = vec2<f32>(st.x - 0.5, (st.y - 0.5) * q0.w);
factor = 1.0 - q0.x * edge_step(q1.y, q1.z, length(d));
} else {
let uv = st * 2.0 - vec2<f32>(1.0);
let r = length(vec2<f32>(uv.x, uv.y * q0.w)) / max(q1.x, 1e-4);
factor = 1.0 - q0.x * safe_pow(max(r, 0.0), q0.z);
}
return vec4<f32>(src.rgb * factor, src.a);
}
fn grade_grain(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
let mixed = mixed_seed();
let n = grain_hash(p.x, p.y, mixed ^ 7u) + grain_hash(p.x, p.y, mixed ^ 19u) - 1.0;
let add = n * q0.x * (1.0 - q0.y * luma(src.rgb));
var out = src.rgb + vec3<f32>(add);
if (q0.z > 0.5) { out = max(out, vec3<f32>(0.0)); }
return vec4<f32>(out, src.a);
}
fn grade_dither(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
if (abs(q0.x) < 1e-8) { return src; }
let h = hash21(f32(p.x) + f32(post.seed % 1024u), f32(p.y) + f32(post.frame % 1024u));
let d = (h - 0.5) * q0.x / 255.0;
return vec4<f32>(src.rgb + vec3<f32>(d), src.a);
}
fn grade_encode(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
return vec4<f32>(encode_channel(src.r), encode_channel(src.g), encode_channel(src.b), src.a);
}
fn grade_bw(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
let l = dot(src.rgb, vec3<f32>(q0.x, q0.y, q0.z));
return vec4<f32>(l, l, l, src.a);
}
fn grade_noir(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
let l = luma(src.rgb);
var out = vec3<f32>(l);
if (q0.z > 0.5) {
let keep = vec3<f32>(q1.x, q1.y, q1.z);
let kdir = keep - vec3<f32>(luma(keep));
let chroma = src.rgb - vec3<f32>(l);
let kn = length(kdir);
let cn = length(chroma);
if (kn > 1e-4 && cn > 1e-4) {
let gate = edge_step(q0.w, 1.0, dot(chroma, kdir) / (kn * cn));
out = mix(vec3<f32>(l), src.rgb, gate);
}
}
out = (out - vec3<f32>(0.5)) * q0.x + vec3<f32>(0.5);
if (q0.y < 0.999) {
out = max((out - vec3<f32>(q0.y)) / max(1.0 - q0.y, 1e-4), vec3<f32>(0.0));
}
return vec4<f32>(max(out, vec3<f32>(0.0)), src.a);
}
fn grade_sepia(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
let tint = vec3<f32>(
dot(src.rgb, vec3<f32>(0.393, 0.769, 0.189)),
dot(src.rgb, vec3<f32>(0.349, 0.686, 0.168)),
dot(src.rgb, vec3<f32>(0.272, 0.534, 0.131))
);
return vec4<f32>(mix(src.rgb, tint, clamp(q0.x, 0.0, 1.0)), src.a);
}
fn grade_posterize(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
let levels = max(q0.x, 2.0);
return vec4<f32>(quantize(src.r, levels), quantize(src.g, levels), quantize(src.b, levels), src.a);
}
fn grade_flicker(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
let h = grain_hash(post.frame, 0u, post.seed ^ 0xF11CE001u);
let scale = 1.0 + (h - 0.5) * q0.x;
return vec4<f32>(src.rgb * scale, src.a);
}
fn grade_scanlines(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
if (abs(q0.x) < 1e-8) { return src; }
let period = max(u32(q0.y), 2u);
var out = src.rgb;
if (p.y % period == period - 1u) { out = src.rgb * max(1.0 - q0.x, 0.0); }
return vec4<f32>(out, src.a);
}
fn grade_aperture(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
if (abs(q0.x) < 1e-8) { return src; }
let s = clamp(q0.x, 0.0, 1.0);
var out = src.rgb;
let column = p.x % 3u;
if (column == 0u) { out = vec3<f32>(src.r, src.g * (1.0 - s), src.b * (1.0 - s)); }
else if (column == 1u) { out = vec3<f32>(src.r * (1.0 - s), src.g, src.b * (1.0 - s)); }
else { out = vec3<f32>(src.r * (1.0 - s), src.g * (1.0 - s), src.b); }
return vec4<f32>(out, src.a);
}
fn grade_duotone(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
let l = clamp(luma(src.rgb), 0.0, 1.0);
let shadow = vec3<f32>(q0.x, q0.y, q0.z);
let highlight = vec3<f32>(q1.x, q1.y, q1.z);
return vec4<f32>(mix(shadow, highlight, l), src.a);
}
fn grade_gradient_map(src: vec4<f32>, p: vec2<u32>, q0: vec4<f32>, q1: vec4<f32>, q2: vec4<f32>, q3: vec4<f32>) -> vec4<f32> {
let l = clamp(luma(src.rgb), 0.0, 1.0);
var lower = q0;
var upper = q3;
if (q0.w <= l) { lower = q0; }
if (q1.w <= l) { lower = q1; }
if (q2.w <= l) { lower = q2; }
if (q3.w <= l) { lower = q3; }
if (q0.w >= l) { upper = q0; }
else if (q1.w >= l) { upper = q1; }
else if (q2.w >= l) { upper = q2; }
else { upper = q3; }
let span = upper.w - lower.w;
var t = 0.0;
if (abs(span) >= 1e-6) { t = clamp((l - lower.w) / span, 0.0, 1.0); }
return vec4<f32>(mix(lower.xyz, upper.xyz, t), src.a);
}
"#;