bevy_iroh 0.4.4

Share Bevy entities between peers over iroh: replication, rooms, presence and typed messages, with optional voice and video
// Converts packed 4:2:2 YUV (YUYV / UYVY) straight from the capture buffer to linear RGB.
// The texture is Rgba8Unorm with width = frame_width / 2; each texel holds one pixel pair.
#import bevy_pbr::forward_io::VertexOutput

struct WebcamParams {
    width: u32,
    height: u32,
    flags: u32,
    _pad: u32,
}

const FLAG_MIRROR: u32 = 1u;
const FLAG_UYVY: u32 = 2u;
const FLAG_FULL_RANGE: u32 = 4u;
const FLAG_RGBA: u32 = 8u;

@group(#{MATERIAL_BIND_GROUP}) @binding(0) var frame_tex: texture_2d<f32>;
@group(#{MATERIAL_BIND_GROUP}) @binding(1) var frame_sampler: sampler;
@group(#{MATERIAL_BIND_GROUP}) @binding(2) var<uniform> params: WebcamParams;

fn srgb_to_linear(c: vec3<f32>) -> vec3<f32> {
    let lo = c / 12.92;
    let hi = pow((c + 0.055) / 1.055, vec3<f32>(2.4));
    return select(hi, lo, c <= vec3<f32>(0.04045));
}

fn chroma_of(px: vec4<f32>) -> vec2<f32> {
    if ((params.flags & FLAG_UYVY) != 0u) {
        return vec2<f32>(px.r, px.b);
    }
    return vec2<f32>(px.g, px.a);
}

fn luma_of(px: vec4<f32>, odd: bool) -> f32 {
    if ((params.flags & FLAG_UYVY) != 0u) {
        return select(px.g, px.a, odd);
    }
    return select(px.r, px.b, odd);
}

@fragment
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
    var uv = in.uv;
    if ((params.flags & FLAG_MIRROR) != 0u) {
        uv.x = 1.0 - uv.x;
    }
    if ((params.flags & FLAG_RGBA) != 0u) {
        // sRGB texture: the sampler returns linear.
        return vec4<f32>(textureSample(frame_tex, frame_sampler, uv).rgb, 1.0);
    }
    let w = f32(params.width);
    let h = f32(params.height);
    let fx = clamp(uv.x * w, 0.0, w - 0.001);
    let fy = clamp(uv.y * h, 0.0, h - 0.001);
    let x = u32(fx);
    let y = i32(fy);
    let pair = i32(x / 2u);

    // Luma: nearest pixel.
    let px = textureLoad(frame_tex, vec2<i32>(pair, y), 0);
    let luma = luma_of(px, (x & 1u) == 1u);

    // Chroma: linear interpolation between the two nearest pairs (chroma is co-sited with the
    // even luma sample, i.e. at the left edge of each pair).
    let max_pair = i32(params.width / 2u) - 1;
    let c = fx * 0.5 - 0.5;
    let c0 = i32(floor(c));
    let t = c - f32(c0);
    let pa = textureLoad(frame_tex, vec2<i32>(clamp(c0, 0, max_pair), y), 0);
    let pb = textureLoad(frame_tex, vec2<i32>(clamp(c0 + 1, 0, max_pair), y), 0);
    let chroma = mix(chroma_of(pa), chroma_of(pb), t);

    var yy: f32;
    var cb: f32;
    var cr: f32;
    if ((params.flags & FLAG_FULL_RANGE) != 0u) {
        yy = luma;
        cb = chroma.x - 0.5;
        cr = chroma.y - 0.5;
    } else {
        // BT.601 limited range: Y in [16,235], C in [16,240].
        yy = (luma * 255.0 - 16.0) / 219.0;
        cb = (chroma.x * 255.0 - 128.0) / 224.0;
        cr = (chroma.y * 255.0 - 128.0) / 224.0;
    }
    let r = yy + 1.402 * cr;
    let g = yy - 0.344136 * cb - 0.714136 * cr;
    let b = yy + 1.772 * cb;
    let srgb = clamp(vec3<f32>(r, g, b), vec3<f32>(0.0), vec3<f32>(1.0));
    return vec4<f32>(srgb_to_linear(srgb), 1.0);
}