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