1use crate::trc::ToneCurveEvaluator;
30use crate::{CmsError, Layout, Matrix3f, PointeeSizeExpressible, Rgb, TransformExecutor};
31use num_traits::AsPrimitive;
32use std::marker::PhantomData;
33
34pub(crate) struct TransformShaperRgbFloat<T: Clone, const BUCKET: usize> {
35 pub(crate) r_linear: Box<[f32; BUCKET]>,
36 pub(crate) g_linear: Box<[f32; BUCKET]>,
37 pub(crate) b_linear: Box<[f32; BUCKET]>,
38 pub(crate) gamma_evaluator: Box<dyn ToneCurveEvaluator + Send + Sync>,
39 pub(crate) adaptation_matrix: Matrix3f,
40 pub(crate) phantom_data: PhantomData<T>,
41}
42
43pub(crate) struct TransformShaperFloatInOut<T: Clone> {
44 pub(crate) linear_evaluator: Box<dyn ToneCurveEvaluator + Send + Sync>,
45 pub(crate) gamma_evaluator: Box<dyn ToneCurveEvaluator + Send + Sync>,
46 pub(crate) adaptation_matrix: Matrix3f,
47 pub(crate) phantom_data: PhantomData<T>,
48}
49
50struct TransformShaperFloatScalar<
51 T: Clone,
52 const SRC_LAYOUT: u8,
53 const DST_LAYOUT: u8,
54 const LINEAR_CAP: usize,
55> {
56 pub(crate) profile: TransformShaperRgbFloat<T, LINEAR_CAP>,
57 pub(crate) bit_depth: usize,
58}
59
60struct TransformShaperRgbFloatInOut<T: Clone, const SRC_LAYOUT: u8, const DST_LAYOUT: u8> {
61 pub(crate) profile: TransformShaperFloatInOut<T>,
62 pub(crate) bit_depth: usize,
63}
64
65pub(crate) fn make_rgb_xyz_rgb_transform_float<
66 T: Clone + Send + Sync + PointeeSizeExpressible + 'static + Copy + Default,
67 const LINEAR_CAP: usize,
68>(
69 src_layout: Layout,
70 dst_layout: Layout,
71 profile: TransformShaperRgbFloat<T, LINEAR_CAP>,
72 bit_depth: usize,
73) -> Result<Box<dyn TransformExecutor<T> + Send + Sync>, CmsError>
74where
75 u32: AsPrimitive<T>,
76 f32: AsPrimitive<T>,
77{
78 if (src_layout == Layout::Rgba) && (dst_layout == Layout::Rgba) {
79 return Ok(Box::new(TransformShaperFloatScalar::<
80 T,
81 { Layout::Rgba as u8 },
82 { Layout::Rgba as u8 },
83 LINEAR_CAP,
84 > {
85 profile,
86 bit_depth,
87 }));
88 } else if (src_layout == Layout::Rgb) && (dst_layout == Layout::Rgba) {
89 return Ok(Box::new(TransformShaperFloatScalar::<
90 T,
91 { Layout::Rgb as u8 },
92 { Layout::Rgba as u8 },
93 LINEAR_CAP,
94 > {
95 profile,
96 bit_depth,
97 }));
98 } else if (src_layout == Layout::Rgba) && (dst_layout == Layout::Rgb) {
99 return Ok(Box::new(TransformShaperFloatScalar::<
100 T,
101 { Layout::Rgba as u8 },
102 { Layout::Rgb as u8 },
103 LINEAR_CAP,
104 > {
105 profile,
106 bit_depth,
107 }));
108 } else if (src_layout == Layout::Rgb) && (dst_layout == Layout::Rgb) {
109 return Ok(Box::new(TransformShaperFloatScalar::<
110 T,
111 { Layout::Rgb as u8 },
112 { Layout::Rgb as u8 },
113 LINEAR_CAP,
114 > {
115 profile,
116 bit_depth,
117 }));
118 }
119 Err(CmsError::UnsupportedProfileConnection)
120}
121
122pub(crate) fn make_rgb_xyz_rgb_transform_float_in_out<
123 T: Clone + Send + Sync + PointeeSizeExpressible + 'static + Copy + Default + AsPrimitive<f32>,
124>(
125 src_layout: Layout,
126 dst_layout: Layout,
127 profile: TransformShaperFloatInOut<T>,
128 bit_depth: usize,
129) -> Result<Box<dyn TransformExecutor<T> + Send + Sync>, CmsError>
130where
131 u32: AsPrimitive<T>,
132 f32: AsPrimitive<T>,
133{
134 if (src_layout == Layout::Rgba) && (dst_layout == Layout::Rgba) {
135 return Ok(Box::new(TransformShaperRgbFloatInOut::<
136 T,
137 { Layout::Rgba as u8 },
138 { Layout::Rgba as u8 },
139 > {
140 profile,
141 bit_depth,
142 }));
143 } else if (src_layout == Layout::Rgb) && (dst_layout == Layout::Rgba) {
144 return Ok(Box::new(TransformShaperRgbFloatInOut::<
145 T,
146 { Layout::Rgb as u8 },
147 { Layout::Rgba as u8 },
148 > {
149 profile,
150 bit_depth,
151 }));
152 } else if (src_layout == Layout::Rgba) && (dst_layout == Layout::Rgb) {
153 return Ok(Box::new(TransformShaperRgbFloatInOut::<
154 T,
155 { Layout::Rgba as u8 },
156 { Layout::Rgb as u8 },
157 > {
158 profile,
159 bit_depth,
160 }));
161 } else if (src_layout == Layout::Rgb) && (dst_layout == Layout::Rgb) {
162 return Ok(Box::new(TransformShaperRgbFloatInOut::<
163 T,
164 { Layout::Rgb as u8 },
165 { Layout::Rgb as u8 },
166 > {
167 profile,
168 bit_depth,
169 }));
170 }
171 Err(CmsError::UnsupportedProfileConnection)
172}
173
174impl<
175 T: Clone + PointeeSizeExpressible + Copy + Default + 'static,
176 const SRC_LAYOUT: u8,
177 const DST_LAYOUT: u8,
178 const LINEAR_CAP: usize,
179> TransformExecutor<T> for TransformShaperFloatScalar<T, SRC_LAYOUT, DST_LAYOUT, LINEAR_CAP>
180where
181 u32: AsPrimitive<T>,
182 f32: AsPrimitive<T>,
183{
184 fn transform(&self, src: &[T], dst: &mut [T]) -> Result<(), CmsError> {
185 use crate::mlaf::mlaf;
186 let src_cn = Layout::from(SRC_LAYOUT);
187 let dst_cn = Layout::from(DST_LAYOUT);
188 let src_channels = src_cn.channels();
189 let dst_channels = dst_cn.channels();
190
191 if src.len() / src_channels != dst.len() / dst_channels {
192 return Err(CmsError::LaneSizeMismatch);
193 }
194 if src.len() % src_channels != 0 {
195 return Err(CmsError::LaneMultipleOfChannels);
196 }
197 if dst.len() % dst_channels != 0 {
198 return Err(CmsError::LaneMultipleOfChannels);
199 }
200
201 let transform = self.profile.adaptation_matrix;
202 let max_colors: T = ((1 << self.bit_depth) - 1).as_();
203
204 for (src, dst) in src
205 .chunks_exact(src_channels)
206 .zip(dst.chunks_exact_mut(dst_channels))
207 {
208 let r = self.profile.r_linear[src[src_cn.r_i()]._as_usize()];
209 let g = self.profile.g_linear[src[src_cn.g_i()]._as_usize()];
210 let b = self.profile.b_linear[src[src_cn.b_i()]._as_usize()];
211 let a = if src_channels == 4 {
212 src[src_cn.a_i()]
213 } else {
214 max_colors
215 };
216
217 let new_r = mlaf(
218 mlaf(r * transform.v[0][0], g, transform.v[0][1]),
219 b,
220 transform.v[0][2],
221 );
222
223 let new_g = mlaf(
224 mlaf(r * transform.v[1][0], g, transform.v[1][1]),
225 b,
226 transform.v[1][2],
227 );
228
229 let new_b = mlaf(
230 mlaf(r * transform.v[2][0], g, transform.v[2][1]),
231 b,
232 transform.v[2][2],
233 );
234
235 let mut rgb = Rgb::new(new_r, new_g, new_b);
236 rgb = self.profile.gamma_evaluator.evaluate_tristimulus(rgb);
237
238 dst[dst_cn.r_i()] = rgb.r.as_();
239 dst[dst_cn.g_i()] = rgb.g.as_();
240 dst[dst_cn.b_i()] = rgb.b.as_();
241 if dst_channels == 4 {
242 dst[dst_cn.a_i()] = a;
243 }
244 }
245
246 Ok(())
247 }
248}
249
250impl<
251 T: Clone + PointeeSizeExpressible + Copy + Default + 'static + AsPrimitive<f32>,
252 const SRC_LAYOUT: u8,
253 const DST_LAYOUT: u8,
254> TransformExecutor<T> for TransformShaperRgbFloatInOut<T, SRC_LAYOUT, DST_LAYOUT>
255where
256 u32: AsPrimitive<T>,
257 f32: AsPrimitive<T>,
258{
259 fn transform(&self, src: &[T], dst: &mut [T]) -> Result<(), CmsError> {
260 use crate::mlaf::mlaf;
261 let src_cn = Layout::from(SRC_LAYOUT);
262 let dst_cn = Layout::from(DST_LAYOUT);
263 let src_channels = src_cn.channels();
264 let dst_channels = dst_cn.channels();
265
266 if src.len() / src_channels != dst.len() / dst_channels {
267 return Err(CmsError::LaneSizeMismatch);
268 }
269 if src.len() % src_channels != 0 {
270 return Err(CmsError::LaneMultipleOfChannels);
271 }
272 if dst.len() % dst_channels != 0 {
273 return Err(CmsError::LaneMultipleOfChannels);
274 }
275
276 let transform = self.profile.adaptation_matrix;
277 let max_colors: T = ((1 << self.bit_depth) - 1).as_();
278
279 for (src, dst) in src
280 .chunks_exact(src_channels)
281 .zip(dst.chunks_exact_mut(dst_channels))
282 {
283 let mut src_rgb = Rgb::new(
284 src[src_cn.r_i()].as_(),
285 src[src_cn.g_i()].as_(),
286 src[src_cn.b_i()].as_(),
287 );
288 src_rgb = self.profile.linear_evaluator.evaluate_tristimulus(src_rgb);
289 let r = src_rgb.r;
290 let g = src_rgb.g;
291 let b = src_rgb.b;
292 let a = if src_channels == 4 {
293 src[src_cn.a_i()]
294 } else {
295 max_colors
296 };
297
298 let new_r = mlaf(
299 mlaf(r * transform.v[0][0], g, transform.v[0][1]),
300 b,
301 transform.v[0][2],
302 );
303
304 let new_g = mlaf(
305 mlaf(r * transform.v[1][0], g, transform.v[1][1]),
306 b,
307 transform.v[1][2],
308 );
309
310 let new_b = mlaf(
311 mlaf(r * transform.v[2][0], g, transform.v[2][1]),
312 b,
313 transform.v[2][2],
314 );
315
316 let mut rgb = Rgb::new(new_r, new_g, new_b);
317 rgb = self.profile.gamma_evaluator.evaluate_tristimulus(rgb);
318
319 dst[dst_cn.r_i()] = rgb.r.as_();
320 dst[dst_cn.g_i()] = rgb.g.as_();
321 dst[dst_cn.b_i()] = rgb.b.as_();
322
323 if dst_channels == 4 {
324 dst[dst_cn.a_i()] = a;
325 }
326 }
327
328 Ok(())
329 }
330}