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