#[cfg(any(
target_arch = "aarch64",
target_arch = "x86_64",
target_arch = "wasm32"
))]
use crate::row::arch;
#[cfg(target_arch = "aarch64")]
use crate::row::neon_available;
#[cfg(target_arch = "wasm32")]
use crate::row::simd128_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, v210_row_bytes},
};
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn v210_to_rgb_row_endian(
packed: &[u8],
rgb_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
big_endian: bool,
) {
assert!(
width.is_multiple_of(2),
"v210 requires even width (4:2:2 chroma pair)"
);
assert!(
packed.len() >= v210_row_bytes(width),
"packed row too short"
);
assert!(
rgb_out.len() >= rgb_row_bytes(width),
"rgb_out row too short"
);
macro_rules! dispatch_be {
($call_le:expr, $call_be:expr) => {
if big_endian { $call_be } else { $call_le }
};
}
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
dispatch_be!(
unsafe { arch::neon::v210_to_rgb_or_rgba_row::<false, false>(packed, rgb_out, width, matrix, full_range); },
unsafe { arch::neon::v210_to_rgb_or_rgba_row::<false, true>(packed, rgb_out, width, matrix, full_range); }
);
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
dispatch_be!(
unsafe { arch::x86_avx512::v210_to_rgb_or_rgba_row::<false, false>(packed, rgb_out, width, matrix, full_range); },
unsafe { arch::x86_avx512::v210_to_rgb_or_rgba_row::<false, true>(packed, rgb_out, width, matrix, full_range); }
);
return;
}
if avx2_available() {
dispatch_be!(
unsafe { arch::x86_avx2::v210_to_rgb_or_rgba_row::<false, false>(packed, rgb_out, width, matrix, full_range); },
unsafe { arch::x86_avx2::v210_to_rgb_or_rgba_row::<false, true>(packed, rgb_out, width, matrix, full_range); }
);
return;
}
if sse41_available() {
dispatch_be!(
unsafe { arch::x86_sse41::v210_to_rgb_or_rgba_row::<false, false>(packed, rgb_out, width, matrix, full_range); },
unsafe { arch::x86_sse41::v210_to_rgb_or_rgba_row::<false, true>(packed, rgb_out, width, matrix, full_range); }
);
return;
}
},
target_arch = "wasm32" => {
if simd128_available() {
dispatch_be!(
unsafe { arch::wasm_simd128::v210_to_rgb_or_rgba_row::<false, false>(packed, rgb_out, width, matrix, full_range); },
unsafe { arch::wasm_simd128::v210_to_rgb_or_rgba_row::<false, true>(packed, rgb_out, width, matrix, full_range); }
);
return;
}
},
_ => {}
}
}
dispatch_be!(
scalar::v210_to_rgb_or_rgba_row::<false, false>(packed, rgb_out, width, matrix, full_range),
scalar::v210_to_rgb_or_rgba_row::<false, true>(packed, rgb_out, width, matrix, full_range)
);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn v210_to_rgb_row(
packed: &[u8],
rgb_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
v210_to_rgb_row_endian(packed, rgb_out, width, matrix, full_range, use_simd, false)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn v210_to_rgba_row_endian(
packed: &[u8],
rgba_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
big_endian: bool,
) {
assert!(
width.is_multiple_of(2),
"v210 requires even width (4:2:2 chroma pair)"
);
assert!(
packed.len() >= v210_row_bytes(width),
"packed row too short"
);
assert!(
rgba_out.len() >= rgba_row_bytes(width),
"rgba_out row too short"
);
macro_rules! dispatch_be {
($call_le:expr, $call_be:expr) => {
if big_endian { $call_be } else { $call_le }
};
}
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
dispatch_be!(
unsafe { arch::neon::v210_to_rgb_or_rgba_row::<true, false>(packed, rgba_out, width, matrix, full_range); },
unsafe { arch::neon::v210_to_rgb_or_rgba_row::<true, true>(packed, rgba_out, width, matrix, full_range); }
);
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
dispatch_be!(
unsafe { arch::x86_avx512::v210_to_rgb_or_rgba_row::<true, false>(packed, rgba_out, width, matrix, full_range); },
unsafe { arch::x86_avx512::v210_to_rgb_or_rgba_row::<true, true>(packed, rgba_out, width, matrix, full_range); }
);
return;
}
if avx2_available() {
dispatch_be!(
unsafe { arch::x86_avx2::v210_to_rgb_or_rgba_row::<true, false>(packed, rgba_out, width, matrix, full_range); },
unsafe { arch::x86_avx2::v210_to_rgb_or_rgba_row::<true, true>(packed, rgba_out, width, matrix, full_range); }
);
return;
}
if sse41_available() {
dispatch_be!(
unsafe { arch::x86_sse41::v210_to_rgb_or_rgba_row::<true, false>(packed, rgba_out, width, matrix, full_range); },
unsafe { arch::x86_sse41::v210_to_rgb_or_rgba_row::<true, true>(packed, rgba_out, width, matrix, full_range); }
);
return;
}
},
target_arch = "wasm32" => {
if simd128_available() {
dispatch_be!(
unsafe { arch::wasm_simd128::v210_to_rgb_or_rgba_row::<true, false>(packed, rgba_out, width, matrix, full_range); },
unsafe { arch::wasm_simd128::v210_to_rgb_or_rgba_row::<true, true>(packed, rgba_out, width, matrix, full_range); }
);
return;
}
},
_ => {}
}
}
dispatch_be!(
scalar::v210_to_rgb_or_rgba_row::<true, false>(packed, rgba_out, width, matrix, full_range),
scalar::v210_to_rgb_or_rgba_row::<true, true>(packed, rgba_out, width, matrix, full_range)
);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn v210_to_rgba_row(
packed: &[u8],
rgba_out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
v210_to_rgba_row_endian(packed, rgba_out, width, matrix, full_range, use_simd, false)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn v210_to_rgb_u16_row_endian(
packed: &[u8],
rgb_out: &mut [u16],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
big_endian: bool,
) {
assert!(
width.is_multiple_of(2),
"v210 requires even width (4:2:2 chroma pair)"
);
assert!(
packed.len() >= v210_row_bytes(width),
"packed row too short"
);
assert!(
rgb_out.len() >= rgb_row_elems(width),
"rgb_out row too short"
);
macro_rules! dispatch_be {
($call_le:expr, $call_be:expr) => {
if big_endian { $call_be } else { $call_le }
};
}
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
dispatch_be!(
unsafe { arch::neon::v210_to_rgb_u16_or_rgba_u16_row::<false, false>(packed, rgb_out, width, matrix, full_range); },
unsafe { arch::neon::v210_to_rgb_u16_or_rgba_u16_row::<false, true>(packed, rgb_out, width, matrix, full_range); }
);
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
dispatch_be!(
unsafe { arch::x86_avx512::v210_to_rgb_u16_or_rgba_u16_row::<false, false>(packed, rgb_out, width, matrix, full_range); },
unsafe { arch::x86_avx512::v210_to_rgb_u16_or_rgba_u16_row::<false, true>(packed, rgb_out, width, matrix, full_range); }
);
return;
}
if avx2_available() {
dispatch_be!(
unsafe { arch::x86_avx2::v210_to_rgb_u16_or_rgba_u16_row::<false, false>(packed, rgb_out, width, matrix, full_range); },
unsafe { arch::x86_avx2::v210_to_rgb_u16_or_rgba_u16_row::<false, true>(packed, rgb_out, width, matrix, full_range); }
);
return;
}
if sse41_available() {
dispatch_be!(
unsafe { arch::x86_sse41::v210_to_rgb_u16_or_rgba_u16_row::<false, false>(packed, rgb_out, width, matrix, full_range); },
unsafe { arch::x86_sse41::v210_to_rgb_u16_or_rgba_u16_row::<false, true>(packed, rgb_out, width, matrix, full_range); }
);
return;
}
},
target_arch = "wasm32" => {
if simd128_available() {
dispatch_be!(
unsafe { arch::wasm_simd128::v210_to_rgb_u16_or_rgba_u16_row::<false, false>(packed, rgb_out, width, matrix, full_range); },
unsafe { arch::wasm_simd128::v210_to_rgb_u16_or_rgba_u16_row::<false, true>(packed, rgb_out, width, matrix, full_range); }
);
return;
}
},
_ => {}
}
}
dispatch_be!(
scalar::v210_to_rgb_u16_or_rgba_u16_row::<false, false>(
packed, rgb_out, width, matrix, full_range
),
scalar::v210_to_rgb_u16_or_rgba_u16_row::<false, true>(
packed, rgb_out, width, matrix, full_range
)
);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn v210_to_rgb_u16_row(
packed: &[u8],
rgb_out: &mut [u16],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
v210_to_rgb_u16_row_endian(packed, rgb_out, width, matrix, full_range, use_simd, false)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn v210_to_rgba_u16_row_endian(
packed: &[u8],
rgba_out: &mut [u16],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
big_endian: bool,
) {
assert!(
width.is_multiple_of(2),
"v210 requires even width (4:2:2 chroma pair)"
);
assert!(
packed.len() >= v210_row_bytes(width),
"packed row too short"
);
assert!(
rgba_out.len() >= rgba_row_elems(width),
"rgba_out row too short"
);
macro_rules! dispatch_be {
($call_le:expr, $call_be:expr) => {
if big_endian { $call_be } else { $call_le }
};
}
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
dispatch_be!(
unsafe { arch::neon::v210_to_rgb_u16_or_rgba_u16_row::<true, false>(packed, rgba_out, width, matrix, full_range); },
unsafe { arch::neon::v210_to_rgb_u16_or_rgba_u16_row::<true, true>(packed, rgba_out, width, matrix, full_range); }
);
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
dispatch_be!(
unsafe { arch::x86_avx512::v210_to_rgb_u16_or_rgba_u16_row::<true, false>(packed, rgba_out, width, matrix, full_range); },
unsafe { arch::x86_avx512::v210_to_rgb_u16_or_rgba_u16_row::<true, true>(packed, rgba_out, width, matrix, full_range); }
);
return;
}
if avx2_available() {
dispatch_be!(
unsafe { arch::x86_avx2::v210_to_rgb_u16_or_rgba_u16_row::<true, false>(packed, rgba_out, width, matrix, full_range); },
unsafe { arch::x86_avx2::v210_to_rgb_u16_or_rgba_u16_row::<true, true>(packed, rgba_out, width, matrix, full_range); }
);
return;
}
if sse41_available() {
dispatch_be!(
unsafe { arch::x86_sse41::v210_to_rgb_u16_or_rgba_u16_row::<true, false>(packed, rgba_out, width, matrix, full_range); },
unsafe { arch::x86_sse41::v210_to_rgb_u16_or_rgba_u16_row::<true, true>(packed, rgba_out, width, matrix, full_range); }
);
return;
}
},
target_arch = "wasm32" => {
if simd128_available() {
dispatch_be!(
unsafe { arch::wasm_simd128::v210_to_rgb_u16_or_rgba_u16_row::<true, false>(packed, rgba_out, width, matrix, full_range); },
unsafe { arch::wasm_simd128::v210_to_rgb_u16_or_rgba_u16_row::<true, true>(packed, rgba_out, width, matrix, full_range); }
);
return;
}
},
_ => {}
}
}
dispatch_be!(
scalar::v210_to_rgb_u16_or_rgba_u16_row::<true, false>(
packed, rgba_out, width, matrix, full_range
),
scalar::v210_to_rgb_u16_or_rgba_u16_row::<true, true>(
packed, rgba_out, width, matrix, full_range
)
);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn v210_to_rgba_u16_row(
packed: &[u8],
rgba_out: &mut [u16],
width: usize,
matrix: ColorMatrix,
full_range: bool,
use_simd: bool,
) {
v210_to_rgba_u16_row_endian(packed, rgba_out, width, matrix, full_range, use_simd, false)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn v210_to_luma_row_endian(
packed: &[u8],
luma_out: &mut [u8],
width: usize,
use_simd: bool,
big_endian: bool,
) {
assert!(
width.is_multiple_of(2),
"v210 requires even width (4:2:2 chroma pair)"
);
assert!(
packed.len() >= v210_row_bytes(width),
"packed row too short"
);
assert!(luma_out.len() >= width, "luma_out row too short");
macro_rules! dispatch_be {
($call_le:expr, $call_be:expr) => {
if big_endian { $call_be } else { $call_le }
};
}
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
dispatch_be!(
unsafe { arch::neon::v210_to_luma_row::<false>(packed, luma_out, width); },
unsafe { arch::neon::v210_to_luma_row::<true>(packed, luma_out, width); }
);
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
dispatch_be!(
unsafe { arch::x86_avx512::v210_to_luma_row::<false>(packed, luma_out, width); },
unsafe { arch::x86_avx512::v210_to_luma_row::<true>(packed, luma_out, width); }
);
return;
}
if avx2_available() {
dispatch_be!(
unsafe { arch::x86_avx2::v210_to_luma_row::<false>(packed, luma_out, width); },
unsafe { arch::x86_avx2::v210_to_luma_row::<true>(packed, luma_out, width); }
);
return;
}
if sse41_available() {
dispatch_be!(
unsafe { arch::x86_sse41::v210_to_luma_row::<false>(packed, luma_out, width); },
unsafe { arch::x86_sse41::v210_to_luma_row::<true>(packed, luma_out, width); }
);
return;
}
},
target_arch = "wasm32" => {
if simd128_available() {
dispatch_be!(
unsafe { arch::wasm_simd128::v210_to_luma_row::<false>(packed, luma_out, width); },
unsafe { arch::wasm_simd128::v210_to_luma_row::<true>(packed, luma_out, width); }
);
return;
}
},
_ => {}
}
}
dispatch_be!(
scalar::v210_to_luma_row::<false>(packed, luma_out, width),
scalar::v210_to_luma_row::<true>(packed, luma_out, width)
);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn v210_to_luma_row(packed: &[u8], luma_out: &mut [u8], width: usize, use_simd: bool) {
v210_to_luma_row_endian(packed, luma_out, width, use_simd, false)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn v210_to_luma_u16_row_endian(
packed: &[u8],
luma_out: &mut [u16],
width: usize,
use_simd: bool,
big_endian: bool,
) {
assert!(
width.is_multiple_of(2),
"v210 requires even width (4:2:2 chroma pair)"
);
assert!(
packed.len() >= v210_row_bytes(width),
"packed row too short"
);
assert!(luma_out.len() >= width, "luma_out row too short");
macro_rules! dispatch_be {
($call_le:expr, $call_be:expr) => {
if big_endian { $call_be } else { $call_le }
};
}
if use_simd {
cfg_select! {
target_arch = "aarch64" => {
if neon_available() {
dispatch_be!(
unsafe { arch::neon::v210_to_luma_u16_row::<false>(packed, luma_out, width); },
unsafe { arch::neon::v210_to_luma_u16_row::<true>(packed, luma_out, width); }
);
return;
}
},
target_arch = "x86_64" => {
if avx512_available() {
dispatch_be!(
unsafe { arch::x86_avx512::v210_to_luma_u16_row::<false>(packed, luma_out, width); },
unsafe { arch::x86_avx512::v210_to_luma_u16_row::<true>(packed, luma_out, width); }
);
return;
}
if avx2_available() {
dispatch_be!(
unsafe { arch::x86_avx2::v210_to_luma_u16_row::<false>(packed, luma_out, width); },
unsafe { arch::x86_avx2::v210_to_luma_u16_row::<true>(packed, luma_out, width); }
);
return;
}
if sse41_available() {
dispatch_be!(
unsafe { arch::x86_sse41::v210_to_luma_u16_row::<false>(packed, luma_out, width); },
unsafe { arch::x86_sse41::v210_to_luma_u16_row::<true>(packed, luma_out, width); }
);
return;
}
},
target_arch = "wasm32" => {
if simd128_available() {
dispatch_be!(
unsafe { arch::wasm_simd128::v210_to_luma_u16_row::<false>(packed, luma_out, width); },
unsafe { arch::wasm_simd128::v210_to_luma_u16_row::<true>(packed, luma_out, width); }
);
return;
}
},
_ => {}
}
}
dispatch_be!(
scalar::v210_to_luma_u16_row::<false>(packed, luma_out, width),
scalar::v210_to_luma_u16_row::<true>(packed, luma_out, width)
);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn v210_to_luma_u16_row(packed: &[u8], luma_out: &mut [u16], width: usize, use_simd: bool) {
v210_to_luma_u16_row_endian(packed, luma_out, width, use_simd, false)
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
fn pack_v210_word(samples: [u16; 12]) -> [u8; 16] {
let mut out = [0u8; 16];
let w0 = (samples[0] as u32 & 0x3FF)
| ((samples[1] as u32 & 0x3FF) << 10)
| ((samples[2] as u32 & 0x3FF) << 20);
let w1 = (samples[3] as u32 & 0x3FF)
| ((samples[4] as u32 & 0x3FF) << 10)
| ((samples[5] as u32 & 0x3FF) << 20);
let w2 = (samples[6] as u32 & 0x3FF)
| ((samples[7] as u32 & 0x3FF) << 10)
| ((samples[8] as u32 & 0x3FF) << 20);
let w3 = (samples[9] as u32 & 0x3FF)
| ((samples[10] as u32 & 0x3FF) << 10)
| ((samples[11] as u32 & 0x3FF) << 20);
out[0..4].copy_from_slice(&w0.to_le_bytes());
out[4..8].copy_from_slice(&w1.to_le_bytes());
out[8..12].copy_from_slice(&w2.to_le_bytes());
out[12..16].copy_from_slice(&w3.to_le_bytes());
out
}
#[test]
fn v210_dispatchers_route_with_simd_false() {
let word = pack_v210_word([512; 12]);
let mut rgb = [0u8; 6 * 3];
v210_to_rgb_row(&word, &mut rgb, 6, ColorMatrix::Bt709, true, false);
for px in rgb.chunks(3) {
assert!(px[0].abs_diff(128) <= 1);
assert_eq!(px[0], px[1]);
assert_eq!(px[1], px[2]);
}
let mut rgba = [0u8; 6 * 4];
v210_to_rgba_row(&word, &mut rgba, 6, ColorMatrix::Bt709, true, false);
for px in rgba.chunks(4) {
assert!(px[0].abs_diff(128) <= 1);
assert_eq!(px[3], 0xFF);
}
let mut rgb_u16 = [0u16; 6 * 3];
v210_to_rgb_u16_row(&word, &mut rgb_u16, 6, ColorMatrix::Bt709, true, false);
for px in rgb_u16.chunks(3) {
assert!(px[0].abs_diff(512) <= 2);
assert_eq!(px[0], px[1]);
assert_eq!(px[1], px[2]);
}
let mut rgba_u16 = [0u16; 6 * 4];
v210_to_rgba_u16_row(&word, &mut rgba_u16, 6, ColorMatrix::Bt709, true, false);
for px in rgba_u16.chunks(4) {
assert_eq!(px[3], 1023);
}
let mut luma = [0u8; 6];
v210_to_luma_row(&word, &mut luma, 6, false);
for &y in &luma {
assert_eq!(y, (512u16 >> 2) as u8);
}
let mut luma_u16 = [0u16; 6];
v210_to_luma_u16_row(&word, &mut luma_u16, 6, false);
for &y in &luma_u16 {
assert_eq!(y, 512);
}
}
}