1use crate::Layout;
30use crate::conversions::TransformMatrixShaper;
31use crate::matrix::Matrix3;
32use crate::{CmsError, TransformExecutor};
33use num_traits::AsPrimitive;
34
35pub(crate) struct TransformMatrixShaperFixedPoint<R, T, const LINEAR_CAP: usize> {
37 pub(crate) r_linear: Box<[R; LINEAR_CAP]>,
38 pub(crate) g_linear: Box<[R; LINEAR_CAP]>,
39 pub(crate) b_linear: Box<[R; LINEAR_CAP]>,
40 pub(crate) r_gamma: Box<[T; 65536]>,
41 pub(crate) g_gamma: Box<[T; 65536]>,
42 pub(crate) b_gamma: Box<[T; 65536]>,
43 pub(crate) adaptation_matrix: Matrix3<i16>,
44}
45
46pub(crate) struct TransformMatrixShaperFixedPointOpt<R, W, T, const LINEAR_CAP: usize> {
50 pub(crate) linear: Box<[R; LINEAR_CAP]>,
51 pub(crate) gamma: Box<[T; 65536]>,
52 pub(crate) adaptation_matrix: Matrix3<W>,
53}
54
55#[allow(unused)]
56struct TransformMatrixShaperQ2_13<
57 T: Copy,
58 const SRC_LAYOUT: u8,
59 const DST_LAYOUT: u8,
60 const LINEAR_CAP: usize,
61 const GAMMA_LUT: usize,
62 const PRECISION: i32,
63> {
64 pub(crate) profile: TransformMatrixShaperFixedPoint<i16, T, LINEAR_CAP>,
65 pub(crate) bit_depth: usize,
66}
67
68#[allow(unused)]
69struct TransformMatrixShaperQ2_13Optimized<
70 T: Copy,
71 const SRC_LAYOUT: u8,
72 const DST_LAYOUT: u8,
73 const LINEAR_CAP: usize,
74 const GAMMA_LUT: usize,
75 const PRECISION: i32,
76> {
77 pub(crate) profile: TransformMatrixShaperFixedPointOpt<i16, i16, T, LINEAR_CAP>,
78 pub(crate) bit_depth: usize,
79}
80
81#[allow(unused)]
82impl<
83 T: Clone + PointeeSizeExpressible + Copy + Default + 'static,
84 const SRC_LAYOUT: u8,
85 const DST_LAYOUT: u8,
86 const LINEAR_CAP: usize,
87 const GAMMA_LUT: usize,
88 const PRECISION: i32,
89> TransformExecutor<T>
90 for TransformMatrixShaperQ2_13<T, SRC_LAYOUT, DST_LAYOUT, LINEAR_CAP, GAMMA_LUT, PRECISION>
91where
92 u32: AsPrimitive<T>,
93{
94 fn transform(&self, src: &[T], dst: &mut [T]) -> Result<(), CmsError> {
95 let src_cn = Layout::from(SRC_LAYOUT);
96 let dst_cn = Layout::from(DST_LAYOUT);
97 let src_channels = src_cn.channels();
98 let dst_channels = dst_cn.channels();
99
100 if src.len() / src_channels != dst.len() / dst_channels {
101 return Err(CmsError::LaneSizeMismatch);
102 }
103 if src.len() % src_channels != 0 {
104 return Err(CmsError::LaneMultipleOfChannels);
105 }
106 if dst.len() % dst_channels != 0 {
107 return Err(CmsError::LaneMultipleOfChannels);
108 }
109
110 let transform = self.profile.adaptation_matrix;
111 let max_colors: T = ((1 << self.bit_depth as u32) - 1u32).as_();
112 let rnd: i32 = (1i32 << (PRECISION - 1));
113
114 let v_gamma_max = GAMMA_LUT as i32 - 1;
115
116 for (src, dst) in src
117 .chunks_exact(src_channels)
118 .zip(dst.chunks_exact_mut(dst_channels))
119 {
120 let r = self.profile.r_linear[src[src_cn.r_i()]._as_usize()];
121 let g = self.profile.g_linear[src[src_cn.g_i()]._as_usize()];
122 let b = self.profile.b_linear[src[src_cn.b_i()]._as_usize()];
123 let a = if src_channels == 4 {
124 src[src_cn.a_i()]
125 } else {
126 max_colors
127 };
128
129 let new_r = r as i32 * transform.v[0][0] as i32
130 + g as i32 * transform.v[0][1] as i32
131 + b as i32 * transform.v[0][2] as i32
132 + rnd;
133
134 let r_q2_13 = (new_r >> PRECISION).min(v_gamma_max).max(0) as u16;
135
136 let new_g = r as i32 * transform.v[1][0] as i32
137 + g as i32 * transform.v[1][1] as i32
138 + b as i32 * transform.v[1][2] as i32
139 + rnd;
140
141 let g_q2_13 = (new_g >> PRECISION).min(v_gamma_max).max(0) as u16;
142
143 let new_b = r as i32 * transform.v[2][0] as i32
144 + g as i32 * transform.v[2][1] as i32
145 + b as i32 * transform.v[2][2] as i32
146 + rnd;
147
148 let b_q2_13 = (new_b >> PRECISION).min(v_gamma_max).max(0) as u16;
149
150 dst[dst_cn.r_i()] = self.profile.r_gamma[r_q2_13 as usize];
151 dst[dst_cn.g_i()] = self.profile.g_gamma[g_q2_13 as usize];
152 dst[dst_cn.b_i()] = self.profile.b_gamma[b_q2_13 as usize];
153 if dst_channels == 4 {
154 dst[dst_cn.a_i()] = a;
155 }
156 }
157 Ok(())
158 }
159}
160
161#[allow(unused)]
162impl<
163 T: Clone + PointeeSizeExpressible + Copy + Default + 'static,
164 const SRC_LAYOUT: u8,
165 const DST_LAYOUT: u8,
166 const LINEAR_CAP: usize,
167 const GAMMA_LUT: usize,
168 const PRECISION: i32,
169> TransformExecutor<T>
170 for TransformMatrixShaperQ2_13Optimized<
171 T,
172 SRC_LAYOUT,
173 DST_LAYOUT,
174 LINEAR_CAP,
175 GAMMA_LUT,
176 PRECISION,
177 >
178where
179 u32: AsPrimitive<T>,
180{
181 fn transform(&self, src: &[T], dst: &mut [T]) -> Result<(), CmsError> {
182 let src_cn = Layout::from(SRC_LAYOUT);
183 let dst_cn = Layout::from(DST_LAYOUT);
184 let src_channels = src_cn.channels();
185 let dst_channels = dst_cn.channels();
186
187 if src.len() / src_channels != dst.len() / dst_channels {
188 return Err(CmsError::LaneSizeMismatch);
189 }
190 if src.len() % src_channels != 0 {
191 return Err(CmsError::LaneMultipleOfChannels);
192 }
193 if dst.len() % dst_channels != 0 {
194 return Err(CmsError::LaneMultipleOfChannels);
195 }
196
197 let transform = self.profile.adaptation_matrix;
198 let max_colors: T = ((1 << self.bit_depth as u32) - 1u32).as_();
199 let rnd: i32 = (1i32 << (PRECISION - 1));
200
201 let v_gamma_max = GAMMA_LUT as i32 - 1;
202
203 for (src, dst) in src
204 .chunks_exact(src_channels)
205 .zip(dst.chunks_exact_mut(dst_channels))
206 {
207 let r = self.profile.linear[src[src_cn.r_i()]._as_usize()];
208 let g = self.profile.linear[src[src_cn.g_i()]._as_usize()];
209 let b = self.profile.linear[src[src_cn.b_i()]._as_usize()];
210 let a = if src_channels == 4 {
211 src[src_cn.a_i()]
212 } else {
213 max_colors
214 };
215
216 let new_r = r as i32 * transform.v[0][0] as i32
217 + g as i32 * transform.v[0][1] as i32
218 + b as i32 * transform.v[0][2] as i32
219 + rnd;
220
221 let r_q2_13 = (new_r >> PRECISION).min(v_gamma_max).max(0) as u16;
222
223 let new_g = r as i32 * transform.v[1][0] as i32
224 + g as i32 * transform.v[1][1] as i32
225 + b as i32 * transform.v[1][2] as i32
226 + rnd;
227
228 let g_q2_13 = (new_g >> PRECISION).min(v_gamma_max).max(0) as u16;
229
230 let new_b = r as i32 * transform.v[2][0] as i32
231 + g as i32 * transform.v[2][1] as i32
232 + b as i32 * transform.v[2][2] as i32
233 + rnd;
234
235 let b_q2_13 = (new_b >> PRECISION).min(v_gamma_max).max(0) as u16;
236
237 dst[dst_cn.r_i()] = self.profile.gamma[r_q2_13 as usize];
238 dst[dst_cn.g_i()] = self.profile.gamma[g_q2_13 as usize];
239 dst[dst_cn.b_i()] = self.profile.gamma[b_q2_13 as usize];
240 if dst_channels == 4 {
241 dst[dst_cn.a_i()] = a;
242 }
243 }
244 Ok(())
245 }
246}
247
248macro_rules! create_rgb_xyz_dependant_q2_13_executor {
249 ($dep_name: ident, $dependant: ident, $resolution: ident, $shaper: ident) => {
250 pub(crate) fn $dep_name<
251 T: Clone + Send + Sync + AsPrimitive<usize> + Default + PointeeSizeExpressible,
252 const LINEAR_CAP: usize,
253 const GAMMA_LUT: usize,
254 const BIT_DEPTH: usize,
255 const PRECISION: i32,
256 >(
257 src_layout: Layout,
258 dst_layout: Layout,
259 profile: $shaper<T, LINEAR_CAP>,
260 ) -> Result<Box<dyn TransformExecutor<T> + Send + Sync>, CmsError>
261 where
262 u32: AsPrimitive<T>,
263 {
264 let q2_13_profile =
265 profile.to_q2_13_n::<$resolution, PRECISION, LINEAR_CAP, GAMMA_LUT, BIT_DEPTH>();
266 if (src_layout == Layout::Rgba) && (dst_layout == Layout::Rgba) {
267 return Ok(Box::new($dependant::<
268 T,
269 { Layout::Rgba as u8 },
270 { Layout::Rgba as u8 },
271 LINEAR_CAP,
272 GAMMA_LUT,
273 PRECISION,
274 > {
275 profile: q2_13_profile,
276 bit_depth: BIT_DEPTH,
277 }));
278 } else if (src_layout == Layout::Rgb) && (dst_layout == Layout::Rgba) {
279 return Ok(Box::new($dependant::<
280 T,
281 { Layout::Rgb as u8 },
282 { Layout::Rgba as u8 },
283 LINEAR_CAP,
284 GAMMA_LUT,
285 PRECISION,
286 > {
287 profile: q2_13_profile,
288 bit_depth: BIT_DEPTH,
289 }));
290 } else if (src_layout == Layout::Rgba) && (dst_layout == Layout::Rgb) {
291 return Ok(Box::new($dependant::<
292 T,
293 { Layout::Rgba as u8 },
294 { Layout::Rgb as u8 },
295 LINEAR_CAP,
296 GAMMA_LUT,
297 PRECISION,
298 > {
299 profile: q2_13_profile,
300 bit_depth: BIT_DEPTH,
301 }));
302 } else if (src_layout == Layout::Rgb) && (dst_layout == Layout::Rgb) {
303 return Ok(Box::new($dependant::<
304 T,
305 { Layout::Rgb as u8 },
306 { Layout::Rgb as u8 },
307 LINEAR_CAP,
308 GAMMA_LUT,
309 PRECISION,
310 > {
311 profile: q2_13_profile,
312 bit_depth: BIT_DEPTH,
313 }));
314 }
315 Err(CmsError::UnsupportedProfileConnection)
316 }
317 };
318}
319
320#[cfg(all(target_arch = "aarch64", feature = "neon"))]
321macro_rules! create_rgb_xyz_dependant_q1_30_executor {
322 ($dep_name: ident, $dependant: ident, $resolution: ident, $shaper: ident) => {
323 pub(crate) fn $dep_name<
324 T: Clone + Send + Sync + AsPrimitive<usize> + Default + PointeeSizeExpressible,
325 const LINEAR_CAP: usize,
326 const GAMMA_LUT: usize,
327 const BIT_DEPTH: usize,
328 const PRECISION: i32,
329 >(
330 src_layout: Layout,
331 dst_layout: Layout,
332 profile: $shaper<T, LINEAR_CAP>,
333 ) -> Result<Box<dyn TransformExecutor<T> + Send + Sync>, CmsError>
334 where
335 u32: AsPrimitive<T>,
336 {
337 let q1_30_profile =
338 profile.to_q1_30_n::<$resolution, PRECISION, LINEAR_CAP, GAMMA_LUT, BIT_DEPTH>();
339 if (src_layout == Layout::Rgba) && (dst_layout == Layout::Rgba) {
340 return Ok(Box::new($dependant::<
341 T,
342 { Layout::Rgba as u8 },
343 { Layout::Rgba as u8 },
344 LINEAR_CAP,
345 GAMMA_LUT,
346 BIT_DEPTH,
347 PRECISION,
348 > {
349 profile: q1_30_profile,
350 }));
351 } else if (src_layout == Layout::Rgb) && (dst_layout == Layout::Rgba) {
352 return Ok(Box::new($dependant::<
353 T,
354 { Layout::Rgb as u8 },
355 { Layout::Rgba as u8 },
356 LINEAR_CAP,
357 GAMMA_LUT,
358 BIT_DEPTH,
359 PRECISION,
360 > {
361 profile: q1_30_profile,
362 }));
363 } else if (src_layout == Layout::Rgba) && (dst_layout == Layout::Rgb) {
364 return Ok(Box::new($dependant::<
365 T,
366 { Layout::Rgba as u8 },
367 { Layout::Rgb as u8 },
368 LINEAR_CAP,
369 GAMMA_LUT,
370 BIT_DEPTH,
371 PRECISION,
372 > {
373 profile: q1_30_profile,
374 }));
375 } else if (src_layout == Layout::Rgb) && (dst_layout == Layout::Rgb) {
376 return Ok(Box::new($dependant::<
377 T,
378 { Layout::Rgb as u8 },
379 { Layout::Rgb as u8 },
380 LINEAR_CAP,
381 GAMMA_LUT,
382 BIT_DEPTH,
383 PRECISION,
384 > {
385 profile: q1_30_profile,
386 }));
387 }
388 Err(CmsError::UnsupportedProfileConnection)
389 }
390 };
391}
392
393#[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
394use crate::conversions::neon::{
395 TransformShaperQ1_30NeonOpt, TransformShaperQ2_13Neon, TransformShaperQ2_13NeonOpt,
396};
397
398#[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
399create_rgb_xyz_dependant_q2_13_executor!(
400 make_rgb_xyz_q2_13,
401 TransformShaperQ2_13Neon,
402 i16,
403 TransformMatrixShaper
404);
405
406#[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
407create_rgb_xyz_dependant_q2_13_executor!(
408 make_rgb_xyz_q2_13_opt,
409 TransformShaperQ2_13NeonOpt,
410 i16,
411 TransformMatrixShaperOptimized
412);
413
414#[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
415create_rgb_xyz_dependant_q1_30_executor!(
416 make_rgb_xyz_q1_30_opt,
417 TransformShaperQ1_30NeonOpt,
418 i32,
419 TransformMatrixShaperOptimized
420);
421
422#[cfg(not(all(target_arch = "aarch64", target_feature = "neon", feature = "neon")))]
423create_rgb_xyz_dependant_q2_13_executor!(
424 make_rgb_xyz_q2_13,
425 TransformMatrixShaperQ2_13,
426 i16,
427 TransformMatrixShaper
428);
429
430#[cfg(not(all(target_arch = "aarch64", target_feature = "neon", feature = "neon")))]
431create_rgb_xyz_dependant_q2_13_executor!(
432 make_rgb_xyz_q2_13_opt,
433 TransformMatrixShaperQ2_13Optimized,
434 i16,
435 TransformMatrixShaperOptimized
436);
437
438#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
439use crate::conversions::sse::{TransformShaperQ2_13OptSse, TransformShaperQ2_13Sse};
440
441#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
442create_rgb_xyz_dependant_q2_13_executor!(
443 make_rgb_xyz_q2_13_transform_sse_41,
444 TransformShaperQ2_13Sse,
445 i32,
446 TransformMatrixShaper
447);
448
449#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "sse"))]
450create_rgb_xyz_dependant_q2_13_executor!(
451 make_rgb_xyz_q2_13_transform_sse_41_opt,
452 TransformShaperQ2_13OptSse,
453 i32,
454 TransformMatrixShaperOptimized
455);
456
457#[cfg(all(target_arch = "x86_64", feature = "avx"))]
458use crate::conversions::avx::{TransformShaperRgbQ2_13Avx, TransformShaperRgbQ2_13OptAvx};
459use crate::conversions::rgbxyz::TransformMatrixShaperOptimized;
460use crate::transform::PointeeSizeExpressible;
461
462#[cfg(all(target_arch = "x86_64", feature = "avx"))]
463create_rgb_xyz_dependant_q2_13_executor!(
464 make_rgb_xyz_q2_13_transform_avx2,
465 TransformShaperRgbQ2_13Avx,
466 i32,
467 TransformMatrixShaper
468);
469
470#[cfg(all(target_arch = "x86_64", feature = "avx"))]
471create_rgb_xyz_dependant_q2_13_executor!(
472 make_rgb_xyz_q2_13_transform_avx2_opt,
473 TransformShaperRgbQ2_13OptAvx,
474 i32,
475 TransformMatrixShaperOptimized
476);
477
478#[cfg(all(target_arch = "x86_64", feature = "avx512"))]
479use crate::conversions::avx512::TransformShaperRgbQ2_13OptAvx512;
480
481#[cfg(all(target_arch = "x86_64", feature = "avx512"))]
482create_rgb_xyz_dependant_q2_13_executor!(
483 make_rgb_xyz_q2_13_transform_avx512_opt,
484 TransformShaperRgbQ2_13OptAvx512,
485 i32,
486 TransformMatrixShaperOptimized
487);