wai-web: expose the byte-exact JPEG-AI mANS decode to wasm (decodeJpegAiMans) #51

Merged
dcharlot merged 1 commit from wai-jpegai-wasm-demo into main 2026-08-01 19:26:06 -04:00

View file

@ -133,3 +133,42 @@ pub fn det_build_index(sigma_q: i64, edges_q: &[i64]) -> usize {
pub fn blake3_hex(bytes: &[u8]) -> String {
blake3::hash(bytes).to_hex().to_string()
}
// ---- JPEG-AI byte-exact mANS: the ISO entropy decode, in the browser ----
/// Decode the ISO/IEC 6048 JPEG-AI **mANS** entropy stream in the browser, byte-exact —
/// the same pure-i64 decoder native sinks run (`wai::jpegai_mans`), compiled to wasm32.
///
/// A **reproduction vector**: given the reference stream `mem`, the integer PMF rows
/// (`pmf_flat`, `n_rows × 256` row-major), the per-distribution `bounds`, and the
/// per-position `sigma` distribution indices + coded-position `masks` (0/1), it returns
/// the decoded residual symbols. The decode is entirely integer, so the symbols — and
/// their hash — are identical on every machine. The page hashes them and compares to the
/// digest the native reference produced; a byte-identical match across host + browser is
/// the whole point of the integer-entropy contract. This is the same stream and
/// symbols the cross-architecture byte-equality matrix checks natively.
#[wasm_bindgen(js_name = decodeJpegAiMans)]
pub fn decode_jpegai_mans(
mem: &[u8],
pmf_flat: &[i64],
bounds: &[u8],
sigma: &[u8],
masks: &[u8],
) -> Vec<i16> {
use wai::jpegai_mans::{build_decode_transitions, cdf_from_pmf, MansDecoder};
const WIDTH: usize = 256;
let rows = pmf_flat.len() / WIDTH;
let pmf: Vec<Vec<i64>> =
(0..rows).map(|r| pmf_flat[r * WIDTH..(r + 1) * WIDTH].to_vec()).collect();
let flat: Vec<u32> = build_decode_transitions(&pmf, &cdf_from_pmf(&pmf))
.iter()
.flatten()
.copied()
.collect();
let mut dec = MansDecoder::new(mem, &[mem.len() as u32]);
dec.set_sgm_transitions(&flat, bounds);
let masks_b: Vec<bool> = masks.iter().map(|&m| m != 0).collect();
let mut out = vec![0i16; sigma.len()];
dec.decode_sgm(sigma, &mut out, &masks_b);
out
}