why2-chat 2.0.1

Lightweight, fast and secure chat application powered by WHY2 encryption.
// This is part of WHY2
// Copyright (C) 2022-2026 Václav Šmejkal
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

// I420 -> RGB (BT.601, LIMITED RANGE), PLUS THE LETTERBOX.
//
// THE DECODER HANDS US THREE SINGLE-CHANNEL PLANES. UPLOADING THEM AS THEY ARE COSTS 1.5 BYTES PER
// PIXEL; CONVERTING ON THE CPU FIRST, AS THE `pixels` PATH DID, COST 4 - AND A FULL-FRAME SCALAR
// PASS ON TOP. THE SAMPLER DOES THE CHROMA UPSCALE AND THE SCALE-TO-WINDOW FOR FREE.

struct Geometry
{
    scale: vec2<f32>,  // LETTERBOX: <=1 ON THE AXIS THAT HAS TO SHRINK
    luma: vec2<f32>,   // x = SPAN, y = OFFSET, BOTH IN NORMALISED TEXTURE COORDINATES
    chroma: vec2<f32>, // AS ABOVE, FOR THE HALF-WIDTH PLANES
};

@group(0) @binding(0) var<uniform> geometry: Geometry;
@group(0) @binding(1) var luma: texture_2d<f32>;
@group(0) @binding(2) var chroma_u: texture_2d<f32>;
@group(0) @binding(3) var chroma_v: texture_2d<f32>;
@group(0) @binding(4) var frame_sampler: sampler;

struct Vertex
{
    @builtin(position) position: vec4<f32>,
    @location(0) uv: vec2<f32>,
};

// FULLSCREEN TRIANGLE - NO VERTEX BUFFER, NO INDEX BUFFER
@vertex
fn vertex(@builtin(vertex_index) index: u32) -> Vertex
{
    var out: Vertex;

    let uv = vec2<f32>(f32((index << 1u) & 2u), f32(index & 2u));

    out.uv = uv;
    out.position = vec4<f32>((uv * 2.0 - 1.0) * vec2<f32>(1.0, -1.0) * geometry.scale, 0.0, 1.0);

    return out;
}

@fragment
fn fragment(in: Vertex) -> @location(0) vec4<f32>
{
    // THE LETTERBOX IS THE PART OF THE TRIANGLE THAT IS NOT THE PICTURE, AND IT HAS TO BE PAINTED
    // BLACK HERE RATHER THAN LEFT TO THE CLEAR. THE FULLSCREEN *TRIANGLE* SPANS uv 0..2, WHICH IS
    // WHY IT COVERS THE WHOLE UNIT SQUARE - BUT SCALED DOWN IT NO LONGER OVERHANGS THE VIEWPORT,
    // SO ITS TWO EXTRA CORNERS LAND INSIDE THE WINDOW AND SAMPLE uv > 1, WHERE A CLAMPED SAMPLER
    // REPEATS THE LAST ROW/COLUMN. THAT IS THE TRIANGULAR SMEAR IN THE BARS: NOT A CLEAR THAT WAS
    // MISSED, BUT THE PICTURE BEING DRAWN OVER IT.
    if (in.uv.x > 1.0 || in.uv.y > 1.0)
    {
        return vec4<f32>(0.0, 0.0, 0.0, 1.0);
    }

    // THE PLANES ARE ALLOCATED AT THEIR *STRIDE* WIDTH, SO THE PADDING PAST EACH ROW IS TRIMMED
    // HERE RATHER THAN BY REPACKING EVERY PLANE ON THE CPU FIRST.
    //
    // THE SPAN REACHES THE *CENTRE* OF THE LAST REAL TEXEL, NOT ITS TRAILING EDGE. MAPPING u=1 TO
    // width/stride LANDS EXACTLY ON THE BOUNDARY, WHERE A LINEAR SAMPLER MIXES IN HALF A TEXEL OF
    // PADDING - WHICH IS A VISIBLE SMEAR DOWN THE RIGHT-HAND COLUMN, NOT A ROUNDING DETAIL.
    let luma_uv = vec2<f32>(geometry.luma.y + in.uv.x * geometry.luma.x, in.uv.y);
    let chroma_uv = vec2<f32>(geometry.chroma.y + in.uv.x * geometry.chroma.x, in.uv.y);

    let y = textureSample(luma, frame_sampler, luma_uv).r;
    let u = textureSample(chroma_u, frame_sampler, chroma_uv).r;
    let v = textureSample(chroma_v, frame_sampler, chroma_uv).r;

    // LIMITED RANGE: LUMA LIVES IN 16..235, CHROMA IN 16..240
    let c = (y - 16.0 / 255.0) * (255.0 / 219.0);
    let d = (u - 128.0 / 255.0) * (255.0 / 224.0);
    let e = (v - 128.0 / 255.0) * (255.0 / 224.0);

    let rgb = vec3<f32>
    (
        c + 1.402 * e,
        c - 0.344136 * d - 0.714136 * e,
        c + 1.772 * d,
    );

    return vec4<f32>(clamp(rgb, vec3<f32>(0.0), vec3<f32>(1.0)), 1.0);
}