use crate::image::ImageConfiguration;
use crate::image_to_hsv_support::HsvTarget;
#[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
use crate::neon::neon_channels_to_hsv_u16;
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
use crate::sse::sse_channels_to_hsv_u16;
use crate::Rgb;
#[cfg(feature = "rayon")]
use rayon::iter::{IndexedParallelIterator, ParallelIterator};
#[cfg(feature = "rayon")]
use rayon::prelude::{ParallelSlice, ParallelSliceMut};
#[cfg(feature = "rayon")]
use std::slice;
#[allow(clippy::type_complexity)]
fn channels_to_hsv_u16<
const CHANNELS_CONFIGURATION: u8,
const USE_ALPHA: bool,
const TARGET: u8,
>(
src: &[u8],
src_stride: u32,
dst: &mut [u16],
dst_stride: u32,
width: u32,
height: u32,
scale: f32,
) {
let target: HsvTarget = TARGET.into();
let image_configuration: ImageConfiguration = CHANNELS_CONFIGURATION.into();
if USE_ALPHA && !image_configuration.has_alpha() {
panic!("Alpha may be set only on images with alpha");
}
let mut _wide_row_handler: Option<
unsafe fn(usize, *const u8, usize, u32, *mut u16, usize, f32) -> usize,
> = None;
#[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
{
_wide_row_handler =
Some(neon_channels_to_hsv_u16::<CHANNELS_CONFIGURATION, USE_ALPHA, TARGET>);
}
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
if std::arch::is_x86_feature_detected!("sse4.1") {
_wide_row_handler =
Some(sse_channels_to_hsv_u16::<CHANNELS_CONFIGURATION, USE_ALPHA, TARGET>);
}
let channels = image_configuration.get_channels_count();
#[cfg(feature = "rayon")]
{
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 {
let mut _cx = 0usize;
if let Some(dispatcher) = _wide_row_handler {
_cx = dispatcher(
_cx,
src.as_ptr(),
0,
width,
dst.as_mut_ptr() as *mut u16,
0,
scale,
);
}
let src_ptr = src.as_ptr();
let dst_ptr = dst.as_mut_ptr() as *mut u16;
for x in _cx..width as usize {
let px = x * channels;
let src = src_ptr.add(px);
let r = src
.add(image_configuration.get_r_channel_offset())
.read_unaligned();
let g = src
.add(image_configuration.get_g_channel_offset())
.read_unaligned();
let b = src
.add(image_configuration.get_b_channel_offset())
.read_unaligned();
let rgb = Rgb::<u8>::new(r, g, b);
let hx = x * channels;
let dst = dst_ptr.add(hx);
match target {
HsvTarget::Hsv => {
let hsv = rgb.to_hsv();
dst.write_unaligned(hsv.h as u16);
dst.add(1).write_unaligned((hsv.s * scale).round() as u16);
dst.add(2).write_unaligned((hsv.v * scale).round() as u16);
}
HsvTarget::Hsl => {
let hsl = rgb.to_hsl();
dst.write_unaligned(hsl.h as u16);
dst.add(1).write_unaligned((hsl.s * scale).round() as u16);
dst.add(2).write_unaligned((hsl.l * scale).round() as u16);
}
}
if image_configuration.has_alpha() {
let a = src
.add(image_configuration.get_a_channel_offset())
.read_unaligned();
dst.add(3).write_unaligned(a as u16);
}
}
});
}
#[cfg(not(feature = "rayon"))]
{
let mut src_offset = 0usize;
let mut dst_offset = 0usize;
for _ in 0..height as usize {
let mut _cx = 0usize;
if let Some(dispatcher) = _wide_row_handler {
unsafe {
_cx = dispatcher(
_cx,
src.as_ptr(),
src_offset,
width,
dst.as_mut_ptr(),
dst_offset,
scale,
)
}
}
let src_ptr = unsafe { src.as_ptr().add(src_offset) };
let dst_ptr = unsafe { (dst.as_mut_ptr() as *mut u8).add(dst_offset) as *mut u16 };
for x in _cx..width as usize {
let px = x * channels;
let src = unsafe { src_ptr.add(px) };
let r = unsafe {
src.add(image_configuration.get_r_channel_offset())
.read_unaligned()
};
let g = unsafe {
src.add(image_configuration.get_g_channel_offset())
.read_unaligned()
};
let b = unsafe {
src.add(image_configuration.get_b_channel_offset())
.read_unaligned()
};
let rgb = Rgb::<u8>::new(r, g, b);
let hx = x * channels;
let dst = unsafe { dst_ptr.add(hx) };
match target {
HsvTarget::Hsv => {
let hsv = rgb.to_hsv();
unsafe {
dst.write_unaligned(hsv.h as u16);
dst.add(1).write_unaligned((hsv.s * scale).round() as u16);
dst.add(2).write_unaligned((hsv.v * scale).round() as u16);
}
}
HsvTarget::Hsl => {
let hsl = rgb.to_hsl();
unsafe {
dst.write_unaligned(hsl.h as u16);
dst.add(1).write_unaligned((hsl.s * scale).round() as u16);
dst.add(2).write_unaligned((hsl.l * scale).round() as u16);
}
}
}
if image_configuration.has_alpha() {
let a = unsafe {
src.add(image_configuration.get_a_channel_offset())
.read_unaligned()
};
unsafe {
dst.add(3).write_unaligned(a as u16);
}
}
}
src_offset += src_stride as usize;
dst_offset += dst_stride as usize;
}
}
}
pub fn rgb_to_hsv(
src: &[u8],
src_stride: u32,
dst: &mut [u16],
dst_stride: u32,
width: u32,
height: u32,
scale: f32,
) {
channels_to_hsv_u16::<{ ImageConfiguration::Rgb as u8 }, false, { HsvTarget::Hsv as u8 }>(
src, src_stride, dst, dst_stride, width, height, scale,
);
}
pub fn bgra_to_hsv(
src: &[u8],
src_stride: u32,
dst: &mut [u16],
dst_stride: u32,
width: u32,
height: u32,
scale: f32,
) {
channels_to_hsv_u16::<{ ImageConfiguration::Bgra as u8 }, true, { HsvTarget::Hsv as u8 }>(
src, src_stride, dst, dst_stride, width, height, scale,
);
}
pub fn rgba_to_hsv(
src: &[u8],
src_stride: u32,
dst: &mut [u16],
dst_stride: u32,
width: u32,
height: u32,
scale: f32,
) {
channels_to_hsv_u16::<{ ImageConfiguration::Rgba as u8 }, true, { HsvTarget::Hsv as u8 }>(
src, src_stride, dst, dst_stride, width, height, scale,
);
}
pub fn rgb_to_hsl(
src: &[u8],
src_stride: u32,
dst: &mut [u16],
dst_stride: u32,
width: u32,
height: u32,
scale: f32,
) {
channels_to_hsv_u16::<{ ImageConfiguration::Rgb as u8 }, false, { HsvTarget::Hsl as u8 }>(
src, src_stride, dst, dst_stride, width, height, scale,
);
}
pub fn bgra_to_hsl(
src: &[u8],
src_stride: u32,
dst: &mut [u16],
dst_stride: u32,
width: u32,
height: u32,
scale: f32,
) {
channels_to_hsv_u16::<{ ImageConfiguration::Bgra as u8 }, true, { HsvTarget::Hsl as u8 }>(
src, src_stride, dst, dst_stride, width, height, scale,
);
}
pub fn rgba_to_hsl(
src: &[u8],
src_stride: u32,
dst: &mut [u16],
dst_stride: u32,
width: u32,
height: u32,
scale: f32,
) {
channels_to_hsv_u16::<{ ImageConfiguration::Rgba as u8 }, true, { HsvTarget::Hsl as u8 }>(
src, src_stride, dst, dst_stride, width, height, scale,
);
}