#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
const PW_F0402: i16 = 26345;
const PW_MF0228: i16 = -14942;
const PW_MF0344: i16 = -22554;
const PW_F0285: i16 = 18734;
pub fn avx2_merged_h2v1_ycbcr_to_rgb(
y_row: &[u8],
cb_row: &[u8],
cr_row: &[u8],
rgb_out: &mut [u8],
width: usize,
) {
unsafe {
avx2_merged_h2v1_inner(y_row, cb_row, cr_row, rgb_out, width);
}
}
pub fn avx2_merged_h2v2_ycbcr_to_rgb(
y_row0: &[u8],
y_row1: &[u8],
cb_row: &[u8],
cr_row: &[u8],
rgb_out0: &mut [u8],
rgb_out1: &mut [u8],
width: usize,
) {
unsafe {
avx2_merged_h2v2_inner(y_row0, y_row1, cb_row, cr_row, rgb_out0, rgb_out1, width);
}
}
#[target_feature(enable = "avx2")]
#[inline]
unsafe fn compute_chroma_deltas(cb16: __m256i, cr16: __m256i) -> (__m256i, __m256i, __m256i) {
let offset_128 = _mm256_set1_epi16(128);
let one = _mm256_set1_epi16(1);
let cb_c = _mm256_sub_epi16(cb16, offset_128); let cr_c = _mm256_sub_epi16(cr16, offset_128);
let cr2 = _mm256_add_epi16(cr_c, cr_c); let r_mul = _mm256_mulhi_epi16(cr2, _mm256_set1_epi16(PW_F0402));
let r_mul_rounded = _mm256_srai_epi16::<1>(_mm256_add_epi16(r_mul, one));
let r_minus_y = _mm256_add_epi16(cr_c, r_mul_rounded);
let cb2 = _mm256_add_epi16(cb_c, cb_c);
let b_mul = _mm256_mulhi_epi16(cb2, _mm256_set1_epi16(PW_MF0228));
let b_mul_rounded = _mm256_srai_epi16::<1>(_mm256_add_epi16(b_mul, one));
let b_minus_y = _mm256_add_epi16(_mm256_add_epi16(cb_c, cb_c), b_mul_rounded);
let cb_cr_lo = _mm256_unpacklo_epi16(cb_c, cr_c); let cb_cr_hi = _mm256_unpackhi_epi16(cb_c, cr_c);
let coeff =
_mm256_set1_epi32(((PW_F0285 as u16 as u32) << 16 | (PW_MF0344 as u16 as u32)) as i32);
let g_lo_32 = _mm256_madd_epi16(cb_cr_lo, coeff); let g_hi_32 = _mm256_madd_epi16(cb_cr_hi, coeff);
let one_half = _mm256_set1_epi32(1 << 15);
let g_lo_shifted = _mm256_srai_epi32::<16>(_mm256_add_epi32(g_lo_32, one_half));
let g_hi_shifted = _mm256_srai_epi32::<16>(_mm256_add_epi32(g_hi_32, one_half));
let g_packed = _mm256_packs_epi32(g_lo_shifted, g_hi_shifted);
let g_minus_y = _mm256_sub_epi16(g_packed, cr_c);
(r_minus_y, g_minus_y, b_minus_y)
}
#[target_feature(enable = "avx2")]
#[inline]
unsafe fn apply_deltas_to_y_row(
y_ptr: *const u8,
rgb_ptr: *mut u8,
r_minus_y: __m256i,
g_minus_y: __m256i,
b_minus_y: __m256i,
) {
let y_raw = _mm256_loadu_si256(y_ptr as *const __m256i);
let even_mask = _mm256_set1_epi16(0x00FF);
let y_even = _mm256_and_si256(y_raw, even_mask); let y_odd = _mm256_srli_epi16(y_raw, 8);
let re = _mm256_add_epi16(y_even, r_minus_y);
let ge = _mm256_add_epi16(y_even, g_minus_y);
let be = _mm256_add_epi16(y_even, b_minus_y);
let ro = _mm256_add_epi16(y_odd, r_minus_y);
let go = _mm256_add_epi16(y_odd, g_minus_y);
let bo = _mm256_add_epi16(y_odd, b_minus_y);
let re_u8 = pack_i16_to_u8(re);
let ge_u8 = pack_i16_to_u8(ge);
let be_u8 = pack_i16_to_u8(be);
let ro_u8 = pack_i16_to_u8(ro);
let go_u8 = pack_i16_to_u8(go);
let bo_u8 = pack_i16_to_u8(bo);
let r_interleaved = _mm_unpacklo_epi8(re_u8, ro_u8); let g_interleaved = _mm_unpacklo_epi8(ge_u8, go_u8);
let b_interleaved = _mm_unpacklo_epi8(be_u8, bo_u8);
super::avx2_color::store_rgb_interleaved_ssse3_pub(
rgb_ptr,
r_interleaved,
g_interleaved,
b_interleaved,
);
let r_hi = _mm_unpackhi_epi8(re_u8, ro_u8);
let g_hi = _mm_unpackhi_epi8(ge_u8, go_u8);
let b_hi = _mm_unpackhi_epi8(be_u8, bo_u8);
super::avx2_color::store_rgb_interleaved_ssse3_pub(rgb_ptr.add(48), r_hi, g_hi, b_hi);
}
#[target_feature(enable = "avx2")]
#[inline]
unsafe fn pack_i16_to_u8(v: __m256i) -> __m128i {
let lo = _mm256_castsi256_si128(v);
let hi = _mm256_extracti128_si256::<1>(v);
_mm_packus_epi16(lo, hi)
}
#[target_feature(enable = "avx2")]
unsafe fn avx2_merged_h2v1_inner(
y_row: &[u8],
cb_row: &[u8],
cr_row: &[u8],
rgb_out: &mut [u8],
width: usize,
) {
let chroma_width: usize = width / 2;
let mut cx: usize = 0;
while cx + 16 <= chroma_width {
let cb16 = _mm256_cvtepu8_epi16(_mm_loadu_si128(cb_row.as_ptr().add(cx) as *const __m128i));
let cr16 = _mm256_cvtepu8_epi16(_mm_loadu_si128(cr_row.as_ptr().add(cx) as *const __m128i));
let (r_my, g_my, b_my) = compute_chroma_deltas(cb16, cr16);
apply_deltas_to_y_row(
y_row.as_ptr().add(cx * 2),
rgb_out.as_mut_ptr().add(cx * 2 * 3),
r_my,
g_my,
b_my,
);
cx += 16;
}
while cx < chroma_width {
let cb_i: i32 = cb_row[cx] as i32 - 128;
let cr_i: i32 = cr_row[cx] as i32 - 128;
let cred: i32 = (91881 * cr_i + 32768) >> 16;
let cgreen: i32 = (-22554 * cb_i + -46802 * cr_i + 32768) >> 16;
let cblue: i32 = (116130 * cb_i + 32768) >> 16;
let px = cx * 2;
for d in 0..2 {
let yi: i32 = y_row[px + d] as i32;
rgb_out[(px + d) * 3] = (yi + cred).clamp(0, 255) as u8;
rgb_out[(px + d) * 3 + 1] = (yi + cgreen).clamp(0, 255) as u8;
rgb_out[(px + d) * 3 + 2] = (yi + cblue).clamp(0, 255) as u8;
}
cx += 1;
}
if width & 1 != 0 {
let last_x = width - 1;
let cc = last_x / 2;
let cb_i: i32 = cb_row[cc] as i32 - 128;
let cr_i: i32 = cr_row[cc] as i32 - 128;
let cred: i32 = (91881 * cr_i + 32768) >> 16;
let cgreen: i32 = (-22554 * cb_i + -46802 * cr_i + 32768) >> 16;
let cblue: i32 = (116130 * cb_i + 32768) >> 16;
let yi: i32 = y_row[last_x] as i32;
rgb_out[last_x * 3] = (yi + cred).clamp(0, 255) as u8;
rgb_out[last_x * 3 + 1] = (yi + cgreen).clamp(0, 255) as u8;
rgb_out[last_x * 3 + 2] = (yi + cblue).clamp(0, 255) as u8;
}
}
#[target_feature(enable = "avx2")]
unsafe fn avx2_merged_h2v2_inner(
y_row0: &[u8],
y_row1: &[u8],
cb_row: &[u8],
cr_row: &[u8],
rgb_out0: &mut [u8],
rgb_out1: &mut [u8],
width: usize,
) {
let chroma_width: usize = width / 2;
let mut cx: usize = 0;
while cx + 16 <= chroma_width {
let cb16 = _mm256_cvtepu8_epi16(_mm_loadu_si128(cb_row.as_ptr().add(cx) as *const __m128i));
let cr16 = _mm256_cvtepu8_epi16(_mm_loadu_si128(cr_row.as_ptr().add(cx) as *const __m128i));
let (r_my, g_my, b_my) = compute_chroma_deltas(cb16, cr16);
apply_deltas_to_y_row(
y_row0.as_ptr().add(cx * 2),
rgb_out0.as_mut_ptr().add(cx * 2 * 3),
r_my,
g_my,
b_my,
);
apply_deltas_to_y_row(
y_row1.as_ptr().add(cx * 2),
rgb_out1.as_mut_ptr().add(cx * 2 * 3),
r_my,
g_my,
b_my,
);
cx += 16;
}
while cx < chroma_width {
let cb_i: i32 = cb_row[cx] as i32 - 128;
let cr_i: i32 = cr_row[cx] as i32 - 128;
let cred: i32 = (91881 * cr_i + 32768) >> 16;
let cgreen: i32 = (-22554 * cb_i + -46802 * cr_i + 32768) >> 16;
let cblue: i32 = (116130 * cb_i + 32768) >> 16;
let px = cx * 2;
for d in 0..2 {
let yi0: i32 = y_row0[px + d] as i32;
rgb_out0[(px + d) * 3] = (yi0 + cred).clamp(0, 255) as u8;
rgb_out0[(px + d) * 3 + 1] = (yi0 + cgreen).clamp(0, 255) as u8;
rgb_out0[(px + d) * 3 + 2] = (yi0 + cblue).clamp(0, 255) as u8;
let yi1: i32 = y_row1[px + d] as i32;
rgb_out1[(px + d) * 3] = (yi1 + cred).clamp(0, 255) as u8;
rgb_out1[(px + d) * 3 + 1] = (yi1 + cgreen).clamp(0, 255) as u8;
rgb_out1[(px + d) * 3 + 2] = (yi1 + cblue).clamp(0, 255) as u8;
}
cx += 1;
}
if width & 1 != 0 {
let last_x = width - 1;
let cc = last_x / 2;
let cb_i: i32 = cb_row[cc] as i32 - 128;
let cr_i: i32 = cr_row[cc] as i32 - 128;
let cred: i32 = (91881 * cr_i + 32768) >> 16;
let cgreen: i32 = (-22554 * cb_i + -46802 * cr_i + 32768) >> 16;
let cblue: i32 = (116130 * cb_i + 32768) >> 16;
let yi0: i32 = y_row0[last_x] as i32;
rgb_out0[last_x * 3] = (yi0 + cred).clamp(0, 255) as u8;
rgb_out0[last_x * 3 + 1] = (yi0 + cgreen).clamp(0, 255) as u8;
rgb_out0[last_x * 3 + 2] = (yi0 + cblue).clamp(0, 255) as u8;
let yi1: i32 = y_row1[last_x] as i32;
rgb_out1[last_x * 3] = (yi1 + cred).clamp(0, 255) as u8;
rgb_out1[last_x * 3 + 1] = (yi1 + cgreen).clamp(0, 255) as u8;
rgb_out1[last_x * 3 + 2] = (yi1 + cblue).clamp(0, 255) as u8;
}
}