pub(crate) mod arch;
pub(crate) mod dispatch;
pub(crate) mod scalar;
#[cfg(all(
any(feature = "std", feature = "alloc"),
any(
feature = "gbr",
feature = "gray",
feature = "rgb",
feature = "v210",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-packed",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
),
))]
pub(crate) use scalar::expand_rgb_to_rgba_row;
#[cfg(all(
any(feature = "std", feature = "alloc"),
any(
feature = "gbr",
feature = "gray",
feature = "rgb",
feature = "v210",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-planar",
feature = "yuva",
),
))]
pub(crate) use scalar::expand_rgb_u16_to_rgba_u16_row;
#[cfg(all(
any(feature = "std", feature = "alloc"),
any(feature = "gbr", feature = "yuv-444-packed", feature = "yuva"),
))]
pub(crate) use dispatch::alpha_extract;
#[cfg(all(
any(feature = "std", feature = "alloc"),
any(
feature = "gray",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
),
))]
pub(crate) use dispatch::y_plane_to_luma_u16::y_plane_to_luma_u16_row;
#[cfg(all(feature = "yuv-packed", any(feature = "std", feature = "alloc")))]
pub(crate) use dispatch::packed_yuv422::{
uyvy422_to_luma_u16_row, yuyv422_to_luma_u16_row, yvyu422_to_luma_u16_row,
};
#[cfg(all(feature = "yuv-packed", any(feature = "std", feature = "alloc")))]
pub(crate) use dispatch::packed_yuv411::uyyvyy411_to_luma_u16_row;
#[cfg(all(feature = "yuv-444-packed", any(feature = "std", feature = "alloc")))]
pub(crate) use dispatch::vuya::vuya_to_luma_u16_row;
#[cfg(all(feature = "yuv-444-packed", any(feature = "std", feature = "alloc")))]
pub(crate) use dispatch::vuyx::vuyx_to_luma_u16_row;
#[cfg(feature = "yuv-444-packed")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuv-444-packed")))]
pub use dispatch::ayuv64::*;
#[cfg(feature = "bayer")]
#[cfg_attr(docsrs, doc(cfg(feature = "bayer")))]
pub use dispatch::bayer::*;
#[cfg(feature = "rgb-legacy")]
#[cfg_attr(docsrs, doc(cfg(feature = "rgb-legacy")))]
pub use dispatch::legacy_rgb::*;
#[cfg(feature = "yuv-semi-planar")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuv-semi-planar")))]
pub use dispatch::nv::*;
#[cfg(feature = "mono")]
#[cfg_attr(docsrs, doc(cfg(feature = "mono")))]
pub use dispatch::pal8::*;
#[cfg(feature = "yuv-semi-planar")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuv-semi-planar")))]
pub use dispatch::pn::*;
#[cfg(feature = "yuv-packed")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuv-packed")))]
pub use dispatch::{packed_yuv411::*, packed_yuv422::*};
#[cfg(feature = "gbr")]
#[cfg_attr(docsrs, doc(cfg(feature = "gbr")))]
pub use dispatch::{planar_gbr::*, planar_gbr_high_bit::*};
#[cfg(feature = "rgb-float")]
#[cfg_attr(docsrs, doc(cfg(feature = "rgb-float")))]
pub use dispatch::{rgb_f16_ops::*, rgb_float_ops::*};
#[cfg(all(feature = "mono", any(feature = "std", feature = "alloc")))]
pub(crate) use dispatch::mono1bit::*;
#[cfg(feature = "rgb")]
#[cfg_attr(docsrs, doc(cfg(feature = "rgb")))]
pub use dispatch::packed_rgb_16bit::{
bgr48_to_rgb_row, bgr48_to_rgb_row_endian, bgr48_to_rgb_u16_row, bgr48_to_rgb_u16_row_endian,
bgr48_to_rgba_row, bgr48_to_rgba_row_endian, bgr48_to_rgba_u16_row, bgr48_to_rgba_u16_row_endian,
bgra64_to_rgb_row, bgra64_to_rgb_row_endian, bgra64_to_rgb_u16_row, bgra64_to_rgb_u16_row_endian,
bgra64_to_rgba_row, bgra64_to_rgba_row_endian, bgra64_to_rgba_u16_row,
bgra64_to_rgba_u16_row_endian, rgb48_to_rgb_row, rgb48_to_rgb_row_endian, rgb48_to_rgb_u16_row,
rgb48_to_rgb_u16_row_endian, rgb48_to_rgba_row, rgb48_to_rgba_row_endian, rgb48_to_rgba_u16_row,
rgb48_to_rgba_u16_row_endian, rgba64_to_rgb_row, rgba64_to_rgb_row_endian, rgba64_to_rgb_u16_row,
rgba64_to_rgb_u16_row_endian, rgba64_to_rgba_row, rgba64_to_rgba_row_endian,
rgba64_to_rgba_u16_row, rgba64_to_rgba_u16_row_endian,
};
pub use dispatch::rgb_ops::*;
#[cfg(feature = "v210")]
#[cfg_attr(docsrs, doc(cfg(feature = "v210")))]
pub use dispatch::v210::*;
#[cfg(all(feature = "xyz", any(feature = "std", feature = "alloc")))]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "xyz", any(feature = "std", feature = "alloc"))))
)]
pub use dispatch::xyz12::*;
#[cfg(feature = "yuv-planar")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuv-planar")))]
pub use dispatch::yuv411p::*;
#[cfg(feature = "yuva")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuva")))]
pub use dispatch::yuva::*;
#[cfg(feature = "yuv-444-packed")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuv-444-packed")))]
pub use dispatch::{v30x::*, v410::*, vuya::*, vuyx::*, xv36::*};
#[cfg(feature = "y2xx")]
#[cfg_attr(docsrs, doc(cfg(feature = "y2xx")))]
pub use dispatch::{y210::*, y212::*, y216::*};
#[cfg(feature = "yuv-planar")]
#[cfg_attr(docsrs, doc(cfg(feature = "yuv-planar")))]
pub use dispatch::{yuv420::*, yuv444::*};
#[cfg(feature = "rgb")]
#[allow(unused_imports)]
pub(crate) use dispatch::packed_rgb_16bit::{
bgr48_to_hsv_row, bgr48_to_hsv_row_endian, bgr48_to_luma_row, bgr48_to_luma_row_endian,
bgr48_to_luma_u16_row, bgr48_to_luma_u16_row_endian, bgra64_to_hsv_row, bgra64_to_hsv_row_endian,
bgra64_to_luma_row, bgra64_to_luma_row_endian, bgra64_to_luma_u16_row,
bgra64_to_luma_u16_row_endian, rgb48_to_hsv_row, rgb48_to_hsv_row_endian, rgb48_to_luma_row,
rgb48_to_luma_row_endian, rgb48_to_luma_u16_row, rgb48_to_luma_u16_row_endian, rgba64_to_hsv_row,
rgba64_to_hsv_row_endian, rgba64_to_luma_row, rgba64_to_luma_row_endian, rgba64_to_luma_u16_row,
rgba64_to_luma_u16_row_endian,
};
#[cfg(all(feature = "gray", any(feature = "std", feature = "alloc")))]
pub(crate) use dispatch::gray::*;
#[cfg(all(feature = "gray", any(feature = "std", feature = "alloc")))]
pub(crate) use dispatch::grayf32::*;
#[cfg(all(feature = "gray", any(feature = "std", feature = "alloc")))]
pub(crate) use dispatch::ya8::*;
#[cfg(all(feature = "gray", any(feature = "std", feature = "alloc")))]
pub(crate) use dispatch::ya16::*;
#[cfg(all(feature = "gbr", any(feature = "std", feature = "alloc")))]
pub(crate) use dispatch::planar_gbr_float::*;
#[cfg(all(
test,
feature = "std",
feature = "yuv-planar",
target_pointer_width = "32"
))]
pub(crate) use dispatch::yuv444::yuv_444p_n_to_rgb_u16_row;
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn rgb_row_bytes(width: usize) -> usize {
match width.checked_mul(3) {
Some(n) => n,
None => panic!("width ({width}) x 3 overflows usize"),
}
}
#[cfg(any(
feature = "gbr",
feature = "gray",
feature = "mono",
feature = "rgb",
feature = "rgb-float",
feature = "rgb-legacy",
feature = "v210",
feature = "xyz",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-packed",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn rgba_row_bytes(width: usize) -> usize {
match width.checked_mul(4) {
Some(n) => n,
None => panic!("width ({width}) x 4 overflows usize"),
}
}
#[cfg(any(
feature = "bayer",
feature = "gbr",
feature = "gray",
feature = "mono",
feature = "rgb",
feature = "rgb-float",
feature = "rgb-legacy",
feature = "v210",
feature = "xyz",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn rgb_row_elems(width: usize) -> usize {
match width.checked_mul(3) {
Some(n) => n,
None => panic!("width ({width}) x 3 overflows usize"),
}
}
#[cfg(any(
feature = "gbr",
feature = "gray",
feature = "mono",
feature = "rgb",
feature = "rgb-float",
feature = "rgb-legacy",
feature = "v210",
feature = "xyz",
feature = "y2xx",
feature = "yuv-444-packed",
feature = "yuv-planar",
feature = "yuv-semi-planar",
feature = "yuva",
))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn rgba_row_elems(width: usize) -> usize {
match width.checked_mul(4) {
Some(n) => n,
None => panic!("width ({width}) x 4 overflows usize"),
}
}
#[cfg(feature = "gray")]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn ya_row_elems(width: usize) -> usize {
match width.checked_mul(2) {
Some(n) => n,
None => panic!("width ({width}) x 2 overflows usize"),
}
}
#[cfg(feature = "bayer")]
pub(crate) const MAX_FUSED_TRANSFORM_ABS: f32 = 1.0e12;
#[cfg(feature = "bayer")]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn assert_color_transform_well_formed(m: &[[f32; 3]; 3]) {
let mut row = 0;
while row < 3 {
let mut col = 0;
while col < 3 {
let v = m[row][col];
assert!(
v.is_finite(),
"color transform m[{row}][{col}] is non-finite (NaN or ±∞)"
);
assert!(
v.abs() <= MAX_FUSED_TRANSFORM_ABS,
"color transform m[{row}][{col}] = {v} exceeds magnitude bound \
(|coeff| ≤ {MAX_FUSED_TRANSFORM_ABS}); validated WB x CCM cannot \
produce values past this bound"
);
col += 1;
}
row += 1;
}
}
#[cfg(feature = "yuv-semi-planar")]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn uv_full_row_elems(width: usize) -> usize {
match width.checked_mul(2) {
Some(n) => n,
None => panic!("width ({width}) x 2 overflows usize (UV row)"),
}
}
#[cfg(any(feature = "rgb-legacy", feature = "yuv-packed"))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn packed_yuv422_row_bytes(width: usize) -> usize {
match width.checked_mul(2) {
Some(n) => n,
None => panic!("width ({width}) x 2 overflows usize (packed YUV 4:2:2 row)"),
}
}
#[cfg(feature = "yuv-packed")]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn packed_yuv411_row_bytes(width: usize) -> usize {
match width.checked_mul(3) {
Some(n) => n / 2,
None => panic!("width ({width}) x 3 / 2 overflows usize (packed YUV 4:1:1 row)"),
}
}
#[cfg(feature = "v210")]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn v210_row_bytes(width: usize) -> usize {
let words = width.div_ceil(6);
match words.checked_mul(16) {
Some(n) => n,
None => panic!("width ({width}) / 6 x 16 overflows usize (v210 row)"),
}
}
#[cfg(feature = "y2xx")]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn y2xx_row_elems(width: usize) -> usize {
match width.checked_mul(2) {
Some(n) => n,
None => panic!("width ({width}) x 2 overflows usize (Y2xx row)"),
}
}
#[cfg(all(target_arch = "aarch64", feature = "std"))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn neon_available() -> bool {
if cfg!(colconv_force_scalar) {
return false;
}
std::arch::is_aarch64_feature_detected!("neon")
}
#[cfg(all(target_arch = "aarch64", not(feature = "std")))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) const fn neon_available() -> bool {
!cfg!(colconv_force_scalar) && cfg!(target_feature = "neon")
}
#[cfg(all(
target_arch = "aarch64",
feature = "std",
any(feature = "gbr", feature = "rgb-float"),
))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn fp16_available() -> bool {
if cfg!(colconv_force_scalar) {
return false;
}
std::arch::is_aarch64_feature_detected!("fp16")
}
#[cfg(all(
target_arch = "aarch64",
not(feature = "std"),
any(feature = "gbr", feature = "rgb-float"),
))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) const fn fp16_available() -> bool {
!cfg!(colconv_force_scalar) && cfg!(target_feature = "fp16")
}
#[cfg(all(target_arch = "x86_64", feature = "std"))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn avx2_available() -> bool {
if cfg!(colconv_force_scalar) || cfg!(colconv_disable_avx2) {
return false;
}
std::arch::is_x86_feature_detected!("avx2")
}
#[cfg(all(target_arch = "x86_64", not(feature = "std")))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) const fn avx2_available() -> bool {
!cfg!(colconv_force_scalar) && !cfg!(colconv_disable_avx2) && cfg!(target_feature = "avx2")
}
#[cfg(all(target_arch = "x86_64", feature = "std"))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn sse41_available() -> bool {
if cfg!(colconv_force_scalar) {
return false;
}
std::arch::is_x86_feature_detected!("sse4.1")
}
#[cfg(all(target_arch = "x86_64", not(feature = "std")))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) const fn sse41_available() -> bool {
!cfg!(colconv_force_scalar) && cfg!(target_feature = "sse4.1")
}
#[cfg(all(target_arch = "x86_64", feature = "std"))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn avx512_available() -> bool {
if cfg!(colconv_force_scalar) || cfg!(colconv_disable_avx512) {
return false;
}
std::arch::is_x86_feature_detected!("avx512bw")
}
#[cfg(all(target_arch = "x86_64", not(feature = "std")))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) const fn avx512_available() -> bool {
!cfg!(colconv_force_scalar) && !cfg!(colconv_disable_avx512) && cfg!(target_feature = "avx512bw")
}
#[cfg(all(
target_arch = "x86_64",
feature = "std",
any(feature = "rgb-float", feature = "gbr"),
))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) fn f16c_available() -> bool {
if cfg!(colconv_force_scalar) {
return false;
}
std::arch::is_x86_feature_detected!("f16c")
}
#[cfg(all(
target_arch = "x86_64",
not(feature = "std"),
any(feature = "rgb-float", feature = "gbr"),
))]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) const fn f16c_available() -> bool {
!cfg!(colconv_force_scalar) && cfg!(target_feature = "f16c")
}
#[cfg(target_arch = "wasm32")]
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) const fn simd128_available() -> bool {
!cfg!(colconv_force_scalar) && cfg!(target_feature = "simd128")
}
#[cfg(all(test, feature = "std"))]
mod overflow_tests {
#[cfg(target_pointer_width = "32")]
use super::*;
#[cfg(target_pointer_width = "32")]
use crate::ColorMatrix;
#[cfg(target_pointer_width = "32")]
const OVERFLOW_WIDTH: usize = {
let candidate = (usize::MAX / 3) + 1;
candidate + (candidate & 1)
};
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn yuv_420_dispatcher_rejects_width_times_3_overflow() {
let y: [u8; 0] = [];
let u: [u8; 0] = [];
let v: [u8; 0] = [];
let mut rgb: [u8; 0] = [];
yuv_420_to_rgb_row(
&y,
&u,
&v,
&mut rgb,
OVERFLOW_WIDTH,
ColorMatrix::Bt601,
true,
false,
);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn yuv_444_dispatcher_rejects_width_times_3_overflow() {
let y: [u8; 0] = [];
let u: [u8; 0] = [];
let v: [u8; 0] = [];
let mut rgb: [u8; 0] = [];
yuv_444_to_rgb_row(
&y,
&u,
&v,
&mut rgb,
OVERFLOW_WIDTH,
ColorMatrix::Bt601,
true,
false,
);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn nv12_dispatcher_rejects_width_times_3_overflow() {
let y: [u8; 0] = [];
let uv: [u8; 0] = [];
let mut rgb: [u8; 0] = [];
nv12_to_rgb_row(
&y,
&uv,
&mut rgb,
OVERFLOW_WIDTH,
ColorMatrix::Bt601,
true,
false,
);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn rgb_to_hsv_dispatcher_rejects_width_times_3_overflow() {
let rgb: [u8; 0] = [];
let mut h: [u8; 0] = [];
let mut s: [u8; 0] = [];
let mut v: [u8; 0] = [];
rgb_to_hsv_row(&rgb, &mut h, &mut s, &mut v, OVERFLOW_WIDTH, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn bgr_to_rgb_dispatcher_rejects_width_times_3_overflow() {
let input: [u8; 0] = [];
let mut output: [u8; 0] = [];
bgr_to_rgb_row(&input, &mut output, OVERFLOW_WIDTH, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gbr_to_rgb_dispatcher_rejects_width_times_3_overflow() {
let g: [u8; 0] = [];
let b: [u8; 0] = [];
let r: [u8; 0] = [];
let mut rgb: [u8; 0] = [];
gbr_to_rgb_row(&g, &b, &r, &mut rgb, OVERFLOW_WIDTH, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gbr_to_rgba_opaque_dispatcher_rejects_width_times_4_overflow() {
let g: [u8; 0] = [];
let b: [u8; 0] = [];
let r: [u8; 0] = [];
let mut rgba: [u8; 0] = [];
gbr_to_rgba_opaque_row(&g, &b, &r, &mut rgba, OVERFLOW_WIDTH, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gbra_to_rgba_dispatcher_rejects_width_times_4_overflow() {
let g: [u8; 0] = [];
let b: [u8; 0] = [];
let r: [u8; 0] = [];
let a: [u8; 0] = [];
let mut rgba: [u8; 0] = [];
gbra_to_rgba_row(&g, &b, &r, &a, &mut rgba, OVERFLOW_WIDTH, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gbr_to_rgb_high_bit_dispatcher_rejects_width_times_3_overflow() {
let g: [u16; 0] = [];
let b: [u16; 0] = [];
let r: [u16; 0] = [];
let mut rgb: [u8; 0] = [];
gbr_to_rgb_high_bit_row::<10, false>(&g, &b, &r, &mut rgb, OVERFLOW_WIDTH, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gbr_to_rgb_u16_high_bit_dispatcher_rejects_width_times_3_overflow() {
let g: [u16; 0] = [];
let b: [u16; 0] = [];
let r: [u16; 0] = [];
let mut rgb: [u16; 0] = [];
gbr_to_rgb_u16_high_bit_row::<10, false>(&g, &b, &r, &mut rgb, OVERFLOW_WIDTH, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gbr_to_rgba_opaque_high_bit_dispatcher_rejects_width_times_4_overflow() {
let g: [u16; 0] = [];
let b: [u16; 0] = [];
let r: [u16; 0] = [];
let mut rgba: [u8; 0] = [];
gbr_to_rgba_opaque_high_bit_row::<10, false>(&g, &b, &r, &mut rgba, OVERFLOW_WIDTH, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gbr_to_rgba_opaque_u16_high_bit_dispatcher_rejects_width_times_4_overflow() {
let g: [u16; 0] = [];
let b: [u16; 0] = [];
let r: [u16; 0] = [];
let mut rgba: [u16; 0] = [];
gbr_to_rgba_opaque_u16_high_bit_row::<10, false>(&g, &b, &r, &mut rgba, OVERFLOW_WIDTH, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gbra_to_rgba_high_bit_dispatcher_rejects_width_times_4_overflow() {
let g: [u16; 0] = [];
let b: [u16; 0] = [];
let r: [u16; 0] = [];
let a: [u16; 0] = [];
let mut rgba: [u8; 0] = [];
gbra_to_rgba_high_bit_row::<10, false>(&g, &b, &r, &a, &mut rgba, OVERFLOW_WIDTH, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gbra_to_rgba_u16_high_bit_dispatcher_rejects_width_times_4_overflow() {
let g: [u16; 0] = [];
let b: [u16; 0] = [];
let r: [u16; 0] = [];
let a: [u16; 0] = [];
let mut rgba: [u16; 0] = [];
gbra_to_rgba_u16_high_bit_row::<10, false>(&g, &b, &r, &a, &mut rgba, OVERFLOW_WIDTH, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gray8_to_rgb_dispatcher_rejects_width_times_3_overflow() {
let y: [u8; 0] = [];
let mut rgb: [u8; 0] = [];
gray8_to_rgb_row(&y, &mut rgb, OVERFLOW_WIDTH, false, true);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gray8_to_rgba_dispatcher_rejects_width_times_4_overflow() {
let y: [u8; 0] = [];
let mut rgba: [u8; 0] = [];
gray8_to_rgba_row(&y, &mut rgba, OVERFLOW_WIDTH, false, true);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gray_n_to_rgb_dispatcher_rejects_width_times_3_overflow() {
let y: [u16; 0] = [];
let mut rgb: [u8; 0] = [];
gray_n_to_rgb_row::<10, false>(&y, &mut rgb, OVERFLOW_WIDTH, false, true);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gray_n_to_rgba_dispatcher_rejects_width_times_4_overflow() {
let y: [u16; 0] = [];
let mut rgba: [u8; 0] = [];
gray_n_to_rgba_row::<10, false>(&y, &mut rgba, OVERFLOW_WIDTH, false, true);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gray_n_to_rgb_u16_dispatcher_rejects_width_times_3_overflow() {
let y: [u16; 0] = [];
let mut rgb: [u16; 0] = [];
gray_n_to_rgb_u16_row::<10, false>(&y, &mut rgb, OVERFLOW_WIDTH, false, true);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gray_n_to_rgba_u16_dispatcher_rejects_width_times_4_overflow() {
let y: [u16; 0] = [];
let mut rgba: [u16; 0] = [];
gray_n_to_rgba_u16_row::<10, false>(&y, &mut rgba, OVERFLOW_WIDTH, false, true);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gray16_to_rgb_dispatcher_rejects_width_times_3_overflow() {
let y: [u16; 0] = [];
let mut rgb: [u8; 0] = [];
gray16_to_rgb_row::<false>(&y, &mut rgb, OVERFLOW_WIDTH, false, true);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gray16_to_rgba_dispatcher_rejects_width_times_4_overflow() {
let y: [u16; 0] = [];
let mut rgba: [u8; 0] = [];
gray16_to_rgba_row::<false>(&y, &mut rgba, OVERFLOW_WIDTH, false, true);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gray16_to_rgb_u16_dispatcher_rejects_width_times_3_overflow() {
let y: [u16; 0] = [];
let mut rgb: [u16; 0] = [];
gray16_to_rgb_u16_row::<false>(&y, &mut rgb, OVERFLOW_WIDTH, false, true);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn gray16_to_rgba_u16_dispatcher_rejects_width_times_4_overflow() {
let y: [u16; 0] = [];
let mut rgba: [u16; 0] = [];
gray16_to_rgba_u16_row::<false>(&y, &mut rgba, OVERFLOW_WIDTH, false, true);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn yuv_444p_n_u16_dispatcher_rejects_width_times_3_overflow() {
let y: [u16; 0] = [];
let u: [u16; 0] = [];
let v: [u16; 0] = [];
let mut rgb: [u16; 0] = [];
yuv_444p_n_to_rgb_u16_row::<10, false>(
&y,
&u,
&v,
&mut rgb,
OVERFLOW_WIDTH,
ColorMatrix::Bt601,
true,
false,
);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn yuv444p16_u16_dispatcher_rejects_width_times_3_overflow() {
let y: [u16; 0] = [];
let u: [u16; 0] = [];
let v: [u16; 0] = [];
let mut rgb: [u16; 0] = [];
yuv444p16_to_rgb_u16_row(
&y,
&u,
&v,
&mut rgb,
OVERFLOW_WIDTH,
ColorMatrix::Bt601,
true,
false,
);
}
#[cfg(target_pointer_width = "32")]
const OVERFLOW_WIDTH_TIMES_2: usize = {
let candidate = (usize::MAX / 2) + 1;
candidate + (candidate & 1)
};
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn yuyv422_dispatcher_rejects_width_times_2_overflow() {
let p: [u8; 0] = [];
let mut rgb: [u8; 0] = [];
yuyv422_to_rgb_row(
&p,
&mut rgb,
OVERFLOW_WIDTH_TIMES_2,
ColorMatrix::Bt601,
true,
false,
);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn uyvy422_dispatcher_rejects_width_times_2_overflow() {
let p: [u8; 0] = [];
let mut rgba: [u8; 0] = [];
uyvy422_to_rgba_row(
&p,
&mut rgba,
OVERFLOW_WIDTH_TIMES_2,
ColorMatrix::Bt601,
true,
false,
);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn yvyu422_dispatcher_rejects_width_times_2_overflow() {
let p: [u8; 0] = [];
let mut rgb: [u8; 0] = [];
yvyu422_to_rgb_row(
&p,
&mut rgb,
OVERFLOW_WIDTH_TIMES_2,
ColorMatrix::Bt601,
true,
false,
);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn yuyv422_luma_dispatcher_rejects_width_times_2_overflow() {
let p: [u8; 0] = [];
let mut luma: [u8; 0] = [];
yuyv422_to_luma_row(&p, &mut luma, OVERFLOW_WIDTH_TIMES_2, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn uyvy422_luma_dispatcher_rejects_width_times_2_overflow() {
let p: [u8; 0] = [];
let mut luma: [u8; 0] = [];
uyvy422_to_luma_row(&p, &mut luma, OVERFLOW_WIDTH_TIMES_2, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn yvyu422_luma_dispatcher_rejects_width_times_2_overflow() {
let p: [u8; 0] = [];
let mut luma: [u8; 0] = [];
yvyu422_to_luma_row(&p, &mut luma, OVERFLOW_WIDTH_TIMES_2, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn v210_dispatcher_rejects_words_times_16_overflow() {
let candidate = ((usize::MAX / 16) + 1) * 6;
let p: [u8; 0] = [];
let mut rgb: [u8; 0] = [];
v210_to_rgb_row(&p, &mut rgb, candidate, ColorMatrix::Bt601, true, false);
}
#[cfg(target_pointer_width = "32")]
#[test]
#[should_panic(expected = "overflows usize")]
fn y210_dispatcher_rejects_width_times_2_overflow() {
let p: [u16; 0] = [];
let mut rgb: [u8; 0] = [];
y210_to_rgb_row(
&p,
&mut rgb,
OVERFLOW_WIDTH_TIMES_2,
ColorMatrix::Bt601,
true,
false,
);
}
}
#[cfg(all(test, feature = "std", feature = "bayer"))]
mod bayer_dispatcher_tests {
use super::*;
use crate::raw::{BayerDemosaic, BayerPattern};
fn ident() -> [[f32; 3]; 3] {
[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
}
#[test]
#[should_panic(expected = "non-finite")]
fn bayer_dispatcher_rejects_nan_in_m() {
let above = [0u8; 4];
let mid = [0u8; 4];
let below = [0u8; 4];
let mut rgb = [0u8; 12];
let mut m = ident();
m[1][1] = f32::NAN;
bayer_to_rgb_row(
&above,
&mid,
&below,
0,
BayerPattern::Rggb,
BayerDemosaic::Bilinear,
&m,
&mut rgb,
false,
);
}
#[test]
#[should_panic(expected = "non-finite")]
fn bayer_dispatcher_rejects_infinity_in_m() {
let above = [0u8; 4];
let mid = [0u8; 4];
let below = [0u8; 4];
let mut rgb = [0u8; 12];
let mut m = ident();
m[0][2] = f32::INFINITY;
bayer_to_rgb_row(
&above,
&mid,
&below,
0,
BayerPattern::Rggb,
BayerDemosaic::Bilinear,
&m,
&mut rgb,
false,
);
}
#[test]
#[should_panic(expected = "non-finite")]
fn bayer16_u8_dispatcher_rejects_neg_infinity_in_m() {
let above = [0u16; 4];
let mid = [0u16; 4];
let below = [0u16; 4];
let mut rgb = [0u8; 12];
let mut m = ident();
m[2][1] = f32::NEG_INFINITY;
bayer16_to_rgb_row::<12>(
&above,
&mid,
&below,
0,
BayerPattern::Rggb,
BayerDemosaic::Bilinear,
&m,
&mut rgb,
false,
);
}
#[test]
#[should_panic(expected = "non-finite")]
fn bayer16_u16_dispatcher_rejects_nan_in_m() {
let above = [0u16; 4];
let mid = [0u16; 4];
let below = [0u16; 4];
let mut rgb = [0u16; 12];
let mut m = ident();
m[2][2] = f32::NAN;
bayer16_to_rgb_u16_row::<10>(
&above,
&mid,
&below,
0,
BayerPattern::Rggb,
BayerDemosaic::Bilinear,
&m,
&mut rgb,
false,
);
}
#[test]
fn bayer_dispatcher_accepts_finite_m() {
let above = [10u8; 4];
let mid = [20u8; 4];
let below = [30u8; 4];
let mut rgb = [0u8; 12];
let m: [[f32; 3]; 3] = [[1.5, -0.3, -0.2], [-0.1, 1.2, -0.1], [-0.05, -0.15, 1.2]];
bayer_to_rgb_row(
&above,
&mid,
&below,
0,
BayerPattern::Rggb,
BayerDemosaic::Bilinear,
&m,
&mut rgb,
false,
);
}
#[test]
#[should_panic(expected = "exceeds magnitude bound")]
fn bayer_dispatcher_rejects_finite_extreme_m() {
let above = [0u8; 4];
let mid = [0u8; 4];
let below = [0u8; 4];
let mut rgb = [0u8; 12];
let mut m = [[1.0f32, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
m[1][1] = f32::MAX; bayer_to_rgb_row(
&above,
&mid,
&below,
0,
BayerPattern::Rggb,
BayerDemosaic::Bilinear,
&m,
&mut rgb,
false,
);
}
#[test]
#[should_panic(expected = "exceeds magnitude bound")]
fn bayer16_u8_dispatcher_rejects_finite_extreme_m() {
let above = [0u16; 4];
let mid = [0u16; 4];
let below = [0u16; 4];
let mut rgb = [0u8; 12];
let mut m = [[1.0f32, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
m[2][0] = -f32::MAX;
bayer16_to_rgb_row::<12>(
&above,
&mid,
&below,
0,
BayerPattern::Rggb,
BayerDemosaic::Bilinear,
&m,
&mut rgb,
false,
);
}
#[test]
#[should_panic(expected = "exceeds magnitude bound")]
fn bayer16_u16_dispatcher_rejects_finite_extreme_m() {
let above = [0u16; 4];
let mid = [0u16; 4];
let below = [0u16; 4];
let mut rgb = [0u16; 12];
let mut m = [[1.0f32, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
m[0][0] = 1e20; bayer16_to_rgb_u16_row::<10>(
&above,
&mid,
&below,
0,
BayerPattern::Rggb,
BayerDemosaic::Bilinear,
&m,
&mut rgb,
false,
);
}
#[test]
fn bayer_dispatcher_accepts_at_bound_m() {
let above = [0u8; 4];
let mid = [0u8; 4];
let below = [0u8; 4];
let mut rgb = [0u8; 12];
let m = [
[super::MAX_FUSED_TRANSFORM_ABS, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
];
bayer_to_rgb_row(
&above,
&mid,
&below,
0,
BayerPattern::Rggb,
BayerDemosaic::Bilinear,
&m,
&mut rgb,
false,
);
}
#[test]
fn bayer16_u8_dispatcher_saturates_on_msb_aligned_input() {
let above = [0x8000u16; 4];
let mid = [0x8000u16; 4];
let below = [0x8000u16; 4];
let mut rgb = [0u8; 12];
let m = ident();
bayer16_to_rgb_row::<12>(
&above,
&mid,
&below,
0,
BayerPattern::Rggb,
BayerDemosaic::Bilinear,
&m,
&mut rgb,
false,
);
assert!(
rgb.iter().all(|&c| c == 255),
"MSB-aligned 12-bit input expected to saturate to 255 across all channels; got {rgb:?}"
);
}
#[test]
fn bayer16_u16_dispatcher_saturates_on_msb_aligned_input() {
let above = [0xFFC0u16; 4]; let mid = [0xFFC0u16; 4];
let below = [0xFFC0u16; 4];
let mut rgb = [0u16; 12];
let m = ident();
bayer16_to_rgb_u16_row::<10>(
&above,
&mid,
&below,
0,
BayerPattern::Rggb,
BayerDemosaic::Bilinear,
&m,
&mut rgb,
false,
);
assert!(
rgb.iter().all(|&c| c == 1023),
"MSB-aligned 10-bit input expected to saturate to 1023 across all channels; got {rgb:?}"
);
}
#[test]
fn bayer16_u8_dispatcher_in_range_input_correct() {
let above = [4095u16; 4]; let mid = [4095u16; 4];
let below = [4095u16; 4];
let mut rgb = [0u8; 12];
let m = ident();
bayer16_to_rgb_row::<12>(
&above,
&mid,
&below,
0,
BayerPattern::Rggb,
BayerDemosaic::Bilinear,
&m,
&mut rgb,
false,
);
assert!(
rgb.iter().all(|&c| c == 255),
"in-range 12-bit white expected to map to 255; got {rgb:?}"
);
}
}