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