#![allow(dead_code)]
#[cfg(any(
target_arch = "aarch64",
target_arch = "x86_64",
all(target_arch = "wasm32", target_feature = "simd128")
))]
use crate::row::arch;
#[cfg(target_arch = "aarch64")]
use crate::row::neon_available;
#[cfg(target_arch = "x86_64")]
use crate::row::{avx2_available, avx512_available, sse41_available};
use crate::{
ColorMatrix,
row::{rgb_row_bytes, rgb_row_elems, rgba_row_bytes, rgba_row_elems, scalar},
};
#[cfg_attr(not(tarpaulin), inline(always))]
fn rgb48_packed_elems(width: usize) -> usize {
match width.checked_mul(3) {
Some(n) => n,
None => panic!("width ({width}) x 3 overflows usize (Rgb48/Bgr48 packed row)"),
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn rgba64_packed_elems(width: usize) -> usize {
match width.checked_mul(4) {
Some(n) => n,
None => panic!("width ({width}) x 4 overflows usize (Rgba64/Bgra64 packed row)"),
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgb48_to_rgb_row_endian<const BE: bool>(
rgb48: &[u16],
rgb_out: &mut [u8],
width: usize,
use_simd: bool,
) {
let in_min = rgb48_packed_elems(width);
let out_min = rgb_row_bytes(width);
assert!(rgb48.len() >= in_min, "rgb48 row too short");
assert!(rgb_out.len() >= out_min, "rgb_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_rgb48_to_rgb_row::<BE>(rgb48, rgb_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_rgb48_to_rgb_row::<BE>(rgb48, rgb_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_rgb48_to_rgb_row::<BE>(rgb48, rgb_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_rgb48_to_rgb_row::<BE>(rgb48, rgb_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_rgb48_to_rgb_row::<BE>(rgb48, rgb_out, width); }
return;
},
_ => {}
}
}
scalar::rgb48_to_rgb_row::<BE>(rgb48, rgb_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgb48_to_rgb_row(rgb48: &[u16], rgb_out: &mut [u8], width: usize, use_simd: bool) {
rgb48_to_rgb_row_endian::<false>(rgb48, rgb_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgb48_to_rgba_row_endian<const BE: bool>(
rgb48: &[u16],
rgba_out: &mut [u8],
width: usize,
use_simd: bool,
) {
let in_min = rgb48_packed_elems(width);
let out_min = rgba_row_bytes(width);
assert!(rgb48.len() >= in_min, "rgb48 row too short");
assert!(rgba_out.len() >= out_min, "rgba_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_rgb48_to_rgba_row::<BE>(rgb48, rgba_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_rgb48_to_rgba_row::<BE>(rgb48, rgba_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_rgb48_to_rgba_row::<BE>(rgb48, rgba_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_rgb48_to_rgba_row::<BE>(rgb48, rgba_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_rgb48_to_rgba_row::<BE>(rgb48, rgba_out, width); }
return;
},
_ => {}
}
}
scalar::rgb48_to_rgba_row::<BE>(rgb48, rgba_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgb48_to_rgba_row(rgb48: &[u16], rgba_out: &mut [u8], width: usize, use_simd: bool) {
rgb48_to_rgba_row_endian::<false>(rgb48, rgba_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgb48_to_rgb_u16_row_endian<const BE: bool>(
rgb48: &[u16],
rgb_out: &mut [u16],
width: usize,
use_simd: bool,
) {
let in_min = rgb48_packed_elems(width);
let out_min = rgb_row_elems(width);
assert!(rgb48.len() >= in_min, "rgb48 row too short");
assert!(rgb_out.len() >= out_min, "rgb_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_rgb48_to_rgb_u16_row::<BE>(rgb48, rgb_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_rgb48_to_rgb_u16_row::<BE>(rgb48, rgb_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_rgb48_to_rgb_u16_row::<BE>(rgb48, rgb_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_rgb48_to_rgb_u16_row::<BE>(rgb48, rgb_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_rgb48_to_rgb_u16_row::<BE>(rgb48, rgb_out, width); }
return;
},
_ => {}
}
}
scalar::rgb48_to_rgb_u16_row::<BE>(rgb48, rgb_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgb48_to_rgb_u16_row(rgb48: &[u16], rgb_out: &mut [u16], width: usize, use_simd: bool) {
rgb48_to_rgb_u16_row_endian::<false>(rgb48, rgb_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgb48_to_rgba_u16_row_endian<const BE: bool>(
rgb48: &[u16],
rgba_out: &mut [u16],
width: usize,
use_simd: bool,
) {
let in_min = rgb48_packed_elems(width);
let out_min = rgba_row_elems(width);
assert!(rgb48.len() >= in_min, "rgb48 row too short");
assert!(rgba_out.len() >= out_min, "rgba_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_rgb48_to_rgba_u16_row::<BE>(rgb48, rgba_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_rgb48_to_rgba_u16_row::<BE>(rgb48, rgba_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_rgb48_to_rgba_u16_row::<BE>(rgb48, rgba_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_rgb48_to_rgba_u16_row::<BE>(rgb48, rgba_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_rgb48_to_rgba_u16_row::<BE>(rgb48, rgba_out, width); }
return;
},
_ => {}
}
}
scalar::rgb48_to_rgba_u16_row::<BE>(rgb48, rgba_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgb48_to_rgba_u16_row(rgb48: &[u16], rgba_out: &mut [u16], width: usize, use_simd: bool) {
rgb48_to_rgba_u16_row_endian::<false>(rgb48, rgba_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub fn rgb48_to_luma_row_endian<const BE: bool>(
rgb48: &[u16],
luma_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
let in_min = rgb48_packed_elems(width);
let scratch_min = rgb_row_bytes(width);
assert!(rgb48.len() >= in_min, "rgb48 row too short");
assert!(rgb_scratch.len() >= scratch_min, "rgb_scratch too short");
assert!(luma_out.len() >= width, "luma_out row too short");
rgb48_to_rgb_row_endian::<BE>(rgb48, rgb_scratch, width, use_simd);
scalar::rgb_to_luma_row(rgb_scratch, luma_out, width, matrix, full_range);
}
#[allow(clippy::too_many_arguments)]
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgb48_to_luma_row(
rgb48: &[u16],
luma_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
rgb48_to_luma_row_endian::<false>(
rgb48,
luma_out,
rgb_scratch,
width,
matrix,
full_range,
use_simd,
)
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub fn rgb48_to_luma_u16_row_endian<const BE: bool>(
rgb48: &[u16],
luma_out: &mut [u16],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
let in_min = rgb48_packed_elems(width);
let scratch_min = rgb_row_bytes(width);
assert!(rgb48.len() >= in_min, "rgb48 row too short");
assert!(rgb_scratch.len() >= scratch_min, "rgb_scratch too short");
assert!(luma_out.len() >= width, "luma_out row too short");
rgb48_to_rgb_row_endian::<BE>(rgb48, rgb_scratch, width, use_simd);
scalar::rgb_to_luma_u16_row(rgb_scratch, luma_out, width, matrix, full_range);
}
#[allow(clippy::too_many_arguments)]
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgb48_to_luma_u16_row(
rgb48: &[u16],
luma_out: &mut [u16],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
rgb48_to_luma_u16_row_endian::<false>(
rgb48,
luma_out,
rgb_scratch,
width,
matrix,
full_range,
use_simd,
)
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub fn rgb48_to_hsv_row_endian<const BE: bool>(
rgb48: &[u16],
h_out: &mut [u8],
s_out: &mut [u8],
v_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
use_simd: bool,
) {
let in_min = rgb48_packed_elems(width);
let scratch_min = rgb_row_bytes(width);
assert!(rgb48.len() >= in_min, "rgb48 row too short");
assert!(rgb_scratch.len() >= scratch_min, "rgb_scratch too short");
assert!(h_out.len() >= width, "h_out row too short");
assert!(s_out.len() >= width, "s_out row too short");
assert!(v_out.len() >= width, "v_out row too short");
rgb48_to_rgb_row_endian::<BE>(rgb48, rgb_scratch, width, use_simd);
scalar::rgb_to_hsv_row(rgb_scratch, h_out, s_out, v_out, width);
}
#[allow(clippy::too_many_arguments)]
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgb48_to_hsv_row(
rgb48: &[u16],
h_out: &mut [u8],
s_out: &mut [u8],
v_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
use_simd: bool,
) {
rgb48_to_hsv_row_endian::<false>(rgb48, h_out, s_out, v_out, rgb_scratch, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgr48_to_rgb_row_endian<const BE: bool>(
bgr48: &[u16],
rgb_out: &mut [u8],
width: usize,
use_simd: bool,
) {
let in_min = rgb48_packed_elems(width);
let out_min = rgb_row_bytes(width);
assert!(bgr48.len() >= in_min, "bgr48 row too short");
assert!(rgb_out.len() >= out_min, "rgb_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_bgr48_to_rgb_row::<BE>(bgr48, rgb_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_bgr48_to_rgb_row::<BE>(bgr48, rgb_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_bgr48_to_rgb_row::<BE>(bgr48, rgb_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_bgr48_to_rgb_row::<BE>(bgr48, rgb_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_bgr48_to_rgb_row::<BE>(bgr48, rgb_out, width); }
return;
},
_ => {}
}
}
scalar::bgr48_to_rgb_row::<BE>(bgr48, rgb_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgr48_to_rgb_row(bgr48: &[u16], rgb_out: &mut [u8], width: usize, use_simd: bool) {
bgr48_to_rgb_row_endian::<false>(bgr48, rgb_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgr48_to_rgba_row_endian<const BE: bool>(
bgr48: &[u16],
rgba_out: &mut [u8],
width: usize,
use_simd: bool,
) {
let in_min = rgb48_packed_elems(width);
let out_min = rgba_row_bytes(width);
assert!(bgr48.len() >= in_min, "bgr48 row too short");
assert!(rgba_out.len() >= out_min, "rgba_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_bgr48_to_rgba_row::<BE>(bgr48, rgba_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_bgr48_to_rgba_row::<BE>(bgr48, rgba_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_bgr48_to_rgba_row::<BE>(bgr48, rgba_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_bgr48_to_rgba_row::<BE>(bgr48, rgba_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_bgr48_to_rgba_row::<BE>(bgr48, rgba_out, width); }
return;
},
_ => {}
}
}
scalar::bgr48_to_rgba_row::<BE>(bgr48, rgba_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgr48_to_rgba_row(bgr48: &[u16], rgba_out: &mut [u8], width: usize, use_simd: bool) {
bgr48_to_rgba_row_endian::<false>(bgr48, rgba_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgr48_to_rgb_u16_row_endian<const BE: bool>(
bgr48: &[u16],
rgb_out: &mut [u16],
width: usize,
use_simd: bool,
) {
let in_min = rgb48_packed_elems(width);
let out_min = rgb_row_elems(width);
assert!(bgr48.len() >= in_min, "bgr48 row too short");
assert!(rgb_out.len() >= out_min, "rgb_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_bgr48_to_rgb_u16_row::<BE>(bgr48, rgb_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_bgr48_to_rgb_u16_row::<BE>(bgr48, rgb_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_bgr48_to_rgb_u16_row::<BE>(bgr48, rgb_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_bgr48_to_rgb_u16_row::<BE>(bgr48, rgb_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_bgr48_to_rgb_u16_row::<BE>(bgr48, rgb_out, width); }
return;
},
_ => {}
}
}
scalar::bgr48_to_rgb_u16_row::<BE>(bgr48, rgb_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgr48_to_rgb_u16_row(bgr48: &[u16], rgb_out: &mut [u16], width: usize, use_simd: bool) {
bgr48_to_rgb_u16_row_endian::<false>(bgr48, rgb_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgr48_to_rgba_u16_row_endian<const BE: bool>(
bgr48: &[u16],
rgba_out: &mut [u16],
width: usize,
use_simd: bool,
) {
let in_min = rgb48_packed_elems(width);
let out_min = rgba_row_elems(width);
assert!(bgr48.len() >= in_min, "bgr48 row too short");
assert!(rgba_out.len() >= out_min, "rgba_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_bgr48_to_rgba_u16_row::<BE>(bgr48, rgba_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_bgr48_to_rgba_u16_row::<BE>(bgr48, rgba_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_bgr48_to_rgba_u16_row::<BE>(bgr48, rgba_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_bgr48_to_rgba_u16_row::<BE>(bgr48, rgba_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_bgr48_to_rgba_u16_row::<BE>(bgr48, rgba_out, width); }
return;
},
_ => {}
}
}
scalar::bgr48_to_rgba_u16_row::<BE>(bgr48, rgba_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgr48_to_rgba_u16_row(bgr48: &[u16], rgba_out: &mut [u16], width: usize, use_simd: bool) {
bgr48_to_rgba_u16_row_endian::<false>(bgr48, rgba_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub fn bgr48_to_luma_row_endian<const BE: bool>(
bgr48: &[u16],
luma_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
let in_min = rgb48_packed_elems(width);
let scratch_min = rgb_row_bytes(width);
assert!(bgr48.len() >= in_min, "bgr48 row too short");
assert!(rgb_scratch.len() >= scratch_min, "rgb_scratch too short");
assert!(luma_out.len() >= width, "luma_out row too short");
bgr48_to_rgb_row_endian::<BE>(bgr48, rgb_scratch, width, use_simd);
scalar::rgb_to_luma_row(rgb_scratch, luma_out, width, matrix, full_range);
}
#[allow(clippy::too_many_arguments)]
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgr48_to_luma_row(
bgr48: &[u16],
luma_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
bgr48_to_luma_row_endian::<false>(
bgr48,
luma_out,
rgb_scratch,
width,
matrix,
full_range,
use_simd,
)
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub fn bgr48_to_luma_u16_row_endian<const BE: bool>(
bgr48: &[u16],
luma_out: &mut [u16],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
let in_min = rgb48_packed_elems(width);
let scratch_min = rgb_row_bytes(width);
assert!(bgr48.len() >= in_min, "bgr48 row too short");
assert!(rgb_scratch.len() >= scratch_min, "rgb_scratch too short");
assert!(luma_out.len() >= width, "luma_out row too short");
bgr48_to_rgb_row_endian::<BE>(bgr48, rgb_scratch, width, use_simd);
scalar::rgb_to_luma_u16_row(rgb_scratch, luma_out, width, matrix, full_range);
}
#[allow(clippy::too_many_arguments)]
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgr48_to_luma_u16_row(
bgr48: &[u16],
luma_out: &mut [u16],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
bgr48_to_luma_u16_row_endian::<false>(
bgr48,
luma_out,
rgb_scratch,
width,
matrix,
full_range,
use_simd,
)
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub fn bgr48_to_hsv_row_endian<const BE: bool>(
bgr48: &[u16],
h_out: &mut [u8],
s_out: &mut [u8],
v_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
use_simd: bool,
) {
let in_min = rgb48_packed_elems(width);
let scratch_min = rgb_row_bytes(width);
assert!(bgr48.len() >= in_min, "bgr48 row too short");
assert!(rgb_scratch.len() >= scratch_min, "rgb_scratch too short");
assert!(h_out.len() >= width, "h_out row too short");
assert!(s_out.len() >= width, "s_out row too short");
assert!(v_out.len() >= width, "v_out row too short");
bgr48_to_rgb_row_endian::<BE>(bgr48, rgb_scratch, width, use_simd);
scalar::rgb_to_hsv_row(rgb_scratch, h_out, s_out, v_out, width);
}
#[allow(clippy::too_many_arguments)]
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgr48_to_hsv_row(
bgr48: &[u16],
h_out: &mut [u8],
s_out: &mut [u8],
v_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
use_simd: bool,
) {
bgr48_to_hsv_row_endian::<false>(bgr48, h_out, s_out, v_out, rgb_scratch, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgba64_to_rgb_row_endian<const BE: bool>(
rgba64: &[u16],
rgb_out: &mut [u8],
width: usize,
use_simd: bool,
) {
let in_min = rgba64_packed_elems(width);
let out_min = rgb_row_bytes(width);
assert!(rgba64.len() >= in_min, "rgba64 row too short");
assert!(rgb_out.len() >= out_min, "rgb_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_rgba64_to_rgb_row::<BE>(rgba64, rgb_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_rgba64_to_rgb_row::<BE>(rgba64, rgb_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_rgba64_to_rgb_row::<BE>(rgba64, rgb_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_rgba64_to_rgb_row::<BE>(rgba64, rgb_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_rgba64_to_rgb_row::<BE>(rgba64, rgb_out, width); }
return;
},
_ => {}
}
}
scalar::rgba64_to_rgb_row::<BE>(rgba64, rgb_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgba64_to_rgb_row(rgba64: &[u16], rgb_out: &mut [u8], width: usize, use_simd: bool) {
rgba64_to_rgb_row_endian::<false>(rgba64, rgb_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgba64_to_rgba_row_endian<const BE: bool>(
rgba64: &[u16],
rgba_out: &mut [u8],
width: usize,
use_simd: bool,
) {
let in_min = rgba64_packed_elems(width);
let out_min = rgba_row_bytes(width);
assert!(rgba64.len() >= in_min, "rgba64 row too short");
assert!(rgba_out.len() >= out_min, "rgba_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_rgba64_to_rgba_row::<BE>(rgba64, rgba_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_rgba64_to_rgba_row::<BE>(rgba64, rgba_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_rgba64_to_rgba_row::<BE>(rgba64, rgba_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_rgba64_to_rgba_row::<BE>(rgba64, rgba_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_rgba64_to_rgba_row::<BE>(rgba64, rgba_out, width); }
return;
},
_ => {}
}
}
scalar::rgba64_to_rgba_row::<BE>(rgba64, rgba_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgba64_to_rgba_row(rgba64: &[u16], rgba_out: &mut [u8], width: usize, use_simd: bool) {
rgba64_to_rgba_row_endian::<false>(rgba64, rgba_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgba64_to_rgb_u16_row_endian<const BE: bool>(
rgba64: &[u16],
rgb_out: &mut [u16],
width: usize,
use_simd: bool,
) {
let in_min = rgba64_packed_elems(width);
let out_min = rgb_row_elems(width);
assert!(rgba64.len() >= in_min, "rgba64 row too short");
assert!(rgb_out.len() >= out_min, "rgb_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_rgba64_to_rgb_u16_row::<BE>(rgba64, rgb_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_rgba64_to_rgb_u16_row::<BE>(rgba64, rgb_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_rgba64_to_rgb_u16_row::<BE>(rgba64, rgb_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_rgba64_to_rgb_u16_row::<BE>(rgba64, rgb_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_rgba64_to_rgb_u16_row::<BE>(rgba64, rgb_out, width); }
return;
},
_ => {}
}
}
scalar::rgba64_to_rgb_u16_row::<BE>(rgba64, rgb_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgba64_to_rgb_u16_row(rgba64: &[u16], rgb_out: &mut [u16], width: usize, use_simd: bool) {
rgba64_to_rgb_u16_row_endian::<false>(rgba64, rgb_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgba64_to_rgba_u16_row_endian<const BE: bool>(
rgba64: &[u16],
rgba_out: &mut [u16],
width: usize,
use_simd: bool,
) {
let in_min = rgba64_packed_elems(width);
let out_min = rgba_row_elems(width);
assert!(rgba64.len() >= in_min, "rgba64 row too short");
assert!(rgba_out.len() >= out_min, "rgba_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_rgba64_to_rgba_u16_row::<BE>(rgba64, rgba_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_rgba64_to_rgba_u16_row::<BE>(rgba64, rgba_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_rgba64_to_rgba_u16_row::<BE>(rgba64, rgba_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_rgba64_to_rgba_u16_row::<BE>(rgba64, rgba_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_rgba64_to_rgba_u16_row::<BE>(rgba64, rgba_out, width); }
return;
},
_ => {}
}
}
scalar::rgba64_to_rgba_u16_row::<BE>(rgba64, rgba_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgba64_to_rgba_u16_row(rgba64: &[u16], rgba_out: &mut [u16], width: usize, use_simd: bool) {
rgba64_to_rgba_u16_row_endian::<false>(rgba64, rgba_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub fn rgba64_to_luma_row_endian<const BE: bool>(
rgba64: &[u16],
luma_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
let in_min = rgba64_packed_elems(width);
let scratch_min = rgb_row_bytes(width);
assert!(rgba64.len() >= in_min, "rgba64 row too short");
assert!(rgb_scratch.len() >= scratch_min, "rgb_scratch too short");
assert!(luma_out.len() >= width, "luma_out row too short");
rgba64_to_rgb_row_endian::<BE>(rgba64, rgb_scratch, width, use_simd);
scalar::rgb_to_luma_row(rgb_scratch, luma_out, width, matrix, full_range);
}
#[allow(clippy::too_many_arguments)]
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgba64_to_luma_row(
rgba64: &[u16],
luma_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
rgba64_to_luma_row_endian::<false>(
rgba64,
luma_out,
rgb_scratch,
width,
matrix,
full_range,
use_simd,
)
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub fn rgba64_to_luma_u16_row_endian<const BE: bool>(
rgba64: &[u16],
luma_out: &mut [u16],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
let in_min = rgba64_packed_elems(width);
let scratch_min = rgb_row_bytes(width);
assert!(rgba64.len() >= in_min, "rgba64 row too short");
assert!(rgb_scratch.len() >= scratch_min, "rgb_scratch too short");
assert!(luma_out.len() >= width, "luma_out row too short");
rgba64_to_rgb_row_endian::<BE>(rgba64, rgb_scratch, width, use_simd);
scalar::rgb_to_luma_u16_row(rgb_scratch, luma_out, width, matrix, full_range);
}
#[allow(clippy::too_many_arguments)]
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgba64_to_luma_u16_row(
rgba64: &[u16],
luma_out: &mut [u16],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
rgba64_to_luma_u16_row_endian::<false>(
rgba64,
luma_out,
rgb_scratch,
width,
matrix,
full_range,
use_simd,
)
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub fn rgba64_to_hsv_row_endian<const BE: bool>(
rgba64: &[u16],
h_out: &mut [u8],
s_out: &mut [u8],
v_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
use_simd: bool,
) {
let in_min = rgba64_packed_elems(width);
let scratch_min = rgb_row_bytes(width);
assert!(rgba64.len() >= in_min, "rgba64 row too short");
assert!(rgb_scratch.len() >= scratch_min, "rgb_scratch too short");
assert!(h_out.len() >= width, "h_out row too short");
assert!(s_out.len() >= width, "s_out row too short");
assert!(v_out.len() >= width, "v_out row too short");
rgba64_to_rgb_row_endian::<BE>(rgba64, rgb_scratch, width, use_simd);
scalar::rgb_to_hsv_row(rgb_scratch, h_out, s_out, v_out, width);
}
#[allow(clippy::too_many_arguments)]
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn rgba64_to_hsv_row(
rgba64: &[u16],
h_out: &mut [u8],
s_out: &mut [u8],
v_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
use_simd: bool,
) {
rgba64_to_hsv_row_endian::<false>(rgba64, h_out, s_out, v_out, rgb_scratch, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgra64_to_rgb_row_endian<const BE: bool>(
bgra64: &[u16],
rgb_out: &mut [u8],
width: usize,
use_simd: bool,
) {
let in_min = rgba64_packed_elems(width);
let out_min = rgb_row_bytes(width);
assert!(bgra64.len() >= in_min, "bgra64 row too short");
assert!(rgb_out.len() >= out_min, "rgb_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_bgra64_to_rgb_row::<BE>(bgra64, rgb_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_bgra64_to_rgb_row::<BE>(bgra64, rgb_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_bgra64_to_rgb_row::<BE>(bgra64, rgb_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_bgra64_to_rgb_row::<BE>(bgra64, rgb_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_bgra64_to_rgb_row::<BE>(bgra64, rgb_out, width); }
return;
},
_ => {}
}
}
scalar::bgra64_to_rgb_row::<BE>(bgra64, rgb_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgra64_to_rgb_row(bgra64: &[u16], rgb_out: &mut [u8], width: usize, use_simd: bool) {
bgra64_to_rgb_row_endian::<false>(bgra64, rgb_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgra64_to_rgba_row_endian<const BE: bool>(
bgra64: &[u16],
rgba_out: &mut [u8],
width: usize,
use_simd: bool,
) {
let in_min = rgba64_packed_elems(width);
let out_min = rgba_row_bytes(width);
assert!(bgra64.len() >= in_min, "bgra64 row too short");
assert!(rgba_out.len() >= out_min, "rgba_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_bgra64_to_rgba_row::<BE>(bgra64, rgba_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_bgra64_to_rgba_row::<BE>(bgra64, rgba_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_bgra64_to_rgba_row::<BE>(bgra64, rgba_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_bgra64_to_rgba_row::<BE>(bgra64, rgba_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_bgra64_to_rgba_row::<BE>(bgra64, rgba_out, width); }
return;
},
_ => {}
}
}
scalar::bgra64_to_rgba_row::<BE>(bgra64, rgba_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgra64_to_rgba_row(bgra64: &[u16], rgba_out: &mut [u8], width: usize, use_simd: bool) {
bgra64_to_rgba_row_endian::<false>(bgra64, rgba_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgra64_to_rgb_u16_row_endian<const BE: bool>(
bgra64: &[u16],
rgb_out: &mut [u16],
width: usize,
use_simd: bool,
) {
let in_min = rgba64_packed_elems(width);
let out_min = rgb_row_elems(width);
assert!(bgra64.len() >= in_min, "bgra64 row too short");
assert!(rgb_out.len() >= out_min, "rgb_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_bgra64_to_rgb_u16_row::<BE>(bgra64, rgb_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_bgra64_to_rgb_u16_row::<BE>(bgra64, rgb_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_bgra64_to_rgb_u16_row::<BE>(bgra64, rgb_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_bgra64_to_rgb_u16_row::<BE>(bgra64, rgb_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_bgra64_to_rgb_u16_row::<BE>(bgra64, rgb_out, width); }
return;
},
_ => {}
}
}
scalar::bgra64_to_rgb_u16_row::<BE>(bgra64, rgb_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgra64_to_rgb_u16_row(bgra64: &[u16], rgb_out: &mut [u16], width: usize, use_simd: bool) {
bgra64_to_rgb_u16_row_endian::<false>(bgra64, rgb_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgra64_to_rgba_u16_row_endian<const BE: bool>(
bgra64: &[u16],
rgba_out: &mut [u16],
width: usize,
use_simd: bool,
) {
let in_min = rgba64_packed_elems(width);
let out_min = rgba_row_elems(width);
assert!(bgra64.len() >= in_min, "bgra64 row too short");
assert!(rgba_out.len() >= out_min, "rgba_out row too short");
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
unsafe { arch::neon::neon_bgra64_to_rgba_u16_row::<BE>(bgra64, rgba_out, width); }
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
unsafe { arch::x86_avx512::avx512_bgra64_to_rgba_u16_row::<BE>(bgra64, rgba_out, width); }
return;
}
if avx2_available() {
unsafe { arch::x86_avx2::avx2_bgra64_to_rgba_u16_row::<BE>(bgra64, rgba_out, width); }
return;
}
if sse41_available() {
unsafe { arch::x86_sse41::sse41_bgra64_to_rgba_u16_row::<BE>(bgra64, rgba_out, width); }
return;
}
},
all(target_arch = "wasm32", target_feature = "simd128") => {
unsafe { arch::wasm_simd128::wasm_bgra64_to_rgba_u16_row::<BE>(bgra64, rgba_out, width); }
return;
},
_ => {}
}
}
scalar::bgra64_to_rgba_u16_row::<BE>(bgra64, rgba_out, width);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgra64_to_rgba_u16_row(bgra64: &[u16], rgba_out: &mut [u16], width: usize, use_simd: bool) {
bgra64_to_rgba_u16_row_endian::<false>(bgra64, rgba_out, width, use_simd)
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub fn bgra64_to_luma_row_endian<const BE: bool>(
bgra64: &[u16],
luma_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
let in_min = rgba64_packed_elems(width);
let scratch_min = rgb_row_bytes(width);
assert!(bgra64.len() >= in_min, "bgra64 row too short");
assert!(rgb_scratch.len() >= scratch_min, "rgb_scratch too short");
assert!(luma_out.len() >= width, "luma_out row too short");
bgra64_to_rgb_row_endian::<BE>(bgra64, rgb_scratch, width, use_simd);
scalar::rgb_to_luma_row(rgb_scratch, luma_out, width, matrix, full_range);
}
#[allow(clippy::too_many_arguments)]
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgra64_to_luma_row(
bgra64: &[u16],
luma_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
bgra64_to_luma_row_endian::<false>(
bgra64,
luma_out,
rgb_scratch,
width,
matrix,
full_range,
use_simd,
)
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub fn bgra64_to_luma_u16_row_endian<const BE: bool>(
bgra64: &[u16],
luma_out: &mut [u16],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
let in_min = rgba64_packed_elems(width);
let scratch_min = rgb_row_bytes(width);
assert!(bgra64.len() >= in_min, "bgra64 row too short");
assert!(rgb_scratch.len() >= scratch_min, "rgb_scratch too short");
assert!(luma_out.len() >= width, "luma_out row too short");
bgra64_to_rgb_row_endian::<BE>(bgra64, rgb_scratch, width, use_simd);
scalar::rgb_to_luma_u16_row(rgb_scratch, luma_out, width, matrix, full_range);
}
#[allow(clippy::too_many_arguments)]
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgra64_to_luma_u16_row(
bgra64: &[u16],
luma_out: &mut [u16],
rgb_scratch: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
bgra64_to_luma_u16_row_endian::<false>(
bgra64,
luma_out,
rgb_scratch,
width,
matrix,
full_range,
use_simd,
)
}
#[cfg_attr(not(tarpaulin), inline(always))]
#[allow(clippy::too_many_arguments)]
pub fn bgra64_to_hsv_row_endian<const BE: bool>(
bgra64: &[u16],
h_out: &mut [u8],
s_out: &mut [u8],
v_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
use_simd: bool,
) {
let in_min = rgba64_packed_elems(width);
let scratch_min = rgb_row_bytes(width);
assert!(bgra64.len() >= in_min, "bgra64 row too short");
assert!(rgb_scratch.len() >= scratch_min, "rgb_scratch too short");
assert!(h_out.len() >= width, "h_out row too short");
assert!(s_out.len() >= width, "s_out row too short");
assert!(v_out.len() >= width, "v_out row too short");
bgra64_to_rgb_row_endian::<BE>(bgra64, rgb_scratch, width, use_simd);
scalar::rgb_to_hsv_row(rgb_scratch, h_out, s_out, v_out, width);
}
#[allow(clippy::too_many_arguments)]
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn bgra64_to_hsv_row(
bgra64: &[u16],
h_out: &mut [u8],
s_out: &mut [u8],
v_out: &mut [u8],
rgb_scratch: &mut [u8],
width: usize,
use_simd: bool,
) {
bgra64_to_hsv_row_endian::<false>(bgra64, h_out, s_out, v_out, rgb_scratch, width, use_simd)
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
fn as_le_u16(host: &[u16]) -> std::vec::Vec<u16> {
host
.iter()
.map(|v| u16::from_ne_bytes(v.to_le_bytes()))
.collect()
}
fn solid_rgb48(width: usize, val: u16) -> std::vec::Vec<u16> {
as_le_u16(&std::vec![val; width * 3])
}
fn solid_rgba64(width: usize, val: u16) -> std::vec::Vec<u16> {
as_le_u16(&std::vec![val; width * 4])
}
#[test]
fn rgb48_dispatcher_to_rgb_scalar_path() {
let src = solid_rgb48(4, 0xFFFF);
let mut rgb = std::vec![0u8; 4 * 3];
rgb48_to_rgb_row_endian::<false>(&src, &mut rgb, 4, false);
assert!(
rgb.iter().all(|&v| v == 0xFF),
"expected all 0xFF, got {rgb:?}"
);
}
#[test]
fn rgb48_dispatcher_to_rgba_scalar_path() {
let src = solid_rgb48(4, 0x1200);
let mut rgba = std::vec![0u8; 4 * 4];
rgb48_to_rgba_row_endian::<false>(&src, &mut rgba, 4, false);
for px in rgba.chunks(4) {
assert_eq!(px[0], 0x12, "R channel");
assert_eq!(px[3], 0xFF, "alpha forced to 0xFF");
}
}
#[test]
fn rgb48_dispatcher_to_rgb_u16_scalar_path() {
let src = solid_rgb48(4, 0xABCD);
let mut rgb_u16 = std::vec![0u16; 4 * 3];
rgb48_to_rgb_u16_row_endian::<false>(&src, &mut rgb_u16, 4, false);
assert!(
rgb_u16.iter().all(|&v| v == 0xABCD),
"expected identity copy"
);
}
#[test]
fn rgb48_dispatcher_to_rgba_u16_scalar_path() {
let src = solid_rgb48(4, 0x1234);
let mut rgba_u16 = std::vec![0u16; 4 * 4];
rgb48_to_rgba_u16_row_endian::<false>(&src, &mut rgba_u16, 4, false);
for px in rgba_u16.chunks(4) {
assert_eq!(px[0], 0x1234, "R channel");
assert_eq!(px[3], 0xFFFF, "alpha forced to 0xFFFF");
}
}
#[test]
fn rgb48_dispatcher_to_luma_scalar_path() {
let src = solid_rgb48(4, 0xFF00);
let mut scratch = std::vec![0u8; 4 * 3];
let mut luma = std::vec![0u8; 4];
rgb48_to_luma_row_endian::<false>(
&src,
&mut luma,
&mut scratch,
4,
ColorMatrix::Bt709,
true,
false,
);
for &y in &luma {
assert!(y >= 240, "full-range white luma must be near 255, got {y}");
}
}
#[test]
fn rgb48_dispatcher_to_luma_u16_scalar_path() {
let src = solid_rgb48(4, 0xFF00);
let mut scratch = std::vec![0u8; 4 * 3];
let mut luma = std::vec![0u16; 4];
rgb48_to_luma_u16_row_endian::<false>(
&src,
&mut luma,
&mut scratch,
4,
ColorMatrix::Bt709,
true,
false,
);
for &y in &luma {
assert!(
y >= 240,
"full-range white luma_u16 must be near 255, got {y}"
);
}
}
#[test]
fn rgb48_dispatcher_to_hsv_scalar_path() {
let src = as_le_u16(&[0xFF00u16, 0x0000, 0x0000]); let mut scratch = std::vec![0u8; 3];
let mut h = std::vec![0u8; 1];
let mut s = std::vec![0u8; 1];
let mut v = std::vec![0u8; 1];
rgb48_to_hsv_row_endian::<false>(&src, &mut h, &mut s, &mut v, &mut scratch, 1, false);
assert_eq!(h[0], 0, "H for pure red must be 0");
assert_eq!(s[0], 255, "S for pure red must be 255");
assert!(v[0] >= 254, "V for pure red must be near 255, got {}", v[0]);
}
#[test]
fn bgr48_dispatcher_to_rgb_scalar_path() {
let src = as_le_u16(&[0x1100u16, 0x2200, 0x3300]);
let mut rgb = [0u8; 3];
bgr48_to_rgb_row_endian::<false>(&src, &mut rgb, 1, false);
assert_eq!(rgb[0], 0x33, "R");
assert_eq!(rgb[1], 0x22, "G");
assert_eq!(rgb[2], 0x11, "B");
}
#[test]
fn bgr48_dispatcher_to_rgba_scalar_path() {
let src = as_le_u16(&[0x1100u16, 0x2200, 0x3300]);
let mut rgba = [0u8; 4];
bgr48_to_rgba_row_endian::<false>(&src, &mut rgba, 1, false);
assert_eq!(rgba[0], 0x33, "R");
assert_eq!(rgba[3], 0xFF, "alpha forced to 0xFF");
}
#[test]
fn bgr48_dispatcher_to_rgb_u16_scalar_path() {
let src = as_le_u16(&[0x1111u16, 0x2222, 0x3333]); let mut rgb_u16 = [0u16; 3];
bgr48_to_rgb_u16_row_endian::<false>(&src, &mut rgb_u16, 1, false);
assert_eq!(rgb_u16[0], 0x3333, "R (from position 2)");
assert_eq!(rgb_u16[1], 0x2222, "G");
assert_eq!(rgb_u16[2], 0x1111, "B (from position 0)");
}
#[test]
fn bgr48_dispatcher_to_rgba_u16_scalar_path() {
let src = as_le_u16(&[0x1111u16, 0x2222, 0x3333]); let mut rgba_u16 = [0u16; 4];
bgr48_to_rgba_u16_row_endian::<false>(&src, &mut rgba_u16, 1, false);
assert_eq!(rgba_u16[0], 0x3333, "R");
assert_eq!(rgba_u16[3], 0xFFFF, "alpha forced to 0xFFFF");
}
#[test]
fn bgr48_dispatcher_to_luma_scalar_path() {
let src = solid_rgb48(4, 0xFF00); let mut scratch = std::vec![0u8; 4 * 3];
let mut luma = std::vec![0u8; 4];
bgr48_to_luma_row_endian::<false>(
&src,
&mut luma,
&mut scratch,
4,
ColorMatrix::Bt709,
true,
false,
);
for &y in &luma {
assert!(y >= 240, "full-range white luma must be near 255, got {y}");
}
}
#[test]
fn bgr48_dispatcher_to_luma_u16_scalar_path() {
let src = solid_rgb48(4, 0xFF00);
let mut scratch = std::vec![0u8; 4 * 3];
let mut luma = std::vec![0u16; 4];
bgr48_to_luma_u16_row_endian::<false>(
&src,
&mut luma,
&mut scratch,
4,
ColorMatrix::Bt709,
true,
false,
);
for &y in &luma {
assert!(y >= 240, "full-range white luma_u16 must be near 255");
}
}
#[test]
fn bgr48_dispatcher_to_hsv_scalar_path() {
let src = as_le_u16(&[0xFF00u16, 0x0000, 0x0000]); let mut scratch = std::vec![0u8; 3];
let mut h = std::vec![0u8; 1];
let mut s = std::vec![0u8; 1];
let mut v = std::vec![0u8; 1];
bgr48_to_hsv_row_endian::<false>(&src, &mut h, &mut s, &mut v, &mut scratch, 1, false);
assert_eq!(h[0], 120, "H for pure blue must be 120 in OpenCV encoding");
assert_eq!(s[0], 255, "S for pure blue must be 255");
assert!(
v[0] >= 254,
"V for pure blue must be near 255, got {}",
v[0]
);
}
#[test]
fn rgba64_dispatcher_to_rgb_scalar_path() {
let src = as_le_u16(&[0x1100u16, 0x2200, 0x3300, 0xDEAD]); let mut rgb = [0u8; 3];
rgba64_to_rgb_row_endian::<false>(&src, &mut rgb, 1, false);
assert_eq!(rgb[0], 0x11, "R");
assert_eq!(rgb[1], 0x22, "G");
assert_eq!(rgb[2], 0x33, "B");
}
#[test]
fn rgba64_dispatcher_to_rgba_scalar_path() {
let src = as_le_u16(&[0x1100u16, 0x2200, 0x3300, 0xABCD]);
let mut rgba = [0u8; 4];
rgba64_to_rgba_row_endian::<false>(&src, &mut rgba, 1, false);
assert_eq!(rgba[3], 0xAB, "source alpha depth-converted >> 8");
}
#[test]
fn rgba64_dispatcher_to_rgb_u16_scalar_path() {
let src = as_le_u16(&[0x1111u16, 0x2222, 0x3333, 0xDEAD]);
let mut rgb_u16 = [0u16; 3];
rgba64_to_rgb_u16_row_endian::<false>(&src, &mut rgb_u16, 1, false);
assert_eq!(rgb_u16[0], 0x1111, "R");
assert_eq!(rgb_u16[1], 0x2222, "G");
assert_eq!(rgb_u16[2], 0x3333, "B");
}
#[test]
fn rgba64_dispatcher_to_rgba_u16_scalar_path() {
let src = as_le_u16(&[0x1111u16, 0x2222, 0x3333, 0xABCD]);
let mut rgba_u16 = [0u16; 4];
rgba64_to_rgba_u16_row_endian::<false>(&src, &mut rgba_u16, 1, false);
assert_eq!(rgba_u16[0], 0x1111, "R");
assert_eq!(rgba_u16[3], 0xABCD, "source alpha preserved");
}
#[test]
fn rgba64_dispatcher_to_luma_scalar_path() {
let src = solid_rgba64(4, 0xFF00);
let mut scratch = std::vec![0u8; 4 * 3];
let mut luma = std::vec![0u8; 4];
rgba64_to_luma_row_endian::<false>(
&src,
&mut luma,
&mut scratch,
4,
ColorMatrix::Bt709,
true,
false,
);
for &y in &luma {
assert!(y >= 240, "full-range white luma must be near 255, got {y}");
}
}
#[test]
fn rgba64_dispatcher_to_luma_u16_scalar_path() {
let src = solid_rgba64(4, 0xFF00);
let mut scratch = std::vec![0u8; 4 * 3];
let mut luma = std::vec![0u16; 4];
rgba64_to_luma_u16_row_endian::<false>(
&src,
&mut luma,
&mut scratch,
4,
ColorMatrix::Bt709,
true,
false,
);
for &y in &luma {
assert!(
y >= 240,
"full-range white luma_u16 must be near 255, got {y}"
);
}
}
#[test]
fn rgba64_dispatcher_to_hsv_scalar_path() {
let src = as_le_u16(&[0x0000u16, 0xFF00, 0x0000, 0x1234]);
let mut scratch = std::vec![0u8; 3];
let mut h = std::vec![0u8; 1];
let mut s = std::vec![0u8; 1];
let mut v = std::vec![0u8; 1];
rgba64_to_hsv_row_endian::<false>(&src, &mut h, &mut s, &mut v, &mut scratch, 1, false);
assert_eq!(h[0], 60, "H for pure green must be 60 in OpenCV encoding");
assert_eq!(s[0], 255, "S for pure green must be 255");
assert!(
v[0] >= 254,
"V for pure green must be near 255, got {}",
v[0]
);
}
#[test]
fn bgra64_dispatcher_to_rgb_scalar_path() {
let src = as_le_u16(&[0x1100u16, 0x2200, 0x3300, 0xDEAD]);
let mut rgb = [0u8; 3];
bgra64_to_rgb_row_endian::<false>(&src, &mut rgb, 1, false);
assert_eq!(rgb[0], 0x33, "R");
assert_eq!(rgb[1], 0x22, "G");
assert_eq!(rgb[2], 0x11, "B");
}
#[test]
fn bgra64_dispatcher_to_rgba_scalar_path() {
let src = as_le_u16(&[0x1100u16, 0x2200, 0x3300, 0xABCD]);
let mut rgba = [0u8; 4];
bgra64_to_rgba_row_endian::<false>(&src, &mut rgba, 1, false);
assert_eq!(rgba[0], 0x33, "R (from position 2)");
assert_eq!(rgba[3], 0xAB, "source alpha depth-converted >> 8");
}
#[test]
fn bgra64_dispatcher_to_rgb_u16_scalar_path() {
let src = as_le_u16(&[0x1111u16, 0x2222, 0x3333, 0xDEAD]); let mut rgb_u16 = [0u16; 3];
bgra64_to_rgb_u16_row_endian::<false>(&src, &mut rgb_u16, 1, false);
assert_eq!(rgb_u16[0], 0x3333, "R (from position 2)");
assert_eq!(rgb_u16[1], 0x2222, "G");
assert_eq!(rgb_u16[2], 0x1111, "B (from position 0)");
}
#[test]
fn bgra64_dispatcher_to_rgba_u16_scalar_path() {
let src = as_le_u16(&[0x1111u16, 0x2222, 0x3333, 0xABCD]); let mut rgba_u16 = [0u16; 4];
bgra64_to_rgba_u16_row_endian::<false>(&src, &mut rgba_u16, 1, false);
assert_eq!(rgba_u16[0], 0x3333, "R (from position 2)");
assert_eq!(rgba_u16[3], 0xABCD, "source alpha preserved");
}
#[test]
fn bgra64_dispatcher_to_luma_scalar_path() {
let src = solid_rgba64(4, 0xFF00);
let mut scratch = std::vec![0u8; 4 * 3];
let mut luma = std::vec![0u8; 4];
bgra64_to_luma_row_endian::<false>(
&src,
&mut luma,
&mut scratch,
4,
ColorMatrix::Bt709,
true,
false,
);
for &y in &luma {
assert!(y >= 240, "full-range white luma must be near 255, got {y}");
}
}
#[test]
fn bgra64_dispatcher_to_luma_u16_scalar_path() {
let src = solid_rgba64(4, 0xFF00);
let mut scratch = std::vec![0u8; 4 * 3];
let mut luma = std::vec![0u16; 4];
bgra64_to_luma_u16_row_endian::<false>(
&src,
&mut luma,
&mut scratch,
4,
ColorMatrix::Bt709,
true,
false,
);
for &y in &luma {
assert!(
y >= 240,
"full-range white luma_u16 must be near 255, got {y}"
);
}
}
#[test]
fn bgra64_dispatcher_to_hsv_scalar_path() {
let src = as_le_u16(&[0xFF00u16, 0x0000, 0x0000, 0x1234]);
let mut scratch = std::vec![0u8; 3];
let mut h = std::vec![0u8; 1];
let mut s = std::vec![0u8; 1];
let mut v = std::vec![0u8; 1];
bgra64_to_hsv_row_endian::<false>(&src, &mut h, &mut s, &mut v, &mut scratch, 1, false);
assert_eq!(h[0], 120, "H for pure blue must be 120 in OpenCV encoding");
assert_eq!(s[0], 255, "S for pure blue must be 255");
assert!(
v[0] >= 254,
"V for pure blue must be near 255, got {}",
v[0]
);
}
#[test]
#[should_panic(expected = "rgb48 row too short")]
fn rgb48_to_rgb_row_rejects_short_input() {
let src = [0u16; 2]; let mut out = [0u8; 3];
rgb48_to_rgb_row_endian::<false>(&src, &mut out, 1, false);
}
#[test]
#[should_panic(expected = "rgb_out row too short")]
fn rgb48_to_rgb_row_rejects_short_output() {
let src = [0u16; 3];
let mut out = [0u8; 2]; rgb48_to_rgb_row_endian::<false>(&src, &mut out, 1, false);
}
#[test]
#[should_panic(expected = "rgba64 row too short")]
fn rgba64_to_rgb_row_rejects_short_input() {
let src = [0u16; 3]; let mut out = [0u8; 3];
rgba64_to_rgb_row_endian::<false>(&src, &mut out, 1, false);
}
#[test]
#[should_panic(expected = "rgba_out row too short")]
fn rgba64_to_rgba_row_rejects_short_output() {
let src = [0u16; 4];
let mut out = [0u8; 3]; rgba64_to_rgba_row_endian::<false>(&src, &mut out, 1, false);
}
#[test]
#[should_panic(expected = "luma_out row too short")]
fn rgb48_to_luma_row_rejects_short_luma_output() {
let src = [0u16; 3];
let mut scratch = [0u8; 3];
let mut luma: [u8; 0] = [];
rgb48_to_luma_row_endian::<false>(
&src,
&mut luma,
&mut scratch,
1,
ColorMatrix::Bt709,
true,
false,
);
}
#[test]
#[should_panic(expected = "rgb_scratch too short")]
fn rgb48_to_luma_row_rejects_short_scratch() {
let src = [0u16; 3];
let mut scratch = [0u8; 2]; let mut luma = [0u8; 1];
rgb48_to_luma_row_endian::<false>(
&src,
&mut luma,
&mut scratch,
1,
ColorMatrix::Bt709,
true,
false,
);
}
#[cfg(target_pointer_width = "32")]
const OVERFLOW_WIDTH_TIMES_3: usize = (usize::MAX / 3) + 1;
#[cfg(target_pointer_width = "32")]
const OVERFLOW_WIDTH_TIMES_4: usize = (usize::MAX / 4) + 1;
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn rgb48_dispatcher_rejects_width_times_3_overflow() {
let p: [u16; 0] = [];
let mut out: [u8; 0] = [];
rgb48_to_rgb_row_endian::<false>(&p, &mut out, OVERFLOW_WIDTH_TIMES_3, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn bgr48_dispatcher_rejects_width_times_3_overflow() {
let p: [u16; 0] = [];
let mut out: [u8; 0] = [];
bgr48_to_rgb_row_endian::<false>(&p, &mut out, OVERFLOW_WIDTH_TIMES_3, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn rgba64_dispatcher_rejects_width_times_4_overflow() {
let p: [u16; 0] = [];
let mut out: [u8; 0] = [];
rgba64_to_rgb_row_endian::<false>(&p, &mut out, OVERFLOW_WIDTH_TIMES_4, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn bgra64_dispatcher_rejects_width_times_4_overflow() {
let p: [u16; 0] = [];
let mut out: [u8; 0] = [];
bgra64_to_rgb_row_endian::<false>(&p, &mut out, OVERFLOW_WIDTH_TIMES_4, false);
}
}