contrast_renderer 0.1.3

A web-gpu based 2D render engine
Documentation
struct DynamicStrokeDescriptor {
    gap_start: array<f32, 4u>,
    gap_end: array<f32, 4u>,
    caps: u32,
    count_dashed_join: u32,
    phase: f32,
};
@group(0) @binding(0)
var<storage> u_stroke: array<DynamicStrokeDescriptor>;



struct Instance {
    @location(0) transform_row_0: vec4<f32>,
    @location(1) transform_row_1: vec4<f32>,
    @location(2) transform_row_2: vec4<f32>,
    @location(3) transform_row_3: vec4<f32>,
};

fn instance_transform(instance: Instance) -> mat4x4<f32> {
    return mat4x4<f32>(
        instance.transform_row_0,
        instance.transform_row_1,
        instance.transform_row_2,
        instance.transform_row_3,
    );
}

struct Fragment0 {
    @builtin(position) gl_Position: vec4<f32>,
};

struct Fragment2f {
    @builtin(position) gl_Position: vec4<f32>,
    @location(0) @interpolate(perspective, sample) weights: vec2<f32>,
};

struct Fragment2f1u {
    @builtin(position) gl_Position: vec4<f32>,
    @location(0) @interpolate(perspective, sample) texcoord: vec2<f32>,
    @location(1) @interpolate(flat) bevel_path_index: u32,
    @location(2) @interpolate(flat) end_texcoord_y: f32,
};

struct Fragment3f {
    @builtin(position) gl_Position: vec4<f32>,
    @location(0) @interpolate(perspective, sample) weights: vec3<f32>,
};

struct Fragment3f1u {
    @builtin(position) gl_Position: vec4<f32>,
    @location(0) @interpolate(perspective, sample) texcoord: vec3<f32>,
    @location(1) @interpolate(flat) bevel_path_index: u32,
};

struct Fragment4f {
    @builtin(position) gl_Position: vec4<f32>,
    @location(0) @interpolate(perspective, sample) weights: vec4<f32>,
};

struct FragmentColor {
    @builtin(position) gl_Position: vec4<f32>,
    @location(0) @interpolate(flat) color: vec4<f32>,
};

@vertex
fn vertex0(
    instance: Instance,
    @location(4) position: vec2<f32>,
) -> Fragment0 {
    var stage_out: Fragment0;
    stage_out.gl_Position = instance_transform(instance) * vec4<f32>(position, 0.0, 1.0);
    return stage_out;
}

@vertex
fn vertex2f(
    instance: Instance,
    @location(4) position: vec2<f32>,
    @location(5) weights: vec2<f32>,
) -> Fragment2f {
    var stage_out: Fragment2f;
    stage_out.gl_Position = instance_transform(instance) * vec4<f32>(position, 0.0, 1.0);
    stage_out.weights = weights;
    return stage_out;
}

@vertex
fn vertex2f1u(
    instance: Instance,
    @location(4) position: vec2<f32>,
    @location(5) texcoord: vec2<f32>,
    @location(6) bevel_path_index: u32,
) -> Fragment2f1u {
    var stage_out: Fragment2f1u;
    stage_out.gl_Position = instance_transform(instance) * vec4<f32>(position, 0.0, 1.0);
    stage_out.texcoord = texcoord;
    stage_out.bevel_path_index = bevel_path_index;
    stage_out.end_texcoord_y = texcoord.y;
    return stage_out;
}

@vertex
fn vertex3f(
    instance: Instance,
    @location(4) position: vec2<f32>,
    @location(5) weights: vec3<f32>,
) -> Fragment3f {
    var stage_out: Fragment3f;
    stage_out.gl_Position = instance_transform(instance) * vec4<f32>(position, 0.0, 1.0);
    stage_out.weights = weights;
    return stage_out;
}

@vertex
fn vertex3f1u(
    instance: Instance,
    @location(4) position: vec2<f32>,
    @location(5) texcoord: vec3<f32>,
    @location(6) bevel_path_index: u32,
) -> Fragment3f1u {
    var stage_out: Fragment3f1u;
    stage_out.gl_Position = instance_transform(instance) * vec4<f32>(position, 0.0, 1.0);
    stage_out.texcoord = texcoord;
    stage_out.bevel_path_index = bevel_path_index;
    return stage_out;
}

@vertex
fn vertex4f(
    instance: Instance,
    @location(4) position: vec2<f32>,
    @location(5) weights: vec4<f32>,
) -> Fragment4f {
    var stage_out: Fragment4f;
    stage_out.gl_Position = instance_transform(instance) * vec4<f32>(position, 0.0, 1.0);
    stage_out.weights = weights;
    return stage_out;
}

@vertex
fn vertex_color(
    instance: Instance,
    @location(4) position: vec2<f32>,
    @location(5) color: vec4<f32>,
) -> FragmentColor {
    var stage_out: FragmentColor;
    stage_out.gl_Position = instance_transform(instance) * vec4<f32>(position, 0.0, 1.0);
    stage_out.color = color;
    return stage_out;
}



struct Coverage {
    @builtin(sample_mask) mask: u32,
};

fn coverage(gl_SampleID: u32, keep: bool) -> Coverage {
    var stage_out: Coverage;
    stage_out.mask = select(0u, 1u << (gl_SampleID & 31u), keep);
    return stage_out;
}

fn cap(texcoord: vec2<f32>, cap_type: u32) -> bool {
    switch(i32(cap_type & 15u)) {
        case 0: { // Square
            return texcoord.y > 0.5;
        }
        case 1: { // Round
            return dot(texcoord, texcoord) < 0.25;
        }
        case 2: { // Out
            return 0.5 - texcoord.y > abs(texcoord.x);
        }
        case 3: { // In
            return texcoord.y < abs(texcoord.x);
        }
        case 4: { // Right
            return 0.5 - texcoord.y > texcoord.x;
        }
        case 5: { // Left
            return texcoord.y - 0.5 < texcoord.x;
        }
        default: { // Butt
            return texcoord.y < 0.0;
        }
    }
}

fn joint(radius: f32, bevel: bool, count_dashed_join: u32) -> bool {
    switch(i32(count_dashed_join)) {
        default: { // Miter
            return true;
        }
        case 1: { // Bevel
            return bevel;
        }
        case 2: { // Round
            return radius <= 0.5;
        }
    }
}

fn stroke_dashed(path_index: u32, texcoord: vec2<f32>) -> bool {
    let last_interval_index: u32 = u_stroke[path_index].count_dashed_join >> 3u;
    let pattern_length: f32 = u_stroke[path_index].gap_end[last_interval_index];
    var interval_index: u32 = 0u;
    var gap_start: f32;
    var gap_end: f32;
    var position_in_pattern = (texcoord.y - u_stroke[path_index].phase) % pattern_length;
    if(position_in_pattern < 0.0) {
        position_in_pattern = position_in_pattern + pattern_length;
    }
    loop {
        gap_end = u_stroke[path_index].gap_end[interval_index] - position_in_pattern;
        if(gap_end >= 0.0 || interval_index >= last_interval_index) {
            break;
        }
        interval_index = interval_index + 1u;
    }
    gap_start = position_in_pattern - u_stroke[path_index].gap_start[interval_index];
    if(gap_start > 0.0) {
        let caps = u_stroke[path_index].caps >> (interval_index * 8u);
        let start_cap = cap(vec2<f32>(texcoord.x, gap_start), caps >> 4u);
        let end_cap = cap(vec2<f32>(texcoord.x, gap_end), caps);
        return start_cap || end_cap;
    } else {
        return true;
    }
}

@fragment
fn stencil_solid() {}

@fragment
fn stencil_integral_quadratic_curve(
    stage_in: Fragment2f,
    @builtin(sample_index) gl_SampleID: u32,
) -> Coverage {
    return coverage(gl_SampleID, stage_in.weights.x * stage_in.weights.x - stage_in.weights.y <= 0.0);
}

@fragment
fn stencil_integral_cubic_curve(
    stage_in: Fragment3f,
    @builtin(sample_index) gl_SampleID: u32,
) -> Coverage {
    return coverage(gl_SampleID, stage_in.weights.x * stage_in.weights.x * stage_in.weights.x - stage_in.weights.y * stage_in.weights.z <= 0.0);
}

@fragment
fn stencil_rational_quadratic_curve(
    stage_in: Fragment3f,
    @builtin(sample_index) gl_SampleID: u32,
) -> Coverage {
    return coverage(gl_SampleID, stage_in.weights.x * stage_in.weights.x - stage_in.weights.y * stage_in.weights.z <= 0.0);
}

@fragment
fn stencil_rational_cubic_curve(
    stage_in: Fragment4f,
    @builtin(sample_index) gl_SampleID: u32,
) -> Coverage {
    return coverage(gl_SampleID, stage_in.weights.x * stage_in.weights.x * stage_in.weights.x - stage_in.weights.y * stage_in.weights.z * stage_in.weights.w <= 0.0);
}

@fragment
fn stencil_stroke_line(
    stage_in: Fragment2f1u,
    @builtin(sample_index) gl_SampleID: u32,
) -> Coverage {
    let path_index = stage_in.bevel_path_index & 65535u;
    var fill: bool;
    if((u_stroke[path_index].count_dashed_join & 4u) != 0u) {
        fill = stroke_dashed(path_index, stage_in.texcoord);
    } else if((stage_in.bevel_path_index & 65536u) != 0u) {
        fill = cap(vec2<f32>(stage_in.texcoord.x, stage_in.texcoord.y - stage_in.end_texcoord_y), u_stroke[path_index].caps >> 4u);
    } else if(stage_in.texcoord.y < 0.0) {
        fill = cap(vec2<f32>(stage_in.texcoord.x, -stage_in.texcoord.y), u_stroke[path_index].caps);
    } else {
        fill = true;
    }
    return coverage(gl_SampleID, fill);
}

@fragment
fn stencil_stroke_joint(
    stage_in: Fragment3f1u,
    @builtin(sample_index) gl_SampleID: u32,
) -> Coverage {
    let radius = length(stage_in.texcoord.xy);
    let path_index = stage_in.bevel_path_index & 65535u;
    var fill: bool = joint(radius, (stage_in.bevel_path_index & 65536u) != 0u, u_stroke[path_index].count_dashed_join & 3u);
    let TAU: f32 = acos(-1.0) * 2.0;
    if(fill && (u_stroke[path_index].count_dashed_join & 4u) != 0u) {
        fill = stroke_dashed(path_index, vec2<f32>(radius, stage_in.texcoord.z + atan2(stage_in.texcoord.y, stage_in.texcoord.x) / TAU));
    }
    return coverage(gl_SampleID, fill);
}



@fragment
fn color_cover(
    stage_in: FragmentColor,
) -> @location(0) vec4<f32> {
    return vec4<f32>(stage_in.color.rgb * stage_in.color.a, stage_in.color.a);
}

@fragment
fn scale_alpha_context_cover(
    stage_in: FragmentColor,
) -> @location(0) vec4<f32> {
    return vec4<f32>(0.0, 0.0, 0.0, 1.0 - stage_in.color.a);
}

@group(0) @binding(0)
var frame: texture_2d<f32>;
@group(0) @binding(0)
var multisampled_frame: texture_multisampled_2d<f32>;

@fragment
fn save_alpha_context_cover(
    stage_in: Fragment0,
) -> @location(0) f32 {
    let alpha: f32 = textureLoad(frame, vec2<i32>(stage_in.gl_Position.xy), 0).a;
    return alpha;
}

@fragment
fn multisampled_save_alpha_context_cover(
    stage_in: Fragment0,
    @builtin(sample_index) gl_SampleID: u32,
) -> @location(0) f32 {
    let alpha: f32 = textureLoad(multisampled_frame, vec2<i32>(stage_in.gl_Position.xy), i32(gl_SampleID)).a;
    return alpha;
}

@fragment
fn restore_alpha_context_cover(
    stage_in: FragmentColor,
) -> @location(0) vec4<f32> {
    let alpha: f32 = textureLoad(frame, vec2<i32>(stage_in.gl_Position.xy), 0).x;
    return vec4<f32>(0.0, 0.0, 0.0, (1.0 - alpha) * (1.0 - stage_in.color.a));
}

@fragment
fn multisampled_restore_alpha_context_cover(
    stage_in: FragmentColor,
    @builtin(sample_index) gl_SampleID: u32,
) -> @location(0) vec4<f32> {
    let alpha: f32 = textureLoad(multisampled_frame, vec2<i32>(stage_in.gl_Position.xy), i32(gl_SampleID)).x;
    return vec4<f32>(0.0, 0.0, 0.0, (1.0 - alpha) * (1.0 - stage_in.color.a));
}