use core::arch::x86_64::*;
use crate::{
ColorMatrix,
row::{
arch::x86_avx512::endian,
scalar::{planar_gbr_f16 as scalar_f16, planar_gbr_float as scalar},
},
};
const HOST_NATIVE_BE: bool = cfg!(target_endian = "big");
#[inline(always)]
unsafe fn clamp01(v: __m512, zero: __m512, one: __m512) -> __m512 {
unsafe { _mm512_min_ps(_mm512_max_ps(v, zero), one) }
}
#[inline(always)]
unsafe fn scale_round_i32(v: __m512, scale: __m512) -> __m512i {
unsafe { _mm512_cvttps_epi32(_mm512_add_ps(_mm512_mul_ps(v, scale), _mm512_set1_ps(0.5))) }
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw")]
pub(crate) unsafe fn gbrpf32_to_rgb_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
out: &mut [u8],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 3, "out row too short");
unsafe {
let zero = _mm512_setzero_ps();
let one = _mm512_set1_ps(1.0);
let scale = _mm512_set1_ps(255.0);
let mut x = 0usize;
while x + 16 <= width {
let gv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let bv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let rv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let g8 = _mm512_cvtusepi32_epi8(scale_round_i32(gv, scale));
let b8 = _mm512_cvtusepi32_epi8(scale_round_i32(bv, scale));
let r8 = _mm512_cvtusepi32_epi8(scale_round_i32(rv, scale));
let mut g_buf = [0u8; 16];
let mut b_buf = [0u8; 16];
let mut r_buf = [0u8; 16];
_mm_storeu_si128(g_buf.as_mut_ptr().cast(), g8);
_mm_storeu_si128(b_buf.as_mut_ptr().cast(), b8);
_mm_storeu_si128(r_buf.as_mut_ptr().cast(), r8);
let base = x * 3;
for p in 0..16 {
out[base + p * 3] = r_buf[p];
out[base + p * 3 + 1] = g_buf[p];
out[base + p * 3 + 2] = b_buf[p];
}
x += 16;
}
if x < width {
scalar::gbrpf32_to_rgb_row::<BE>(&g[x..], &b[x..], &r[x..], &mut out[x * 3..], width - x);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw")]
pub(crate) unsafe fn gbrpf32_to_rgba_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
out: &mut [u8],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
unsafe {
let zero = _mm512_setzero_ps();
let one = _mm512_set1_ps(1.0);
let scale = _mm512_set1_ps(255.0);
let mut x = 0usize;
while x + 16 <= width {
let gv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let bv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let rv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let g8 = _mm512_cvtusepi32_epi8(scale_round_i32(gv, scale));
let b8 = _mm512_cvtusepi32_epi8(scale_round_i32(bv, scale));
let r8 = _mm512_cvtusepi32_epi8(scale_round_i32(rv, scale));
let mut g_buf = [0u8; 16];
let mut b_buf = [0u8; 16];
let mut r_buf = [0u8; 16];
_mm_storeu_si128(g_buf.as_mut_ptr().cast(), g8);
_mm_storeu_si128(b_buf.as_mut_ptr().cast(), b8);
_mm_storeu_si128(r_buf.as_mut_ptr().cast(), r8);
let base = x * 4;
for p in 0..16 {
out[base + p * 4] = r_buf[p];
out[base + p * 4 + 1] = g_buf[p];
out[base + p * 4 + 2] = b_buf[p];
out[base + p * 4 + 3] = 0xFF;
}
x += 16;
}
if x < width {
scalar::gbrpf32_to_rgba_row::<BE>(&g[x..], &b[x..], &r[x..], &mut out[x * 4..], width - x);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw")]
pub(crate) unsafe fn gbrpf32_to_rgb_u16_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
out: &mut [u16],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 3, "out row too short");
unsafe {
let zero = _mm512_setzero_ps();
let one = _mm512_set1_ps(1.0);
let scale = _mm512_set1_ps(65535.0);
let mut x = 0usize;
while x + 16 <= width {
let gv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let bv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let rv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let gw = _mm512_cvtusepi32_epi16(scale_round_i32(gv, scale));
let bw = _mm512_cvtusepi32_epi16(scale_round_i32(bv, scale));
let rw = _mm512_cvtusepi32_epi16(scale_round_i32(rv, scale));
let mut g_buf = [0u16; 16];
let mut b_buf = [0u16; 16];
let mut r_buf = [0u16; 16];
_mm256_storeu_si256(g_buf.as_mut_ptr().cast(), gw);
_mm256_storeu_si256(b_buf.as_mut_ptr().cast(), bw);
_mm256_storeu_si256(r_buf.as_mut_ptr().cast(), rw);
let base = x * 3;
for p in 0..16 {
out[base + p * 3] = r_buf[p];
out[base + p * 3 + 1] = g_buf[p];
out[base + p * 3 + 2] = b_buf[p];
}
x += 16;
}
if x < width {
scalar::gbrpf32_to_rgb_u16_row::<BE>(&g[x..], &b[x..], &r[x..], &mut out[x * 3..], width - x);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw")]
pub(crate) unsafe fn gbrpf32_to_rgba_u16_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
out: &mut [u16],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
unsafe {
let zero = _mm512_setzero_ps();
let one = _mm512_set1_ps(1.0);
let scale = _mm512_set1_ps(65535.0);
let mut x = 0usize;
while x + 16 <= width {
let gv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let bv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let rv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let gw = _mm512_cvtusepi32_epi16(scale_round_i32(gv, scale));
let bw = _mm512_cvtusepi32_epi16(scale_round_i32(bv, scale));
let rw = _mm512_cvtusepi32_epi16(scale_round_i32(rv, scale));
let mut g_buf = [0u16; 16];
let mut b_buf = [0u16; 16];
let mut r_buf = [0u16; 16];
_mm256_storeu_si256(g_buf.as_mut_ptr().cast(), gw);
_mm256_storeu_si256(b_buf.as_mut_ptr().cast(), bw);
_mm256_storeu_si256(r_buf.as_mut_ptr().cast(), rw);
let base = x * 4;
for p in 0..16 {
out[base + p * 4] = r_buf[p];
out[base + p * 4 + 1] = g_buf[p];
out[base + p * 4 + 2] = b_buf[p];
out[base + p * 4 + 3] = 0xFFFF;
}
x += 16;
}
if x < width {
scalar::gbrpf32_to_rgba_u16_row::<BE>(
&g[x..],
&b[x..],
&r[x..],
&mut out[x * 4..],
width - x,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw")]
#[allow(dead_code)] pub(crate) unsafe fn gbrpf32_to_rgb_f32_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
out: &mut [f32],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 3, "out row too short");
scalar::gbrpf32_to_rgb_f32_row::<BE>(g, b, r, out, width);
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw")]
#[allow(dead_code)] pub(crate) unsafe fn gbrpf32_to_rgba_f32_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
out: &mut [f32],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
scalar::gbrpf32_to_rgba_f32_row::<BE>(g, b, r, out, width);
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw,f16c")]
pub(crate) unsafe fn gbrpf32_to_rgb_f16_row_f16c<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
out: &mut [half::f16],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 3, "out row too short");
unsafe {
let mut x = 0usize;
while x + 16 <= width {
let gv = _mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
));
let bv = _mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
));
let rv = _mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
));
let gh = _mm512_cvtps_ph::<{ _MM_FROUND_TO_NEAREST_INT }>(gv);
let bh = _mm512_cvtps_ph::<{ _MM_FROUND_TO_NEAREST_INT }>(bv);
let rh = _mm512_cvtps_ph::<{ _MM_FROUND_TO_NEAREST_INT }>(rv);
let mut g_buf = [0u16; 16];
let mut b_buf = [0u16; 16];
let mut r_buf = [0u16; 16];
_mm256_storeu_si256(g_buf.as_mut_ptr().cast(), gh);
_mm256_storeu_si256(b_buf.as_mut_ptr().cast(), bh);
_mm256_storeu_si256(r_buf.as_mut_ptr().cast(), rh);
let base = x * 3;
for p in 0..16 {
let dst = out.as_mut_ptr().add(base + p * 3);
*dst.cast::<u16>() = r_buf[p];
*dst.add(1).cast::<u16>() = g_buf[p];
*dst.add(2).cast::<u16>() = b_buf[p];
}
x += 16;
}
if x < width {
scalar::gbrpf32_to_rgb_f16_row::<BE>(&g[x..], &b[x..], &r[x..], &mut out[x * 3..], width - x);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw,f16c")]
pub(crate) unsafe fn gbrpf32_to_rgba_f16_row_f16c<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
out: &mut [half::f16],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
unsafe {
let mut x = 0usize;
while x + 16 <= width {
let gv = _mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
));
let bv = _mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
));
let rv = _mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
));
let gh = _mm512_cvtps_ph::<{ _MM_FROUND_TO_NEAREST_INT }>(gv);
let bh = _mm512_cvtps_ph::<{ _MM_FROUND_TO_NEAREST_INT }>(bv);
let rh = _mm512_cvtps_ph::<{ _MM_FROUND_TO_NEAREST_INT }>(rv);
let mut g_buf = [0u16; 16];
let mut b_buf = [0u16; 16];
let mut r_buf = [0u16; 16];
_mm256_storeu_si256(g_buf.as_mut_ptr().cast(), gh);
_mm256_storeu_si256(b_buf.as_mut_ptr().cast(), bh);
_mm256_storeu_si256(r_buf.as_mut_ptr().cast(), rh);
let base = x * 4;
for p in 0..16 {
let dst = out.as_mut_ptr().add(base + p * 4);
*dst.cast::<u16>() = r_buf[p];
*dst.add(1).cast::<u16>() = g_buf[p];
*dst.add(2).cast::<u16>() = b_buf[p];
*dst.add(3).cast::<u16>() = 0x3C00u16; }
x += 16;
}
if x < width {
scalar::gbrpf32_to_rgba_f16_row::<BE>(
&g[x..],
&b[x..],
&r[x..],
&mut out[x * 4..],
width - x,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw")]
#[allow(clippy::too_many_arguments)]
pub(crate) unsafe fn gbrpf32_to_luma_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width, "out row too short");
const CHUNK: usize = 64;
let mut scratch = [0u8; CHUNK * 3];
let mut offset = 0;
while offset < width {
let n = (width - offset).min(CHUNK);
unsafe {
gbrpf32_to_rgb_row::<BE>(
&g[offset..],
&b[offset..],
&r[offset..],
&mut scratch[..n * 3],
n,
);
}
crate::row::scalar::rgb_to_luma_row(
&scratch[..n * 3],
&mut out[offset..offset + n],
n,
matrix,
full_range,
);
offset += n;
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw")]
#[allow(clippy::too_many_arguments)]
pub(crate) unsafe fn gbrpf32_to_luma_u16_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
out: &mut [u16],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width, "out row too short");
const CHUNK: usize = 64;
let mut scratch = [0u8; CHUNK * 3];
let mut offset = 0;
while offset < width {
let n = (width - offset).min(CHUNK);
unsafe {
gbrpf32_to_rgb_row::<BE>(
&g[offset..],
&b[offset..],
&r[offset..],
&mut scratch[..n * 3],
n,
);
}
crate::row::scalar::rgb_to_luma_u16_row(
&scratch[..n * 3],
&mut out[offset..offset + n],
n,
matrix,
full_range,
);
offset += n;
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw")]
pub(crate) unsafe fn gbrpf32_to_hsv_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
h_out: &mut [u8],
s_out: &mut [u8],
v_out: &mut [u8],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(h_out.len() >= width, "h_out row too short");
debug_assert!(s_out.len() >= width, "s_out row too short");
debug_assert!(v_out.len() >= width, "v_out row too short");
const CHUNK: usize = 64;
let mut scratch = [0u8; CHUNK * 3];
let mut offset = 0;
while offset < width {
let n = (width - offset).min(CHUNK);
unsafe {
gbrpf32_to_rgb_row::<BE>(
&g[offset..],
&b[offset..],
&r[offset..],
&mut scratch[..n * 3],
n,
);
}
crate::row::scalar::rgb_to_hsv_row(
&scratch[..n * 3],
&mut h_out[offset..offset + n],
&mut s_out[offset..offset + n],
&mut v_out[offset..offset + n],
n,
);
offset += n;
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw")]
pub(crate) unsafe fn gbrapf32_to_rgba_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
a: &[f32],
out: &mut [u8],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(a.len() >= width, "a row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
unsafe {
let zero = _mm512_setzero_ps();
let one = _mm512_set1_ps(1.0);
let scale = _mm512_set1_ps(255.0);
let mut x = 0usize;
while x + 16 <= width {
let gv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let bv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let rv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let av = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
a.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let g8 = _mm512_cvtusepi32_epi8(scale_round_i32(gv, scale));
let b8 = _mm512_cvtusepi32_epi8(scale_round_i32(bv, scale));
let r8 = _mm512_cvtusepi32_epi8(scale_round_i32(rv, scale));
let a8 = _mm512_cvtusepi32_epi8(scale_round_i32(av, scale));
let mut g_buf = [0u8; 16];
let mut b_buf = [0u8; 16];
let mut r_buf = [0u8; 16];
let mut a_buf = [0u8; 16];
_mm_storeu_si128(g_buf.as_mut_ptr().cast(), g8);
_mm_storeu_si128(b_buf.as_mut_ptr().cast(), b8);
_mm_storeu_si128(r_buf.as_mut_ptr().cast(), r8);
_mm_storeu_si128(a_buf.as_mut_ptr().cast(), a8);
let base = x * 4;
for p in 0..16 {
out[base + p * 4] = r_buf[p];
out[base + p * 4 + 1] = g_buf[p];
out[base + p * 4 + 2] = b_buf[p];
out[base + p * 4 + 3] = a_buf[p];
}
x += 16;
}
if x < width {
scalar::gbrapf32_to_rgba_row::<BE>(
&g[x..],
&b[x..],
&r[x..],
&a[x..],
&mut out[x * 4..],
width - x,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw")]
pub(crate) unsafe fn gbrapf32_to_rgba_u16_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
a: &[f32],
out: &mut [u16],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(a.len() >= width, "a row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
unsafe {
let zero = _mm512_setzero_ps();
let one = _mm512_set1_ps(1.0);
let scale = _mm512_set1_ps(65535.0);
let mut x = 0usize;
while x + 16 <= width {
let gv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let bv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let rv = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let av = clamp01(
_mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
a.as_ptr().add(x).cast::<u8>(),
)),
zero,
one,
);
let gw = _mm512_cvtusepi32_epi16(scale_round_i32(gv, scale));
let bw = _mm512_cvtusepi32_epi16(scale_round_i32(bv, scale));
let rw = _mm512_cvtusepi32_epi16(scale_round_i32(rv, scale));
let aw = _mm512_cvtusepi32_epi16(scale_round_i32(av, scale));
let mut g_buf = [0u16; 16];
let mut b_buf = [0u16; 16];
let mut r_buf = [0u16; 16];
let mut a_buf = [0u16; 16];
_mm256_storeu_si256(g_buf.as_mut_ptr().cast(), gw);
_mm256_storeu_si256(b_buf.as_mut_ptr().cast(), bw);
_mm256_storeu_si256(r_buf.as_mut_ptr().cast(), rw);
_mm256_storeu_si256(a_buf.as_mut_ptr().cast(), aw);
let base = x * 4;
for p in 0..16 {
out[base + p * 4] = r_buf[p];
out[base + p * 4 + 1] = g_buf[p];
out[base + p * 4 + 2] = b_buf[p];
out[base + p * 4 + 3] = a_buf[p];
}
x += 16;
}
if x < width {
scalar::gbrapf32_to_rgba_u16_row::<BE>(
&g[x..],
&b[x..],
&r[x..],
&a[x..],
&mut out[x * 4..],
width - x,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw")]
#[allow(dead_code)] pub(crate) unsafe fn gbrapf32_to_rgba_f32_row<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
a: &[f32],
out: &mut [f32],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(a.len() >= width, "a row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
scalar::gbrapf32_to_rgba_f32_row::<BE>(g, b, r, a, out, width);
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw,f16c")]
pub(crate) unsafe fn gbrapf32_to_rgba_f16_row_f16c<const BE: bool>(
g: &[f32],
b: &[f32],
r: &[f32],
a: &[f32],
out: &mut [half::f16],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(a.len() >= width, "a row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
unsafe {
let mut x = 0usize;
while x + 16 <= width {
let gv = _mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
));
let bv = _mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
));
let rv = _mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
));
let av = _mm512_castsi512_ps(endian::load_endian_u32x16::<BE>(
a.as_ptr().add(x).cast::<u8>(),
));
let gh = _mm512_cvtps_ph::<{ _MM_FROUND_TO_NEAREST_INT }>(gv);
let bh = _mm512_cvtps_ph::<{ _MM_FROUND_TO_NEAREST_INT }>(bv);
let rh = _mm512_cvtps_ph::<{ _MM_FROUND_TO_NEAREST_INT }>(rv);
let ah = _mm512_cvtps_ph::<{ _MM_FROUND_TO_NEAREST_INT }>(av);
let mut g_buf = [0u16; 16];
let mut b_buf = [0u16; 16];
let mut r_buf = [0u16; 16];
let mut a_buf = [0u16; 16];
_mm256_storeu_si256(g_buf.as_mut_ptr().cast(), gh);
_mm256_storeu_si256(b_buf.as_mut_ptr().cast(), bh);
_mm256_storeu_si256(r_buf.as_mut_ptr().cast(), rh);
_mm256_storeu_si256(a_buf.as_mut_ptr().cast(), ah);
let base = x * 4;
for p in 0..16 {
let dst = out.as_mut_ptr().add(base + p * 4);
*dst.cast::<u16>() = r_buf[p];
*dst.add(1).cast::<u16>() = g_buf[p];
*dst.add(2).cast::<u16>() = b_buf[p];
*dst.add(3).cast::<u16>() = a_buf[p];
}
x += 16;
}
if x < width {
scalar::gbrapf32_to_rgba_f16_row::<BE>(
&g[x..],
&b[x..],
&r[x..],
&a[x..],
&mut out[x * 4..],
width - x,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw,f16c")]
pub(crate) unsafe fn gbrpf16_to_rgb_row_f16c<const BE: bool>(
g: &[half::f16],
b: &[half::f16],
r: &[half::f16],
out: &mut [u8],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 3, "out row too short");
unsafe {
let zero = _mm512_setzero_ps();
let one = _mm512_set1_ps(1.0);
let scale = _mm512_set1_ps(255.0);
let mut x = 0usize;
while x + 16 <= width {
let gv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
));
let bv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
));
let rv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
));
let gc = clamp01(gv, zero, one);
let bc = clamp01(bv, zero, one);
let rc = clamp01(rv, zero, one);
let g8 = _mm512_cvtusepi32_epi8(scale_round_i32(gc, scale));
let b8 = _mm512_cvtusepi32_epi8(scale_round_i32(bc, scale));
let r8 = _mm512_cvtusepi32_epi8(scale_round_i32(rc, scale));
let mut g_buf = [0u8; 16];
let mut b_buf = [0u8; 16];
let mut r_buf = [0u8; 16];
_mm_storeu_si128(g_buf.as_mut_ptr().cast(), g8);
_mm_storeu_si128(b_buf.as_mut_ptr().cast(), b8);
_mm_storeu_si128(r_buf.as_mut_ptr().cast(), r8);
let base = x * 3;
for p in 0..16 {
out[base + p * 3] = r_buf[p];
out[base + p * 3 + 1] = g_buf[p];
out[base + p * 3 + 2] = b_buf[p];
}
x += 16;
}
if x < width {
let tail = width - x;
let mut gf = [0.0f32; 16];
let mut bf = [0.0f32; 16];
let mut rf = [0.0f32; 16];
scalar_f16::widen_f16_be_to_host_f32::<BE>(g, x, &mut gf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(b, x, &mut bf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(r, x, &mut rf, tail);
scalar::gbrpf32_to_rgb_row::<HOST_NATIVE_BE>(
&gf[..tail],
&bf[..tail],
&rf[..tail],
&mut out[x * 3..],
tail,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw,f16c")]
pub(crate) unsafe fn gbrpf16_to_rgba_row_f16c<const BE: bool>(
g: &[half::f16],
b: &[half::f16],
r: &[half::f16],
out: &mut [u8],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
unsafe {
let zero = _mm512_setzero_ps();
let one = _mm512_set1_ps(1.0);
let scale = _mm512_set1_ps(255.0);
let mut x = 0usize;
while x + 16 <= width {
let gv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
));
let bv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
));
let rv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
));
let gc = clamp01(gv, zero, one);
let bc = clamp01(bv, zero, one);
let rc = clamp01(rv, zero, one);
let g8 = _mm512_cvtusepi32_epi8(scale_round_i32(gc, scale));
let b8 = _mm512_cvtusepi32_epi8(scale_round_i32(bc, scale));
let r8 = _mm512_cvtusepi32_epi8(scale_round_i32(rc, scale));
let mut g_buf = [0u8; 16];
let mut b_buf = [0u8; 16];
let mut r_buf = [0u8; 16];
_mm_storeu_si128(g_buf.as_mut_ptr().cast(), g8);
_mm_storeu_si128(b_buf.as_mut_ptr().cast(), b8);
_mm_storeu_si128(r_buf.as_mut_ptr().cast(), r8);
let base = x * 4;
for p in 0..16 {
out[base + p * 4] = r_buf[p];
out[base + p * 4 + 1] = g_buf[p];
out[base + p * 4 + 2] = b_buf[p];
out[base + p * 4 + 3] = 0xFF;
}
x += 16;
}
if x < width {
let tail = width - x;
let mut gf = [0.0f32; 16];
let mut bf = [0.0f32; 16];
let mut rf = [0.0f32; 16];
scalar_f16::widen_f16_be_to_host_f32::<BE>(g, x, &mut gf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(b, x, &mut bf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(r, x, &mut rf, tail);
scalar::gbrpf32_to_rgba_row::<HOST_NATIVE_BE>(
&gf[..tail],
&bf[..tail],
&rf[..tail],
&mut out[x * 4..],
tail,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw,f16c")]
#[allow(dead_code)] pub(crate) unsafe fn gbrpf16_to_rgb_u16_row_f16c<const BE: bool>(
g: &[half::f16],
b: &[half::f16],
r: &[half::f16],
out: &mut [u16],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 3, "out row too short");
unsafe {
let zero = _mm512_setzero_ps();
let one = _mm512_set1_ps(1.0);
let scale = _mm512_set1_ps(65535.0);
let mut x = 0usize;
while x + 16 <= width {
let gv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
));
let bv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
));
let rv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
));
let gc = clamp01(gv, zero, one);
let bc = clamp01(bv, zero, one);
let rc = clamp01(rv, zero, one);
let gw = _mm512_cvtusepi32_epi16(scale_round_i32(gc, scale));
let bw = _mm512_cvtusepi32_epi16(scale_round_i32(bc, scale));
let rw = _mm512_cvtusepi32_epi16(scale_round_i32(rc, scale));
let mut g_buf = [0u16; 16];
let mut b_buf = [0u16; 16];
let mut r_buf = [0u16; 16];
_mm256_storeu_si256(g_buf.as_mut_ptr().cast(), gw);
_mm256_storeu_si256(b_buf.as_mut_ptr().cast(), bw);
_mm256_storeu_si256(r_buf.as_mut_ptr().cast(), rw);
let base = x * 3;
for p in 0..16 {
out[base + p * 3] = r_buf[p];
out[base + p * 3 + 1] = g_buf[p];
out[base + p * 3 + 2] = b_buf[p];
}
x += 16;
}
if x < width {
let tail = width - x;
let mut gf = [0.0f32; 16];
let mut bf = [0.0f32; 16];
let mut rf = [0.0f32; 16];
scalar_f16::widen_f16_be_to_host_f32::<BE>(g, x, &mut gf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(b, x, &mut bf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(r, x, &mut rf, tail);
scalar::gbrpf32_to_rgb_u16_row::<HOST_NATIVE_BE>(
&gf[..tail],
&bf[..tail],
&rf[..tail],
&mut out[x * 3..],
tail,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw,f16c")]
#[allow(dead_code)] pub(crate) unsafe fn gbrpf16_to_rgba_u16_row_f16c<const BE: bool>(
g: &[half::f16],
b: &[half::f16],
r: &[half::f16],
out: &mut [u16],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
unsafe {
let zero = _mm512_setzero_ps();
let one = _mm512_set1_ps(1.0);
let scale = _mm512_set1_ps(65535.0);
let mut x = 0usize;
while x + 16 <= width {
let gv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
));
let bv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
));
let rv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
));
let gc = clamp01(gv, zero, one);
let bc = clamp01(bv, zero, one);
let rc = clamp01(rv, zero, one);
let gw = _mm512_cvtusepi32_epi16(scale_round_i32(gc, scale));
let bw = _mm512_cvtusepi32_epi16(scale_round_i32(bc, scale));
let rw = _mm512_cvtusepi32_epi16(scale_round_i32(rc, scale));
let mut g_buf = [0u16; 16];
let mut b_buf = [0u16; 16];
let mut r_buf = [0u16; 16];
_mm256_storeu_si256(g_buf.as_mut_ptr().cast(), gw);
_mm256_storeu_si256(b_buf.as_mut_ptr().cast(), bw);
_mm256_storeu_si256(r_buf.as_mut_ptr().cast(), rw);
let base = x * 4;
for p in 0..16 {
out[base + p * 4] = r_buf[p];
out[base + p * 4 + 1] = g_buf[p];
out[base + p * 4 + 2] = b_buf[p];
out[base + p * 4 + 3] = 0xFFFF;
}
x += 16;
}
if x < width {
let tail = width - x;
let mut gf = [0.0f32; 16];
let mut bf = [0.0f32; 16];
let mut rf = [0.0f32; 16];
scalar_f16::widen_f16_be_to_host_f32::<BE>(g, x, &mut gf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(b, x, &mut bf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(r, x, &mut rf, tail);
scalar::gbrpf32_to_rgba_u16_row::<HOST_NATIVE_BE>(
&gf[..tail],
&bf[..tail],
&rf[..tail],
&mut out[x * 4..],
tail,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw,f16c")]
#[allow(dead_code)] pub(crate) unsafe fn gbrpf16_to_rgb_f32_row_f16c<const BE: bool>(
g: &[half::f16],
b: &[half::f16],
r: &[half::f16],
out: &mut [f32],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 3, "out row too short");
unsafe {
let mut x = 0usize;
while x + 16 <= width {
let gv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
));
let bv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
));
let rv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
));
let mut gf = [0.0f32; 16];
let mut bf = [0.0f32; 16];
let mut rf = [0.0f32; 16];
_mm512_storeu_ps(gf.as_mut_ptr(), gv);
_mm512_storeu_ps(bf.as_mut_ptr(), bv);
_mm512_storeu_ps(rf.as_mut_ptr(), rv);
let base = x * 3;
for p in 0..16 {
out[base + p * 3] = rf[p];
out[base + p * 3 + 1] = gf[p];
out[base + p * 3 + 2] = bf[p];
}
x += 16;
}
if x < width {
let tail = width - x;
let mut gf = [0.0f32; 16];
let mut bf = [0.0f32; 16];
let mut rf = [0.0f32; 16];
scalar_f16::widen_f16_be_to_host_f32::<BE>(g, x, &mut gf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(b, x, &mut bf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(r, x, &mut rf, tail);
scalar::gbrpf32_to_rgb_f32_row::<HOST_NATIVE_BE>(
&gf[..tail],
&bf[..tail],
&rf[..tail],
&mut out[x * 3..],
tail,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw,f16c")]
#[allow(dead_code)] pub(crate) unsafe fn gbrpf16_to_rgba_f32_row_f16c<const BE: bool>(
g: &[half::f16],
b: &[half::f16],
r: &[half::f16],
out: &mut [f32],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
unsafe {
let mut x = 0usize;
while x + 16 <= width {
let gv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
));
let bv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
));
let rv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
));
let mut gf = [0.0f32; 16];
let mut bf = [0.0f32; 16];
let mut rf = [0.0f32; 16];
_mm512_storeu_ps(gf.as_mut_ptr(), gv);
_mm512_storeu_ps(bf.as_mut_ptr(), bv);
_mm512_storeu_ps(rf.as_mut_ptr(), rv);
let base = x * 4;
for p in 0..16 {
out[base + p * 4] = rf[p];
out[base + p * 4 + 1] = gf[p];
out[base + p * 4 + 2] = bf[p];
out[base + p * 4 + 3] = 1.0;
}
x += 16;
}
if x < width {
let tail = width - x;
let mut gf = [0.0f32; 16];
let mut bf = [0.0f32; 16];
let mut rf = [0.0f32; 16];
scalar_f16::widen_f16_be_to_host_f32::<BE>(g, x, &mut gf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(b, x, &mut bf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(r, x, &mut rf, tail);
scalar::gbrpf32_to_rgba_f32_row::<HOST_NATIVE_BE>(
&gf[..tail],
&bf[..tail],
&rf[..tail],
&mut out[x * 4..],
tail,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw")]
pub(crate) unsafe fn gbrpf16_to_rgb_f16_row<const BE: bool>(
g: &[half::f16],
b: &[half::f16],
r: &[half::f16],
out: &mut [half::f16],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 3, "out row too short");
unsafe {
let mut x = 0usize;
while x + 16 <= width {
let gu = endian::load_endian_u16x16::<BE>(g.as_ptr().add(x).cast::<u8>());
let bu = endian::load_endian_u16x16::<BE>(b.as_ptr().add(x).cast::<u8>());
let ru = endian::load_endian_u16x16::<BE>(r.as_ptr().add(x).cast::<u8>());
let mut g_buf = [0u16; 16];
let mut b_buf = [0u16; 16];
let mut r_buf = [0u16; 16];
_mm256_storeu_si256(g_buf.as_mut_ptr().cast(), gu);
_mm256_storeu_si256(b_buf.as_mut_ptr().cast(), bu);
_mm256_storeu_si256(r_buf.as_mut_ptr().cast(), ru);
let base = x * 3;
for p in 0..16 {
let dst = out.as_mut_ptr().add(base + p * 3);
*dst.cast::<u16>() = r_buf[p];
*dst.add(1).cast::<u16>() = g_buf[p];
*dst.add(2).cast::<u16>() = b_buf[p];
}
x += 16;
}
if x < width {
scalar_f16::gbrpf16_to_rgb_f16_row::<BE>(
&g[x..],
&b[x..],
&r[x..],
&mut out[x * 3..],
width - x,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw")]
pub(crate) unsafe fn gbrpf16_to_rgba_f16_row<const BE: bool>(
g: &[half::f16],
b: &[half::f16],
r: &[half::f16],
out: &mut [half::f16],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
unsafe {
let mut x = 0usize;
while x + 16 <= width {
let gu = endian::load_endian_u16x16::<BE>(g.as_ptr().add(x).cast::<u8>());
let bu = endian::load_endian_u16x16::<BE>(b.as_ptr().add(x).cast::<u8>());
let ru = endian::load_endian_u16x16::<BE>(r.as_ptr().add(x).cast::<u8>());
let mut g_buf = [0u16; 16];
let mut b_buf = [0u16; 16];
let mut r_buf = [0u16; 16];
_mm256_storeu_si256(g_buf.as_mut_ptr().cast(), gu);
_mm256_storeu_si256(b_buf.as_mut_ptr().cast(), bu);
_mm256_storeu_si256(r_buf.as_mut_ptr().cast(), ru);
let base = x * 4;
for p in 0..16 {
let dst = out.as_mut_ptr().add(base + p * 4);
*dst.cast::<u16>() = r_buf[p];
*dst.add(1).cast::<u16>() = g_buf[p];
*dst.add(2).cast::<u16>() = b_buf[p];
*dst.add(3).cast::<u16>() = 0x3C00u16; }
x += 16;
}
if x < width {
scalar_f16::gbrpf16_to_rgba_f16_row::<BE>(
&g[x..],
&b[x..],
&r[x..],
&mut out[x * 4..],
width - x,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw,f16c")]
#[allow(clippy::too_many_arguments)]
#[allow(dead_code)] pub(crate) unsafe fn gbrpf16_to_luma_row_f16c<const BE: bool>(
g: &[half::f16],
b: &[half::f16],
r: &[half::f16],
out: &mut [u8],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width, "out row too short");
const CHUNK: usize = 64;
let mut scratch = [0u8; CHUNK * 3];
let mut offset = 0;
while offset < width {
let n = (width - offset).min(CHUNK);
unsafe {
gbrpf16_to_rgb_row_f16c::<BE>(
&g[offset..],
&b[offset..],
&r[offset..],
&mut scratch[..n * 3],
n,
);
}
crate::row::scalar::rgb_to_luma_row(
&scratch[..n * 3],
&mut out[offset..offset + n],
n,
matrix,
full_range,
);
offset += n;
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw,f16c")]
#[allow(clippy::too_many_arguments)]
#[allow(dead_code)] pub(crate) unsafe fn gbrpf16_to_luma_u16_row_f16c<const BE: bool>(
g: &[half::f16],
b: &[half::f16],
r: &[half::f16],
out: &mut [u16],
width: usize,
matrix: ColorMatrix,
full_range: bool,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(out.len() >= width, "out row too short");
const CHUNK: usize = 64;
let mut scratch = [0u8; CHUNK * 3];
let mut offset = 0;
while offset < width {
let n = (width - offset).min(CHUNK);
unsafe {
gbrpf16_to_rgb_row_f16c::<BE>(
&g[offset..],
&b[offset..],
&r[offset..],
&mut scratch[..n * 3],
n,
);
}
crate::row::scalar::rgb_to_luma_u16_row(
&scratch[..n * 3],
&mut out[offset..offset + n],
n,
matrix,
full_range,
);
offset += n;
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw,f16c")]
#[allow(dead_code)] pub(crate) unsafe fn gbrpf16_to_hsv_row_f16c<const BE: bool>(
g: &[half::f16],
b: &[half::f16],
r: &[half::f16],
h_out: &mut [u8],
s_out: &mut [u8],
v_out: &mut [u8],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(h_out.len() >= width, "h_out row too short");
debug_assert!(s_out.len() >= width, "s_out row too short");
debug_assert!(v_out.len() >= width, "v_out row too short");
const CHUNK: usize = 64;
let mut scratch = [0u8; CHUNK * 3];
let mut offset = 0;
while offset < width {
let n = (width - offset).min(CHUNK);
unsafe {
gbrpf16_to_rgb_row_f16c::<BE>(
&g[offset..],
&b[offset..],
&r[offset..],
&mut scratch[..n * 3],
n,
);
}
crate::row::scalar::rgb_to_hsv_row(
&scratch[..n * 3],
&mut h_out[offset..offset + n],
&mut s_out[offset..offset + n],
&mut v_out[offset..offset + n],
n,
);
offset += n;
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw,f16c")]
#[allow(dead_code)] pub(crate) unsafe fn gbrapf16_to_rgba_row_f16c<const BE: bool>(
g: &[half::f16],
b: &[half::f16],
r: &[half::f16],
a: &[half::f16],
out: &mut [u8],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(a.len() >= width, "a row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
unsafe {
let zero = _mm512_setzero_ps();
let one = _mm512_set1_ps(1.0);
let scale = _mm512_set1_ps(255.0);
let mut x = 0usize;
while x + 16 <= width {
let gv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
));
let bv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
));
let rv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
));
let av = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
a.as_ptr().add(x).cast::<u8>(),
));
let gc = clamp01(gv, zero, one);
let bc = clamp01(bv, zero, one);
let rc = clamp01(rv, zero, one);
let ac = clamp01(av, zero, one);
let g8 = _mm512_cvtusepi32_epi8(scale_round_i32(gc, scale));
let b8 = _mm512_cvtusepi32_epi8(scale_round_i32(bc, scale));
let r8 = _mm512_cvtusepi32_epi8(scale_round_i32(rc, scale));
let a8 = _mm512_cvtusepi32_epi8(scale_round_i32(ac, scale));
let mut g_buf = [0u8; 16];
let mut b_buf = [0u8; 16];
let mut r_buf = [0u8; 16];
let mut a_buf = [0u8; 16];
_mm_storeu_si128(g_buf.as_mut_ptr().cast(), g8);
_mm_storeu_si128(b_buf.as_mut_ptr().cast(), b8);
_mm_storeu_si128(r_buf.as_mut_ptr().cast(), r8);
_mm_storeu_si128(a_buf.as_mut_ptr().cast(), a8);
let base = x * 4;
for p in 0..16 {
out[base + p * 4] = r_buf[p];
out[base + p * 4 + 1] = g_buf[p];
out[base + p * 4 + 2] = b_buf[p];
out[base + p * 4 + 3] = a_buf[p];
}
x += 16;
}
if x < width {
let tail = width - x;
let mut gf = [0.0f32; 16];
let mut bf = [0.0f32; 16];
let mut rf = [0.0f32; 16];
let mut af = [0.0f32; 16];
scalar_f16::widen_f16_be_to_host_f32::<BE>(g, x, &mut gf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(b, x, &mut bf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(r, x, &mut rf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(a, x, &mut af, tail);
scalar::gbrapf32_to_rgba_row::<HOST_NATIVE_BE>(
&gf[..tail],
&bf[..tail],
&rf[..tail],
&af[..tail],
&mut out[x * 4..],
tail,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw,f16c")]
#[allow(dead_code)] pub(crate) unsafe fn gbrapf16_to_rgba_u16_row_f16c<const BE: bool>(
g: &[half::f16],
b: &[half::f16],
r: &[half::f16],
a: &[half::f16],
out: &mut [u16],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(a.len() >= width, "a row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
unsafe {
let zero = _mm512_setzero_ps();
let one = _mm512_set1_ps(1.0);
let scale = _mm512_set1_ps(65535.0);
let mut x = 0usize;
while x + 16 <= width {
let gv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
));
let bv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
));
let rv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
));
let av = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
a.as_ptr().add(x).cast::<u8>(),
));
let gc = clamp01(gv, zero, one);
let bc = clamp01(bv, zero, one);
let rc = clamp01(rv, zero, one);
let ac = clamp01(av, zero, one);
let gw = _mm512_cvtusepi32_epi16(scale_round_i32(gc, scale));
let bw = _mm512_cvtusepi32_epi16(scale_round_i32(bc, scale));
let rw = _mm512_cvtusepi32_epi16(scale_round_i32(rc, scale));
let aw = _mm512_cvtusepi32_epi16(scale_round_i32(ac, scale));
let mut g_buf = [0u16; 16];
let mut b_buf = [0u16; 16];
let mut r_buf = [0u16; 16];
let mut a_buf = [0u16; 16];
_mm256_storeu_si256(g_buf.as_mut_ptr().cast(), gw);
_mm256_storeu_si256(b_buf.as_mut_ptr().cast(), bw);
_mm256_storeu_si256(r_buf.as_mut_ptr().cast(), rw);
_mm256_storeu_si256(a_buf.as_mut_ptr().cast(), aw);
let base = x * 4;
for p in 0..16 {
out[base + p * 4] = r_buf[p];
out[base + p * 4 + 1] = g_buf[p];
out[base + p * 4 + 2] = b_buf[p];
out[base + p * 4 + 3] = a_buf[p];
}
x += 16;
}
if x < width {
let tail = width - x;
let mut gf = [0.0f32; 16];
let mut bf = [0.0f32; 16];
let mut rf = [0.0f32; 16];
let mut af = [0.0f32; 16];
scalar_f16::widen_f16_be_to_host_f32::<BE>(g, x, &mut gf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(b, x, &mut bf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(r, x, &mut rf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(a, x, &mut af, tail);
scalar::gbrapf32_to_rgba_u16_row::<HOST_NATIVE_BE>(
&gf[..tail],
&bf[..tail],
&rf[..tail],
&af[..tail],
&mut out[x * 4..],
tail,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw,f16c")]
#[allow(dead_code)] pub(crate) unsafe fn gbrapf16_to_rgba_f32_row_f16c<const BE: bool>(
g: &[half::f16],
b: &[half::f16],
r: &[half::f16],
a: &[half::f16],
out: &mut [f32],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(a.len() >= width, "a row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
unsafe {
let mut x = 0usize;
while x + 16 <= width {
let gv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
g.as_ptr().add(x).cast::<u8>(),
));
let bv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
b.as_ptr().add(x).cast::<u8>(),
));
let rv = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
r.as_ptr().add(x).cast::<u8>(),
));
let av = _mm512_cvtph_ps(endian::load_endian_u16x16::<BE>(
a.as_ptr().add(x).cast::<u8>(),
));
let mut gf = [0.0f32; 16];
let mut bf = [0.0f32; 16];
let mut rf = [0.0f32; 16];
let mut af = [0.0f32; 16];
_mm512_storeu_ps(gf.as_mut_ptr(), gv);
_mm512_storeu_ps(bf.as_mut_ptr(), bv);
_mm512_storeu_ps(rf.as_mut_ptr(), rv);
_mm512_storeu_ps(af.as_mut_ptr(), av);
let base = x * 4;
for p in 0..16 {
out[base + p * 4] = rf[p];
out[base + p * 4 + 1] = gf[p];
out[base + p * 4 + 2] = bf[p];
out[base + p * 4 + 3] = af[p];
}
x += 16;
}
if x < width {
let tail = width - x;
let mut gf = [0.0f32; 16];
let mut bf = [0.0f32; 16];
let mut rf = [0.0f32; 16];
let mut af = [0.0f32; 16];
scalar_f16::widen_f16_be_to_host_f32::<BE>(g, x, &mut gf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(b, x, &mut bf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(r, x, &mut rf, tail);
scalar_f16::widen_f16_be_to_host_f32::<BE>(a, x, &mut af, tail);
scalar::gbrapf32_to_rgba_f32_row::<HOST_NATIVE_BE>(
&gf[..tail],
&bf[..tail],
&rf[..tail],
&af[..tail],
&mut out[x * 4..],
tail,
);
}
}
}
#[inline]
#[target_feature(enable = "avx512f,avx512bw")]
pub(crate) unsafe fn gbrapf16_to_rgba_f16_row<const BE: bool>(
g: &[half::f16],
b: &[half::f16],
r: &[half::f16],
a: &[half::f16],
out: &mut [half::f16],
width: usize,
) {
debug_assert!(g.len() >= width, "g row too short");
debug_assert!(b.len() >= width, "b row too short");
debug_assert!(r.len() >= width, "r row too short");
debug_assert!(a.len() >= width, "a row too short");
debug_assert!(out.len() >= width * 4, "out row too short");
unsafe {
let mut x = 0usize;
while x + 16 <= width {
let gu = endian::load_endian_u16x16::<BE>(g.as_ptr().add(x).cast::<u8>());
let bu = endian::load_endian_u16x16::<BE>(b.as_ptr().add(x).cast::<u8>());
let ru = endian::load_endian_u16x16::<BE>(r.as_ptr().add(x).cast::<u8>());
let au = endian::load_endian_u16x16::<BE>(a.as_ptr().add(x).cast::<u8>());
let mut g_buf = [0u16; 16];
let mut b_buf = [0u16; 16];
let mut r_buf = [0u16; 16];
let mut a_buf = [0u16; 16];
_mm256_storeu_si256(g_buf.as_mut_ptr().cast(), gu);
_mm256_storeu_si256(b_buf.as_mut_ptr().cast(), bu);
_mm256_storeu_si256(r_buf.as_mut_ptr().cast(), ru);
_mm256_storeu_si256(a_buf.as_mut_ptr().cast(), au);
let base = x * 4;
for p in 0..16 {
let dst = out.as_mut_ptr().add(base + p * 4);
*dst.cast::<u16>() = r_buf[p];
*dst.add(1).cast::<u16>() = g_buf[p];
*dst.add(2).cast::<u16>() = b_buf[p];
*dst.add(3).cast::<u16>() = a_buf[p];
}
x += 16;
}
if x < width {
scalar_f16::gbrapf16_to_rgba_f16_row::<BE>(
&g[x..],
&b[x..],
&r[x..],
&a[x..],
&mut out[x * 4..],
width - x,
);
}
}
}