#![cfg(feature = "extended_range")]
use crate::mlaf::mlaf;
use crate::transform::PointeeSizeExpressible;
use crate::trc::ToneCurveEvaluator;
use crate::{CmsError, Layout, Rgb, TransformExecutor, Vector3f};
use num_traits::AsPrimitive;
use std::marker::PhantomData;
use std::sync::Arc;
struct TransformRgbToGrayExtendedExecutor<T, const SRC_LAYOUT: u8, const DST_LAYOUT: u8> {
linear_eval: Box<dyn ToneCurveEvaluator + Send + Sync>,
gamma_eval: Box<dyn ToneCurveEvaluator + Send + Sync>,
weights: Vector3f,
_phantom: PhantomData<T>,
bit_depth: usize,
}
pub(crate) fn make_rgb_to_gray_extended<
T: Copy + Default + PointeeSizeExpressible + Send + Sync + 'static + AsPrimitive<f32>,
>(
src_layout: Layout,
dst_layout: Layout,
linear_eval: Box<dyn ToneCurveEvaluator + Send + Sync>,
gamma_eval: Box<dyn ToneCurveEvaluator + Send + Sync>,
weights: Vector3f,
bit_depth: usize,
) -> Result<Arc<dyn TransformExecutor<T> + Send + Sync>, CmsError>
where
u32: AsPrimitive<T>,
f32: 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(TransformRgbToGrayExtendedExecutor::<
T,
{ Layout::Rgb as u8 },
{ Layout::Gray as u8 },
> {
linear_eval,
gamma_eval,
weights,
_phantom: PhantomData,
bit_depth,
})),
Layout::GrayAlpha => Ok(Arc::new(TransformRgbToGrayExtendedExecutor::<
T,
{ Layout::Rgb as u8 },
{ Layout::GrayAlpha as u8 },
> {
linear_eval,
gamma_eval,
weights,
_phantom: PhantomData,
bit_depth,
})),
_ => Err(CmsError::UnsupportedProfileConnection),
},
Layout::Rgba => match dst_layout {
Layout::Rgb => Err(CmsError::UnsupportedProfileConnection),
Layout::Rgba => Err(CmsError::UnsupportedProfileConnection),
Layout::Gray => Ok(Arc::new(TransformRgbToGrayExtendedExecutor::<
T,
{ Layout::Rgba as u8 },
{ Layout::Gray as u8 },
> {
linear_eval,
gamma_eval,
weights,
_phantom: PhantomData,
bit_depth,
})),
Layout::GrayAlpha => Ok(Arc::new(TransformRgbToGrayExtendedExecutor::<
T,
{ Layout::Rgba as u8 },
{ Layout::GrayAlpha as u8 },
> {
linear_eval,
gamma_eval,
weights,
_phantom: PhantomData,
bit_depth,
})),
_ => Err(CmsError::UnsupportedProfileConnection),
},
Layout::Gray => Err(CmsError::UnsupportedProfileConnection),
Layout::GrayAlpha => Err(CmsError::UnsupportedProfileConnection),
_ => Err(CmsError::UnsupportedProfileConnection),
}
}
impl<
T: Copy + Default + PointeeSizeExpressible + 'static + AsPrimitive<f32>,
const SRC_LAYOUT: u8,
const DST_LAYOUT: u8,
> TransformExecutor<T> for TransformRgbToGrayExtendedExecutor<T, SRC_LAYOUT, DST_LAYOUT>
where
u32: AsPrimitive<T>,
f32: 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 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 in_tristimulus = Rgb::<f32>::new(
src[src_cn.r_i()].as_(),
src[src_cn.g_i()].as_(),
src[src_cn.b_i()].as_(),
);
let lin_tristimulus = self.linear_eval.evaluate_tristimulus(in_tristimulus);
let a = if src_channels == 4 {
src[src_cn.a_i()]
} else {
max_value
};
let grey = mlaf(
mlaf(
self.weights.v[0] * lin_tristimulus.r,
self.weights.v[1],
lin_tristimulus.g,
),
self.weights.v[2],
lin_tristimulus.b,
)
.min(1.)
.max(0.);
let gamma_value = self.gamma_eval.evaluate_value(grey);
dst[0] = gamma_value.as_();
if dst_channels == 2 {
dst[1] = a;
}
}
Ok(())
}
}