colconv 0.1.0

SIMD-dispatched color-conversion kernels covering the FFmpeg AVPixelFormat space, with a Sink-based API so consumers pick which derived outputs (RGB / Luma / HSV / custom) they want without paying for the ones they don't.
Documentation
//! wasm-simd128 mono1bit parity tests vs scalar reference.
//!
//! These tests are compiled and run only when targeting `wasm32` with
//! `target-feature=+simd128` (e.g. via `cargo test --target wasm32-unknown-unknown`
//! using a wasm-bindgen-test runtime). On native aarch64 / x86_64 hosts the
//! entire wasm-simd128 backend is `cfg`-out and these tests don't compile.

use crate::row::scalar::mono1bit as scalar;

const WIDTHS: &[usize] = &[1, 7, 8, 15, 16, 17, 32, 33, 64, 65, 128, 130];

fn make_data(n_bytes: usize, seed: u32) -> std::vec::Vec<u8> {
  let mut s = seed;
  (0..n_bytes)
    .map(|_| {
      s = s.wrapping_mul(1664525).wrapping_add(1013904223);
      (s >> 16) as u8
    })
    .collect()
}

// ---- Monoblack ---------------------------------------------------------------

#[test]
fn wasm_monoblack_to_rgb_matches_scalar() {
  for &w in WIDTHS {
    let data = make_data(w.div_ceil(8), 0xABCD_1234);
    let mut simd = std::vec![0u8; w * 3];
    let mut scal = std::vec![0u8; w * 3];
    unsafe { super::super::mono1bit::monoblack_to_rgb_row(&data, &mut simd, w) };
    scalar::monoblack_to_rgb_row(&data, &mut scal, w);
    assert_eq!(simd, scal, "monoblack→rgb width={w}");
  }
}

#[test]
fn wasm_monoblack_to_rgba_matches_scalar() {
  for &w in WIDTHS {
    let data = make_data(w.div_ceil(8), 0x5678_ABCD);
    let mut simd = std::vec![0u8; w * 4];
    let mut scal = std::vec![0u8; w * 4];
    unsafe { super::super::mono1bit::monoblack_to_rgba_row(&data, &mut simd, w) };
    scalar::monoblack_to_rgba_row(&data, &mut scal, w);
    assert_eq!(simd, scal, "monoblack→rgba width={w}");
  }
}

#[test]
fn wasm_monoblack_to_luma_matches_scalar() {
  for &w in WIDTHS {
    let data = make_data(w.div_ceil(8), 0xCAFE_BABE);
    let mut simd = std::vec![0u8; w];
    let mut scal = std::vec![0u8; w];
    unsafe { super::super::mono1bit::monoblack_to_luma_row(&data, &mut simd, w) };
    scalar::monoblack_to_luma_row(&data, &mut scal, w);
    assert_eq!(simd, scal, "monoblack→luma width={w}");
  }
}

#[test]
fn wasm_monoblack_to_luma_u16_matches_scalar() {
  for &w in WIDTHS {
    let data = make_data(w.div_ceil(8), 0xDEAD_BEEF);
    let mut simd = std::vec![0u16; w];
    let mut scal = std::vec![0u16; w];
    unsafe { super::super::mono1bit::monoblack_to_luma_u16_row(&data, &mut simd, w) };
    scalar::monoblack_to_luma_u16_row(&data, &mut scal, w);
    assert_eq!(simd, scal, "monoblack→luma_u16 width={w}");
  }
}

#[test]
fn wasm_monoblack_to_rgb_u16_matches_scalar() {
  for &w in WIDTHS {
    let data = make_data(w.div_ceil(8), 0x1234_5678);
    let mut simd = std::vec![0u16; w * 3];
    let mut scal = std::vec![0u16; w * 3];
    unsafe { super::super::mono1bit::monoblack_to_rgb_u16_row(&data, &mut simd, w) };
    scalar::monoblack_to_rgb_u16_row(&data, &mut scal, w);
    assert_eq!(simd, scal, "monoblack→rgb_u16 width={w}");
  }
}

#[test]
fn wasm_monoblack_to_rgba_u16_matches_scalar() {
  for &w in WIDTHS {
    let data = make_data(w.div_ceil(8), 0xFEDC_BA98);
    let mut simd = std::vec![0u16; w * 4];
    let mut scal = std::vec![0u16; w * 4];
    unsafe { super::super::mono1bit::monoblack_to_rgba_u16_row(&data, &mut simd, w) };
    scalar::monoblack_to_rgba_u16_row(&data, &mut scal, w);
    assert_eq!(simd, scal, "monoblack→rgba_u16 width={w}");
  }
}

#[test]
fn wasm_monoblack_to_hsv_matches_scalar() {
  for &w in WIDTHS {
    let data = make_data(w.div_ceil(8), 0xA1B2_C3D4);
    let mut sh = std::vec![0u8; w];
    let mut ss = std::vec![0u8; w];
    let mut sv = std::vec![0u8; w];
    let mut rh = std::vec![0u8; w];
    let mut rs = std::vec![0u8; w];
    let mut rv = std::vec![0u8; w];
    unsafe { super::super::mono1bit::monoblack_to_hsv_row(&data, &mut sh, &mut ss, &mut sv, w) };
    scalar::monoblack_to_hsv_row(&data, &mut rh, &mut rs, &mut rv, w);
    assert_eq!(sh, rh, "monoblack→hsv H width={w}");
    assert_eq!(ss, rs, "monoblack→hsv S width={w}");
    assert_eq!(sv, rv, "monoblack→hsv V width={w}");
  }
}

// ---- Monowhite ---------------------------------------------------------------

#[test]
fn wasm_monowhite_to_rgb_matches_scalar() {
  for &w in WIDTHS {
    let data = make_data(w.div_ceil(8), 0x1111_2222);
    let mut simd = std::vec![0u8; w * 3];
    let mut scal = std::vec![0u8; w * 3];
    unsafe { super::super::mono1bit::monowhite_to_rgb_row(&data, &mut simd, w) };
    scalar::monowhite_to_rgb_row(&data, &mut scal, w);
    assert_eq!(simd, scal, "monowhite→rgb width={w}");
  }
}

#[test]
fn wasm_monowhite_to_rgba_matches_scalar() {
  for &w in WIDTHS {
    let data = make_data(w.div_ceil(8), 0x3333_4444);
    let mut simd = std::vec![0u8; w * 4];
    let mut scal = std::vec![0u8; w * 4];
    unsafe { super::super::mono1bit::monowhite_to_rgba_row(&data, &mut simd, w) };
    scalar::monowhite_to_rgba_row(&data, &mut scal, w);
    assert_eq!(simd, scal, "monowhite→rgba width={w}");
  }
}

#[test]
fn wasm_monowhite_to_luma_matches_scalar() {
  for &w in WIDTHS {
    let data = make_data(w.div_ceil(8), 0x5555_6666);
    let mut simd = std::vec![0u8; w];
    let mut scal = std::vec![0u8; w];
    unsafe { super::super::mono1bit::monowhite_to_luma_row(&data, &mut simd, w) };
    scalar::monowhite_to_luma_row(&data, &mut scal, w);
    assert_eq!(simd, scal, "monowhite→luma width={w}");
  }
}

#[test]
fn wasm_monowhite_to_luma_u16_matches_scalar() {
  for &w in WIDTHS {
    let data = make_data(w.div_ceil(8), 0x7777_8888);
    let mut simd = std::vec![0u16; w];
    let mut scal = std::vec![0u16; w];
    unsafe { super::super::mono1bit::monowhite_to_luma_u16_row(&data, &mut simd, w) };
    scalar::monowhite_to_luma_u16_row(&data, &mut scal, w);
    assert_eq!(simd, scal, "monowhite→luma_u16 width={w}");
  }
}

#[test]
fn wasm_monowhite_to_rgb_u16_matches_scalar() {
  for &w in WIDTHS {
    let data = make_data(w.div_ceil(8), 0x9999_AAAA);
    let mut simd = std::vec![0u16; w * 3];
    let mut scal = std::vec![0u16; w * 3];
    unsafe { super::super::mono1bit::monowhite_to_rgb_u16_row(&data, &mut simd, w) };
    scalar::monowhite_to_rgb_u16_row(&data, &mut scal, w);
    assert_eq!(simd, scal, "monowhite→rgb_u16 width={w}");
  }
}

#[test]
fn wasm_monowhite_to_rgba_u16_matches_scalar() {
  for &w in WIDTHS {
    let data = make_data(w.div_ceil(8), 0xBBBB_CCCC);
    let mut simd = std::vec![0u16; w * 4];
    let mut scal = std::vec![0u16; w * 4];
    unsafe { super::super::mono1bit::monowhite_to_rgba_u16_row(&data, &mut simd, w) };
    scalar::monowhite_to_rgba_u16_row(&data, &mut scal, w);
    assert_eq!(simd, scal, "monowhite→rgba_u16 width={w}");
  }
}

#[test]
fn wasm_monowhite_to_hsv_matches_scalar() {
  for &w in WIDTHS {
    let data = make_data(w.div_ceil(8), 0xDDDD_EEEE);
    let mut sh = std::vec![0u8; w];
    let mut ss = std::vec![0u8; w];
    let mut sv = std::vec![0u8; w];
    let mut rh = std::vec![0u8; w];
    let mut rs = std::vec![0u8; w];
    let mut rv = std::vec![0u8; w];
    unsafe { super::super::mono1bit::monowhite_to_hsv_row(&data, &mut sh, &mut ss, &mut sv, w) };
    scalar::monowhite_to_hsv_row(&data, &mut rh, &mut rs, &mut rv, w);
    assert_eq!(sh, rh, "monowhite→hsv H width={w}");
    assert_eq!(ss, rs, "monowhite→hsv S width={w}");
    assert_eq!(sv, rv, "monowhite→hsv V width={w}");
  }
}