// Converts a region of RGBA8 pixels into per-pixel match values (0..255):
// mode 0 = mean of RGB, mode 1 = Sobel L1 edge magnitude of the srgb luma.
// Transparent pixels (alpha 0, e.g. unfilled composite padding) additionally
// carry the 0x100 invalid flag so scoring won't treat them as real content.
// Output is written in "match space": rows become columns when transpose == 1,
// so downstream scoring is direction-agnostic.
struct Params {
in_width: u32,
in_height: u32,
out_width: u32,
out_height: u32,
halo_x: u32,
halo_y: u32,
mode: u32,
transpose: u32,
}
@group(0) @binding(0) var<uniform> params: Params;
@group(0) @binding(1) var<storage, read> input: array<u32>;
@group(0) @binding(2) var<storage, read_write> output: array<u32>;
fn luma_at(x: i32, y: i32) -> i32 {
let cx = u32(clamp(x, 0, i32(params.in_width) - 1));
let cy = u32(clamp(y, 0, i32(params.in_height) - 1));
let word = input[cy * params.in_width + cx];
let r = word & 0xffu;
let g = (word >> 8u) & 0xffu;
let b = (word >> 16u) & 0xffu;
return i32((2126u * r + 7152u * g + 722u * b) / 10000u);
}
@compute @workgroup_size(16, 16)
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
let ox = gid.x;
let oy = gid.y;
if ox >= params.out_width || oy >= params.out_height {
return;
}
let ix = i32(ox + params.halo_x);
let iy = i32(oy + params.halo_y);
let own_word = input[u32(iy) * params.in_width + u32(ix)];
var value: u32;
if params.mode == 0u {
let r = own_word & 0xffu;
let g = (own_word >> 8u) & 0xffu;
let b = (own_word >> 16u) & 0xffu;
value = (r + g + b) / 3u;
} else {
let tl = luma_at(ix - 1, iy - 1);
let tc = luma_at(ix, iy - 1);
let tr = luma_at(ix + 1, iy - 1);
let ml = luma_at(ix - 1, iy);
let mr = luma_at(ix + 1, iy);
let bl = luma_at(ix - 1, iy + 1);
let bc = luma_at(ix, iy + 1);
let br = luma_at(ix + 1, iy + 1);
let gx = (tr + 2 * mr + br) - (tl + 2 * ml + bl);
let gy = (bl + 2 * bc + br) - (tl + 2 * tc + tr);
value = u32(abs(gx) + abs(gy)) / 8u;
}
if ((own_word >> 24u) & 0xffu) == 0u {
value |= 0x100u;
}
if params.transpose == 0u {
output[oy * params.out_width + ox] = value;
} else {
output[ox * params.out_height + oy] = value;
}
}