Skip to main content

FX_WGSL

Constant FX_WGSL 

Source
pub const FX_WGSL: &str = r#"
const FLAT_BLUR: u32 = 2048u;
const FLAT_ADDITIVE: u32 = 32768u;
const FLAT_UNPICKED: u32 = 65536u;
const FLAT_BLUR_ROWS: u32 = 4u;
const FLAT_BLUR_STEPS: u32 = 48u;
const FLAT_BLUR_FLOOR: f32 = 0.35;

@vertex
fn flat_id_vertex_rich(instance: FlatInstance, @builtin(vertex_index) index: u32) -> FlatVaryings {
    var out = flat_place(instance, index);
    if ((instance.info.x & 15u) == FLAT_SHADOW || (instance.info.x & FLAT_UNPICKED) != 0u) {
        out.position = vec4f(2.0, 2.0, 2.0, 1.0);
    }
    return out;
}

fn flat_blur_distance(v: FlatVaryings, kind: u32, p: vec2f) -> f32 {
    if (kind == FLAT_ICON_FILL || kind == FLAT_ICON_LINE) {
        let c = clamp(p, v.shape.xy - v.shape.zw, v.shape.xy + v.shape.zw);
        let outside = length(p - c);
        if (outside > 0.0) {
            return outside + 0.5 * v.stroke.x;
        }
        return textureSampleLevel(flat_icons, flat_icon_sampler, v.icon.xy + c * v.icon.zw, 0.0).r;
    }
    return flat_field(v, kind, p).x;
}

fn flat_blur_band(v: FlatVaryings, kind: u32, stroke: bool, p: vec2f) -> f32 {
    let d = flat_blur_distance(v, kind, p);
    if (!stroke) {
        return d;
    }
    let band = abs(d) - v.stroke.x * 0.5;
    if ((v.info.x & FLAT_DASHED) == 0u) {
        return band;
    }
    var along = flat_circle_along(p, v.shape.x);
    if (kind == FLAT_RECT) {
        along = flat_rect_along(p, v.shape.xy, v.corners);
    }
    return max(band, flat_dash(along, v.stroke.y, v.stroke.z, v.stroke.w).x);
}

fn flat_blur_cdf(x: f32, span: f32) -> f32 {
    let hi = 0.5 * (span + 1.0);
    let lo = 0.5 * abs(span - 1.0);
    let ramp = min(span, 1.0);
    let height = 1.0 / max(span, 1.0);
    if (x <= -hi) {
        return 0.0;
    }
    if (x >= hi) {
        return 1.0;
    }
    if (x < -lo) {
        let u = x + hi;
        return 0.5 * height / ramp * u * u;
    }
    if (x > lo) {
        let u = hi - x;
        return 1.0 - 0.5 * height / ramp * u * u;
    }
    return 0.5 * height * ramp + height * (x + lo);
}

fn flat_blur_line(v: FlatVaryings, kind: u32, stroke: bool, start: vec2f, along: vec2f, unit: f32, span: f32) -> f32 {
    let hi = 0.5 * (span + 1.0);
    let least = max(FLAT_BLUR_FLOOR, 2.0 * hi / f32(FLAT_BLUR_STEPS - 8u));
    var x = -hi;
    var s0 = flat_blur_band(v, kind, stroke, start + along * x) / unit;
    var total = 0.0;
    for (var step = 0u; step < FLAT_BLUR_STEPS; step += 1u) {
        if (x >= hi) {
            break;
        }
        let x1 = min(x + max(abs(s0), least), hi);
        let s1 = flat_blur_band(v, kind, stroke, start + along * x1) / unit;
        if (s0 <= 0.0 && s1 <= 0.0) {
            total += flat_blur_cdf(x1, span) - flat_blur_cdf(x, span);
        } else if (s0 <= 0.0) {
            let r = x + (x1 - x) * s0 / (s0 - s1);
            total += flat_blur_cdf(r, span) - flat_blur_cdf(x, span);
        } else if (s1 <= 0.0) {
            let r = x + (x1 - x) * s0 / (s0 - s1);
            total += flat_blur_cdf(x1, span) - flat_blur_cdf(r, span);
        }
        x = x1;
        s0 = s1;
    }
    if (x < hi && s0 <= 0.0) {
        total += 1.0 - flat_blur_cdf(x, span);
    }
    return total;
}

fn flat_line_empty() -> vec2f {
    return vec2f(1.0, -1.0);
}

fn flat_line_merge(a: vec2f, b: vec2f) -> vec2f {
    if (a.x >= a.y) {
        return b;
    }
    if (b.x >= b.y) {
        return a;
    }
    return vec2f(min(a.x, b.x), max(a.y, b.y));
}

fn flat_line_disc(start: vec2f, along: vec2f, centre: vec2f, radius: f32) -> vec2f {
    if (radius <= 0.0) {
        return flat_line_empty();
    }
    let o = start - centre;
    let aa = dot(along, along);
    let b = dot(o, along);
    let c = dot(o, o) - radius * radius;
    let d = b * b - aa * c;
    if (d <= 0.0) {
        return flat_line_empty();
    }
    let root = sqrt(d);
    return vec2f((-b - root) / aa, (-b + root) / aa);
}

fn flat_line_box(start: vec2f, along: vec2f, half: vec2f) -> vec2f {
    if (half.x < 0.0 || half.y < 0.0) {
        return flat_line_empty();
    }
    var low = -1e30;
    var high = 1e30;
    for (var axis = 0; axis < 2; axis += 1) {
        if (abs(along[axis]) < 1e-12) {
            if (abs(start[axis]) > half[axis]) {
                return flat_line_empty();
            }
        } else {
            let a = (-half[axis] - start[axis]) / along[axis];
            let b = (half[axis] - start[axis]) / along[axis];
            low = max(low, min(a, b));
            high = min(high, max(a, b));
        }
    }
    return vec2f(low, high);
}

fn flat_line_rounded(start: vec2f, along: vec2f, half: vec2f, radius: f32) -> vec2f {
    if (half.x <= 0.0 || half.y <= 0.0) {
        return flat_line_empty();
    }
    let r = clamp(radius, 0.0, min(half.x, half.y));
    var hit = flat_line_box(start, along, vec2f(half.x, half.y - r));
    hit = flat_line_merge(hit, flat_line_box(start, along, vec2f(half.x - r, half.y)));
    if (r > 0.0) {
        let c = half - vec2f(r);
        hit = flat_line_merge(hit, flat_line_disc(start, along, c, r));
        hit = flat_line_merge(hit, flat_line_disc(start, along, vec2f(-c.x, c.y), r));
        hit = flat_line_merge(hit, flat_line_disc(start, along, vec2f(c.x, -c.y), r));
        hit = flat_line_merge(hit, flat_line_disc(start, along, -c, r));
    }
    return hit;
}

fn flat_blur_weight(hit: vec2f, span: f32) -> f32 {
    if (hit.x >= hit.y) {
        return 0.0;
    }
    return flat_blur_cdf(hit.y, span) - flat_blur_cdf(hit.x, span);
}

fn flat_blur_closed(v: FlatVaryings, kind: u32, stroke: bool) -> bool {
    if (kind == FLAT_RING) {
        return !stroke;
    }
    if (kind != FLAT_RECT && kind != FLAT_CIRCLE) {
        return false;
    }
    if (kind == FLAT_RECT && any(v.corners != vec4f(v.corners.x))) {
        return false;
    }
    return !stroke || (v.info.x & FLAT_DASHED) == 0u;
}

fn flat_blur_row(v: FlatVaryings, kind: u32, stroke: bool, start: vec2f, a: vec2f, span: f32) -> f32 {
    let w = select(0.0, v.stroke.x * 0.5, stroke);
    var outer = flat_line_empty();
    var inner = flat_line_empty();
    if (kind == FLAT_RECT) {
        outer = flat_line_rounded(start, a, v.shape.xy + vec2f(w), v.corners.x + w);
        if (stroke) {
            inner = flat_line_rounded(start, a, v.shape.xy - vec2f(w), max(v.corners.x - w, 0.0));
        }
    } else if (kind == FLAT_CIRCLE) {
        outer = flat_line_disc(start, a, vec2f(0.0), v.shape.x + w);
        if (stroke) {
            inner = flat_line_disc(start, a, vec2f(0.0), v.shape.x - w);
        }
    } else {
        outer = flat_line_disc(start, a, vec2f(0.0), v.shape.x + 0.5 * v.shape.y);
        inner = flat_line_disc(start, a, vec2f(0.0), v.shape.x - 0.5 * v.shape.y);
    }
    return flat_blur_weight(outer, span) - flat_blur_weight(inner, span);
}

fn flat_blur_cover(v: FlatVaryings, kind: u32, stroke: bool, p: vec2f, inverse: mat2x2f) -> f32 {
    let axes = mat2x2f(vec2f(inverse[1].y, -inverse[0].y), vec2f(-inverse[1].x, inverse[0].x))
        * (1.0 / determinant(inverse));
    let screen = axes * v.material.xy;
    let span = max(length(screen), 1e-6);
    let direction = screen / span;
    let along = inverse * direction;
    let across = inverse * vec2f(-direction.y, direction.x);
    let unit = max(length(along), 1e-12);
    let closed = flat_blur_closed(v, kind, stroke);
    var total = 0.0;
    for (var row = 0u; row < FLAT_BLUR_ROWS; row += 1u) {
        let u = (f32(row) + 0.5) / f32(FLAT_BLUR_ROWS) - 0.5;
        if (closed) {
            total += flat_blur_row(v, kind, stroke, p + across * u, along, span);
        } else {
            total += flat_blur_line(v, kind, stroke, p + across * u, along, unit, span);
        }
    }
    return clamp(total / f32(FLAT_BLUR_ROWS), 0.0, 1.0);
}

fn flat_blur(v: FlatVaryings) -> FlatPaint {
    let p = v.local;
    let inverse = mat2x2f(v.inverse.xy, v.inverse.zw);
    let kind = v.info.x & 15u;
    let fill_mode = (v.info.x >> FLAT_FILL_SHIFT) & 15u;
    let opacity = bitcast<f32>(v.info.w);
    var fill = vec4f(0.0);
    var fill_cover = 0.0;
    if (fill_mode != 0u) {
        fill_cover = flat_blur_cover(v, kind, false, p, inverse);
        var base = flat_fill_base(v, fill_mode, p);
        let pattern = (v.info.x >> 12u) & 7u;
        if (pattern != 0u) {
            base = flat_pattern_over(base, pattern, v.icon, v.info.z, p, inverse);
        }
        fill = base * fill_cover;
    }
    var stroke = vec4f(0.0);
    var stroke_cover = 0.0;
    if (v.stroke.x > 0.0) {
        stroke_cover = flat_blur_cover(v, kind, true, p, inverse);
        stroke = vec4f(v.stroke_colour.rgb * v.stroke_colour.a, v.stroke_colour.a) * stroke_cover;
    }
    let colour = (stroke + fill * (1.0 - stroke.a)) * opacity;
    let fill_seen = select(0.0, fill_cover, fill_mode != 0u && flat_fill_alpha(v, fill_mode) > 0.0);
    let stroke_seen = select(0.0, stroke_cover, v.stroke_colour.a > 0.0);
    let footprint = select(0.0, max(fill_seen, stroke_seen), opacity > 0.0);
    return FlatPaint(colour, footprint);
}
"#;