1use crate::mlaf::mlaf;
30use crate::safe_math::SafeMul;
31use crate::{
32 CmsError, Cube, DataColorSpace, InPlaceStage, InterpolationMethod, LutMultidimensionalType,
33 MalformedSize, Matrix3d, Matrix3f, TransformOptions, Vector3d, Vector3f,
34};
35
36#[allow(unused)]
37struct ACurves3<'a, const DEPTH: usize> {
38 curve0: Box<[f32; 65536]>,
39 curve1: Box<[f32; 65536]>,
40 curve2: Box<[f32; 65536]>,
41 clut: &'a [f32],
42 grid_size: [u8; 3],
43 interpolation_method: InterpolationMethod,
44 pcs: DataColorSpace,
45}
46
47#[allow(unused)]
48struct ACurves3Optimized<'a> {
49 clut: &'a [f32],
50 grid_size: [u8; 3],
51 interpolation_method: InterpolationMethod,
52 pcs: DataColorSpace,
53}
54
55#[allow(unused)]
56impl<const DEPTH: usize> ACurves3<'_, DEPTH> {
57 fn transform_impl<Fetch: Fn(f32, f32, f32) -> Vector3f>(
58 &self,
59 dst: &mut [f32],
60 fetch: Fetch,
61 ) -> Result<(), CmsError> {
62 let scale_value = (DEPTH - 1) as f32;
63
64 for dst in dst.chunks_exact_mut(3) {
65 let a0 = (dst[0] * scale_value).round().min(scale_value) as u16;
66 let a1 = (dst[1] * scale_value).round().min(scale_value) as u16;
67 let a2 = (dst[2] * scale_value).round().min(scale_value) as u16;
68 let b0 = self.curve0[a0 as usize];
69 let b1 = self.curve1[a1 as usize];
70 let b2 = self.curve2[a2 as usize];
71 let interpolated = fetch(b0, b1, b2);
72 dst[0] = interpolated.v[0];
73 dst[1] = interpolated.v[1];
74 dst[2] = interpolated.v[2];
75 }
76 Ok(())
77 }
78}
79
80#[allow(unused)]
81impl ACurves3Optimized<'_> {
82 fn transform_impl<Fetch: Fn(f32, f32, f32) -> Vector3f>(
83 &self,
84 dst: &mut [f32],
85 fetch: Fetch,
86 ) -> Result<(), CmsError> {
87 for dst in dst.chunks_exact_mut(3) {
88 let a0 = dst[0];
89 let a1 = dst[1];
90 let a2 = dst[2];
91 let interpolated = fetch(a0, a1, a2);
92 dst[0] = interpolated.v[0];
93 dst[1] = interpolated.v[1];
94 dst[2] = interpolated.v[2];
95 }
96 Ok(())
97 }
98}
99
100impl<const DEPTH: usize> InPlaceStage for ACurves3<'_, DEPTH> {
101 fn transform(&self, dst: &mut [f32]) -> Result<(), CmsError> {
102 let lut = Cube::new_cube(self.clut, self.grid_size);
103
104 if self.pcs == DataColorSpace::Lab || self.pcs == DataColorSpace::Xyz {
106 return self.transform_impl(dst, |x, y, z| lut.trilinear_vec3(x, y, z));
107 }
108
109 match self.interpolation_method {
110 #[cfg(feature = "options")]
111 InterpolationMethod::Tetrahedral => {
112 self.transform_impl(dst, |x, y, z| lut.tetra_vec3(x, y, z))?;
113 }
114 #[cfg(feature = "options")]
115 InterpolationMethod::Pyramid => {
116 self.transform_impl(dst, |x, y, z| lut.pyramid_vec3(x, y, z))?;
117 }
118 #[cfg(feature = "options")]
119 InterpolationMethod::Prism => {
120 self.transform_impl(dst, |x, y, z| lut.prism_vec3(x, y, z))?;
121 }
122 InterpolationMethod::Linear => {
123 self.transform_impl(dst, |x, y, z| lut.trilinear_vec3(x, y, z))?;
124 }
125 }
126 Ok(())
127 }
128}
129
130impl InPlaceStage for ACurves3Optimized<'_> {
131 fn transform(&self, dst: &mut [f32]) -> Result<(), CmsError> {
132 let lut = Cube::new_cube(self.clut, self.grid_size);
133
134 if self.pcs == DataColorSpace::Lab {
136 return self.transform_impl(dst, |x, y, z| lut.trilinear_vec3(x, y, z));
137 }
138
139 match self.interpolation_method {
140 #[cfg(feature = "options")]
141 InterpolationMethod::Tetrahedral => {
142 self.transform_impl(dst, |x, y, z| lut.tetra_vec3(x, y, z))?;
143 }
144 #[cfg(feature = "options")]
145 InterpolationMethod::Pyramid => {
146 self.transform_impl(dst, |x, y, z| lut.pyramid_vec3(x, y, z))?;
147 }
148 #[cfg(feature = "options")]
149 InterpolationMethod::Prism => {
150 self.transform_impl(dst, |x, y, z| lut.prism_vec3(x, y, z))?;
151 }
152 InterpolationMethod::Linear => {
153 self.transform_impl(dst, |x, y, z| lut.trilinear_vec3(x, y, z))?;
154 }
155 }
156 Ok(())
157 }
158}
159
160#[allow(unused)]
161struct ACurves3Inverse<'a, const DEPTH: usize> {
162 curve0: Box<[f32; 65536]>,
163 curve1: Box<[f32; 65536]>,
164 curve2: Box<[f32; 65536]>,
165 clut: &'a [f32],
166 grid_size: [u8; 3],
167 interpolation_method: InterpolationMethod,
168 pcs: DataColorSpace,
169}
170
171#[allow(unused)]
172impl<const DEPTH: usize> ACurves3Inverse<'_, DEPTH> {
173 fn transform_impl<Fetch: Fn(f32, f32, f32) -> Vector3f>(
174 &self,
175 dst: &mut [f32],
176 fetch: Fetch,
177 ) -> Result<(), CmsError> {
178 let scale_value = (DEPTH as u32 - 1u32) as f32;
179
180 for dst in dst.chunks_exact_mut(3) {
181 let interpolated = fetch(dst[0], dst[1], dst[2]);
182 let a0 = (interpolated.v[0] * scale_value).round().min(scale_value) as u16;
183 let a1 = (interpolated.v[1] * scale_value).round().min(scale_value) as u16;
184 let a2 = (interpolated.v[2] * scale_value).round().min(scale_value) as u16;
185 let b0 = self.curve0[a0 as usize];
186 let b1 = self.curve1[a1 as usize];
187 let b2 = self.curve2[a2 as usize];
188 dst[0] = b0;
189 dst[1] = b1;
190 dst[2] = b2;
191 }
192 Ok(())
193 }
194}
195
196impl<const DEPTH: usize> InPlaceStage for ACurves3Inverse<'_, DEPTH> {
197 fn transform(&self, dst: &mut [f32]) -> Result<(), CmsError> {
198 let lut = Cube::new_cube(self.clut, self.grid_size);
199
200 if self.pcs == DataColorSpace::Lab || self.pcs == DataColorSpace::Xyz {
202 return self.transform_impl(dst, |x, y, z| lut.trilinear_vec3(x, y, z));
203 }
204
205 match self.interpolation_method {
206 #[cfg(feature = "options")]
207 InterpolationMethod::Tetrahedral => {
208 self.transform_impl(dst, |x, y, z| lut.tetra_vec3(x, y, z))?;
209 }
210 #[cfg(feature = "options")]
211 InterpolationMethod::Pyramid => {
212 self.transform_impl(dst, |x, y, z| lut.pyramid_vec3(x, y, z))?;
213 }
214 #[cfg(feature = "options")]
215 InterpolationMethod::Prism => {
216 self.transform_impl(dst, |x, y, z| lut.prism_vec3(x, y, z))?;
217 }
218 InterpolationMethod::Linear => {
219 self.transform_impl(dst, |x, y, z| lut.trilinear_vec3(x, y, z))?;
220 }
221 }
222 Ok(())
223 }
224}
225
226pub(crate) struct MCurves3<const DEPTH: usize> {
227 pub(crate) curve0: Box<[f32; 65536]>,
228 pub(crate) curve1: Box<[f32; 65536]>,
229 pub(crate) curve2: Box<[f32; 65536]>,
230 pub(crate) matrix: Matrix3f,
231 pub(crate) bias: Vector3f,
232 pub(crate) inverse: bool,
233}
234
235impl<const DEPTH: usize> MCurves3<DEPTH> {
236 fn execute_matrix_stage(&self, dst: &mut [f32]) {
237 let m = self.matrix;
238 let b = self.bias;
239
240 if !m.test_equality(Matrix3f::IDENTITY) || !b.eq(&Vector3f::default()) {
241 for dst in dst.chunks_exact_mut(3) {
242 let x = dst[0];
243 let y = dst[1];
244 let z = dst[2];
245 dst[0] = mlaf(mlaf(mlaf(b.v[0], x, m.v[0][0]), y, m.v[0][1]), z, m.v[0][2]);
246 dst[1] = mlaf(mlaf(mlaf(b.v[1], x, m.v[1][0]), y, m.v[1][1]), z, m.v[1][2]);
247 dst[2] = mlaf(mlaf(mlaf(b.v[2], x, m.v[2][0]), y, m.v[2][1]), z, m.v[2][2]);
248 }
249 }
250 }
251}
252
253impl<const DEPTH: usize> InPlaceStage for MCurves3<DEPTH> {
254 fn transform(&self, dst: &mut [f32]) -> Result<(), CmsError> {
255 let scale_value = (DEPTH - 1) as f32;
256
257 if self.inverse {
258 self.execute_matrix_stage(dst);
259 }
260
261 for dst in dst.chunks_exact_mut(3) {
262 let a0 = (dst[0] * scale_value).round().min(scale_value) as u16;
263 let a1 = (dst[1] * scale_value).round().min(scale_value) as u16;
264 let a2 = (dst[2] * scale_value).round().min(scale_value) as u16;
265 let b0 = self.curve0[a0 as usize];
266 let b1 = self.curve1[a1 as usize];
267 let b2 = self.curve2[a2 as usize];
268 dst[0] = b0;
269 dst[1] = b1;
270 dst[2] = b2;
271 }
272
273 if !self.inverse {
274 self.execute_matrix_stage(dst);
275 }
276
277 Ok(())
278 }
279}
280
281pub(crate) struct BCurves3<const DEPTH: usize> {
282 pub(crate) curve0: Box<[f32; 65536]>,
283 pub(crate) curve1: Box<[f32; 65536]>,
284 pub(crate) curve2: Box<[f32; 65536]>,
285}
286
287impl<const DEPTH: usize> InPlaceStage for BCurves3<DEPTH> {
288 fn transform(&self, dst: &mut [f32]) -> Result<(), CmsError> {
289 let scale_value = (DEPTH - 1) as f32;
290
291 for dst in dst.chunks_exact_mut(3) {
292 let a0 = (dst[0] * scale_value).round().min(scale_value) as u16;
293 let a1 = (dst[1] * scale_value).round().min(scale_value) as u16;
294 let a2 = (dst[2] * scale_value).round().min(scale_value) as u16;
295 let b0 = self.curve0[a0 as usize];
296 let b1 = self.curve1[a1 as usize];
297 let b2 = self.curve2[a2 as usize];
298 dst[0] = b0;
299 dst[1] = b1;
300 dst[2] = b2;
301 }
302
303 Ok(())
304 }
305}
306
307pub(crate) fn prepare_mab_3x3(
308 mab: &LutMultidimensionalType,
309 lut: &mut [f32],
310 options: TransformOptions,
311 pcs: DataColorSpace,
312) -> Result<(), CmsError> {
313 const LERP_DEPTH: usize = 65536;
314 const BP: usize = 13;
315 const DEPTH: usize = 8192;
316
317 if mab.num_input_channels != 3 && mab.num_output_channels != 3 {
318 return Err(CmsError::UnsupportedProfileConnection);
319 }
320 if mab.a_curves.len() == 3 && mab.clut.is_some() {
321 let clut = &mab.clut.as_ref().map(|x| x.to_clut_f32()).unwrap();
322 let lut_grid = (mab.grid_points[0] as usize)
323 .safe_mul(mab.grid_points[1] as usize)?
324 .safe_mul(mab.grid_points[2] as usize)?
325 .safe_mul(mab.num_output_channels as usize)?;
326 if clut.len() != lut_grid {
327 return Err(CmsError::MalformedCurveLutTable(MalformedSize {
328 size: clut.len(),
329 expected: lut_grid,
330 }));
331 }
332
333 let all_curves_linear = mab.a_curves.iter().all(|curve| curve.is_linear());
334 let grid_size = [mab.grid_points[0], mab.grid_points[1], mab.grid_points[2]];
335
336 #[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
337 if all_curves_linear {
338 use crate::conversions::neon::ACurves3OptimizedNeon;
339 let a_curves = ACurves3OptimizedNeon {
340 clut,
341 grid_size,
342 interpolation_method: options.interpolation_method,
343 pcs,
344 };
345 a_curves.transform(lut)?;
346 } else {
347 use crate::conversions::neon::ACurves3Neon;
348 let curves: Result<Vec<_>, _> = mab
349 .a_curves
350 .iter()
351 .map(|c| {
352 c.build_linearize_table::<u16, LERP_DEPTH, BP>()
353 .ok_or(CmsError::InvalidTrcCurve)
354 })
355 .collect();
356
357 let [curve0, curve1, curve2] =
358 curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
359 let a_curves = ACurves3Neon::<DEPTH> {
360 curve0,
361 curve1,
362 curve2,
363 clut,
364 grid_size,
365 interpolation_method: options.interpolation_method,
366 pcs,
367 };
368 a_curves.transform(lut)?;
369 }
370
371 #[cfg(not(all(target_arch = "aarch64", target_feature = "neon", feature = "neon")))]
372 {
373 let mut execution_box: Option<Box<dyn InPlaceStage>> = None;
374
375 if all_curves_linear {
376 #[cfg(all(target_arch = "x86_64", feature = "avx"))]
377 {
378 use crate::conversions::avx::ACurves3OptimizedAvxFma;
379 if std::arch::is_x86_feature_detected!("avx2")
380 && std::arch::is_x86_feature_detected!("fma")
381 {
382 execution_box = Some(Box::new(ACurves3OptimizedAvxFma {
383 clut,
384 grid_size,
385 interpolation_method: options.interpolation_method,
386 pcs,
387 }));
388 }
389 }
390 if execution_box.is_none() {
391 execution_box = Some(Box::new(ACurves3Optimized {
392 clut,
393 grid_size,
394 interpolation_method: options.interpolation_method,
395 pcs,
396 }));
397 }
398 } else {
399 #[cfg(all(target_arch = "x86_64", feature = "avx"))]
400 {
401 use crate::conversions::avx::ACurves3AvxFma;
402 if std::arch::is_x86_feature_detected!("avx2")
403 && std::arch::is_x86_feature_detected!("fma")
404 {
405 let curves: Result<Vec<_>, _> = mab
406 .a_curves
407 .iter()
408 .map(|c| {
409 c.build_linearize_table::<u16, LERP_DEPTH, BP>()
410 .ok_or(CmsError::InvalidTrcCurve)
411 })
412 .collect();
413
414 let [curve0, curve1, curve2] =
415 curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
416 execution_box = Some(Box::new(ACurves3AvxFma::<DEPTH> {
417 curve0,
418 curve1,
419 curve2,
420 clut,
421 grid_size,
422 interpolation_method: options.interpolation_method,
423 pcs,
424 }));
425 }
426 }
427
428 if execution_box.is_none() {
429 let curves: Result<Vec<_>, _> = mab
430 .a_curves
431 .iter()
432 .map(|c| {
433 c.build_linearize_table::<u16, LERP_DEPTH, BP>()
434 .ok_or(CmsError::InvalidTrcCurve)
435 })
436 .collect();
437
438 let [curve0, curve1, curve2] =
439 curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
440 execution_box = Some(Box::new(ACurves3::<DEPTH> {
441 curve0,
442 curve1,
443 curve2,
444 clut,
445 grid_size,
446 interpolation_method: options.interpolation_method,
447 pcs,
448 }));
449 }
450 }
451
452 execution_box
453 .expect("LUT Sampler on Multidimensional 3x3 must be set")
454 .transform(lut)?;
455 }
456 }
457
458 if mab.m_curves.len() == 3 {
459 let all_curves_linear = mab.m_curves.iter().all(|curve| curve.is_linear());
460 if !all_curves_linear
461 || !mab.matrix.test_equality(Matrix3d::IDENTITY)
462 || mab.bias.ne(&Vector3d::default())
463 {
464 let curves: Result<Vec<_>, _> = mab
465 .m_curves
466 .iter()
467 .map(|c| {
468 c.build_linearize_table::<u16, LERP_DEPTH, BP>()
469 .ok_or(CmsError::InvalidTrcCurve)
470 })
471 .collect();
472
473 let [curve0, curve1, curve2] =
474 curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
475 let matrix = mab.matrix.to_f32();
476 let bias: Vector3f = mab.bias.cast();
477 let m_curves = MCurves3::<DEPTH> {
478 curve0,
479 curve1,
480 curve2,
481 matrix,
482 bias,
483 inverse: false,
484 };
485 m_curves.transform(lut)?;
486 }
487 }
488
489 if mab.b_curves.len() == 3 {
490 const LERP_DEPTH: usize = 65536;
491 const BP: usize = 13;
492 const DEPTH: usize = 8192;
493 let all_curves_linear = mab.b_curves.iter().all(|curve| curve.is_linear());
494 if !all_curves_linear {
495 let curves: Result<Vec<_>, _> = mab
496 .b_curves
497 .iter()
498 .map(|c| {
499 c.build_linearize_table::<u16, LERP_DEPTH, BP>()
500 .ok_or(CmsError::InvalidTrcCurve)
501 })
502 .collect();
503
504 let [curve0, curve1, curve2] =
505 curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
506
507 let b_curves = BCurves3::<DEPTH> {
508 curve0,
509 curve1,
510 curve2,
511 };
512 b_curves.transform(lut)?;
513 }
514 } else {
515 return Err(CmsError::InvalidAtoBLut);
516 }
517
518 Ok(())
519}
520
521pub(crate) fn prepare_mba_3x3(
522 mab: &LutMultidimensionalType,
523 lut: &mut [f32],
524 options: TransformOptions,
525 pcs: DataColorSpace,
526) -> Result<(), CmsError> {
527 if mab.num_input_channels != 3 && mab.num_output_channels != 3 {
528 return Err(CmsError::UnsupportedProfileConnection);
529 }
530 const LERP_DEPTH: usize = 65536;
531 const BP: usize = 13;
532 const DEPTH: usize = 8192;
533
534 if mab.b_curves.len() == 3 {
535 let all_curves_linear = mab.b_curves.iter().all(|curve| curve.is_linear());
536 if !all_curves_linear {
537 let curves: Result<Vec<_>, _> = mab
538 .b_curves
539 .iter()
540 .map(|c| {
541 c.build_linearize_table::<u16, LERP_DEPTH, BP>()
542 .ok_or(CmsError::InvalidTrcCurve)
543 })
544 .collect();
545
546 let [curve0, curve1, curve2] =
547 curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
548 let b_curves = BCurves3::<DEPTH> {
549 curve0,
550 curve1,
551 curve2,
552 };
553 b_curves.transform(lut)?;
554 }
555 } else {
556 return Err(CmsError::InvalidAtoBLut);
557 }
558
559 if mab.m_curves.len() == 3 {
560 let all_curves_linear = mab.m_curves.iter().all(|curve| curve.is_linear());
561 if !all_curves_linear
562 || !mab.matrix.test_equality(Matrix3d::IDENTITY)
563 || mab.bias.ne(&Vector3d::default())
564 {
565 let curves: Result<Vec<_>, _> = mab
566 .m_curves
567 .iter()
568 .map(|c| {
569 c.build_linearize_table::<u16, LERP_DEPTH, BP>()
570 .ok_or(CmsError::InvalidTrcCurve)
571 })
572 .collect();
573
574 let [curve0, curve1, curve2] =
575 curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
576
577 let matrix = mab.matrix.to_f32();
578 let bias: Vector3f = mab.bias.cast();
579 let m_curves = MCurves3::<DEPTH> {
580 curve0,
581 curve1,
582 curve2,
583 matrix,
584 bias,
585 inverse: true,
586 };
587 m_curves.transform(lut)?;
588 }
589 }
590
591 if mab.a_curves.len() == 3 && mab.clut.is_some() {
592 let clut = &mab.clut.as_ref().map(|x| x.to_clut_f32()).unwrap();
593 let lut_grid = (mab.grid_points[0] as usize)
594 .safe_mul(mab.grid_points[1] as usize)?
595 .safe_mul(mab.grid_points[2] as usize)?
596 .safe_mul(mab.num_output_channels as usize)?;
597 if clut.len() != lut_grid {
598 return Err(CmsError::MalformedCurveLutTable(MalformedSize {
599 size: clut.len(),
600 expected: lut_grid,
601 }));
602 }
603
604 let all_curves_linear = mab.a_curves.iter().all(|curve| curve.is_linear());
605 let grid_size = [mab.grid_points[0], mab.grid_points[1], mab.grid_points[2]];
606
607 #[cfg(all(target_arch = "aarch64", target_feature = "neon", feature = "neon"))]
608 if all_curves_linear {
609 use crate::conversions::neon::ACurves3OptimizedNeon;
610 let a_curves = ACurves3OptimizedNeon {
611 clut,
612 grid_size,
613 interpolation_method: options.interpolation_method,
614 pcs,
615 };
616 a_curves.transform(lut)?;
617 } else {
618 use crate::conversions::neon::ACurves3InverseNeon;
619 let curves: Result<Vec<_>, _> = mab
620 .a_curves
621 .iter()
622 .map(|c| {
623 c.build_linearize_table::<u16, LERP_DEPTH, BP>()
624 .ok_or(CmsError::InvalidTrcCurve)
625 })
626 .collect();
627
628 let [curve0, curve1, curve2] =
629 curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
630 let a_curves = ACurves3InverseNeon::<DEPTH> {
631 curve0,
632 curve1,
633 curve2,
634 clut,
635 grid_size,
636 interpolation_method: options.interpolation_method,
637 pcs,
638 };
639 a_curves.transform(lut)?;
640 }
641 #[cfg(not(all(target_arch = "aarch64", target_feature = "neon", feature = "neon")))]
642 {
643 let mut execution_box: Option<Box<dyn InPlaceStage>> = None;
644
645 if all_curves_linear {
646 #[cfg(all(target_arch = "x86_64", feature = "avx"))]
647 {
648 use crate::conversions::avx::ACurves3OptimizedAvxFma;
649 if std::arch::is_x86_feature_detected!("avx2")
650 && std::arch::is_x86_feature_detected!("fma")
651 {
652 execution_box = Some(Box::new(ACurves3OptimizedAvxFma {
653 clut,
654 grid_size,
655 interpolation_method: options.interpolation_method,
656 pcs,
657 }));
658 }
659 }
660
661 if execution_box.is_none() {
662 execution_box = Some(Box::new(ACurves3Optimized {
663 clut,
664 grid_size,
665 interpolation_method: options.interpolation_method,
666 pcs,
667 }));
668 }
669 } else {
670 #[cfg(all(target_arch = "x86_64", feature = "avx"))]
671 {
672 use crate::conversions::avx::ACurves3InverseAvxFma;
673 if std::arch::is_x86_feature_detected!("avx2")
674 && std::arch::is_x86_feature_detected!("fma")
675 {
676 let curves: Result<Vec<_>, _> = mab
677 .a_curves
678 .iter()
679 .map(|c| {
680 c.build_linearize_table::<u16, LERP_DEPTH, BP>()
681 .ok_or(CmsError::InvalidTrcCurve)
682 })
683 .collect();
684
685 let [curve0, curve1, curve2] =
686 curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
687 execution_box = Some(Box::new(ACurves3InverseAvxFma::<DEPTH> {
688 curve0,
689 curve1,
690 curve2,
691 clut,
692 grid_size,
693 interpolation_method: options.interpolation_method,
694 pcs,
695 }));
696 }
697 }
698
699 if execution_box.is_none() {
700 let curves: Result<Vec<_>, _> = mab
701 .a_curves
702 .iter()
703 .map(|c| {
704 c.build_linearize_table::<u16, LERP_DEPTH, BP>()
705 .ok_or(CmsError::InvalidTrcCurve)
706 })
707 .collect();
708
709 let [curve0, curve1, curve2] =
710 curves?.try_into().map_err(|_| CmsError::InvalidTrcCurve)?;
711 execution_box = Some(Box::new(ACurves3Inverse::<DEPTH> {
712 curve0,
713 curve1,
714 curve2,
715 clut,
716 grid_size,
717 interpolation_method: options.interpolation_method,
718 pcs,
719 }));
720 }
721 }
722
723 execution_box
724 .expect("LUT Sampler on Multidimensional Inverse 3x3 must be set")
725 .transform(lut)?;
726 }
727 }
728
729 Ok(())
730}