1 //  qcms
2 //  Copyright (C) 2009 Mozilla Corporation
3 //  Copyright (C) 1998-2007 Marti Maria
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the Software
10 // is furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
17 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 use crate::{
23     iccread::LAB_SIGNATURE,
24     iccread::RGB_SIGNATURE,
25     iccread::XYZ_SIGNATURE,
26     iccread::{lutType, lutmABType, Profile, CMYK_SIGNATURE},
27     matrix::Matrix,
28     s15Fixed16Number_to_float,
29     transform_util::clamp_float,
30     transform_util::{
31         build_colorant_matrix, build_input_gamma_table, build_output_lut, lut_interp_linear,
32         lut_interp_linear_float,
33     },
34 };
35 
36 trait ModularTransform {
transform(&self, src: &[f32], dst: &mut [f32])37     fn transform(&self, src: &[f32], dst: &mut [f32]);
38 }
39 
40 #[inline]
lerp(a: f32, b: f32, t: f32) -> f3241 fn lerp(a: f32, b: f32, t: f32) -> f32 {
42     a * (1.0 - t) + b * t
43 }
44 
build_lut_matrix(lut: &lutType) -> Matrix45 fn build_lut_matrix(lut: &lutType) -> Matrix {
46     let mut result: Matrix = Matrix { m: [[0.; 3]; 3] };
47     result.m[0][0] = s15Fixed16Number_to_float(lut.e00);
48     result.m[0][1] = s15Fixed16Number_to_float(lut.e01);
49     result.m[0][2] = s15Fixed16Number_to_float(lut.e02);
50     result.m[1][0] = s15Fixed16Number_to_float(lut.e10);
51     result.m[1][1] = s15Fixed16Number_to_float(lut.e11);
52     result.m[1][2] = s15Fixed16Number_to_float(lut.e12);
53     result.m[2][0] = s15Fixed16Number_to_float(lut.e20);
54     result.m[2][1] = s15Fixed16Number_to_float(lut.e21);
55     result.m[2][2] = s15Fixed16Number_to_float(lut.e22);
56     result
57 }
build_mAB_matrix(lut: &lutmABType) -> Matrix58 fn build_mAB_matrix(lut: &lutmABType) -> Matrix {
59     let mut result: Matrix = Matrix { m: [[0.; 3]; 3] };
60 
61     result.m[0][0] = s15Fixed16Number_to_float(lut.e00);
62     result.m[0][1] = s15Fixed16Number_to_float(lut.e01);
63     result.m[0][2] = s15Fixed16Number_to_float(lut.e02);
64     result.m[1][0] = s15Fixed16Number_to_float(lut.e10);
65     result.m[1][1] = s15Fixed16Number_to_float(lut.e11);
66     result.m[1][2] = s15Fixed16Number_to_float(lut.e12);
67     result.m[2][0] = s15Fixed16Number_to_float(lut.e20);
68     result.m[2][1] = s15Fixed16Number_to_float(lut.e21);
69     result.m[2][2] = s15Fixed16Number_to_float(lut.e22);
70 
71     result
72 }
73 //Based on lcms cmsLab2XYZ
f(t: f32) -> f3274 fn f(t: f32) -> f32 {
75     if t <= 24. / 116. * (24. / 116.) * (24. / 116.) {
76         (841. / 108. * t) + 16. / 116.
77     } else {
78         t.powf(1. / 3.)
79     }
80 }
f_1(t: f32) -> f3281 fn f_1(t: f32) -> f32 {
82     if t <= 24.0 / 116.0 {
83         (108.0 / 841.0) * (t - 16.0 / 116.0)
84     } else {
85         t * t * t
86     }
87 }
88 
89 #[allow(clippy::upper_case_acronyms)]
90 struct LABtoXYZ;
91 impl ModularTransform for LABtoXYZ {
transform(&self, src: &[f32], dest: &mut [f32])92     fn transform(&self, src: &[f32], dest: &mut [f32]) {
93         // lcms: D50 XYZ values
94         let WhitePointX: f32 = 0.9642;
95         let WhitePointY: f32 = 1.0;
96         let WhitePointZ: f32 = 0.8249;
97 
98         for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
99             let device_L: f32 = src[0] * 100.0;
100             let device_a: f32 = src[1] * 255.0 - 128.0;
101             let device_b: f32 = src[2] * 255.0 - 128.0;
102 
103             let y: f32 = (device_L + 16.0) / 116.0;
104 
105             let X = f_1(y + 0.002 * device_a) * WhitePointX;
106             let Y = f_1(y) * WhitePointY;
107             let Z = f_1(y - 0.005 * device_b) * WhitePointZ;
108 
109             dest[0] = (X as f64 / (1.0f64 + 32767.0f64 / 32768.0f64)) as f32;
110             dest[1] = (Y as f64 / (1.0f64 + 32767.0f64 / 32768.0f64)) as f32;
111             dest[2] = (Z as f64 / (1.0f64 + 32767.0f64 / 32768.0f64)) as f32;
112         }
113     }
114 }
115 
116 #[allow(clippy::upper_case_acronyms)]
117 struct XYZtoLAB;
118 impl ModularTransform for XYZtoLAB {
119     //Based on lcms cmsXYZ2Lab
transform(&self, src: &[f32], dest: &mut [f32])120     fn transform(&self, src: &[f32], dest: &mut [f32]) {
121         // lcms: D50 XYZ values
122         let WhitePointX: f32 = 0.9642;
123         let WhitePointY: f32 = 1.0;
124         let WhitePointZ: f32 = 0.8249;
125         for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
126             let device_x: f32 =
127                 (src[0] as f64 * (1.0f64 + 32767.0f64 / 32768.0f64) / WhitePointX as f64) as f32;
128             let device_y: f32 =
129                 (src[1] as f64 * (1.0f64 + 32767.0f64 / 32768.0f64) / WhitePointY as f64) as f32;
130             let device_z: f32 =
131                 (src[2] as f64 * (1.0f64 + 32767.0f64 / 32768.0f64) / WhitePointZ as f64) as f32;
132 
133             let fx = f(device_x);
134             let fy = f(device_y);
135             let fz = f(device_z);
136 
137             let L: f32 = 116.0 * fy - 16.0;
138             let a: f32 = 500.0 * (fx - fy);
139             let b: f32 = 200.0 * (fy - fz);
140 
141             dest[0] = L / 100.0;
142             dest[1] = (a + 128.0) / 255.0;
143             dest[2] = (b + 128.0) / 255.0;
144         }
145     }
146 }
147 #[derive(Default)]
148 struct ClutOnly {
149     clut: Option<Vec<f32>>,
150     grid_size: u16,
151 }
152 impl ModularTransform for ClutOnly {
transform(&self, src: &[f32], dest: &mut [f32])153     fn transform(&self, src: &[f32], dest: &mut [f32]) {
154         let xy_len: i32 = 1;
155         let x_len: i32 = self.grid_size as i32;
156         let len: i32 = x_len * x_len;
157 
158         let r_table = &self.clut.as_ref().unwrap()[0..];
159         let g_table = &self.clut.as_ref().unwrap()[1..];
160         let b_table = &self.clut.as_ref().unwrap()[2..];
161 
162         let CLU = |table: &[f32], x, y, z| table[((x * len + y * x_len + z * xy_len) * 3) as usize];
163 
164         for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
165             debug_assert!(self.grid_size as i32 >= 1);
166             let linear_r: f32 = src[0];
167             let linear_g: f32 = src[1];
168             let linear_b: f32 = src[2];
169             let x: i32 = (linear_r * (self.grid_size as i32 - 1) as f32).floor() as i32;
170             let y: i32 = (linear_g * (self.grid_size as i32 - 1) as f32).floor() as i32;
171             let z: i32 = (linear_b * (self.grid_size as i32 - 1) as f32).floor() as i32;
172             let x_n: i32 = (linear_r * (self.grid_size as i32 - 1) as f32).ceil() as i32;
173             let y_n: i32 = (linear_g * (self.grid_size as i32 - 1) as f32).ceil() as i32;
174             let z_n: i32 = (linear_b * (self.grid_size as i32 - 1) as f32).ceil() as i32;
175             let x_d: f32 = linear_r * (self.grid_size as i32 - 1) as f32 - x as f32;
176             let y_d: f32 = linear_g * (self.grid_size as i32 - 1) as f32 - y as f32;
177             let z_d: f32 = linear_b * (self.grid_size as i32 - 1) as f32 - z as f32;
178 
179             let r_x1: f32 = lerp(CLU(r_table, x, y, z), CLU(r_table, x_n, y, z), x_d);
180             let r_x2: f32 = lerp(CLU(r_table, x, y_n, z), CLU(r_table, x_n, y_n, z), x_d);
181             let r_y1: f32 = lerp(r_x1, r_x2, y_d);
182             let r_x3: f32 = lerp(CLU(r_table, x, y, z_n), CLU(r_table, x_n, y, z_n), x_d);
183             let r_x4: f32 = lerp(CLU(r_table, x, y_n, z_n), CLU(r_table, x_n, y_n, z_n), x_d);
184             let r_y2: f32 = lerp(r_x3, r_x4, y_d);
185             let clut_r: f32 = lerp(r_y1, r_y2, z_d);
186 
187             let g_x1: f32 = lerp(CLU(g_table, x, y, z), CLU(g_table, x_n, y, z), x_d);
188             let g_x2: f32 = lerp(CLU(g_table, x, y_n, z), CLU(g_table, x_n, y_n, z), x_d);
189             let g_y1: f32 = lerp(g_x1, g_x2, y_d);
190             let g_x3: f32 = lerp(CLU(g_table, x, y, z_n), CLU(g_table, x_n, y, z_n), x_d);
191             let g_x4: f32 = lerp(CLU(g_table, x, y_n, z_n), CLU(g_table, x_n, y_n, z_n), x_d);
192             let g_y2: f32 = lerp(g_x3, g_x4, y_d);
193             let clut_g: f32 = lerp(g_y1, g_y2, z_d);
194 
195             let b_x1: f32 = lerp(CLU(b_table, x, y, z), CLU(b_table, x_n, y, z), x_d);
196             let b_x2: f32 = lerp(CLU(b_table, x, y_n, z), CLU(b_table, x_n, y_n, z), x_d);
197             let b_y1: f32 = lerp(b_x1, b_x2, y_d);
198             let b_x3: f32 = lerp(CLU(b_table, x, y, z_n), CLU(b_table, x_n, y, z_n), x_d);
199             let b_x4: f32 = lerp(CLU(b_table, x, y_n, z_n), CLU(b_table, x_n, y_n, z_n), x_d);
200             let b_y2: f32 = lerp(b_x3, b_x4, y_d);
201             let clut_b: f32 = lerp(b_y1, b_y2, z_d);
202 
203             dest[0] = clamp_float(clut_r);
204             dest[1] = clamp_float(clut_g);
205             dest[2] = clamp_float(clut_b);
206         }
207     }
208 }
209 #[derive(Default)]
210 struct Clut3x3 {
211     input_clut_table: [Option<Vec<f32>>; 3],
212     clut: Option<Vec<f32>>,
213     grid_size: u16,
214     output_clut_table: [Option<Vec<f32>>; 3],
215 }
216 impl ModularTransform for Clut3x3 {
transform(&self, src: &[f32], dest: &mut [f32])217     fn transform(&self, src: &[f32], dest: &mut [f32]) {
218         let xy_len: i32 = 1;
219         let x_len: i32 = self.grid_size as i32;
220         let len: i32 = x_len * x_len;
221 
222         let r_table = &self.clut.as_ref().unwrap()[0..];
223         let g_table = &self.clut.as_ref().unwrap()[1..];
224         let b_table = &self.clut.as_ref().unwrap()[2..];
225         let CLU = |table: &[f32], x, y, z| table[((x * len + y * x_len + z * xy_len) * 3) as usize];
226 
227         let input_clut_table_r = self.input_clut_table[0].as_ref().unwrap();
228         let input_clut_table_g = self.input_clut_table[1].as_ref().unwrap();
229         let input_clut_table_b = self.input_clut_table[2].as_ref().unwrap();
230         for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
231             debug_assert!(self.grid_size as i32 >= 1);
232             let device_r: f32 = src[0];
233             let device_g: f32 = src[1];
234             let device_b: f32 = src[2];
235             let linear_r: f32 = lut_interp_linear_float(device_r, &input_clut_table_r);
236             let linear_g: f32 = lut_interp_linear_float(device_g, &input_clut_table_g);
237             let linear_b: f32 = lut_interp_linear_float(device_b, &input_clut_table_b);
238             let x: i32 = (linear_r * (self.grid_size as i32 - 1) as f32).floor() as i32;
239             let y: i32 = (linear_g * (self.grid_size as i32 - 1) as f32).floor() as i32;
240             let z: i32 = (linear_b * (self.grid_size as i32 - 1) as f32).floor() as i32;
241             let x_n: i32 = (linear_r * (self.grid_size as i32 - 1) as f32).ceil() as i32;
242             let y_n: i32 = (linear_g * (self.grid_size as i32 - 1) as f32).ceil() as i32;
243             let z_n: i32 = (linear_b * (self.grid_size as i32 - 1) as f32).ceil() as i32;
244             let x_d: f32 = linear_r * (self.grid_size as i32 - 1) as f32 - x as f32;
245             let y_d: f32 = linear_g * (self.grid_size as i32 - 1) as f32 - y as f32;
246             let z_d: f32 = linear_b * (self.grid_size as i32 - 1) as f32 - z as f32;
247 
248             let r_x1: f32 = lerp(CLU(r_table, x, y, z), CLU(r_table, x_n, y, z), x_d);
249             let r_x2: f32 = lerp(CLU(r_table, x, y_n, z), CLU(r_table, x_n, y_n, z), x_d);
250             let r_y1: f32 = lerp(r_x1, r_x2, y_d);
251             let r_x3: f32 = lerp(CLU(r_table, x, y, z_n), CLU(r_table, x_n, y, z_n), x_d);
252             let r_x4: f32 = lerp(CLU(r_table, x, y_n, z_n), CLU(r_table, x_n, y_n, z_n), x_d);
253             let r_y2: f32 = lerp(r_x3, r_x4, y_d);
254             let clut_r: f32 = lerp(r_y1, r_y2, z_d);
255 
256             let g_x1: f32 = lerp(CLU(g_table, x, y, z), CLU(g_table, x_n, y, z), x_d);
257             let g_x2: f32 = lerp(CLU(g_table, x, y_n, z), CLU(g_table, x_n, y_n, z), x_d);
258             let g_y1: f32 = lerp(g_x1, g_x2, y_d);
259             let g_x3: f32 = lerp(CLU(g_table, x, y, z_n), CLU(g_table, x_n, y, z_n), x_d);
260             let g_x4: f32 = lerp(CLU(g_table, x, y_n, z_n), CLU(g_table, x_n, y_n, z_n), x_d);
261             let g_y2: f32 = lerp(g_x3, g_x4, y_d);
262             let clut_g: f32 = lerp(g_y1, g_y2, z_d);
263 
264             let b_x1: f32 = lerp(CLU(b_table, x, y, z), CLU(b_table, x_n, y, z), x_d);
265             let b_x2: f32 = lerp(CLU(b_table, x, y_n, z), CLU(b_table, x_n, y_n, z), x_d);
266             let b_y1: f32 = lerp(b_x1, b_x2, y_d);
267             let b_x3: f32 = lerp(CLU(b_table, x, y, z_n), CLU(b_table, x_n, y, z_n), x_d);
268             let b_x4: f32 = lerp(CLU(b_table, x, y_n, z_n), CLU(b_table, x_n, y_n, z_n), x_d);
269             let b_y2: f32 = lerp(b_x3, b_x4, y_d);
270             let clut_b: f32 = lerp(b_y1, b_y2, z_d);
271             let pcs_r: f32 =
272                 lut_interp_linear_float(clut_r, &self.output_clut_table[0].as_ref().unwrap());
273             let pcs_g: f32 =
274                 lut_interp_linear_float(clut_g, &self.output_clut_table[1].as_ref().unwrap());
275             let pcs_b: f32 =
276                 lut_interp_linear_float(clut_b, &self.output_clut_table[2].as_ref().unwrap());
277             dest[0] = clamp_float(pcs_r);
278             dest[1] = clamp_float(pcs_g);
279             dest[2] = clamp_float(pcs_b);
280         }
281     }
282 }
283 #[derive(Default)]
284 struct Clut4x3 {
285     input_clut_table: [Option<Vec<f32>>; 4],
286     clut: Option<Vec<f32>>,
287     grid_size: u16,
288     output_clut_table: [Option<Vec<f32>>; 3],
289 }
290 impl ModularTransform for Clut4x3 {
transform(&self, src: &[f32], dest: &mut [f32])291     fn transform(&self, src: &[f32], dest: &mut [f32]) {
292         let z_stride: i32 = self.grid_size as i32;
293         let y_stride: i32 = z_stride * z_stride;
294         let x_stride: i32 = z_stride * z_stride * z_stride;
295 
296         let r_tbl = &self.clut.as_ref().unwrap()[0..];
297         let g_tbl = &self.clut.as_ref().unwrap()[1..];
298         let b_tbl = &self.clut.as_ref().unwrap()[2..];
299 
300         let CLU = |table: &[f32], x, y, z, w| {
301             table[((x * x_stride + y * y_stride + z * z_stride + w) * 3) as usize]
302         };
303 
304         let input_clut_table_0 = self.input_clut_table[0].as_ref().unwrap();
305         let input_clut_table_1 = self.input_clut_table[1].as_ref().unwrap();
306         let input_clut_table_2 = self.input_clut_table[2].as_ref().unwrap();
307         let input_clut_table_3 = self.input_clut_table[3].as_ref().unwrap();
308         for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(4)) {
309             debug_assert!(self.grid_size as i32 >= 1);
310             let linear_x: f32 = lut_interp_linear_float(src[0], &input_clut_table_0);
311             let linear_y: f32 = lut_interp_linear_float(src[1], &input_clut_table_1);
312             let linear_z: f32 = lut_interp_linear_float(src[2], &input_clut_table_2);
313             let linear_w: f32 = lut_interp_linear_float(src[3], &input_clut_table_3);
314 
315             let x: i32 = (linear_x * (self.grid_size as i32 - 1) as f32).floor() as i32;
316             let y: i32 = (linear_y * (self.grid_size as i32 - 1) as f32).floor() as i32;
317             let z: i32 = (linear_z * (self.grid_size as i32 - 1) as f32).floor() as i32;
318             let w: i32 = (linear_w * (self.grid_size as i32 - 1) as f32).floor() as i32;
319 
320             let x_n: i32 = (linear_x * (self.grid_size as i32 - 1) as f32).ceil() as i32;
321             let y_n: i32 = (linear_y * (self.grid_size as i32 - 1) as f32).ceil() as i32;
322             let z_n: i32 = (linear_z * (self.grid_size as i32 - 1) as f32).ceil() as i32;
323             let w_n: i32 = (linear_w * (self.grid_size as i32 - 1) as f32).ceil() as i32;
324 
325             let x_d: f32 = linear_x * (self.grid_size as i32 - 1) as f32 - x as f32;
326             let y_d: f32 = linear_y * (self.grid_size as i32 - 1) as f32 - y as f32;
327             let z_d: f32 = linear_z * (self.grid_size as i32 - 1) as f32 - z as f32;
328             let w_d: f32 = linear_w * (self.grid_size as i32 - 1) as f32 - w as f32;
329 
330             let quadlinear = |tbl| {
331                 let CLU = |x, y, z, w| CLU(tbl, x, y, z, w);
332                 let r_x1 = lerp(CLU(x, y, z, w), CLU(x_n, y, z, w), x_d);
333                 let r_x2 = lerp(CLU(x, y_n, z, w), CLU(x_n, y_n, z, w), x_d);
334                 let r_y1 = lerp(r_x1, r_x2, y_d);
335                 let r_x3 = lerp(CLU(x, y, z_n, w), CLU(x_n, y, z_n, w), x_d);
336                 let r_x4 = lerp(CLU(x, y_n, z_n, w), CLU(x_n, y_n, z_n, w), x_d);
337                 let r_y2 = lerp(r_x3, r_x4, y_d);
338                 let r_z1 = lerp(r_y1, r_y2, z_d);
339 
340                 let r_x1 = lerp(CLU(x, y, z, w_n), CLU(x_n, y, z, w_n), x_d);
341                 let r_x2 = lerp(CLU(x, y_n, z, w_n), CLU(x_n, y_n, z, w_n), x_d);
342                 let r_y1 = lerp(r_x1, r_x2, y_d);
343                 let r_x3 = lerp(CLU(x, y, z_n, w_n), CLU(x_n, y, z_n, w_n), x_d);
344                 let r_x4 = lerp(CLU(x, y_n, z_n, w_n), CLU(x_n, y_n, z_n, w_n), x_d);
345                 let r_y2 = lerp(r_x3, r_x4, y_d);
346                 let r_z2 = lerp(r_y1, r_y2, z_d);
347                 lerp(r_z1, r_z2, w_d)
348             };
349             // TODO: instead of reading each component separately we should read all three components at once.
350             let clut_r = quadlinear(r_tbl);
351             let clut_g = quadlinear(g_tbl);
352             let clut_b = quadlinear(b_tbl);
353 
354             let pcs_r =
355                 lut_interp_linear_float(clut_r, &self.output_clut_table[0].as_ref().unwrap());
356             let pcs_g =
357                 lut_interp_linear_float(clut_g, &self.output_clut_table[1].as_ref().unwrap());
358             let pcs_b =
359                 lut_interp_linear_float(clut_b, &self.output_clut_table[2].as_ref().unwrap());
360             dest[0] = clamp_float(pcs_r);
361             dest[1] = clamp_float(pcs_g);
362             dest[2] = clamp_float(pcs_b);
363         }
364     }
365 }
366 /* NOT USED
367 static void qcms_transform_module_tetra_clut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
368 {
369     size_t i;
370     int xy_len = 1;
371     int x_len = transform->grid_size;
372     int len = x_len * x_len;
373     float* r_table = transform->r_clut;
374     float* g_table = transform->g_clut;
375     float* b_table = transform->b_clut;
376     float c0_r, c1_r, c2_r, c3_r;
377     float c0_g, c1_g, c2_g, c3_g;
378     float c0_b, c1_b, c2_b, c3_b;
379     float clut_r, clut_g, clut_b;
380     float pcs_r, pcs_g, pcs_b;
381     for (i = 0; i < length; i++) {
382         float device_r = *src++;
383         float device_g = *src++;
384         float device_b = *src++;
385         float linear_r = lut_interp_linear_float(device_r,
386                 transform->input_clut_table_r, transform->input_clut_table_length);
387         float linear_g = lut_interp_linear_float(device_g,
388                 transform->input_clut_table_g, transform->input_clut_table_length);
389         float linear_b = lut_interp_linear_float(device_b,
390                 transform->input_clut_table_b, transform->input_clut_table_length);
391 
392         int x = floorf(linear_r * (transform->grid_size-1));
393         int y = floorf(linear_g * (transform->grid_size-1));
394         int z = floorf(linear_b * (transform->grid_size-1));
395         int x_n = ceilf(linear_r * (transform->grid_size-1));
396         int y_n = ceilf(linear_g * (transform->grid_size-1));
397         int z_n = ceilf(linear_b * (transform->grid_size-1));
398         float rx = linear_r * (transform->grid_size-1) - x;
399         float ry = linear_g * (transform->grid_size-1) - y;
400         float rz = linear_b * (transform->grid_size-1) - z;
401 
402         c0_r = CLU(r_table, x, y, z);
403         c0_g = CLU(g_table, x, y, z);
404         c0_b = CLU(b_table, x, y, z);
405         if( rx >= ry ) {
406             if (ry >= rz) { //rx >= ry && ry >= rz
407                 c1_r = CLU(r_table, x_n, y, z) - c0_r;
408                 c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x_n, y, z);
409                 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
410                 c1_g = CLU(g_table, x_n, y, z) - c0_g;
411                 c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x_n, y, z);
412                 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
413                 c1_b = CLU(b_table, x_n, y, z) - c0_b;
414                 c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x_n, y, z);
415                 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
416             } else {
417                 if (rx >= rz) { //rx >= rz && rz >= ry
418                     c1_r = CLU(r_table, x_n, y, z) - c0_r;
419                     c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
420                     c3_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x_n, y, z);
421                     c1_g = CLU(g_table, x_n, y, z) - c0_g;
422                     c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
423                     c3_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x_n, y, z);
424                     c1_b = CLU(b_table, x_n, y, z) - c0_b;
425                     c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
426                     c3_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x_n, y, z);
427                 } else { //rz > rx && rx >= ry
428                     c1_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x, y, z_n);
429                     c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
430                     c3_r = CLU(r_table, x, y, z_n) - c0_r;
431                     c1_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x, y, z_n);
432                     c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
433                     c3_g = CLU(g_table, x, y, z_n) - c0_g;
434                     c1_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x, y, z_n);
435                     c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
436                     c3_b = CLU(b_table, x, y, z_n) - c0_b;
437                 }
438             }
439         } else {
440             if (rx >= rz) { //ry > rx && rx >= rz
441                 c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x, y_n, z);
442                 c2_r = CLU(r_table, x_n, y_n, z) - c0_r;
443                 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
444                 c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x, y_n, z);
445                 c2_g = CLU(g_table, x_n, y_n, z) - c0_g;
446                 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
447                 c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x, y_n, z);
448                 c2_b = CLU(b_table, x_n, y_n, z) - c0_b;
449                 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
450             } else {
451                 if (ry >= rz) { //ry >= rz && rz > rx
452                     c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
453                     c2_r = CLU(r_table, x, y_n, z) - c0_r;
454                     c3_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y_n, z);
455                     c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
456                     c2_g = CLU(g_table, x, y_n, z) - c0_g;
457                     c3_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y_n, z);
458                     c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
459                     c2_b = CLU(b_table, x, y_n, z) - c0_b;
460                     c3_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y_n, z);
461                 } else { //rz > ry && ry > rx
462                     c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
463                     c2_r = CLU(r_table, x, y_n, z) - c0_r;
464                     c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
465                     c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
466                     c2_g = CLU(g_table, x, y_n, z) - c0_g;
467                     c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
468                     c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
469                     c2_b = CLU(b_table, x, y_n, z) - c0_b;
470                     c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
471                 }
472             }
473         }
474 
475         clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz;
476         clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz;
477         clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz;
478 
479         pcs_r = lut_interp_linear_float(clut_r,
480                 transform->output_clut_table_r, transform->output_clut_table_length);
481         pcs_g = lut_interp_linear_float(clut_g,
482                 transform->output_clut_table_g, transform->output_clut_table_length);
483         pcs_b = lut_interp_linear_float(clut_b,
484                 transform->output_clut_table_b, transform->output_clut_table_length);
485         *dest++ = clamp_float(pcs_r);
486         *dest++ = clamp_float(pcs_g);
487         *dest++ = clamp_float(pcs_b);
488     }
489 }
490 */
491 #[derive(Default)]
492 struct GammaTable {
493     input_clut_table: [Option<Vec<f32>>; 3],
494 }
495 impl ModularTransform for GammaTable {
transform(&self, src: &[f32], dest: &mut [f32])496     fn transform(&self, src: &[f32], dest: &mut [f32]) {
497         let mut out_r: f32;
498         let mut out_g: f32;
499         let mut out_b: f32;
500         let input_clut_table_r = self.input_clut_table[0].as_ref().unwrap();
501         let input_clut_table_g = self.input_clut_table[1].as_ref().unwrap();
502         let input_clut_table_b = self.input_clut_table[2].as_ref().unwrap();
503 
504         for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
505             let in_r: f32 = src[0];
506             let in_g: f32 = src[1];
507             let in_b: f32 = src[2];
508             out_r = lut_interp_linear_float(in_r, input_clut_table_r);
509             out_g = lut_interp_linear_float(in_g, input_clut_table_g);
510             out_b = lut_interp_linear_float(in_b, input_clut_table_b);
511 
512             dest[0] = clamp_float(out_r);
513             dest[1] = clamp_float(out_g);
514             dest[2] = clamp_float(out_b);
515         }
516     }
517 }
518 #[derive(Default)]
519 struct GammaLut {
520     output_gamma_lut_r: Option<Vec<u16>>,
521     output_gamma_lut_g: Option<Vec<u16>>,
522     output_gamma_lut_b: Option<Vec<u16>>,
523 }
524 impl ModularTransform for GammaLut {
transform(&self, src: &[f32], dest: &mut [f32])525     fn transform(&self, src: &[f32], dest: &mut [f32]) {
526         let mut out_r: f32;
527         let mut out_g: f32;
528         let mut out_b: f32;
529         for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
530             let in_r: f32 = src[0];
531             let in_g: f32 = src[1];
532             let in_b: f32 = src[2];
533             out_r = lut_interp_linear(in_r as f64, &self.output_gamma_lut_r.as_ref().unwrap());
534             out_g = lut_interp_linear(in_g as f64, &self.output_gamma_lut_g.as_ref().unwrap());
535             out_b = lut_interp_linear(in_b as f64, &self.output_gamma_lut_b.as_ref().unwrap());
536             dest[0] = clamp_float(out_r);
537             dest[1] = clamp_float(out_g);
538             dest[2] = clamp_float(out_b);
539         }
540     }
541 }
542 #[derive(Default)]
543 struct MatrixTranslate {
544     matrix: Matrix,
545     tx: f32,
546     ty: f32,
547     tz: f32,
548 }
549 impl ModularTransform for MatrixTranslate {
transform(&self, src: &[f32], dest: &mut [f32])550     fn transform(&self, src: &[f32], dest: &mut [f32]) {
551         let mut mat: Matrix = Matrix { m: [[0.; 3]; 3] };
552         /* store the results in column major mode
553          * this makes doing the multiplication with sse easier */
554         mat.m[0][0] = self.matrix.m[0][0];
555         mat.m[1][0] = self.matrix.m[0][1];
556         mat.m[2][0] = self.matrix.m[0][2];
557         mat.m[0][1] = self.matrix.m[1][0];
558         mat.m[1][1] = self.matrix.m[1][1];
559         mat.m[2][1] = self.matrix.m[1][2];
560         mat.m[0][2] = self.matrix.m[2][0];
561         mat.m[1][2] = self.matrix.m[2][1];
562         mat.m[2][2] = self.matrix.m[2][2];
563         for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
564             let in_r: f32 = src[0];
565             let in_g: f32 = src[1];
566             let in_b: f32 = src[2];
567             let out_r: f32 = mat.m[0][0] * in_r + mat.m[1][0] * in_g + mat.m[2][0] * in_b + self.tx;
568             let out_g: f32 = mat.m[0][1] * in_r + mat.m[1][1] * in_g + mat.m[2][1] * in_b + self.ty;
569             let out_b: f32 = mat.m[0][2] * in_r + mat.m[1][2] * in_g + mat.m[2][2] * in_b + self.tz;
570             dest[0] = clamp_float(out_r);
571             dest[1] = clamp_float(out_g);
572             dest[2] = clamp_float(out_b);
573         }
574     }
575 }
576 #[derive(Default)]
577 struct MatrixTransform {
578     matrix: Matrix,
579 }
580 impl ModularTransform for MatrixTransform {
transform(&self, src: &[f32], dest: &mut [f32])581     fn transform(&self, src: &[f32], dest: &mut [f32]) {
582         let mut mat: Matrix = Matrix { m: [[0.; 3]; 3] };
583         /* store the results in column major mode
584          * this makes doing the multiplication with sse easier */
585         mat.m[0][0] = self.matrix.m[0][0];
586         mat.m[1][0] = self.matrix.m[0][1];
587         mat.m[2][0] = self.matrix.m[0][2];
588         mat.m[0][1] = self.matrix.m[1][0];
589         mat.m[1][1] = self.matrix.m[1][1];
590         mat.m[2][1] = self.matrix.m[1][2];
591         mat.m[0][2] = self.matrix.m[2][0];
592         mat.m[1][2] = self.matrix.m[2][1];
593         mat.m[2][2] = self.matrix.m[2][2];
594         for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
595             let in_r: f32 = src[0];
596             let in_g: f32 = src[1];
597             let in_b: f32 = src[2];
598             let out_r: f32 = mat.m[0][0] * in_r + mat.m[1][0] * in_g + mat.m[2][0] * in_b;
599             let out_g: f32 = mat.m[0][1] * in_r + mat.m[1][1] * in_g + mat.m[2][1] * in_b;
600             let out_b: f32 = mat.m[0][2] * in_r + mat.m[1][2] * in_g + mat.m[2][2] * in_b;
601             dest[0] = clamp_float(out_r);
602             dest[1] = clamp_float(out_g);
603             dest[2] = clamp_float(out_b);
604         }
605     }
606 }
607 
modular_transform_create_mAB(lut: &lutmABType) -> Option<Vec<Box<dyn ModularTransform>>>608 fn modular_transform_create_mAB(lut: &lutmABType) -> Option<Vec<Box<dyn ModularTransform>>> {
609     let mut transforms: Vec<Box<dyn ModularTransform>> = Vec::new();
610     if lut.a_curves[0].is_some() {
611         let clut_length: usize;
612         // If the A curve is present this also implies the
613         // presence of a CLUT.
614         lut.clut_table.as_ref()?;
615 
616         // Prepare A curve.
617         let mut transform = Box::new(GammaTable::default());
618         transform.input_clut_table[0] = build_input_gamma_table(lut.a_curves[0].as_deref())
619             .map(|x| (x as Box<[f32]>).into_vec());
620         transform.input_clut_table[1] = build_input_gamma_table(lut.a_curves[1].as_deref())
621             .map(|x| (x as Box<[f32]>).into_vec());
622         transform.input_clut_table[2] = build_input_gamma_table(lut.a_curves[2].as_deref())
623             .map(|x| (x as Box<[f32]>).into_vec());
624 
625         if lut.num_grid_points[0] as i32 != lut.num_grid_points[1] as i32
626             || lut.num_grid_points[1] as i32 != lut.num_grid_points[2] as i32
627         {
628             //XXX: We don't currently support clut that are not squared!
629             return None;
630         }
631         transforms.push(transform);
632 
633         // Prepare CLUT
634         let mut transform = Box::new(ClutOnly::default());
635         clut_length = (lut.num_grid_points[0] as usize).pow(3) * 3;
636         assert_eq!(clut_length, lut.clut_table.as_ref().unwrap().len());
637         transform.clut = lut.clut_table.clone();
638         transform.grid_size = lut.num_grid_points[0] as u16;
639         transforms.push(transform);
640     }
641 
642     if lut.m_curves[0].is_some() {
643         // M curve imples the presence of a Matrix
644 
645         // Prepare M curve
646         let mut transform = Box::new(GammaTable::default());
647         transform.input_clut_table[0] = build_input_gamma_table(lut.m_curves[0].as_deref())
648             .map(|x| (x as Box<[f32]>).into_vec());
649         transform.input_clut_table[1] = build_input_gamma_table(lut.m_curves[1].as_deref())
650             .map(|x| (x as Box<[f32]>).into_vec());
651         transform.input_clut_table[2] = build_input_gamma_table(lut.m_curves[2].as_deref())
652             .map(|x| (x as Box<[f32]>).into_vec());
653         transforms.push(transform);
654 
655         // Prepare Matrix
656         let mut transform = Box::new(MatrixTranslate::default());
657         transform.matrix = build_mAB_matrix(lut);
658         transform.tx = s15Fixed16Number_to_float(lut.e03);
659         transform.ty = s15Fixed16Number_to_float(lut.e13);
660         transform.tz = s15Fixed16Number_to_float(lut.e23);
661         transforms.push(transform);
662     }
663 
664     if lut.b_curves[0].is_some() {
665         // Prepare B curve
666         let mut transform = Box::new(GammaTable::default());
667         transform.input_clut_table[0] = build_input_gamma_table(lut.b_curves[0].as_deref())
668             .map(|x| (x as Box<[f32]>).into_vec());
669         transform.input_clut_table[1] = build_input_gamma_table(lut.b_curves[1].as_deref())
670             .map(|x| (x as Box<[f32]>).into_vec());
671         transform.input_clut_table[2] = build_input_gamma_table(lut.b_curves[2].as_deref())
672             .map(|x| (x as Box<[f32]>).into_vec());
673         transforms.push(transform);
674     } else {
675         // B curve is mandatory
676         return None;
677     }
678 
679     if lut.reversed {
680         // mBA are identical to mAB except that the transformation order
681         // is reversed
682         transforms.reverse();
683     }
684     Some(transforms)
685 }
686 
modular_transform_create_lut(lut: &lutType) -> Option<Vec<Box<dyn ModularTransform>>>687 fn modular_transform_create_lut(lut: &lutType) -> Option<Vec<Box<dyn ModularTransform>>> {
688     let mut transforms: Vec<Box<dyn ModularTransform>> = Vec::new();
689 
690     let clut_length: usize;
691     let mut transform = Box::new(MatrixTransform::default());
692 
693     transform.matrix = build_lut_matrix(lut);
694     if true {
695         transforms.push(transform);
696 
697         // Prepare input curves
698         let mut transform = Box::new(Clut3x3::default());
699         transform.input_clut_table[0] =
700             Some(lut.input_table[0..lut.num_input_table_entries as usize].to_vec());
701         transform.input_clut_table[1] = Some(
702             lut.input_table
703                 [lut.num_input_table_entries as usize..lut.num_input_table_entries as usize * 2]
704                 .to_vec(),
705         );
706         transform.input_clut_table[2] = Some(
707             lut.input_table[lut.num_input_table_entries as usize * 2
708                 ..lut.num_input_table_entries as usize * 3]
709                 .to_vec(),
710         );
711         // Prepare table
712         clut_length = (lut.num_clut_grid_points as usize).pow(3) * 3;
713         assert_eq!(clut_length, lut.clut_table.len());
714         transform.clut = Some(lut.clut_table.clone());
715 
716         transform.grid_size = lut.num_clut_grid_points as u16;
717         // Prepare output curves
718         transform.output_clut_table[0] =
719             Some(lut.output_table[0..lut.num_output_table_entries as usize].to_vec());
720         transform.output_clut_table[1] = Some(
721             lut.output_table
722                 [lut.num_output_table_entries as usize..lut.num_output_table_entries as usize * 2]
723                 .to_vec(),
724         );
725         transform.output_clut_table[2] = Some(
726             lut.output_table[lut.num_output_table_entries as usize * 2
727                 ..lut.num_output_table_entries as usize * 3]
728                 .to_vec(),
729         );
730         transforms.push(transform);
731         return Some(transforms);
732     }
733     None
734 }
735 
modular_transform_create_lut4x3(lut: &lutType) -> Vec<Box<dyn ModularTransform>>736 fn modular_transform_create_lut4x3(lut: &lutType) -> Vec<Box<dyn ModularTransform>> {
737     let mut transforms: Vec<Box<dyn ModularTransform>> = Vec::new();
738 
739     let clut_length: usize;
740     // the matrix of lutType is only used when the input color space is XYZ.
741 
742     // Prepare input curves
743     let mut transform = Box::new(Clut4x3::default());
744     transform.input_clut_table[0] =
745         Some(lut.input_table[0..lut.num_input_table_entries as usize].to_vec());
746     transform.input_clut_table[1] = Some(
747         lut.input_table
748             [lut.num_input_table_entries as usize..lut.num_input_table_entries as usize * 2]
749             .to_vec(),
750     );
751     transform.input_clut_table[2] = Some(
752         lut.input_table
753             [lut.num_input_table_entries as usize * 2..lut.num_input_table_entries as usize * 3]
754             .to_vec(),
755     );
756     transform.input_clut_table[3] = Some(
757         lut.input_table
758             [lut.num_input_table_entries as usize * 3..lut.num_input_table_entries as usize * 4]
759             .to_vec(),
760     );
761     // Prepare table
762     clut_length = (lut.num_clut_grid_points as usize).pow(lut.num_input_channels as u32)
763         * lut.num_output_channels as usize;
764     assert_eq!(clut_length, lut.clut_table.len());
765     transform.clut = Some(lut.clut_table.clone());
766 
767     transform.grid_size = lut.num_clut_grid_points as u16;
768     // Prepare output curves
769     transform.output_clut_table[0] =
770         Some(lut.output_table[0..lut.num_output_table_entries as usize].to_vec());
771     transform.output_clut_table[1] = Some(
772         lut.output_table
773             [lut.num_output_table_entries as usize..lut.num_output_table_entries as usize * 2]
774             .to_vec(),
775     );
776     transform.output_clut_table[2] = Some(
777         lut.output_table
778             [lut.num_output_table_entries as usize * 2..lut.num_output_table_entries as usize * 3]
779             .to_vec(),
780     );
781     transforms.push(transform);
782     transforms
783 }
784 
modular_transform_create_input(input: &Profile) -> Option<Vec<Box<dyn ModularTransform>>>785 fn modular_transform_create_input(input: &Profile) -> Option<Vec<Box<dyn ModularTransform>>> {
786     let mut transforms = Vec::new();
787     if let Some(A2B0) = &input.A2B0 {
788         let lut_transform;
789         if A2B0.num_input_channels == 4 {
790             lut_transform = Some(modular_transform_create_lut4x3(&A2B0));
791         } else {
792             lut_transform = modular_transform_create_lut(&A2B0);
793         }
794         if let Some(lut_transform) = lut_transform {
795             transforms.extend(lut_transform);
796         } else {
797             return None;
798         }
799     } else if input.mAB.is_some()
800         && (*input.mAB.as_deref().unwrap()).num_in_channels == 3
801         && (*input.mAB.as_deref().unwrap()).num_out_channels == 3
802     {
803         let mAB_transform = modular_transform_create_mAB(input.mAB.as_deref().unwrap());
804         if let Some(mAB_transform) = mAB_transform {
805             transforms.extend(mAB_transform);
806         } else {
807             return None;
808         }
809     } else {
810         let mut transform = Box::new(GammaTable::default());
811         transform.input_clut_table[0] =
812             build_input_gamma_table(input.redTRC.as_deref()).map(|x| (x as Box<[f32]>).into_vec());
813         transform.input_clut_table[1] = build_input_gamma_table(input.greenTRC.as_deref())
814             .map(|x| (x as Box<[f32]>).into_vec());
815         transform.input_clut_table[2] =
816             build_input_gamma_table(input.blueTRC.as_deref()).map(|x| (x as Box<[f32]>).into_vec());
817         if transform.input_clut_table[0].is_none()
818             || transform.input_clut_table[1].is_none()
819             || transform.input_clut_table[2].is_none()
820         {
821             return None;
822         } else {
823             transforms.push(transform);
824 
825             let mut transform = Box::new(MatrixTransform::default());
826             transform.matrix.m[0][0] = 1. / 1.999_969_5;
827             transform.matrix.m[0][1] = 0.0;
828             transform.matrix.m[0][2] = 0.0;
829             transform.matrix.m[1][0] = 0.0;
830             transform.matrix.m[1][1] = 1. / 1.999_969_5;
831             transform.matrix.m[1][2] = 0.0;
832             transform.matrix.m[2][0] = 0.0;
833             transform.matrix.m[2][1] = 0.0;
834             transform.matrix.m[2][2] = 1. / 1.999_969_5;
835             transforms.push(transform);
836 
837             let mut transform = Box::new(MatrixTransform::default());
838             transform.matrix = build_colorant_matrix(input);
839             transforms.push(transform);
840         }
841     }
842     Some(transforms)
843 }
modular_transform_create_output(out: &Profile) -> Option<Vec<Box<dyn ModularTransform>>>844 fn modular_transform_create_output(out: &Profile) -> Option<Vec<Box<dyn ModularTransform>>> {
845     let mut transforms = Vec::new();
846     if let Some(B2A0) = &out.B2A0 {
847         if B2A0.num_input_channels != 3 || B2A0.num_output_channels != 3 {
848             return None;
849         }
850         let lut_transform = modular_transform_create_lut(B2A0);
851         if let Some(lut_transform) = lut_transform {
852             transforms.extend(lut_transform);
853         } else {
854             return None;
855         }
856     } else if out.mBA.is_some()
857         && (*out.mBA.as_deref().unwrap()).num_in_channels == 3
858         && (*out.mBA.as_deref().unwrap()).num_out_channels == 3
859     {
860         let lut_transform = modular_transform_create_mAB(out.mBA.as_deref().unwrap());
861         if let Some(lut_transform) = lut_transform {
862             transforms.extend(lut_transform)
863         } else {
864             return None;
865         }
866     } else if let (Some(redTRC), Some(greenTRC), Some(blueTRC)) =
867         (&out.redTRC, &out.greenTRC, &out.blueTRC)
868     {
869         let mut transform = Box::new(MatrixTransform::default());
870         transform.matrix = build_colorant_matrix(out).invert()?;
871         transforms.push(transform);
872 
873         let mut transform = Box::new(MatrixTransform::default());
874         transform.matrix.m[0][0] = 1.999_969_5;
875         transform.matrix.m[0][1] = 0.0;
876         transform.matrix.m[0][2] = 0.0;
877         transform.matrix.m[1][0] = 0.0;
878         transform.matrix.m[1][1] = 1.999_969_5;
879         transform.matrix.m[1][2] = 0.0;
880         transform.matrix.m[2][0] = 0.0;
881         transform.matrix.m[2][1] = 0.0;
882         transform.matrix.m[2][2] = 1.999_969_5;
883         transforms.push(transform);
884 
885         let mut transform = Box::new(GammaLut::default());
886         transform.output_gamma_lut_r = Some(build_output_lut(redTRC)?);
887         transform.output_gamma_lut_g = Some(build_output_lut(greenTRC)?);
888         transform.output_gamma_lut_b = Some(build_output_lut(blueTRC)?);
889         transforms.push(transform);
890     } else {
891         debug_assert!(false, "Unsupported output profile workflow.");
892         return None;
893     }
894     Some(transforms)
895 }
896 /* Not Completed
897 // Simplify the transformation chain to an equivalent transformation chain
898 static struct qcms_modular_transform* qcms_modular_transform_reduce(struct qcms_modular_transform *transform)
899 {
900     struct qcms_modular_transform *first_transform = NULL;
901     struct qcms_modular_transform *curr_trans = transform;
902     struct qcms_modular_transform *prev_trans = NULL;
903     while (curr_trans) {
904         struct qcms_modular_transform *next_trans = curr_trans->next_transform;
905         if (curr_trans->transform_module_fn == qcms_transform_module_matrix) {
906             if (next_trans && next_trans->transform_module_fn == qcms_transform_module_matrix) {
907                 curr_trans->matrix = matrix_multiply(curr_trans->matrix, next_trans->matrix);
908                 goto remove_next;
909             }
910         }
911         if (curr_trans->transform_module_fn == qcms_transform_module_gamma_table) {
912             bool isLinear = true;
913             uint16_t i;
914             for (i = 0; isLinear && i < 256; i++) {
915                 isLinear &= (int)(curr_trans->input_clut_table_r[i] * 255) == i;
916                 isLinear &= (int)(curr_trans->input_clut_table_g[i] * 255) == i;
917                 isLinear &= (int)(curr_trans->input_clut_table_b[i] * 255) == i;
918             }
919             goto remove_current;
920         }
921 
922 next_transform:
923         if (!next_trans) break;
924         prev_trans = curr_trans;
925         curr_trans = next_trans;
926         continue;
927 remove_current:
928         if (curr_trans == transform) {
929             //Update head
930             transform = next_trans;
931         } else {
932             prev_trans->next_transform = next_trans;
933         }
934         curr_trans->next_transform = NULL;
935         qcms_modular_transform_release(curr_trans);
936         //return transform;
937         return qcms_modular_transform_reduce(transform);
938 remove_next:
939         curr_trans->next_transform = next_trans->next_transform;
940         next_trans->next_transform = NULL;
941         qcms_modular_transform_release(next_trans);
942         continue;
943     }
944     return transform;
945 }
946 */
modular_transform_create( input: &Profile, output: &Profile, ) -> Option<Vec<Box<dyn ModularTransform>>>947 fn modular_transform_create(
948     input: &Profile,
949     output: &Profile,
950 ) -> Option<Vec<Box<dyn ModularTransform>>> {
951     let mut transforms = Vec::new();
952     if input.color_space == RGB_SIGNATURE || input.color_space == CMYK_SIGNATURE {
953         let rgb_to_pcs = modular_transform_create_input(input);
954         if let Some(rgb_to_pcs) = rgb_to_pcs {
955             transforms.extend(rgb_to_pcs);
956         } else {
957             return None;
958         }
959     } else {
960         debug_assert!(false, "input color space not supported");
961         return None;
962     }
963 
964     if input.pcs == LAB_SIGNATURE && output.pcs == XYZ_SIGNATURE {
965         transforms.push(Box::new(LABtoXYZ {}));
966     }
967 
968     // This does not improve accuracy in practice, something is wrong here.
969     //if (in->chromaticAdaption.invalid == false) {
970     //	struct qcms_modular_transform* chromaticAdaption;
971     //	chromaticAdaption = qcms_modular_transform_alloc();
972     //	if (!chromaticAdaption)
973     //		goto fail;
974     //	append_transform(chromaticAdaption, &next_transform);
975     //	chromaticAdaption->matrix = matrix_invert(in->chromaticAdaption);
976     //	chromaticAdaption->transform_module_fn = qcms_transform_module_matrix;
977     //}
978 
979     if input.pcs == XYZ_SIGNATURE && output.pcs == LAB_SIGNATURE {
980         transforms.push(Box::new(XYZtoLAB {}));
981     }
982 
983     if output.color_space == RGB_SIGNATURE {
984         let pcs_to_rgb = modular_transform_create_output(output);
985         if let Some(pcs_to_rgb) = pcs_to_rgb {
986             transforms.extend(pcs_to_rgb);
987         } else {
988             return None;
989         }
990     } else if output.color_space == CMYK_SIGNATURE {
991         let pcs_to_cmyk = modular_transform_create_output(output)?;
992         transforms.extend(pcs_to_cmyk);
993     } else {
994         debug_assert!(false, "output color space not supported");
995     }
996 
997     // Not Completed
998     //return qcms_modular_transform_reduce(first_transform);
999     Some(transforms)
1000 }
modular_transform_data( transforms: Vec<Box<dyn ModularTransform>>, mut src: Vec<f32>, mut dest: Vec<f32>, _len: usize, ) -> Vec<f32>1001 fn modular_transform_data(
1002     transforms: Vec<Box<dyn ModularTransform>>,
1003     mut src: Vec<f32>,
1004     mut dest: Vec<f32>,
1005     _len: usize,
1006 ) -> Vec<f32> {
1007     for transform in transforms {
1008         // Keep swaping src/dest when performing a transform to use less memory.
1009         transform.transform(&src, &mut dest);
1010         std::mem::swap(&mut src, &mut dest);
1011     }
1012     // The results end up in the src buffer because of the switching
1013     src
1014 }
1015 
chain_transform( input: &Profile, output: &Profile, src: Vec<f32>, dest: Vec<f32>, lutSize: usize, ) -> Option<Vec<f32>>1016 pub fn chain_transform(
1017     input: &Profile,
1018     output: &Profile,
1019     src: Vec<f32>,
1020     dest: Vec<f32>,
1021     lutSize: usize,
1022 ) -> Option<Vec<f32>> {
1023     let transform_list = modular_transform_create(input, output);
1024     if let Some(transform_list) = transform_list {
1025         let lut = modular_transform_data(transform_list, src, dest, lutSize / 3);
1026         return Some(lut);
1027     }
1028     None
1029 }
1030