#![forbid(unsafe_code)]
#[cfg(all(target_arch = "x86_64", feature = "avx"))]
use crate::avx2::{avx_premultiply_alpha_rgba_f16, avx_unpremultiply_alpha_rgba_f16};
#[cfg(all(target_arch = "aarch64", feature = "neon"))]
use crate::neon::{neon_premultiply_alpha_rgba_f16, neon_unpremultiply_alpha_rgba_f16};
#[cfg(all(target_arch = "aarch64", feature = "neon"))]
use crate::neon::{neon_premultiply_alpha_rgba_f16_full, neon_unpremultiply_alpha_rgba_f16_full};
#[cfg(all(any(target_arch = "x86_64", target_arch = "x86"), feature = "sse"))]
use crate::sse::{sse_premultiply_alpha_rgba_f16, sse_unpremultiply_alpha_rgba_f16};
use core::f16;
use novtb::{ParallelZonedIterator, TbSliceMut};
#[inline]
pub(crate) fn unpremultiply_pixel_f16_row(in_place: &mut [f16]) {
for dst in in_place.as_chunks_mut::<4>().0.iter_mut() {
let mut r = dst[0] as f32;
let mut g = dst[1] as f32;
let mut b = dst[2] as f32;
let a = dst[3] as f32;
if a != 0. {
let scale_alpha = 1. / a;
r *= scale_alpha;
g *= scale_alpha;
b *= scale_alpha;
} else {
r = 0.;
g = 0.;
b = 0.;
}
dst[0] = r as f16;
dst[1] = g as f16;
dst[2] = b as f16;
}
}
#[inline]
pub(crate) fn premultiply_pixel_f16_row(dst: &mut [f16], src: &[f16]) {
for (dst, src) in dst
.as_chunks_mut::<4>()
.0
.iter_mut()
.zip(src.as_chunks::<4>().0.iter())
{
let mut r = src[0] as f32;
let mut g = src[1] as f32;
let mut b = src[2] as f32;
let a = src[3] as f32;
r *= a;
g *= a;
b *= a;
dst[0] = r as f16;
dst[1] = g as f16;
dst[2] = b as f16;
dst[3] = a as f16;
}
}
pub(crate) fn premultiply_alpha_rgba_f16(
dst: &mut [f16],
dst_stride: usize,
src: &[f16],
src_stride: usize,
width: usize,
_: usize,
pool: &novtb::ThreadPool,
) {
#[allow(clippy::type_complexity)]
let mut _dispatcher: fn(&mut [f16], &[f16]) = premultiply_pixel_f16_row;
#[cfg(all(target_arch = "aarch64", feature = "neon"))]
{
_dispatcher = neon_premultiply_alpha_rgba_f16;
if std::arch::is_aarch64_feature_detected!("fp16") {
_dispatcher = neon_premultiply_alpha_rgba_f16_full;
}
}
#[cfg(all(any(target_arch = "x86_64", target_arch = "x86"), feature = "sse"))]
{
if std::arch::is_x86_feature_detected!("sse4.1") {
_dispatcher = sse_premultiply_alpha_rgba_f16;
}
}
#[cfg(all(target_arch = "x86_64", feature = "avx"))]
{
if std::arch::is_x86_feature_detected!("avx2")
&& std::arch::is_x86_feature_detected!("f16c")
{
_dispatcher = avx_premultiply_alpha_rgba_f16;
}
}
dst.tb_par_chunks_mut(dst_stride)
.zip(src.chunks(src_stride))
.for_each(pool, |(dst, src)| {
_dispatcher(&mut dst[..width * 4], &src[..width * 4]);
});
}
pub(crate) fn unpremultiply_alpha_rgba_f16(
in_place: &mut [f16],
stride: usize,
width: usize,
_: usize,
pool: &novtb::ThreadPool,
) {
let mut _dispatcher: fn(&mut [f16]) = unpremultiply_pixel_f16_row;
#[cfg(all(target_arch = "aarch64", feature = "neon"))]
{
_dispatcher = neon_unpremultiply_alpha_rgba_f16;
if std::arch::is_aarch64_feature_detected!("fp16") {
_dispatcher = neon_unpremultiply_alpha_rgba_f16_full;
}
}
#[cfg(all(any(target_arch = "x86_64", target_arch = "x86"), feature = "sse"))]
{
if std::arch::is_x86_feature_detected!("sse4.1") {
_dispatcher = sse_unpremultiply_alpha_rgba_f16;
}
}
#[cfg(all(target_arch = "x86_64", feature = "avx"))]
{
if std::arch::is_x86_feature_detected!("avx2")
&& std::arch::is_x86_feature_detected!("f16c")
{
_dispatcher = avx_unpremultiply_alpha_rgba_f16;
}
}
in_place.tb_par_chunks_mut(stride).for_each(pool, |row| {
_dispatcher(&mut row[..width * 4]);
});
}