use crate::image_configuration::ImageConfiguration;
use colorutils_rs::{Luv, Rgb};
use rayon::iter::{IndexedParallelIterator, ParallelIterator};
use rayon::prelude::{ParallelSlice, ParallelSliceMut};
use std::slice;
#[inline]
pub(crate) fn generic_image_to_luv<const IMAGE: u8>(
src: &[u8],
src_stride: u32,
dst: &mut [u16],
dst_stride: u32,
width: u32,
height: u32,
scale: f32,
) {
let image_configuration: ImageConfiguration = IMAGE.into();
let channels = image_configuration.get_channels_count();
let full_scale = 1f32 / 100f32 * scale;
let dst_slice_safe_align = unsafe {
slice::from_raw_parts_mut(
dst.as_mut_ptr() as *mut u8,
dst_stride as usize * height as usize,
)
};
dst_slice_safe_align
.par_chunks_exact_mut(dst_stride as usize)
.zip(src.par_chunks_exact(src_stride as usize))
.for_each(|(dst, src)| unsafe {
for x in 0..width as usize {
let px = x * channels;
let rgb = Rgb::<u8>::new(
*src.get_unchecked(px + image_configuration.get_r_channel_offset()),
*src.get_unchecked(px + image_configuration.get_g_channel_offset()),
*src.get_unchecked(px + image_configuration.get_b_channel_offset()),
);
let luv = rgb.to_luv();
let dst_ptr = (dst.as_mut_ptr() as *mut u16).add(px);
dst_ptr.write_unaligned((luv.l * full_scale).round().min(scale) as u16);
dst_ptr
.add(1)
.write_unaligned((luv.u * 100f32 + 100f32) as u16);
dst_ptr
.add(2)
.write_unaligned((luv.v * 100f32 + 100f32) as u16);
if image_configuration.has_alpha() {
let a = *src.get_unchecked(px + image_configuration.get_a_channel_offset());
dst_ptr.add(3).write_unaligned(a as u16);
}
}
});
}
#[inline]
pub(crate) fn luv_to_generic_image<const IMAGE: u8>(
src: &[u16],
src_stride: u32,
dst: &mut [u8],
dst_stride: u32,
width: u32,
height: u32,
scale: f32,
) {
let image_configuration: ImageConfiguration = IMAGE.into();
let channels = image_configuration.get_channels_count();
let full_scale = 1f32 / scale * 100f32;
let src_slice_safe_align = unsafe {
slice::from_raw_parts(
src.as_ptr() as *const u8,
src_stride as usize * height as usize,
)
};
dst.par_chunks_exact_mut(dst_stride as usize)
.zip(src_slice_safe_align.par_chunks_exact(src_stride as usize))
.for_each(|(dst, src)| unsafe {
for x in 0..width as usize {
let px = x * channels;
let src_ptr = (src.as_ptr() as *const u16).add(px);
let l = src_ptr.read_unaligned() as f32 * full_scale;
let a = (src_ptr.add(1).read_unaligned() as f32 - 100f32) / 100.;
let b = (src_ptr.add(2).read_unaligned() as f32 - 100f32) / 100.;
let rgb = Luv::new(l, a, b);
let rgb = rgb.to_rgb();
*dst.get_unchecked_mut(px + image_configuration.get_r_channel_offset()) = rgb.r;
*dst.get_unchecked_mut(px + image_configuration.get_g_channel_offset()) = rgb.g;
*dst.get_unchecked_mut(px + image_configuration.get_b_channel_offset()) = rgb.b;
if image_configuration.has_alpha() {
let a = src_ptr.add(3).read_unaligned();
*dst.get_unchecked_mut(px + image_configuration.get_a_channel_offset()) =
a as u8;
}
}
});
}
pub(crate) fn rgb_to_luv(
src: &[u8],
src_stride: u32,
dst: &mut [u16],
dst_stride: u32,
width: u32,
height: u32,
scale: f32,
) {
generic_image_to_luv::<{ ImageConfiguration::Rgb as u8 }>(
src, src_stride, dst, dst_stride, width, height, scale,
);
}
pub(crate) fn bgra_to_luv(
src: &[u8],
src_stride: u32,
dst: &mut [u16],
dst_stride: u32,
width: u32,
height: u32,
scale: f32,
) {
generic_image_to_luv::<{ ImageConfiguration::Bgra as u8 }>(
src, src_stride, dst, dst_stride, width, height, scale,
);
}
pub(crate) fn rgba_to_luv(
src: &[u8],
src_stride: u32,
dst: &mut [u16],
dst_stride: u32,
width: u32,
height: u32,
scale: f32,
) {
generic_image_to_luv::<{ ImageConfiguration::Rgba as u8 }>(
src, src_stride, dst, dst_stride, width, height, scale,
);
}
pub(crate) fn luv_to_rgb(
src: &[u16],
src_stride: u32,
dst: &mut [u8],
dst_stride: u32,
width: u32,
height: u32,
scale: f32,
) {
luv_to_generic_image::<{ ImageConfiguration::Rgb as u8 }>(
src, src_stride, dst, dst_stride, width, height, scale,
);
}
pub(crate) fn luv_to_bgra(
src: &[u16],
src_stride: u32,
dst: &mut [u8],
dst_stride: u32,
width: u32,
height: u32,
scale: f32,
) {
luv_to_generic_image::<{ ImageConfiguration::Bgra as u8 }>(
src, src_stride, dst, dst_stride, width, height, scale,
);
}
pub(crate) fn luv_to_rgba(
src: &[u16],
src_stride: u32,
dst: &mut [u8],
dst_stride: u32,
width: u32,
height: u32,
scale: f32,
) {
luv_to_generic_image::<{ ImageConfiguration::Rgba as u8 }>(
src, src_stride, dst, dst_stride, width, height, scale,
);
}