#![allow(
clippy::many_single_char_names,
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::similar_names,
clippy::cast_sign_loss
)]
#[cfg(all(feature = "simd", any(target_arch = "x86_64", target_arch = "x86")))]
#[allow(unsafe_op_in_unsafe_fn, clippy::similar_names)]
pub(crate) unsafe fn cl_quad_to_bgra_sse2(quad: &[u8; 8]) -> [u8; 16] {
use core::arch::x86_64::{
__m128i, _mm_add_epi32, _mm_cvtsi32_si128, _mm_set_epi32, _mm_setzero_si128, _mm_storeu_si128, _mm_sub_epi32,
_mm_unpacklo_epi8, _mm_unpacklo_epi16,
};
let mut rc_arr = [0i32; 4];
let mut gb_arr = [0i32; 4];
let mut gr_arr = [0i32; 4];
let mut bc_arr = [0i32; 4];
for i in 0..4 {
let raw = quad[4 + i];
let cb = i32::from((raw & 0x0F) << 4) - 128;
let cr = i32::from(raw & 0xF0) - 128;
rc_arr[i] = (cr * 359) >> 8;
gb_arr[i] = (cb * 88) >> 8;
gr_arr[i] = (cr * 183) >> 8;
bc_arr[i] = (cb * 454) >> 8;
}
let y_bytes = _mm_cvtsi32_si128(i32::from_le_bytes([quad[0], quad[1], quad[2], quad[3]]));
let y_words = _mm_unpacklo_epi8(y_bytes, _mm_setzero_si128());
let y = _mm_unpacklo_epi16(y_words, _mm_setzero_si128());
let rc = _mm_set_epi32(rc_arr[3], rc_arr[2], rc_arr[1], rc_arr[0]);
let gb = _mm_set_epi32(gb_arr[3], gb_arr[2], gb_arr[1], gb_arr[0]);
let gr = _mm_set_epi32(gr_arr[3], gr_arr[2], gr_arr[1], gr_arr[0]);
let bc = _mm_set_epi32(bc_arr[3], bc_arr[2], bc_arr[1], bc_arr[0]);
let r = _mm_add_epi32(y, rc);
let g = _mm_sub_epi32(_mm_sub_epi32(y, gb), gr);
let b = _mm_add_epi32(y, bc);
let mut r_arr = [0i32; 4];
let mut g_arr = [0i32; 4];
let mut b_arr = [0i32; 4];
_mm_storeu_si128(r_arr.as_mut_ptr().cast::<__m128i>(), r);
_mm_storeu_si128(g_arr.as_mut_ptr().cast::<__m128i>(), g);
_mm_storeu_si128(b_arr.as_mut_ptr().cast::<__m128i>(), b);
let mut out = [0u8; 16];
for i in 0..4 {
out[i * 4] = crate::yuv::clamp(b_arr[i]);
out[i * 4 + 1] = crate::yuv::clamp(g_arr[i]);
out[i * 4 + 2] = crate::yuv::clamp(r_arr[i]);
out[i * 4 + 3] = 255;
}
out
}
#[cfg(all(feature = "simd", any(target_arch = "x86_64", target_arch = "x86")))]
#[inline]
#[allow(unsafe_op_in_unsafe_fn)]
pub(crate) unsafe fn cl_row_to_bgra_sse2(src: &[u8], dst: &mut [u8]) {
let n_pixels = src.len() / 2;
let (y, chroma) = src.split_at(n_pixels);
let full_end = (n_pixels / 8) * 8;
let mut i = 0usize;
while i < full_end {
let q0 = [
y[i],
y[i + 1],
y[i + 2],
y[i + 3],
chroma[i],
chroma[i + 1],
chroma[i + 2],
chroma[i + 3],
];
let out0 = cl_quad_to_bgra_sse2(&q0);
let q1 = [
y[i + 4],
y[i + 5],
y[i + 6],
y[i + 7],
chroma[i + 4],
chroma[i + 5],
chroma[i + 6],
chroma[i + 7],
];
let out1 = cl_quad_to_bgra_sse2(&q1);
let d_off = i * 4;
dst[d_off..d_off + 16].copy_from_slice(&out0);
dst[d_off + 16..d_off + 32].copy_from_slice(&out1);
i += 8;
}
while i + 4 <= n_pixels {
let q = [
y[i],
y[i + 1],
y[i + 2],
y[i + 3],
chroma[i],
chroma[i + 1],
chroma[i + 2],
chroma[i + 3],
];
let out = cl_quad_to_bgra_sse2(&q);
let d_off = i * 4;
dst[d_off..d_off + 16].copy_from_slice(&out);
i += 4;
}
for j in i..n_pixels {
let cr = chroma[j] & 0xF0; let cb = (chroma[j] & 0x0F) << 4; let px = crate::yuv::yuv_to_bgra(y[j], cb, cr);
let o = j * 4;
dst[o..o + 4].copy_from_slice(&px);
}
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
#[target_feature(enable = "sse4.1")]
#[inline]
#[allow(unsafe_op_in_unsafe_fn, clippy::similar_names)]
pub(crate) unsafe fn cl_quad_to_bgra_sse41(quad: &[u8; 8]) -> [u8; 16] {
use core::arch::x86_64::{
__m128i, _mm_add_epi32, _mm_cvtepu8_epi32, _mm_cvtsi32_si128, _mm_max_epi32, _mm_min_epi32, _mm_packus_epi16,
_mm_packus_epi32, _mm_set_epi32, _mm_set1_epi16, _mm_set1_epi32, _mm_setzero_si128, _mm_storeu_si128,
_mm_sub_epi32, _mm_unpacklo_epi8, _mm_unpacklo_epi16,
};
let mut rc_arr = [0i32; 4];
let mut gb_arr = [0i32; 4];
let mut gr_arr = [0i32; 4];
let mut bc_arr = [0i32; 4];
for i in 0..4 {
let raw = quad[4 + i];
let cb = i32::from((raw & 0x0F) << 4) - 128;
let cr = i32::from(raw & 0xF0) - 128;
rc_arr[i] = (cr * 359) >> 8;
gb_arr[i] = (cb * 88) >> 8;
gr_arr[i] = (cr * 183) >> 8;
bc_arr[i] = (cb * 454) >> 8;
}
let y_chunk = _mm_cvtsi32_si128(i32::from_le_bytes([quad[0], quad[1], quad[2], quad[3]]));
let y = _mm_cvtepu8_epi32(y_chunk);
let rc = _mm_set_epi32(rc_arr[3], rc_arr[2], rc_arr[1], rc_arr[0]);
let gb = _mm_set_epi32(gb_arr[3], gb_arr[2], gb_arr[1], gb_arr[0]);
let gr = _mm_set_epi32(gr_arr[3], gr_arr[2], gr_arr[1], gr_arr[0]);
let bc = _mm_set_epi32(bc_arr[3], bc_arr[2], bc_arr[1], bc_arr[0]);
let r = _mm_add_epi32(y, rc);
let g = _mm_sub_epi32(_mm_sub_epi32(y, gb), gr);
let b = _mm_add_epi32(y, bc);
let zero = _mm_setzero_si128();
let max_val = _mm_set1_epi32(255);
let r_c = _mm_max_epi32(_mm_min_epi32(r, max_val), zero);
let g_c = _mm_max_epi32(_mm_min_epi32(g, max_val), zero);
let b_c = _mm_max_epi32(_mm_min_epi32(b, max_val), zero);
let b16 = _mm_packus_epi32(b_c, zero);
let g16 = _mm_packus_epi32(g_c, zero);
let r16 = _mm_packus_epi32(r_c, zero);
let a16 = _mm_set1_epi16(255i16);
let br = _mm_unpacklo_epi16(b16, r16);
let ga = _mm_unpacklo_epi16(g16, a16);
let br_u8 = _mm_packus_epi16(br, zero);
let ga_u8 = _mm_packus_epi16(ga, zero);
let result = _mm_unpacklo_epi8(br_u8, ga_u8);
let mut out = [0u8; 16];
_mm_storeu_si128(out.as_mut_ptr().cast::<__m128i>(), result);
out
}
#[cfg(all(feature = "simd", any(target_arch = "x86_64", target_arch = "x86")))]
#[target_feature(enable = "sse4.1,ssse3")]
#[inline]
#[allow(unsafe_op_in_unsafe_fn)]
pub(crate) unsafe fn cl_row_to_bgra_sse41(src: &[u8], dst: &mut [u8]) {
use core::arch::x86_64::{
__m128i, _mm_add_epi32, _mm_and_si128, _mm_cvtepu8_epi32, _mm_cvtsi32_si128, _mm_max_epi32, _mm_min_epi32,
_mm_mullo_epi32, _mm_packus_epi16, _mm_packus_epi32, _mm_set1_epi8, _mm_set1_epi16, _mm_set1_epi32,
_mm_setr_epi8, _mm_setzero_si128, _mm_shuffle_epi8, _mm_srai_epi32, _mm_srli_epi16, _mm_storeu_si128,
_mm_sub_epi32, _mm_unpacklo_epi8, _mm_unpacklo_epi16,
};
let n_pixels = src.len() / 2;
let (y, chroma) = src.split_at(n_pixels);
let full_end = (n_pixels / 4) * 4;
let tbl = _mm_setr_epi8(
0i8, 16i8, 32i8, 48i8, 64i8, 80i8, 96i8, 112i8, -128i8, -112i8, -96i8, -80i8, -64i8, -48i8, -32i8, -16i8,
);
let mask_lo = _mm_set1_epi8(0x0F);
let zero = _mm_setzero_si128();
let max_val = _mm_set1_epi32(255);
let cent = _mm_set1_epi32(128);
let coef_359 = _mm_set1_epi32(359);
let coef_88 = _mm_set1_epi32(88);
let coef_183 = _mm_set1_epi32(183);
let coef_454 = _mm_set1_epi32(454);
let a16 = _mm_set1_epi16(255i16);
let mut i = 0usize;
while i < full_end {
let y_chunk = _mm_cvtsi32_si128(i32::from_le_bytes([y[i], y[i + 1], y[i + 2], y[i + 3]]));
let y_vals = _mm_cvtepu8_epi32(y_chunk);
let c_chunk = _mm_cvtsi32_si128(i32::from_le_bytes([
chroma[i],
chroma[i + 1],
chroma[i + 2],
chroma[i + 3],
]));
let cb_idx = _mm_and_si128(c_chunk, mask_lo);
let cb_bytes = _mm_shuffle_epi8(tbl, cb_idx);
let cb = _mm_cvtepu8_epi32(cb_bytes);
let cr_idx = _mm_and_si128(_mm_srli_epi16(c_chunk, 4), mask_lo);
let cr_bytes = _mm_shuffle_epi8(tbl, cr_idx);
let cr = _mm_cvtepu8_epi32(cr_bytes);
let cb_c = _mm_sub_epi32(cb, cent);
let cr_c = _mm_sub_epi32(cr, cent);
let rc = _mm_srai_epi32(_mm_mullo_epi32(cr_c, coef_359), 8);
let gb = _mm_srai_epi32(_mm_mullo_epi32(cb_c, coef_88), 8);
let gr = _mm_srai_epi32(_mm_mullo_epi32(cr_c, coef_183), 8);
let bc = _mm_srai_epi32(_mm_mullo_epi32(cb_c, coef_454), 8);
let r = _mm_add_epi32(y_vals, rc);
let g = _mm_sub_epi32(_mm_sub_epi32(y_vals, gb), gr);
let b = _mm_add_epi32(y_vals, bc);
let r_c = _mm_max_epi32(_mm_min_epi32(r, max_val), zero);
let g_c = _mm_max_epi32(_mm_min_epi32(g, max_val), zero);
let b_c = _mm_max_epi32(_mm_min_epi32(b, max_val), zero);
let b16 = _mm_packus_epi32(b_c, zero);
let g16 = _mm_packus_epi32(g_c, zero);
let r16 = _mm_packus_epi32(r_c, zero);
let br = _mm_unpacklo_epi16(b16, r16);
let ga = _mm_unpacklo_epi16(g16, a16);
let br_u8 = _mm_packus_epi16(br, zero);
let ga_u8 = _mm_packus_epi16(ga, zero);
let result = _mm_unpacklo_epi8(br_u8, ga_u8);
let d_off = i * 4;
_mm_storeu_si128(dst.as_mut_ptr().add(d_off).cast::<__m128i>(), result);
i += 4;
}
for j in i..n_pixels {
let cr = chroma[j] & 0xF0;
let cb = (chroma[j] & 0x0F) << 4;
let px = crate::yuv::yuv_to_bgra(y[j], cb, cr);
let o = j * 4;
dst[o..o + 4].copy_from_slice(&px);
}
}
#[cfg(all(feature = "simd", any(target_arch = "x86_64", target_arch = "x86")))]
#[target_feature(enable = "ssse3")]
#[cfg(test)]
#[allow(unsafe_op_in_unsafe_fn)]
pub(crate) unsafe fn cl_quad_to_bgra_ssse3(quad: &[u8; 8]) -> [u8; 16] {
use core::arch::x86_64::{
__m128i, _mm_and_si128, _mm_cvtsi32_si128, _mm_loadu_si128, _mm_set1_epi8, _mm_shuffle_epi8, _mm_srli_epi16,
_mm_storeu_si128,
};
let table = _mm_loadu_si128(super::CL_NIBBLE_TABLE.as_ptr().cast::<__m128i>());
let mask_lo = _mm_set1_epi8(0x0F);
let chroma = _mm_cvtsi32_si128(i32::from_le_bytes([quad[4], quad[5], quad[6], quad[7]]));
let cb_idx = _mm_and_si128(chroma, mask_lo);
let cb = _mm_shuffle_epi8(table, cb_idx);
let cr_idx = _mm_and_si128(_mm_srli_epi16(chroma, 4), mask_lo);
let cr = _mm_shuffle_epi8(table, cr_idx);
let mut cb_vals = [0u8; 16];
let mut cr_vals = [0u8; 16];
_mm_storeu_si128(cb_vals.as_mut_ptr().cast::<__m128i>(), cb);
_mm_storeu_si128(cr_vals.as_mut_ptr().cast::<__m128i>(), cr);
let mut out = [0u8; 16];
for i in 0..4 {
let px = crate::yuv::yuv_to_bgra(quad[i], cb_vals[i], cr_vals[i]);
out[i * 4..][..4].copy_from_slice(&px);
}
out
}
#[cfg(all(feature = "simd", target_arch = "x86_64"))]
#[inline]
#[cfg(test)]
#[allow(unsafe_op_in_unsafe_fn, clippy::similar_names)]
pub(crate) unsafe fn cl_quad_to_bgra_avx2(quad: &[u8; 8]) -> [u8; 16] {
use core::arch::x86_64::{
__m128i, _mm_cvtsi32_si128, _mm_loadu_si128, _mm_storeu_si128, _mm256_add_epi32, _mm256_and_si256,
_mm256_broadcastsi128_si256, _mm256_cvtepu8_epi32, _mm256_extracti128_si256, _mm256_set1_epi8,
_mm256_setr_epi32, _mm256_shuffle_epi8, _mm256_srli_epi16, _mm256_sub_epi32,
};
let table_128 = _mm_loadu_si128(super::CL_NIBBLE_TABLE.as_ptr().cast::<__m128i>());
let table = _mm256_broadcastsi128_si256(table_128);
let mask_lo = _mm256_set1_epi8(0x0F);
let chroma_128 = _mm_cvtsi32_si128(i32::from_le_bytes([quad[4], quad[5], quad[6], quad[7]]));
let chroma = _mm256_broadcastsi128_si256(chroma_128);
let cb_idx = _mm256_and_si256(chroma, mask_lo);
let cb = _mm256_shuffle_epi8(table, cb_idx);
let cr_idx = _mm256_and_si256(_mm256_srli_epi16(chroma, 4), mask_lo);
let cr = _mm256_shuffle_epi8(table, cr_idx);
let cb_128 = _mm256_extracti128_si256(cb, 0);
let cr_128 = _mm256_extracti128_si256(cr, 0);
let mut cb_vals = [0u8; 16];
let mut cr_vals = [0u8; 16];
_mm_storeu_si128(cb_vals.as_mut_ptr().cast::<__m128i>(), cb_128);
_mm_storeu_si128(cr_vals.as_mut_ptr().cast::<__m128i>(), cr_128);
let mut rc_arr = [0i32; 4];
let mut gb_arr = [0i32; 4];
let mut gr_arr = [0i32; 4];
let mut bc_arr = [0i32; 4];
for i in 0..4 {
let cb_c = i32::from(cb_vals[i]) - 128;
let cr_c = i32::from(cr_vals[i]) - 128;
rc_arr[i] = (cr_c * 359) >> 8;
gb_arr[i] = (cb_c * 88) >> 8;
gr_arr[i] = (cr_c * 183) >> 8;
bc_arr[i] = (cb_c * 454) >> 8;
}
let y_bytes = _mm_cvtsi32_si128(i32::from_le_bytes([quad[0], quad[1], quad[2], quad[3]]));
let y = _mm256_cvtepu8_epi32(y_bytes);
let rc = _mm256_setr_epi32(rc_arr[0], rc_arr[1], rc_arr[2], rc_arr[3], 0, 0, 0, 0);
let gb = _mm256_setr_epi32(gb_arr[0], gb_arr[1], gb_arr[2], gb_arr[3], 0, 0, 0, 0);
let gr = _mm256_setr_epi32(gr_arr[0], gr_arr[1], gr_arr[2], gr_arr[3], 0, 0, 0, 0);
let bc = _mm256_setr_epi32(bc_arr[0], bc_arr[1], bc_arr[2], bc_arr[3], 0, 0, 0, 0);
let r = _mm256_add_epi32(y, rc);
let g = _mm256_sub_epi32(_mm256_sub_epi32(y, gb), gr);
let b = _mm256_add_epi32(y, bc);
let r_lo = _mm256_extracti128_si256(r, 0);
let g_lo = _mm256_extracti128_si256(g, 0);
let b_lo = _mm256_extracti128_si256(b, 0);
let mut r_arr = [0i32; 4];
let mut g_arr = [0i32; 4];
let mut b_arr = [0i32; 4];
_mm_storeu_si128(r_arr.as_mut_ptr().cast::<__m128i>(), r_lo);
_mm_storeu_si128(g_arr.as_mut_ptr().cast::<__m128i>(), g_lo);
_mm_storeu_si128(b_arr.as_mut_ptr().cast::<__m128i>(), b_lo);
let mut out = [0u8; 16];
for i in 0..4 {
out[i * 4] = crate::yuv::clamp(b_arr[i]);
out[i * 4 + 1] = crate::yuv::clamp(g_arr[i]);
out[i * 4 + 2] = crate::yuv::clamp(r_arr[i]);
out[i * 4 + 3] = 255;
}
out
}