use crate::mlaf::mlaf;
use crate::transform::PointeeSizeExpressible;
use crate::{CmsError, Layout, TransformExecutor, Vector3f};
use num_traits::AsPrimitive;
use std::sync::Arc;
#[derive(Clone)]
pub(crate) struct ToneReproductionRgbToGray<T, const BUCKET: usize> {
pub(crate) r_linear: Box<[f32; BUCKET]>,
pub(crate) g_linear: Box<[f32; BUCKET]>,
pub(crate) b_linear: Box<[f32; BUCKET]>,
pub(crate) gray_gamma: Box<[T; 65536]>,
}
#[derive(Clone)]
struct TransformRgbToGrayExecutor<
T,
const SRC_LAYOUT: u8,
const DST_LAYOUT: u8,
const BUCKET: usize,
> {
trc_box: ToneReproductionRgbToGray<T, BUCKET>,
weights: Vector3f,
bit_depth: usize,
gamma_lut: usize,
}
pub(crate) fn make_rgb_to_gray<
T: Copy + Default + PointeeSizeExpressible + Send + Sync + 'static,
const BUCKET: usize,
>(
src_layout: Layout,
dst_layout: Layout,
trc: ToneReproductionRgbToGray<T, BUCKET>,
weights: Vector3f,
gamma_lut: usize,
bit_depth: usize,
) -> Result<Arc<dyn TransformExecutor<T> + Send + Sync>, CmsError>
where
u32: AsPrimitive<T>,
{
match src_layout {
Layout::Rgb => match dst_layout {
Layout::Rgb => Err(CmsError::UnsupportedProfileConnection),
Layout::Rgba => Err(CmsError::UnsupportedProfileConnection),
Layout::Gray => Ok(Arc::new(TransformRgbToGrayExecutor::<
T,
{ Layout::Rgb as u8 },
{ Layout::Gray as u8 },
BUCKET,
> {
trc_box: trc,
weights,
bit_depth,
gamma_lut,
})),
Layout::GrayAlpha => Ok(Arc::new(TransformRgbToGrayExecutor::<
T,
{ Layout::Rgb as u8 },
{ Layout::GrayAlpha as u8 },
BUCKET,
> {
trc_box: trc,
weights,
bit_depth,
gamma_lut,
})),
_ => Err(CmsError::UnsupportedProfileConnection),
},
Layout::Rgba => match dst_layout {
Layout::Rgb => Err(CmsError::UnsupportedProfileConnection),
Layout::Rgba => Err(CmsError::UnsupportedProfileConnection),
Layout::Gray => Ok(Arc::new(TransformRgbToGrayExecutor::<
T,
{ Layout::Rgba as u8 },
{ Layout::Gray as u8 },
BUCKET,
> {
trc_box: trc,
weights,
bit_depth,
gamma_lut,
})),
Layout::GrayAlpha => Ok(Arc::new(TransformRgbToGrayExecutor::<
T,
{ Layout::Rgba as u8 },
{ Layout::GrayAlpha as u8 },
BUCKET,
> {
trc_box: trc,
weights,
bit_depth,
gamma_lut,
})),
_ => Err(CmsError::UnsupportedProfileConnection),
},
Layout::Gray => Err(CmsError::UnsupportedProfileConnection),
Layout::GrayAlpha => Err(CmsError::UnsupportedProfileConnection),
_ => Err(CmsError::UnsupportedProfileConnection),
}
}
impl<
T: Copy + Default + PointeeSizeExpressible + 'static,
const SRC_LAYOUT: u8,
const DST_LAYOUT: u8,
const BUCKET: usize,
> TransformExecutor<T> for TransformRgbToGrayExecutor<T, SRC_LAYOUT, DST_LAYOUT, BUCKET>
where
u32: AsPrimitive<T>,
{
fn transform(&self, src: &[T], dst: &mut [T]) -> Result<(), CmsError> {
let src_cn = Layout::from(SRC_LAYOUT);
let dst_cn = Layout::from(DST_LAYOUT);
let src_channels = src_cn.channels();
let dst_channels = dst_cn.channels();
if src.len() / src_channels != dst.len() / dst_channels {
return Err(CmsError::LaneSizeMismatch);
}
if src.len() % src_channels != 0 {
return Err(CmsError::LaneMultipleOfChannels);
}
if dst.len() % dst_channels != 0 {
return Err(CmsError::LaneMultipleOfChannels);
}
let scale_value = (self.gamma_lut - 1) as f32;
let max_value = ((1u32 << self.bit_depth) - 1).as_();
for (src, dst) in src
.chunks_exact(src_channels)
.zip(dst.chunks_exact_mut(dst_channels))
{
let r = self.trc_box.r_linear[src[src_cn.r_i()]._as_usize()];
let g = self.trc_box.g_linear[src[src_cn.g_i()]._as_usize()];
let b = self.trc_box.b_linear[src[src_cn.b_i()]._as_usize()];
let a = if src_channels == 4 {
src[src_cn.a_i()]
} else {
max_value
};
let grey = mlaf(
0.5,
mlaf(
mlaf(self.weights.v[0] * r, self.weights.v[1], g),
self.weights.v[2],
b,
)
.min(1.)
.max(0.),
scale_value,
);
dst[0] = self.trc_box.gray_gamma[(grey as u16) as usize];
if dst_channels == 2 {
dst[1] = a;
}
}
Ok(())
}
}