1use crate::mlaf::mlaf;
30use crate::transform::PointeeSizeExpressible;
31use crate::trc::ToneCurveEvaluator;
32use crate::{CmsError, Layout, Rgb, TransformExecutor, Vector3f};
33use num_traits::AsPrimitive;
34use std::marker::PhantomData;
35
36struct TransformRgbToGrayExtendedExecutor<T, const SRC_LAYOUT: u8, const DST_LAYOUT: u8> {
37 linear_eval: Box<dyn ToneCurveEvaluator + Send + Sync>,
38 gamma_eval: Box<dyn ToneCurveEvaluator + Send + Sync>,
39 weights: Vector3f,
40 _phantom: PhantomData<T>,
41 bit_depth: usize,
42}
43
44pub(crate) fn make_rgb_to_gray_extended<
45 T: Copy + Default + PointeeSizeExpressible + Send + Sync + 'static + AsPrimitive<f32>,
46>(
47 src_layout: Layout,
48 dst_layout: Layout,
49 linear_eval: Box<dyn ToneCurveEvaluator + Send + Sync>,
50 gamma_eval: Box<dyn ToneCurveEvaluator + Send + Sync>,
51 weights: Vector3f,
52 bit_depth: usize,
53) -> Box<dyn TransformExecutor<T> + Send + Sync>
54where
55 u32: AsPrimitive<T>,
56 f32: AsPrimitive<T>,
57{
58 match src_layout {
59 Layout::Rgb => match dst_layout {
60 Layout::Rgb => unreachable!(),
61 Layout::Rgba => unreachable!(),
62 Layout::Gray => Box::new(TransformRgbToGrayExtendedExecutor::<
63 T,
64 { Layout::Rgb as u8 },
65 { Layout::Gray as u8 },
66 > {
67 linear_eval,
68 gamma_eval,
69 weights,
70 _phantom: PhantomData,
71 bit_depth,
72 }),
73 Layout::GrayAlpha => Box::new(TransformRgbToGrayExtendedExecutor::<
74 T,
75 { Layout::Rgb as u8 },
76 { Layout::GrayAlpha as u8 },
77 > {
78 linear_eval,
79 gamma_eval,
80 weights,
81 _phantom: PhantomData,
82 bit_depth,
83 }),
84 _ => unreachable!(),
85 },
86 Layout::Rgba => match dst_layout {
87 Layout::Rgb => unreachable!(),
88 Layout::Rgba => unreachable!(),
89 Layout::Gray => Box::new(TransformRgbToGrayExtendedExecutor::<
90 T,
91 { Layout::Rgba as u8 },
92 { Layout::Gray as u8 },
93 > {
94 linear_eval,
95 gamma_eval,
96 weights,
97 _phantom: PhantomData,
98 bit_depth,
99 }),
100 Layout::GrayAlpha => Box::new(TransformRgbToGrayExtendedExecutor::<
101 T,
102 { Layout::Rgba as u8 },
103 { Layout::GrayAlpha as u8 },
104 > {
105 linear_eval,
106 gamma_eval,
107 weights,
108 _phantom: PhantomData,
109 bit_depth,
110 }),
111 _ => unreachable!(),
112 },
113 Layout::Gray => unreachable!(),
114 Layout::GrayAlpha => unreachable!(),
115 _ => unreachable!(),
116 }
117}
118
119impl<
120 T: Copy + Default + PointeeSizeExpressible + 'static + AsPrimitive<f32>,
121 const SRC_LAYOUT: u8,
122 const DST_LAYOUT: u8,
123> TransformExecutor<T> for TransformRgbToGrayExtendedExecutor<T, SRC_LAYOUT, DST_LAYOUT>
124where
125 u32: AsPrimitive<T>,
126 f32: AsPrimitive<T>,
127{
128 fn transform(&self, src: &[T], dst: &mut [T]) -> Result<(), CmsError> {
129 let src_cn = Layout::from(SRC_LAYOUT);
130 let dst_cn = Layout::from(DST_LAYOUT);
131 let src_channels = src_cn.channels();
132 let dst_channels = dst_cn.channels();
133
134 if src.len() / src_channels != dst.len() / dst_channels {
135 return Err(CmsError::LaneSizeMismatch);
136 }
137 if src.len() % src_channels != 0 {
138 return Err(CmsError::LaneMultipleOfChannels);
139 }
140 if dst.len() % dst_channels != 0 {
141 return Err(CmsError::LaneMultipleOfChannels);
142 }
143
144 let max_value = ((1u32 << self.bit_depth) - 1).as_();
145
146 for (src, dst) in src
147 .chunks_exact(src_channels)
148 .zip(dst.chunks_exact_mut(dst_channels))
149 {
150 let in_tristimulus = Rgb::<f32>::new(
151 src[src_cn.r_i()].as_(),
152 src[src_cn.g_i()].as_(),
153 src[src_cn.b_i()].as_(),
154 );
155 let lin_tristimulus = self.linear_eval.evaluate_tristimulus(in_tristimulus);
156 let a = if src_channels == 4 {
157 src[src_cn.a_i()]
158 } else {
159 max_value
160 };
161 let grey = mlaf(
162 mlaf(
163 self.weights.v[0] * lin_tristimulus.r,
164 self.weights.v[1],
165 lin_tristimulus.g,
166 ),
167 self.weights.v[2],
168 lin_tristimulus.b,
169 )
170 .min(1.)
171 .max(0.);
172 let gamma_value = self.gamma_eval.evaluate_value(grey);
173 dst[0] = gamma_value.as_();
174 if dst_channels == 2 {
175 dst[1] = a;
176 }
177 }
178
179 Ok(())
180 }
181}