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