summaryrefslogtreecommitdiffstats
path: root/gfx/qcms/src/chain.rs
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/qcms/src/chain.rs')
-rw-r--r--gfx/qcms/src/chain.rs1029
1 files changed, 1029 insertions, 0 deletions
diff --git a/gfx/qcms/src/chain.rs b/gfx/qcms/src/chain.rs
new file mode 100644
index 0000000000..35a3896138
--- /dev/null
+++ b/gfx/qcms/src/chain.rs
@@ -0,0 +1,1029 @@
+// qcms
+// Copyright (C) 2009 Mozilla Corporation
+// Copyright (C) 1998-2007 Marti Maria
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the Software
+// is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+use crate::{
+ iccread::LAB_SIGNATURE,
+ iccread::RGB_SIGNATURE,
+ iccread::XYZ_SIGNATURE,
+ iccread::{lutType, lutmABType, Profile, CMYK_SIGNATURE},
+ matrix::Matrix,
+ s15Fixed16Number_to_float,
+ transform_util::clamp_float,
+ transform_util::{
+ build_colorant_matrix, build_input_gamma_table, build_output_lut, lut_interp_linear,
+ lut_interp_linear_float,
+ },
+};
+
+trait ModularTransform {
+ fn transform(&self, src: &[f32], dst: &mut [f32]);
+}
+
+#[inline]
+fn lerp(a: f32, b: f32, t: f32) -> f32 {
+ a * (1.0 - t) + b * t
+}
+
+fn build_lut_matrix(lut: &lutType) -> Matrix {
+ let mut result: Matrix = Matrix { m: [[0.; 3]; 3] };
+ result.m[0][0] = s15Fixed16Number_to_float(lut.e00);
+ result.m[0][1] = s15Fixed16Number_to_float(lut.e01);
+ result.m[0][2] = s15Fixed16Number_to_float(lut.e02);
+ result.m[1][0] = s15Fixed16Number_to_float(lut.e10);
+ result.m[1][1] = s15Fixed16Number_to_float(lut.e11);
+ result.m[1][2] = s15Fixed16Number_to_float(lut.e12);
+ result.m[2][0] = s15Fixed16Number_to_float(lut.e20);
+ result.m[2][1] = s15Fixed16Number_to_float(lut.e21);
+ result.m[2][2] = s15Fixed16Number_to_float(lut.e22);
+ result
+}
+fn build_mAB_matrix(lut: &lutmABType) -> Matrix {
+ let mut result: Matrix = Matrix { m: [[0.; 3]; 3] };
+
+ result.m[0][0] = s15Fixed16Number_to_float(lut.e00);
+ result.m[0][1] = s15Fixed16Number_to_float(lut.e01);
+ result.m[0][2] = s15Fixed16Number_to_float(lut.e02);
+ result.m[1][0] = s15Fixed16Number_to_float(lut.e10);
+ result.m[1][1] = s15Fixed16Number_to_float(lut.e11);
+ result.m[1][2] = s15Fixed16Number_to_float(lut.e12);
+ result.m[2][0] = s15Fixed16Number_to_float(lut.e20);
+ result.m[2][1] = s15Fixed16Number_to_float(lut.e21);
+ result.m[2][2] = s15Fixed16Number_to_float(lut.e22);
+
+ result
+}
+//Based on lcms cmsLab2XYZ
+fn f(t: f32) -> f32 {
+ if t <= 24. / 116. * (24. / 116.) * (24. / 116.) {
+ (841. / 108. * t) + 16. / 116.
+ } else {
+ t.powf(1. / 3.)
+ }
+}
+fn f_1(t: f32) -> f32 {
+ if t <= 24.0 / 116.0 {
+ (108.0 / 841.0) * (t - 16.0 / 116.0)
+ } else {
+ t * t * t
+ }
+}
+
+#[allow(clippy::upper_case_acronyms)]
+struct LABtoXYZ;
+impl ModularTransform for LABtoXYZ {
+ fn transform(&self, src: &[f32], dest: &mut [f32]) {
+ // lcms: D50 XYZ values
+ let WhitePointX: f32 = 0.9642;
+ let WhitePointY: f32 = 1.0;
+ let WhitePointZ: f32 = 0.8249;
+
+ for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
+ let device_L: f32 = src[0] * 100.0;
+ let device_a: f32 = src[1] * 255.0 - 128.0;
+ let device_b: f32 = src[2] * 255.0 - 128.0;
+
+ let y: f32 = (device_L + 16.0) / 116.0;
+
+ let X = f_1(y + 0.002 * device_a) * WhitePointX;
+ let Y = f_1(y) * WhitePointY;
+ let Z = f_1(y - 0.005 * device_b) * WhitePointZ;
+
+ dest[0] = (X as f64 / (1.0f64 + 32767.0f64 / 32768.0f64)) as f32;
+ dest[1] = (Y as f64 / (1.0f64 + 32767.0f64 / 32768.0f64)) as f32;
+ dest[2] = (Z as f64 / (1.0f64 + 32767.0f64 / 32768.0f64)) as f32;
+ }
+ }
+}
+
+#[allow(clippy::upper_case_acronyms)]
+struct XYZtoLAB;
+impl ModularTransform for XYZtoLAB {
+ //Based on lcms cmsXYZ2Lab
+ fn transform(&self, src: &[f32], dest: &mut [f32]) {
+ // lcms: D50 XYZ values
+ let WhitePointX: f32 = 0.9642;
+ let WhitePointY: f32 = 1.0;
+ let WhitePointZ: f32 = 0.8249;
+ for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
+ let device_x: f32 =
+ (src[0] as f64 * (1.0f64 + 32767.0f64 / 32768.0f64) / WhitePointX as f64) as f32;
+ let device_y: f32 =
+ (src[1] as f64 * (1.0f64 + 32767.0f64 / 32768.0f64) / WhitePointY as f64) as f32;
+ let device_z: f32 =
+ (src[2] as f64 * (1.0f64 + 32767.0f64 / 32768.0f64) / WhitePointZ as f64) as f32;
+
+ let fx = f(device_x);
+ let fy = f(device_y);
+ let fz = f(device_z);
+
+ let L: f32 = 116.0 * fy - 16.0;
+ let a: f32 = 500.0 * (fx - fy);
+ let b: f32 = 200.0 * (fy - fz);
+
+ dest[0] = L / 100.0;
+ dest[1] = (a + 128.0) / 255.0;
+ dest[2] = (b + 128.0) / 255.0;
+ }
+ }
+}
+#[derive(Default)]
+struct ClutOnly {
+ clut: Option<Vec<f32>>,
+ grid_size: u16,
+}
+impl ModularTransform for ClutOnly {
+ fn transform(&self, src: &[f32], dest: &mut [f32]) {
+ let xy_len: i32 = 1;
+ let x_len: i32 = self.grid_size as i32;
+ let len: i32 = x_len * x_len;
+
+ let r_table = &self.clut.as_ref().unwrap()[0..];
+ let g_table = &self.clut.as_ref().unwrap()[1..];
+ let b_table = &self.clut.as_ref().unwrap()[2..];
+
+ let CLU = |table: &[f32], x, y, z| table[((x * len + y * x_len + z * xy_len) * 3) as usize];
+
+ for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
+ debug_assert!(self.grid_size as i32 >= 1);
+ let linear_r: f32 = src[0];
+ let linear_g: f32 = src[1];
+ let linear_b: f32 = src[2];
+ let x: i32 = (linear_r * (self.grid_size as i32 - 1) as f32).floor() as i32;
+ let y: i32 = (linear_g * (self.grid_size as i32 - 1) as f32).floor() as i32;
+ let z: i32 = (linear_b * (self.grid_size as i32 - 1) as f32).floor() as i32;
+ let x_n: i32 = (linear_r * (self.grid_size as i32 - 1) as f32).ceil() as i32;
+ let y_n: i32 = (linear_g * (self.grid_size as i32 - 1) as f32).ceil() as i32;
+ let z_n: i32 = (linear_b * (self.grid_size as i32 - 1) as f32).ceil() as i32;
+ let x_d: f32 = linear_r * (self.grid_size as i32 - 1) as f32 - x as f32;
+ let y_d: f32 = linear_g * (self.grid_size as i32 - 1) as f32 - y as f32;
+ let z_d: f32 = linear_b * (self.grid_size as i32 - 1) as f32 - z as f32;
+
+ let r_x1: f32 = lerp(CLU(r_table, x, y, z), CLU(r_table, x_n, y, z), x_d);
+ let r_x2: f32 = lerp(CLU(r_table, x, y_n, z), CLU(r_table, x_n, y_n, z), x_d);
+ let r_y1: f32 = lerp(r_x1, r_x2, y_d);
+ let r_x3: f32 = lerp(CLU(r_table, x, y, z_n), CLU(r_table, x_n, y, z_n), x_d);
+ let r_x4: f32 = lerp(CLU(r_table, x, y_n, z_n), CLU(r_table, x_n, y_n, z_n), x_d);
+ let r_y2: f32 = lerp(r_x3, r_x4, y_d);
+ let clut_r: f32 = lerp(r_y1, r_y2, z_d);
+
+ let g_x1: f32 = lerp(CLU(g_table, x, y, z), CLU(g_table, x_n, y, z), x_d);
+ let g_x2: f32 = lerp(CLU(g_table, x, y_n, z), CLU(g_table, x_n, y_n, z), x_d);
+ let g_y1: f32 = lerp(g_x1, g_x2, y_d);
+ let g_x3: f32 = lerp(CLU(g_table, x, y, z_n), CLU(g_table, x_n, y, z_n), x_d);
+ let g_x4: f32 = lerp(CLU(g_table, x, y_n, z_n), CLU(g_table, x_n, y_n, z_n), x_d);
+ let g_y2: f32 = lerp(g_x3, g_x4, y_d);
+ let clut_g: f32 = lerp(g_y1, g_y2, z_d);
+
+ let b_x1: f32 = lerp(CLU(b_table, x, y, z), CLU(b_table, x_n, y, z), x_d);
+ let b_x2: f32 = lerp(CLU(b_table, x, y_n, z), CLU(b_table, x_n, y_n, z), x_d);
+ let b_y1: f32 = lerp(b_x1, b_x2, y_d);
+ let b_x3: f32 = lerp(CLU(b_table, x, y, z_n), CLU(b_table, x_n, y, z_n), x_d);
+ let b_x4: f32 = lerp(CLU(b_table, x, y_n, z_n), CLU(b_table, x_n, y_n, z_n), x_d);
+ let b_y2: f32 = lerp(b_x3, b_x4, y_d);
+ let clut_b: f32 = lerp(b_y1, b_y2, z_d);
+
+ dest[0] = clamp_float(clut_r);
+ dest[1] = clamp_float(clut_g);
+ dest[2] = clamp_float(clut_b);
+ }
+ }
+}
+#[derive(Default)]
+struct Clut3x3 {
+ input_clut_table: [Option<Vec<f32>>; 3],
+ clut: Option<Vec<f32>>,
+ grid_size: u16,
+ output_clut_table: [Option<Vec<f32>>; 3],
+}
+impl ModularTransform for Clut3x3 {
+ fn transform(&self, src: &[f32], dest: &mut [f32]) {
+ let xy_len: i32 = 1;
+ let x_len: i32 = self.grid_size as i32;
+ let len: i32 = x_len * x_len;
+
+ let r_table = &self.clut.as_ref().unwrap()[0..];
+ let g_table = &self.clut.as_ref().unwrap()[1..];
+ let b_table = &self.clut.as_ref().unwrap()[2..];
+ let CLU = |table: &[f32], x, y, z| table[((x * len + y * x_len + z * xy_len) * 3) as usize];
+
+ let input_clut_table_r = self.input_clut_table[0].as_ref().unwrap();
+ let input_clut_table_g = self.input_clut_table[1].as_ref().unwrap();
+ let input_clut_table_b = self.input_clut_table[2].as_ref().unwrap();
+ for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
+ debug_assert!(self.grid_size as i32 >= 1);
+ let device_r: f32 = src[0];
+ let device_g: f32 = src[1];
+ let device_b: f32 = src[2];
+ let linear_r: f32 = lut_interp_linear_float(device_r, &input_clut_table_r);
+ let linear_g: f32 = lut_interp_linear_float(device_g, &input_clut_table_g);
+ let linear_b: f32 = lut_interp_linear_float(device_b, &input_clut_table_b);
+ let x: i32 = (linear_r * (self.grid_size as i32 - 1) as f32).floor() as i32;
+ let y: i32 = (linear_g * (self.grid_size as i32 - 1) as f32).floor() as i32;
+ let z: i32 = (linear_b * (self.grid_size as i32 - 1) as f32).floor() as i32;
+ let x_n: i32 = (linear_r * (self.grid_size as i32 - 1) as f32).ceil() as i32;
+ let y_n: i32 = (linear_g * (self.grid_size as i32 - 1) as f32).ceil() as i32;
+ let z_n: i32 = (linear_b * (self.grid_size as i32 - 1) as f32).ceil() as i32;
+ let x_d: f32 = linear_r * (self.grid_size as i32 - 1) as f32 - x as f32;
+ let y_d: f32 = linear_g * (self.grid_size as i32 - 1) as f32 - y as f32;
+ let z_d: f32 = linear_b * (self.grid_size as i32 - 1) as f32 - z as f32;
+
+ let r_x1: f32 = lerp(CLU(r_table, x, y, z), CLU(r_table, x_n, y, z), x_d);
+ let r_x2: f32 = lerp(CLU(r_table, x, y_n, z), CLU(r_table, x_n, y_n, z), x_d);
+ let r_y1: f32 = lerp(r_x1, r_x2, y_d);
+ let r_x3: f32 = lerp(CLU(r_table, x, y, z_n), CLU(r_table, x_n, y, z_n), x_d);
+ let r_x4: f32 = lerp(CLU(r_table, x, y_n, z_n), CLU(r_table, x_n, y_n, z_n), x_d);
+ let r_y2: f32 = lerp(r_x3, r_x4, y_d);
+ let clut_r: f32 = lerp(r_y1, r_y2, z_d);
+
+ let g_x1: f32 = lerp(CLU(g_table, x, y, z), CLU(g_table, x_n, y, z), x_d);
+ let g_x2: f32 = lerp(CLU(g_table, x, y_n, z), CLU(g_table, x_n, y_n, z), x_d);
+ let g_y1: f32 = lerp(g_x1, g_x2, y_d);
+ let g_x3: f32 = lerp(CLU(g_table, x, y, z_n), CLU(g_table, x_n, y, z_n), x_d);
+ let g_x4: f32 = lerp(CLU(g_table, x, y_n, z_n), CLU(g_table, x_n, y_n, z_n), x_d);
+ let g_y2: f32 = lerp(g_x3, g_x4, y_d);
+ let clut_g: f32 = lerp(g_y1, g_y2, z_d);
+
+ let b_x1: f32 = lerp(CLU(b_table, x, y, z), CLU(b_table, x_n, y, z), x_d);
+ let b_x2: f32 = lerp(CLU(b_table, x, y_n, z), CLU(b_table, x_n, y_n, z), x_d);
+ let b_y1: f32 = lerp(b_x1, b_x2, y_d);
+ let b_x3: f32 = lerp(CLU(b_table, x, y, z_n), CLU(b_table, x_n, y, z_n), x_d);
+ let b_x4: f32 = lerp(CLU(b_table, x, y_n, z_n), CLU(b_table, x_n, y_n, z_n), x_d);
+ let b_y2: f32 = lerp(b_x3, b_x4, y_d);
+ let clut_b: f32 = lerp(b_y1, b_y2, z_d);
+ let pcs_r: f32 =
+ lut_interp_linear_float(clut_r, &self.output_clut_table[0].as_ref().unwrap());
+ let pcs_g: f32 =
+ lut_interp_linear_float(clut_g, &self.output_clut_table[1].as_ref().unwrap());
+ let pcs_b: f32 =
+ lut_interp_linear_float(clut_b, &self.output_clut_table[2].as_ref().unwrap());
+ dest[0] = clamp_float(pcs_r);
+ dest[1] = clamp_float(pcs_g);
+ dest[2] = clamp_float(pcs_b);
+ }
+ }
+}
+#[derive(Default)]
+struct Clut4x3 {
+ input_clut_table: [Option<Vec<f32>>; 4],
+ clut: Option<Vec<f32>>,
+ grid_size: u16,
+ output_clut_table: [Option<Vec<f32>>; 3],
+}
+impl ModularTransform for Clut4x3 {
+ fn transform(&self, src: &[f32], dest: &mut [f32]) {
+ let z_stride: i32 = self.grid_size as i32;
+ let y_stride: i32 = z_stride * z_stride;
+ let x_stride: i32 = z_stride * z_stride * z_stride;
+
+ let r_tbl = &self.clut.as_ref().unwrap()[0..];
+ let g_tbl = &self.clut.as_ref().unwrap()[1..];
+ let b_tbl = &self.clut.as_ref().unwrap()[2..];
+
+ let CLU = |table: &[f32], x, y, z, w| {
+ table[((x * x_stride + y * y_stride + z * z_stride + w) * 3) as usize]
+ };
+
+ let input_clut_table_0 = self.input_clut_table[0].as_ref().unwrap();
+ let input_clut_table_1 = self.input_clut_table[1].as_ref().unwrap();
+ let input_clut_table_2 = self.input_clut_table[2].as_ref().unwrap();
+ let input_clut_table_3 = self.input_clut_table[3].as_ref().unwrap();
+ for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(4)) {
+ debug_assert!(self.grid_size as i32 >= 1);
+ let linear_x: f32 = lut_interp_linear_float(src[0], &input_clut_table_0);
+ let linear_y: f32 = lut_interp_linear_float(src[1], &input_clut_table_1);
+ let linear_z: f32 = lut_interp_linear_float(src[2], &input_clut_table_2);
+ let linear_w: f32 = lut_interp_linear_float(src[3], &input_clut_table_3);
+
+ let x: i32 = (linear_x * (self.grid_size as i32 - 1) as f32).floor() as i32;
+ let y: i32 = (linear_y * (self.grid_size as i32 - 1) as f32).floor() as i32;
+ let z: i32 = (linear_z * (self.grid_size as i32 - 1) as f32).floor() as i32;
+ let w: i32 = (linear_w * (self.grid_size as i32 - 1) as f32).floor() as i32;
+
+ let x_n: i32 = (linear_x * (self.grid_size as i32 - 1) as f32).ceil() as i32;
+ let y_n: i32 = (linear_y * (self.grid_size as i32 - 1) as f32).ceil() as i32;
+ let z_n: i32 = (linear_z * (self.grid_size as i32 - 1) as f32).ceil() as i32;
+ let w_n: i32 = (linear_w * (self.grid_size as i32 - 1) as f32).ceil() as i32;
+
+ let x_d: f32 = linear_x * (self.grid_size as i32 - 1) as f32 - x as f32;
+ let y_d: f32 = linear_y * (self.grid_size as i32 - 1) as f32 - y as f32;
+ let z_d: f32 = linear_z * (self.grid_size as i32 - 1) as f32 - z as f32;
+ let w_d: f32 = linear_w * (self.grid_size as i32 - 1) as f32 - w as f32;
+
+ let quadlinear = |tbl| {
+ let CLU = |x, y, z, w| CLU(tbl, x, y, z, w);
+ let r_x1 = lerp(CLU(x, y, z, w), CLU(x_n, y, z, w), x_d);
+ let r_x2 = lerp(CLU(x, y_n, z, w), CLU(x_n, y_n, z, w), x_d);
+ let r_y1 = lerp(r_x1, r_x2, y_d);
+ let r_x3 = lerp(CLU(x, y, z_n, w), CLU(x_n, y, z_n, w), x_d);
+ let r_x4 = lerp(CLU(x, y_n, z_n, w), CLU(x_n, y_n, z_n, w), x_d);
+ let r_y2 = lerp(r_x3, r_x4, y_d);
+ let r_z1 = lerp(r_y1, r_y2, z_d);
+
+ let r_x1 = lerp(CLU(x, y, z, w_n), CLU(x_n, y, z, w_n), x_d);
+ let r_x2 = lerp(CLU(x, y_n, z, w_n), CLU(x_n, y_n, z, w_n), x_d);
+ let r_y1 = lerp(r_x1, r_x2, y_d);
+ let r_x3 = lerp(CLU(x, y, z_n, w_n), CLU(x_n, y, z_n, w_n), x_d);
+ let r_x4 = lerp(CLU(x, y_n, z_n, w_n), CLU(x_n, y_n, z_n, w_n), x_d);
+ let r_y2 = lerp(r_x3, r_x4, y_d);
+ let r_z2 = lerp(r_y1, r_y2, z_d);
+ lerp(r_z1, r_z2, w_d)
+ };
+ // TODO: instead of reading each component separately we should read all three components at once.
+ let clut_r = quadlinear(r_tbl);
+ let clut_g = quadlinear(g_tbl);
+ let clut_b = quadlinear(b_tbl);
+
+ let pcs_r =
+ lut_interp_linear_float(clut_r, &self.output_clut_table[0].as_ref().unwrap());
+ let pcs_g =
+ lut_interp_linear_float(clut_g, &self.output_clut_table[1].as_ref().unwrap());
+ let pcs_b =
+ lut_interp_linear_float(clut_b, &self.output_clut_table[2].as_ref().unwrap());
+ dest[0] = clamp_float(pcs_r);
+ dest[1] = clamp_float(pcs_g);
+ dest[2] = clamp_float(pcs_b);
+ }
+ }
+}
+/* NOT USED
+static void qcms_transform_module_tetra_clut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
+{
+ size_t i;
+ int xy_len = 1;
+ int x_len = transform->grid_size;
+ int len = x_len * x_len;
+ float* r_table = transform->r_clut;
+ float* g_table = transform->g_clut;
+ float* b_table = transform->b_clut;
+ float c0_r, c1_r, c2_r, c3_r;
+ float c0_g, c1_g, c2_g, c3_g;
+ float c0_b, c1_b, c2_b, c3_b;
+ float clut_r, clut_g, clut_b;
+ float pcs_r, pcs_g, pcs_b;
+ for (i = 0; i < length; i++) {
+ float device_r = *src++;
+ float device_g = *src++;
+ float device_b = *src++;
+ float linear_r = lut_interp_linear_float(device_r,
+ transform->input_clut_table_r, transform->input_clut_table_length);
+ float linear_g = lut_interp_linear_float(device_g,
+ transform->input_clut_table_g, transform->input_clut_table_length);
+ float linear_b = lut_interp_linear_float(device_b,
+ transform->input_clut_table_b, transform->input_clut_table_length);
+
+ int x = floorf(linear_r * (transform->grid_size-1));
+ int y = floorf(linear_g * (transform->grid_size-1));
+ int z = floorf(linear_b * (transform->grid_size-1));
+ int x_n = ceilf(linear_r * (transform->grid_size-1));
+ int y_n = ceilf(linear_g * (transform->grid_size-1));
+ int z_n = ceilf(linear_b * (transform->grid_size-1));
+ float rx = linear_r * (transform->grid_size-1) - x;
+ float ry = linear_g * (transform->grid_size-1) - y;
+ float rz = linear_b * (transform->grid_size-1) - z;
+
+ c0_r = CLU(r_table, x, y, z);
+ c0_g = CLU(g_table, x, y, z);
+ c0_b = CLU(b_table, x, y, z);
+ if( rx >= ry ) {
+ if (ry >= rz) { //rx >= ry && ry >= rz
+ c1_r = CLU(r_table, x_n, y, z) - c0_r;
+ c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x_n, y, z);
+ c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
+ c1_g = CLU(g_table, x_n, y, z) - c0_g;
+ c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x_n, y, z);
+ c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
+ c1_b = CLU(b_table, x_n, y, z) - c0_b;
+ c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x_n, y, z);
+ c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
+ } else {
+ if (rx >= rz) { //rx >= rz && rz >= ry
+ c1_r = CLU(r_table, x_n, y, z) - c0_r;
+ c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
+ c3_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x_n, y, z);
+ c1_g = CLU(g_table, x_n, y, z) - c0_g;
+ c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
+ c3_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x_n, y, z);
+ c1_b = CLU(b_table, x_n, y, z) - c0_b;
+ c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
+ c3_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x_n, y, z);
+ } else { //rz > rx && rx >= ry
+ c1_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x, y, z_n);
+ c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
+ c3_r = CLU(r_table, x, y, z_n) - c0_r;
+ c1_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x, y, z_n);
+ c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
+ c3_g = CLU(g_table, x, y, z_n) - c0_g;
+ c1_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x, y, z_n);
+ c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
+ c3_b = CLU(b_table, x, y, z_n) - c0_b;
+ }
+ }
+ } else {
+ if (rx >= rz) { //ry > rx && rx >= rz
+ c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x, y_n, z);
+ c2_r = CLU(r_table, x_n, y_n, z) - c0_r;
+ c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
+ c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x, y_n, z);
+ c2_g = CLU(g_table, x_n, y_n, z) - c0_g;
+ c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
+ c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x, y_n, z);
+ c2_b = CLU(b_table, x_n, y_n, z) - c0_b;
+ c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
+ } else {
+ if (ry >= rz) { //ry >= rz && rz > rx
+ c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
+ c2_r = CLU(r_table, x, y_n, z) - c0_r;
+ c3_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y_n, z);
+ c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
+ c2_g = CLU(g_table, x, y_n, z) - c0_g;
+ c3_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y_n, z);
+ c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
+ c2_b = CLU(b_table, x, y_n, z) - c0_b;
+ c3_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y_n, z);
+ } else { //rz > ry && ry > rx
+ c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
+ c2_r = CLU(r_table, x, y_n, z) - c0_r;
+ c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
+ c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
+ c2_g = CLU(g_table, x, y_n, z) - c0_g;
+ c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
+ c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
+ c2_b = CLU(b_table, x, y_n, z) - c0_b;
+ c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
+ }
+ }
+ }
+
+ clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz;
+ clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz;
+ clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz;
+
+ pcs_r = lut_interp_linear_float(clut_r,
+ transform->output_clut_table_r, transform->output_clut_table_length);
+ pcs_g = lut_interp_linear_float(clut_g,
+ transform->output_clut_table_g, transform->output_clut_table_length);
+ pcs_b = lut_interp_linear_float(clut_b,
+ transform->output_clut_table_b, transform->output_clut_table_length);
+ *dest++ = clamp_float(pcs_r);
+ *dest++ = clamp_float(pcs_g);
+ *dest++ = clamp_float(pcs_b);
+ }
+}
+*/
+#[derive(Default)]
+struct GammaTable {
+ input_clut_table: [Option<Vec<f32>>; 3],
+}
+impl ModularTransform for GammaTable {
+ fn transform(&self, src: &[f32], dest: &mut [f32]) {
+ let mut out_r: f32;
+ let mut out_g: f32;
+ let mut out_b: f32;
+ let input_clut_table_r = self.input_clut_table[0].as_ref().unwrap();
+ let input_clut_table_g = self.input_clut_table[1].as_ref().unwrap();
+ let input_clut_table_b = self.input_clut_table[2].as_ref().unwrap();
+
+ for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
+ let in_r: f32 = src[0];
+ let in_g: f32 = src[1];
+ let in_b: f32 = src[2];
+ out_r = lut_interp_linear_float(in_r, input_clut_table_r);
+ out_g = lut_interp_linear_float(in_g, input_clut_table_g);
+ out_b = lut_interp_linear_float(in_b, input_clut_table_b);
+
+ dest[0] = clamp_float(out_r);
+ dest[1] = clamp_float(out_g);
+ dest[2] = clamp_float(out_b);
+ }
+ }
+}
+#[derive(Default)]
+struct GammaLut {
+ output_gamma_lut_r: Option<Vec<u16>>,
+ output_gamma_lut_g: Option<Vec<u16>>,
+ output_gamma_lut_b: Option<Vec<u16>>,
+}
+impl ModularTransform for GammaLut {
+ fn transform(&self, src: &[f32], dest: &mut [f32]) {
+ let mut out_r: f32;
+ let mut out_g: f32;
+ let mut out_b: f32;
+ for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
+ let in_r: f32 = src[0];
+ let in_g: f32 = src[1];
+ let in_b: f32 = src[2];
+ out_r = lut_interp_linear(in_r as f64, &self.output_gamma_lut_r.as_ref().unwrap());
+ out_g = lut_interp_linear(in_g as f64, &self.output_gamma_lut_g.as_ref().unwrap());
+ out_b = lut_interp_linear(in_b as f64, &self.output_gamma_lut_b.as_ref().unwrap());
+ dest[0] = clamp_float(out_r);
+ dest[1] = clamp_float(out_g);
+ dest[2] = clamp_float(out_b);
+ }
+ }
+}
+#[derive(Default)]
+struct MatrixTranslate {
+ matrix: Matrix,
+ tx: f32,
+ ty: f32,
+ tz: f32,
+}
+impl ModularTransform for MatrixTranslate {
+ fn transform(&self, src: &[f32], dest: &mut [f32]) {
+ let mut mat: Matrix = Matrix { m: [[0.; 3]; 3] };
+ /* store the results in column major mode
+ * this makes doing the multiplication with sse easier */
+ mat.m[0][0] = self.matrix.m[0][0];
+ mat.m[1][0] = self.matrix.m[0][1];
+ mat.m[2][0] = self.matrix.m[0][2];
+ mat.m[0][1] = self.matrix.m[1][0];
+ mat.m[1][1] = self.matrix.m[1][1];
+ mat.m[2][1] = self.matrix.m[1][2];
+ mat.m[0][2] = self.matrix.m[2][0];
+ mat.m[1][2] = self.matrix.m[2][1];
+ mat.m[2][2] = self.matrix.m[2][2];
+ for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
+ let in_r: f32 = src[0];
+ let in_g: f32 = src[1];
+ let in_b: f32 = src[2];
+ let out_r: f32 = mat.m[0][0] * in_r + mat.m[1][0] * in_g + mat.m[2][0] * in_b + self.tx;
+ let out_g: f32 = mat.m[0][1] * in_r + mat.m[1][1] * in_g + mat.m[2][1] * in_b + self.ty;
+ let out_b: f32 = mat.m[0][2] * in_r + mat.m[1][2] * in_g + mat.m[2][2] * in_b + self.tz;
+ dest[0] = clamp_float(out_r);
+ dest[1] = clamp_float(out_g);
+ dest[2] = clamp_float(out_b);
+ }
+ }
+}
+#[derive(Default)]
+struct MatrixTransform {
+ matrix: Matrix,
+}
+impl ModularTransform for MatrixTransform {
+ fn transform(&self, src: &[f32], dest: &mut [f32]) {
+ let mut mat: Matrix = Matrix { m: [[0.; 3]; 3] };
+ /* store the results in column major mode
+ * this makes doing the multiplication with sse easier */
+ mat.m[0][0] = self.matrix.m[0][0];
+ mat.m[1][0] = self.matrix.m[0][1];
+ mat.m[2][0] = self.matrix.m[0][2];
+ mat.m[0][1] = self.matrix.m[1][0];
+ mat.m[1][1] = self.matrix.m[1][1];
+ mat.m[2][1] = self.matrix.m[1][2];
+ mat.m[0][2] = self.matrix.m[2][0];
+ mat.m[1][2] = self.matrix.m[2][1];
+ mat.m[2][2] = self.matrix.m[2][2];
+ for (dest, src) in dest.chunks_exact_mut(3).zip(src.chunks_exact(3)) {
+ let in_r: f32 = src[0];
+ let in_g: f32 = src[1];
+ let in_b: f32 = src[2];
+ let out_r: f32 = mat.m[0][0] * in_r + mat.m[1][0] * in_g + mat.m[2][0] * in_b;
+ let out_g: f32 = mat.m[0][1] * in_r + mat.m[1][1] * in_g + mat.m[2][1] * in_b;
+ let out_b: f32 = mat.m[0][2] * in_r + mat.m[1][2] * in_g + mat.m[2][2] * in_b;
+ dest[0] = clamp_float(out_r);
+ dest[1] = clamp_float(out_g);
+ dest[2] = clamp_float(out_b);
+ }
+ }
+}
+
+fn modular_transform_create_mAB(lut: &lutmABType) -> Option<Vec<Box<dyn ModularTransform>>> {
+ let mut transforms: Vec<Box<dyn ModularTransform>> = Vec::new();
+ if lut.a_curves[0].is_some() {
+ let clut_length: usize;
+ // If the A curve is present this also implies the
+ // presence of a CLUT.
+ lut.clut_table.as_ref()?;
+
+ // Prepare A curve.
+ let mut transform = Box::new(GammaTable::default());
+ transform.input_clut_table[0] = build_input_gamma_table(lut.a_curves[0].as_deref())
+ .map(|x| (x as Box<[f32]>).into_vec());
+ transform.input_clut_table[1] = build_input_gamma_table(lut.a_curves[1].as_deref())
+ .map(|x| (x as Box<[f32]>).into_vec());
+ transform.input_clut_table[2] = build_input_gamma_table(lut.a_curves[2].as_deref())
+ .map(|x| (x as Box<[f32]>).into_vec());
+
+ if lut.num_grid_points[0] as i32 != lut.num_grid_points[1] as i32
+ || lut.num_grid_points[1] as i32 != lut.num_grid_points[2] as i32
+ {
+ //XXX: We don't currently support clut that are not squared!
+ return None;
+ }
+ transforms.push(transform);
+
+ // Prepare CLUT
+ let mut transform = Box::new(ClutOnly::default());
+ clut_length = (lut.num_grid_points[0] as usize).pow(3) * 3;
+ assert_eq!(clut_length, lut.clut_table.as_ref().unwrap().len());
+ transform.clut = lut.clut_table.clone();
+ transform.grid_size = lut.num_grid_points[0] as u16;
+ transforms.push(transform);
+ }
+
+ if lut.m_curves[0].is_some() {
+ // M curve imples the presence of a Matrix
+
+ // Prepare M curve
+ let mut transform = Box::new(GammaTable::default());
+ transform.input_clut_table[0] = build_input_gamma_table(lut.m_curves[0].as_deref())
+ .map(|x| (x as Box<[f32]>).into_vec());
+ transform.input_clut_table[1] = build_input_gamma_table(lut.m_curves[1].as_deref())
+ .map(|x| (x as Box<[f32]>).into_vec());
+ transform.input_clut_table[2] = build_input_gamma_table(lut.m_curves[2].as_deref())
+ .map(|x| (x as Box<[f32]>).into_vec());
+ transforms.push(transform);
+
+ // Prepare Matrix
+ let mut transform = Box::new(MatrixTranslate::default());
+ transform.matrix = build_mAB_matrix(lut);
+ transform.tx = s15Fixed16Number_to_float(lut.e03);
+ transform.ty = s15Fixed16Number_to_float(lut.e13);
+ transform.tz = s15Fixed16Number_to_float(lut.e23);
+ transforms.push(transform);
+ }
+
+ if lut.b_curves[0].is_some() {
+ // Prepare B curve
+ let mut transform = Box::new(GammaTable::default());
+ transform.input_clut_table[0] = build_input_gamma_table(lut.b_curves[0].as_deref())
+ .map(|x| (x as Box<[f32]>).into_vec());
+ transform.input_clut_table[1] = build_input_gamma_table(lut.b_curves[1].as_deref())
+ .map(|x| (x as Box<[f32]>).into_vec());
+ transform.input_clut_table[2] = build_input_gamma_table(lut.b_curves[2].as_deref())
+ .map(|x| (x as Box<[f32]>).into_vec());
+ transforms.push(transform);
+ } else {
+ // B curve is mandatory
+ return None;
+ }
+
+ if lut.reversed {
+ // mBA are identical to mAB except that the transformation order
+ // is reversed
+ transforms.reverse();
+ }
+ Some(transforms)
+}
+
+fn modular_transform_create_lut(lut: &lutType) -> Option<Vec<Box<dyn ModularTransform>>> {
+ let mut transforms: Vec<Box<dyn ModularTransform>> = Vec::new();
+
+ let clut_length: usize;
+ let mut transform = Box::new(MatrixTransform::default());
+
+ transform.matrix = build_lut_matrix(lut);
+ if true {
+ transforms.push(transform);
+
+ // Prepare input curves
+ let mut transform = Box::new(Clut3x3::default());
+ transform.input_clut_table[0] =
+ Some(lut.input_table[0..lut.num_input_table_entries as usize].to_vec());
+ transform.input_clut_table[1] = Some(
+ lut.input_table
+ [lut.num_input_table_entries as usize..lut.num_input_table_entries as usize * 2]
+ .to_vec(),
+ );
+ transform.input_clut_table[2] = Some(
+ lut.input_table[lut.num_input_table_entries as usize * 2
+ ..lut.num_input_table_entries as usize * 3]
+ .to_vec(),
+ );
+ // Prepare table
+ clut_length = (lut.num_clut_grid_points as usize).pow(3) * 3;
+ assert_eq!(clut_length, lut.clut_table.len());
+ transform.clut = Some(lut.clut_table.clone());
+
+ transform.grid_size = lut.num_clut_grid_points as u16;
+ // Prepare output curves
+ transform.output_clut_table[0] =
+ Some(lut.output_table[0..lut.num_output_table_entries as usize].to_vec());
+ transform.output_clut_table[1] = Some(
+ lut.output_table
+ [lut.num_output_table_entries as usize..lut.num_output_table_entries as usize * 2]
+ .to_vec(),
+ );
+ transform.output_clut_table[2] = Some(
+ lut.output_table[lut.num_output_table_entries as usize * 2
+ ..lut.num_output_table_entries as usize * 3]
+ .to_vec(),
+ );
+ transforms.push(transform);
+ return Some(transforms);
+ }
+ None
+}
+
+fn modular_transform_create_lut4x3(lut: &lutType) -> Vec<Box<dyn ModularTransform>> {
+ let mut transforms: Vec<Box<dyn ModularTransform>> = Vec::new();
+
+ let clut_length: usize;
+ // the matrix of lutType is only used when the input color space is XYZ.
+
+ // Prepare input curves
+ let mut transform = Box::new(Clut4x3::default());
+ transform.input_clut_table[0] =
+ Some(lut.input_table[0..lut.num_input_table_entries as usize].to_vec());
+ transform.input_clut_table[1] = Some(
+ lut.input_table
+ [lut.num_input_table_entries as usize..lut.num_input_table_entries as usize * 2]
+ .to_vec(),
+ );
+ transform.input_clut_table[2] = Some(
+ lut.input_table
+ [lut.num_input_table_entries as usize * 2..lut.num_input_table_entries as usize * 3]
+ .to_vec(),
+ );
+ transform.input_clut_table[3] = Some(
+ lut.input_table
+ [lut.num_input_table_entries as usize * 3..lut.num_input_table_entries as usize * 4]
+ .to_vec(),
+ );
+ // Prepare table
+ clut_length = (lut.num_clut_grid_points as usize).pow(lut.num_input_channels as u32)
+ * lut.num_output_channels as usize;
+ assert_eq!(clut_length, lut.clut_table.len());
+ transform.clut = Some(lut.clut_table.clone());
+
+ transform.grid_size = lut.num_clut_grid_points as u16;
+ // Prepare output curves
+ transform.output_clut_table[0] =
+ Some(lut.output_table[0..lut.num_output_table_entries as usize].to_vec());
+ transform.output_clut_table[1] = Some(
+ lut.output_table
+ [lut.num_output_table_entries as usize..lut.num_output_table_entries as usize * 2]
+ .to_vec(),
+ );
+ transform.output_clut_table[2] = Some(
+ lut.output_table
+ [lut.num_output_table_entries as usize * 2..lut.num_output_table_entries as usize * 3]
+ .to_vec(),
+ );
+ transforms.push(transform);
+ transforms
+}
+
+fn modular_transform_create_input(input: &Profile) -> Option<Vec<Box<dyn ModularTransform>>> {
+ let mut transforms = Vec::new();
+ if let Some(A2B0) = &input.A2B0 {
+ let lut_transform;
+ if A2B0.num_input_channels == 4 {
+ lut_transform = Some(modular_transform_create_lut4x3(&A2B0));
+ } else {
+ lut_transform = modular_transform_create_lut(&A2B0);
+ }
+ if let Some(lut_transform) = lut_transform {
+ transforms.extend(lut_transform);
+ } else {
+ return None;
+ }
+ } else if input.mAB.is_some()
+ && (*input.mAB.as_deref().unwrap()).num_in_channels == 3
+ && (*input.mAB.as_deref().unwrap()).num_out_channels == 3
+ {
+ let mAB_transform = modular_transform_create_mAB(input.mAB.as_deref().unwrap());
+ if let Some(mAB_transform) = mAB_transform {
+ transforms.extend(mAB_transform);
+ } else {
+ return None;
+ }
+ } else {
+ let mut transform = Box::new(GammaTable::default());
+ transform.input_clut_table[0] =
+ build_input_gamma_table(input.redTRC.as_deref()).map(|x| (x as Box<[f32]>).into_vec());
+ transform.input_clut_table[1] = build_input_gamma_table(input.greenTRC.as_deref())
+ .map(|x| (x as Box<[f32]>).into_vec());
+ transform.input_clut_table[2] =
+ build_input_gamma_table(input.blueTRC.as_deref()).map(|x| (x as Box<[f32]>).into_vec());
+ if transform.input_clut_table[0].is_none()
+ || transform.input_clut_table[1].is_none()
+ || transform.input_clut_table[2].is_none()
+ {
+ return None;
+ } else {
+ transforms.push(transform);
+
+ let mut transform = Box::new(MatrixTransform::default());
+ transform.matrix.m[0][0] = 1. / 1.999_969_5;
+ transform.matrix.m[0][1] = 0.0;
+ transform.matrix.m[0][2] = 0.0;
+ transform.matrix.m[1][0] = 0.0;
+ transform.matrix.m[1][1] = 1. / 1.999_969_5;
+ transform.matrix.m[1][2] = 0.0;
+ transform.matrix.m[2][0] = 0.0;
+ transform.matrix.m[2][1] = 0.0;
+ transform.matrix.m[2][2] = 1. / 1.999_969_5;
+ transforms.push(transform);
+
+ let mut transform = Box::new(MatrixTransform::default());
+ transform.matrix = build_colorant_matrix(input);
+ transforms.push(transform);
+ }
+ }
+ Some(transforms)
+}
+fn modular_transform_create_output(out: &Profile) -> Option<Vec<Box<dyn ModularTransform>>> {
+ let mut transforms = Vec::new();
+ if let Some(B2A0) = &out.B2A0 {
+ if B2A0.num_input_channels != 3 || B2A0.num_output_channels != 3 {
+ return None;
+ }
+ let lut_transform = modular_transform_create_lut(B2A0);
+ if let Some(lut_transform) = lut_transform {
+ transforms.extend(lut_transform);
+ } else {
+ return None;
+ }
+ } else if out.mBA.is_some()
+ && (*out.mBA.as_deref().unwrap()).num_in_channels == 3
+ && (*out.mBA.as_deref().unwrap()).num_out_channels == 3
+ {
+ let lut_transform = modular_transform_create_mAB(out.mBA.as_deref().unwrap());
+ if let Some(lut_transform) = lut_transform {
+ transforms.extend(lut_transform)
+ } else {
+ return None;
+ }
+ } else if let (Some(redTRC), Some(greenTRC), Some(blueTRC)) =
+ (&out.redTRC, &out.greenTRC, &out.blueTRC)
+ {
+ let mut transform = Box::new(MatrixTransform::default());
+ transform.matrix = build_colorant_matrix(out).invert()?;
+ transforms.push(transform);
+
+ let mut transform = Box::new(MatrixTransform::default());
+ transform.matrix.m[0][0] = 1.999_969_5;
+ transform.matrix.m[0][1] = 0.0;
+ transform.matrix.m[0][2] = 0.0;
+ transform.matrix.m[1][0] = 0.0;
+ transform.matrix.m[1][1] = 1.999_969_5;
+ transform.matrix.m[1][2] = 0.0;
+ transform.matrix.m[2][0] = 0.0;
+ transform.matrix.m[2][1] = 0.0;
+ transform.matrix.m[2][2] = 1.999_969_5;
+ transforms.push(transform);
+
+ let mut transform = Box::new(GammaLut::default());
+ transform.output_gamma_lut_r = Some(build_output_lut(redTRC)?);
+ transform.output_gamma_lut_g = Some(build_output_lut(greenTRC)?);
+ transform.output_gamma_lut_b = Some(build_output_lut(blueTRC)?);
+ transforms.push(transform);
+ } else {
+ debug_assert!(false, "Unsupported output profile workflow.");
+ return None;
+ }
+ Some(transforms)
+}
+/* Not Completed
+// Simplify the transformation chain to an equivalent transformation chain
+static struct qcms_modular_transform* qcms_modular_transform_reduce(struct qcms_modular_transform *transform)
+{
+ struct qcms_modular_transform *first_transform = NULL;
+ struct qcms_modular_transform *curr_trans = transform;
+ struct qcms_modular_transform *prev_trans = NULL;
+ while (curr_trans) {
+ struct qcms_modular_transform *next_trans = curr_trans->next_transform;
+ if (curr_trans->transform_module_fn == qcms_transform_module_matrix) {
+ if (next_trans && next_trans->transform_module_fn == qcms_transform_module_matrix) {
+ curr_trans->matrix = matrix_multiply(curr_trans->matrix, next_trans->matrix);
+ goto remove_next;
+ }
+ }
+ if (curr_trans->transform_module_fn == qcms_transform_module_gamma_table) {
+ bool isLinear = true;
+ uint16_t i;
+ for (i = 0; isLinear && i < 256; i++) {
+ isLinear &= (int)(curr_trans->input_clut_table_r[i] * 255) == i;
+ isLinear &= (int)(curr_trans->input_clut_table_g[i] * 255) == i;
+ isLinear &= (int)(curr_trans->input_clut_table_b[i] * 255) == i;
+ }
+ goto remove_current;
+ }
+
+next_transform:
+ if (!next_trans) break;
+ prev_trans = curr_trans;
+ curr_trans = next_trans;
+ continue;
+remove_current:
+ if (curr_trans == transform) {
+ //Update head
+ transform = next_trans;
+ } else {
+ prev_trans->next_transform = next_trans;
+ }
+ curr_trans->next_transform = NULL;
+ qcms_modular_transform_release(curr_trans);
+ //return transform;
+ return qcms_modular_transform_reduce(transform);
+remove_next:
+ curr_trans->next_transform = next_trans->next_transform;
+ next_trans->next_transform = NULL;
+ qcms_modular_transform_release(next_trans);
+ continue;
+ }
+ return transform;
+}
+*/
+fn modular_transform_create(
+ input: &Profile,
+ output: &Profile,
+) -> Option<Vec<Box<dyn ModularTransform>>> {
+ let mut transforms = Vec::new();
+ if input.color_space == RGB_SIGNATURE || input.color_space == CMYK_SIGNATURE {
+ let rgb_to_pcs = modular_transform_create_input(input);
+ if let Some(rgb_to_pcs) = rgb_to_pcs {
+ transforms.extend(rgb_to_pcs);
+ } else {
+ return None;
+ }
+ } else {
+ debug_assert!(false, "input color space not supported");
+ return None;
+ }
+
+ if input.pcs == LAB_SIGNATURE && output.pcs == XYZ_SIGNATURE {
+ transforms.push(Box::new(LABtoXYZ {}));
+ }
+
+ // This does not improve accuracy in practice, something is wrong here.
+ //if (in->chromaticAdaption.invalid == false) {
+ // struct qcms_modular_transform* chromaticAdaption;
+ // chromaticAdaption = qcms_modular_transform_alloc();
+ // if (!chromaticAdaption)
+ // goto fail;
+ // append_transform(chromaticAdaption, &next_transform);
+ // chromaticAdaption->matrix = matrix_invert(in->chromaticAdaption);
+ // chromaticAdaption->transform_module_fn = qcms_transform_module_matrix;
+ //}
+
+ if input.pcs == XYZ_SIGNATURE && output.pcs == LAB_SIGNATURE {
+ transforms.push(Box::new(XYZtoLAB {}));
+ }
+
+ if output.color_space == RGB_SIGNATURE {
+ let pcs_to_rgb = modular_transform_create_output(output);
+ if let Some(pcs_to_rgb) = pcs_to_rgb {
+ transforms.extend(pcs_to_rgb);
+ } else {
+ return None;
+ }
+ } else if output.color_space == CMYK_SIGNATURE {
+ let pcs_to_cmyk = modular_transform_create_output(output)?;
+ transforms.extend(pcs_to_cmyk);
+ } else {
+ debug_assert!(false, "output color space not supported");
+ }
+
+ // Not Completed
+ //return qcms_modular_transform_reduce(first_transform);
+ Some(transforms)
+}
+fn modular_transform_data(
+ transforms: Vec<Box<dyn ModularTransform>>,
+ mut src: Vec<f32>,
+ mut dest: Vec<f32>,
+ _len: usize,
+) -> Vec<f32> {
+ for transform in transforms {
+ // Keep swaping src/dest when performing a transform to use less memory.
+ transform.transform(&src, &mut dest);
+ std::mem::swap(&mut src, &mut dest);
+ }
+ // The results end up in the src buffer because of the switching
+ src
+}
+
+pub fn chain_transform(
+ input: &Profile,
+ output: &Profile,
+ src: Vec<f32>,
+ dest: Vec<f32>,
+ lutSize: usize,
+) -> Option<Vec<f32>> {
+ let transform_list = modular_transform_create(input, output);
+ if let Some(transform_list) = transform_list {
+ let lut = modular_transform_data(transform_list, src, dest, lutSize / 3);
+ return Some(lut);
+ }
+ None
+}