Skip to main content

GRADE_OPS

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);
}
"#;