sugarloaf 0.4.3

Sugarloaf is Rio rendering engine, designed to be multiplatform. It is based on WebGPU, Rust library for Desktops and WebAssembly for Web (JavaScript). This project is created and maintained for Rio terminal purposes but feel free to use it.
struct Globals {
    transform: mat4x4<f32>,
}

@group(0) @binding(0) var<uniform> globals: Globals;
@group(0) @binding(1) var font_sampler: sampler;
@group(1) @binding(0) var color_texture: texture_2d<f32>; // RGBA texture for color glyphs
@group(1) @binding(1) var mask_texture: texture_2d<f32>;  // R8 texture for alpha masks

// Per-instance data for instanced quad rendering (matches Rust QuadInstance)
struct QuadInstanceInput {
    @location(0) pos: vec3<f32>,
    @location(1) color: vec4<f32>,
    @location(2) uv_rect: vec4<f32>,
    @location(3) layers: vec2<i32>,
    @location(4) size: vec2<f32>,
    @location(5) corner_radii: vec4<f32>,
    @location(6) underline_style: i32,
    @location(7) clip_rect: vec4<f32>,
}

// Per-vertex data for non-quad geometry (lines, triangles, arcs)
struct VertexInput {
    @builtin(vertex_index) vertex_index: u32,
    @location(0) v_pos: vec3<f32>,
    @location(1) v_color: vec4<f32>,
    @location(2) v_uv: vec2<f32>,
    @location(3) layers: vec2<i32>,
    @location(4) corner_radii: vec4<f32>,
    @location(5) rect_size: vec2<f32>,
    @location(6) underline_style: i32,
    @location(7) clip_rect: vec4<f32>,
}

struct VertexOutput {
    @builtin(position) position: vec4<f32>,
    @location(0) f_color: vec4<f32>,
    @location(1) f_uv: vec2<f32>,
    @location(2) color_layer: i32,
    @location(3) mask_layer: i32,
    @location(4) corner_radii: vec4<f32>,
    @location(5) rect_size: vec2<f32>,
    @location(6) @interpolate(flat) underline_style: i32,
    @location(7) @interpolate(flat) clip_rect: vec4<f32>,
}

// Unit quad corners for triangle strip: TL, BL, TR, BR
const UNIT_QUAD = array<vec2<f32>, 4>(
    vec2<f32>(0.0, 0.0),
    vec2<f32>(0.0, 1.0),
    vec2<f32>(1.0, 0.0),
    vec2<f32>(1.0, 1.0),
);

@vertex
fn vs_instanced(
    @builtin(vertex_index) vertex_id: u32,
    input: QuadInstanceInput,
) -> VertexOutput {
    let unit = UNIT_QUAD[vertex_id];
    let pos = input.pos.xy + unit * input.size;
    let uv = mix(input.uv_rect.xy, input.uv_rect.zw, unit);

    var out: VertexOutput;
    out.position = globals.transform * vec4<f32>(pos, input.pos.z, 1.0);
    out.f_color = input.color;
    out.f_uv = uv;
    out.color_layer = input.layers.x;
    out.mask_layer = input.layers.y;
    out.corner_radii = input.corner_radii;
    out.rect_size = input.size;
    out.underline_style = input.underline_style;
    out.clip_rect = input.clip_rect;
    return out;
}

@vertex
fn vs_main(input: VertexInput) -> VertexOutput {
    var out: VertexOutput;
    out.f_color = input.v_color;
    out.f_uv = input.v_uv;
    out.color_layer = input.layers.x;
    out.mask_layer = input.layers.y;
    out.corner_radii = input.corner_radii;
    out.rect_size = input.rect_size;
    out.underline_style = input.underline_style;
    out.clip_rect = input.clip_rect;

    out.position = globals.transform * vec4<f32>(input.v_pos, 1.0);
    return out;
}

// Pick the corner radius based on which quadrant the point is in
fn pick_corner_radius(center_to_point: vec2<f32>, corner_radii: vec4<f32>) -> f32 {
    if (center_to_point.x < 0.0) {
        if (center_to_point.y < 0.0) {
            return corner_radii.x; // top_left
        } else {
            return corner_radii.w; // bottom_left
        }
    } else {
        if (center_to_point.y < 0.0) {
            return corner_radii.y; // top_right
        } else {
            return corner_radii.z; // bottom_right
        }
    }
}

// Signed distance field for a quad (rectangle)
fn quad_sdf(corner_center_to_point: vec2<f32>, corner_radius: f32) -> f32 {
    if (corner_radius == 0.0) {
        // Fast path for sharp corners
        return max(corner_center_to_point.x, corner_center_to_point.y);
    } else {
        // Signed distance of the point from a quad that is inset by corner_radius.
        // It is negative inside this quad, and positive outside.
        let signed_distance_to_inset_quad =
            // 0 inside the inset quad, and positive outside.
            length(max(vec2<f32>(0.0), corner_center_to_point)) +
            // 0 outside the inset quad, and negative inside.
            min(0.0, max(corner_center_to_point.x, corner_center_to_point.y));
        return signed_distance_to_inset_quad - corner_radius;
    }
}

const M_PI_F: f32 = 3.1415926;

// Modulus that has the same sign as `a`.
fn fmod(a: f32, b: f32) -> f32 {
    return a - b * trunc(a / b);
}

// Calculate underline alpha for pattern rendering
fn underline_alpha(x_pos: f32, y_pos: f32, rect_height: f32, thickness: f32, style: i32) -> f32 {
    // style 1: regular solid line
    if (style == 1) {
        return 1.0;
    }

    // style 2: dashed (6px dash, 2px gap)
    if (style == 2) {
        let antialias = 0.5;
        let dash_width = 6.0;
        let gap_width = 2.0;
        let period = dash_width + gap_width;
        let pos_in_period = fmod(x_pos, period);
        let start_aa = saturate(pos_in_period / antialias);
        let end_aa = saturate((dash_width - pos_in_period) / antialias);
        return min(start_aa, end_aa);
    }

    // style 3: dotted (2px dot, 2px gap)
    if (style == 3) {
        let antialias = 0.5;
        let dot_width = 2.0;
        let gap_width = 2.0;
        let period = dot_width + gap_width;
        let pos_in_period = fmod(x_pos, period);
        let start_aa = saturate(pos_in_period / antialias);
        let end_aa = saturate((dot_width - pos_in_period) / antialias);
        return min(start_aa, end_aa);
    }

    // style 4: curly (sine wave) using SDF
    if (style == 4) {
        let WAVE_FREQUENCY: f32 = 2.0;
        let WAVE_HEIGHT_RATIO: f32 = 0.8;

        let half_thickness = thickness * 0.5;
        let st = vec2<f32>(x_pos / rect_height, y_pos / rect_height - 0.5);
        let frequency = M_PI_F * WAVE_FREQUENCY * thickness / rect_height;
        let amplitude = (thickness * WAVE_HEIGHT_RATIO) / rect_height;

        let sine = sin(st.x * frequency) * amplitude;
        let dSine = cos(st.x * frequency) * amplitude * frequency;
        let distance = (st.y - sine) / sqrt(1.0 + dSine * dSine);
        let distance_in_pixels = distance * rect_height;
        let distance_from_top_border = distance_in_pixels - half_thickness;
        let distance_from_bottom_border = distance_in_pixels + half_thickness;
        return saturate(0.5 - max(-distance_from_bottom_border, distance_from_top_border));
    }

    return 1.0;
}

@fragment
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
    if (input.clip_rect.z > 0.0) {
        let px = input.position.x;
        let py = input.position.y;
        if (px < input.clip_rect.x || px >= input.clip_rect.x + input.clip_rect.z ||
            py < input.clip_rect.y || py >= input.clip_rect.y + input.clip_rect.w) {
            discard;
        }
    }

    var out: vec4<f32> = input.f_color;

    // Handle GPU-rendered underlines
    // Underlines have: underline_style > 0, thickness in corner_radii.x
    if (input.underline_style > 0) {
        let width = input.rect_size.x;
        let rect_height = input.rect_size.y;
        let x_pos = input.f_uv.x * width;
        let y_pos = input.f_uv.y * rect_height;
        let thickness = input.corner_radii.x;

        let alpha = underline_alpha(x_pos, y_pos, rect_height, thickness, input.underline_style);
        return vec4<f32>(input.f_color.rgb, input.f_color.a * alpha);
    }

    // Handle texture sampling for glyphs
    if input.color_layer > 0 {
        let tex_sample = textureSampleLevel(color_texture, font_sampler, input.f_uv, 0.0);
        out = tex_sample;
    }

    if input.mask_layer > 0 {
        let tex_alpha = textureSampleLevel(mask_texture, font_sampler, input.f_uv, 0.0).x;
        out = vec4<f32>(out.xyz, input.f_color.a * tex_alpha);
    }

    // Check if we have any rounding
    let has_corners = input.corner_radii.x != 0.0 || input.corner_radii.y != 0.0 ||
                      input.corner_radii.z != 0.0 || input.corner_radii.w != 0.0;

    // Fast path: no rounding
    if (!has_corners) {
        return out;
    }

    let size = input.rect_size;
    let half_size = size / 2.0;

    // Convert UV (0-1) to local position centered at rect center
    let center_to_point = (input.f_uv - 0.5) * size;

    // Antialiasing threshold
    let antialias_threshold = 0.5;

    // Pick the corner radius for this quadrant
    let corner_radius = pick_corner_radius(center_to_point, input.corner_radii);

    // Vector from corner to point (mirrored to bottom-right quadrant)
    let corner_to_point = abs(center_to_point) - half_size;

    // Vector from corner center (for rounded corner) to point
    let corner_center_to_point = corner_to_point + corner_radius;

    // Outer SDF: distance to the outer edge of the quad
    let outer_sdf = quad_sdf(corner_center_to_point, corner_radius);

    // If outside the quad, discard
    if (outer_sdf >= antialias_threshold) {
        discard;
    }

    return out * vec4<f32>(1.0, 1.0, 1.0, saturate(antialias_threshold - outer_sdf));
}