tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }