darkly 0.5.0

A GPU-native paint engine on wgpu: brushes, layers, blend modes, masks, selections, and undo.
Documentation
// Linear gradient: fullscreen quad with per-pixel gradient interpolation.
// Selection masking via bound selection texture.
// Hardware blend state = REPLACE (gradient overwrites existing content,
// modulated by selection coverage).

struct Uniforms {
    // Quad origin in canvas pixels (top-left corner).
    origin: vec2f,
    // Quad size in canvas pixels.
    size: vec2f,
    // Canvas-space offset of the target's (0,0) pixel.
    target_offset: vec2f,
    // Target texture pixel dimensions (used for vertex NDC mapping).
    target_size: vec2f,
    // Document canvas size (used for fragment-stage selection UV).
    canvas_size: vec2f,
    // Plane-space offset of the canvas window (selection-mask anchor).
    canvas_origin: vec2f,
    // Gradient start point in canvas pixels.
    start: vec2f,
    // Gradient end point in canvas pixels.
    end: vec2f,
    // Start color (RGBA, straight alpha).
    color0: vec4f,
    // End color (RGBA, straight alpha).
    color1: vec4f,
}

@group(0) @binding(0) var<uniform> uniforms: Uniforms;
@group(1) @binding(0) var t_selection: texture_2d<f32>;
@group(1) @binding(1) var t_sampler: sampler;

struct VertexOutput {
    @builtin(position) position: vec4f,
    @location(0) canvas_pos: vec2f,
}

@vertex fn vs_main(@builtin(vertex_index) idx: u32) -> VertexOutput {
    let unit = vec2f(f32((idx << 1u) & 2u), f32(idx & 2u));
    let canvas_pos = uniforms.origin + unit * uniforms.size;

    // Translate canvas-space → target-local, then to NDC against target size.
    let target_local = canvas_pos - uniforms.target_offset;
    let ndc = vec2f(
        target_local.x / uniforms.target_size.x * 2.0 - 1.0,
        1.0 - target_local.y / uniforms.target_size.y * 2.0,
    );

    var out: VertexOutput;
    out.position = vec4f(ndc, 0.0, 1.0);
    out.canvas_pos = canvas_pos;
    return out;
}

@fragment fn fs_main(in: VertexOutput) -> @location(0) vec4f {
    // Project pixel onto gradient axis.
    let d = uniforms.end - uniforms.start;
    let len2 = dot(d, d);
    var t = 0.0;
    if len2 > 0.001 {
        t = clamp(dot(in.canvas_pos - uniforms.start, d) / len2, 0.0, 1.0);
    }

    let color = mix(uniforms.color0, uniforms.color1, t);

    // Selection masking: map the plane position to the window-anchored
    // selection mask before sampling (see shaders/lib/canvas.wgsl).
    let sel_uv = plane_to_selection_uv(in.canvas_pos, uniforms.canvas_origin, uniforms.canvas_size);
    let sel = textureSample(t_selection, t_sampler, sel_uv).r;

    return vec4f(color.rgb, color.a * sel);
}