Home | History | Annotate | Download | only in src
      1 /* vim: set ts=8 sw=8 noexpandtab: */
      2 //  qcms
      3 //  Copyright (C) 2009 Mozilla Corporation
      4 //  Copyright (C) 1998-2007 Marti Maria
      5 //
      6 // Permission is hereby granted, free of charge, to any person obtaining
      7 // a copy of this software and associated documentation files (the "Software"),
      8 // to deal in the Software without restriction, including without limitation
      9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10 // and/or sell copies of the Software, and to permit persons to whom the Software
     11 // is furnished to do so, subject to the following conditions:
     12 //
     13 // The above copyright notice and this permission notice shall be included in
     14 // all copies or substantial portions of the Software.
     15 //
     16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
     18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     23 
     24 #include <stdlib.h>
     25 #include <math.h>
     26 #include <assert.h>
     27 #include <string.h> //memcpy
     28 #include "qcmsint.h"
     29 #include "transform_util.h"
     30 #include "matrix.h"
     31 
     32 static struct matrix build_lut_matrix(struct lutType *lut)
     33 {
     34 	struct matrix result;
     35 	if (lut) {
     36 		result.m[0][0] = s15Fixed16Number_to_float(lut->e00);
     37 		result.m[0][1] = s15Fixed16Number_to_float(lut->e01);
     38 		result.m[0][2] = s15Fixed16Number_to_float(lut->e02);
     39 		result.m[1][0] = s15Fixed16Number_to_float(lut->e10);
     40 		result.m[1][1] = s15Fixed16Number_to_float(lut->e11);
     41 		result.m[1][2] = s15Fixed16Number_to_float(lut->e12);
     42 		result.m[2][0] = s15Fixed16Number_to_float(lut->e20);
     43 		result.m[2][1] = s15Fixed16Number_to_float(lut->e21);
     44 		result.m[2][2] = s15Fixed16Number_to_float(lut->e22);
     45 		result.invalid = false;
     46 	} else {
     47 		memset(&result, 0, sizeof(struct matrix));
     48 		result.invalid = true;
     49 	}
     50 	return result;
     51 }
     52 
     53 static struct matrix build_mAB_matrix(struct lutmABType *lut)
     54 {
     55 	struct matrix result;
     56 	if (lut) {
     57 		result.m[0][0] = s15Fixed16Number_to_float(lut->e00);
     58 		result.m[0][1] = s15Fixed16Number_to_float(lut->e01);
     59 		result.m[0][2] = s15Fixed16Number_to_float(lut->e02);
     60 		result.m[1][0] = s15Fixed16Number_to_float(lut->e10);
     61 		result.m[1][1] = s15Fixed16Number_to_float(lut->e11);
     62 		result.m[1][2] = s15Fixed16Number_to_float(lut->e12);
     63 		result.m[2][0] = s15Fixed16Number_to_float(lut->e20);
     64 		result.m[2][1] = s15Fixed16Number_to_float(lut->e21);
     65 		result.m[2][2] = s15Fixed16Number_to_float(lut->e22);
     66 		result.invalid = false;
     67 	} else {
     68 		memset(&result, 0, sizeof(struct matrix));
     69 		result.invalid = true;
     70 	}
     71 	return result;
     72 }
     73 
     74 //Based on lcms cmsLab2XYZ
     75 #define f(t) (t <= (24.0f/116.0f)*(24.0f/116.0f)*(24.0f/116.0f)) ? ((841.0/108.0) * t + (16.0/116.0)) : pow(t,1.0/3.0)
     76 #define f_1(t) (t <= (24.0f/116.0f)) ? ((108.0/841.0) * (t - (16.0/116.0))) : (t * t * t)
     77 static void qcms_transform_module_LAB_to_XYZ(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
     78 {
     79 	size_t i;
     80 	// lcms: D50 XYZ values
     81 	float WhitePointX = 0.9642f;
     82 	float WhitePointY = 1.0f;
     83 	float WhitePointZ = 0.8249f;
     84 	for (i = 0; i < length; i++) {
     85 		float device_L = *src++ * 100.0f;
     86 		float device_a = *src++ * 255.0f - 128.0f;
     87 		float device_b = *src++ * 255.0f - 128.0f;
     88 		float y = (device_L + 16.0f) / 116.0f;
     89 
     90 		float X = f_1((y + 0.002f * device_a)) * WhitePointX;
     91 		float Y = f_1(y) * WhitePointY;
     92 		float Z = f_1((y - 0.005f * device_b)) * WhitePointZ;
     93 		*dest++ = X / (1.0 + 32767.0/32768.0);
     94 		*dest++ = Y / (1.0 + 32767.0/32768.0);
     95 		*dest++ = Z / (1.0 + 32767.0/32768.0);
     96 	}
     97 }
     98 
     99 //Based on lcms cmsXYZ2Lab
    100 static void qcms_transform_module_XYZ_to_LAB(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
    101 {
    102 	size_t i;
    103         // lcms: D50 XYZ values
    104         float WhitePointX = 0.9642f;
    105         float WhitePointY = 1.0f;
    106         float WhitePointZ = 0.8249f;
    107         for (i = 0; i < length; i++) {
    108                 float device_x = *src++ * (1.0 + 32767.0/32768.0) / WhitePointX;
    109                 float device_y = *src++ * (1.0 + 32767.0/32768.0) / WhitePointY;
    110                 float device_z = *src++ * (1.0 + 32767.0/32768.0) / WhitePointZ;
    111 
    112 		float fx = f(device_x);
    113 		float fy = f(device_y);
    114 		float fz = f(device_z);
    115 
    116                 float L = 116.0f*fy - 16.0f;
    117                 float a = 500.0f*(fx - fy);
    118                 float b = 200.0f*(fy - fz);
    119                 *dest++ = L / 100.0f;
    120                 *dest++ = (a+128.0f) / 255.0f;
    121                 *dest++ = (b+128.0f) / 255.0f;
    122         }
    123 
    124 }
    125 
    126 static void qcms_transform_module_clut_only(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
    127 {
    128 	size_t i;
    129 	int xy_len = 1;
    130 	int x_len = transform->grid_size;
    131 	int len = x_len * x_len;
    132 	float* r_table = transform->r_clut;
    133 	float* g_table = transform->g_clut;
    134 	float* b_table = transform->b_clut;
    135 
    136 	for (i = 0; i < length; i++) {
    137 		float linear_r = *src++;
    138 		float linear_g = *src++;
    139 		float linear_b = *src++;
    140 
    141 		int x = floor(linear_r * (transform->grid_size-1));
    142 		int y = floor(linear_g * (transform->grid_size-1));
    143 		int z = floor(linear_b * (transform->grid_size-1));
    144 		int x_n = ceil(linear_r * (transform->grid_size-1));
    145 		int y_n = ceil(linear_g * (transform->grid_size-1));
    146 		int z_n = ceil(linear_b * (transform->grid_size-1));
    147 		float x_d = linear_r * (transform->grid_size-1) - x;
    148 		float y_d = linear_g * (transform->grid_size-1) - y;
    149 		float z_d = linear_b * (transform->grid_size-1) - z;
    150 
    151 		float r_x1 = lerp(CLU(r_table,x,y,z), CLU(r_table,x_n,y,z), x_d);
    152 		float r_x2 = lerp(CLU(r_table,x,y_n,z), CLU(r_table,x_n,y_n,z), x_d);
    153 		float r_y1 = lerp(r_x1, r_x2, y_d);
    154 		float r_x3 = lerp(CLU(r_table,x,y,z_n), CLU(r_table,x_n,y,z_n), x_d);
    155 		float r_x4 = lerp(CLU(r_table,x,y_n,z_n), CLU(r_table,x_n,y_n,z_n), x_d);
    156 		float r_y2 = lerp(r_x3, r_x4, y_d);
    157 		float clut_r = lerp(r_y1, r_y2, z_d);
    158 
    159 		float g_x1 = lerp(CLU(g_table,x,y,z), CLU(g_table,x_n,y,z), x_d);
    160 		float g_x2 = lerp(CLU(g_table,x,y_n,z), CLU(g_table,x_n,y_n,z), x_d);
    161 		float g_y1 = lerp(g_x1, g_x2, y_d);
    162 		float g_x3 = lerp(CLU(g_table,x,y,z_n), CLU(g_table,x_n,y,z_n), x_d);
    163 		float g_x4 = lerp(CLU(g_table,x,y_n,z_n), CLU(g_table,x_n,y_n,z_n), x_d);
    164 		float g_y2 = lerp(g_x3, g_x4, y_d);
    165 		float clut_g = lerp(g_y1, g_y2, z_d);
    166 
    167 		float b_x1 = lerp(CLU(b_table,x,y,z), CLU(b_table,x_n,y,z), x_d);
    168 		float b_x2 = lerp(CLU(b_table,x,y_n,z), CLU(b_table,x_n,y_n,z), x_d);
    169 		float b_y1 = lerp(b_x1, b_x2, y_d);
    170 		float b_x3 = lerp(CLU(b_table,x,y,z_n), CLU(b_table,x_n,y,z_n), x_d);
    171 		float b_x4 = lerp(CLU(b_table,x,y_n,z_n), CLU(b_table,x_n,y_n,z_n), x_d);
    172 		float b_y2 = lerp(b_x3, b_x4, y_d);
    173 		float clut_b = lerp(b_y1, b_y2, z_d);
    174 
    175 		*dest++ = clamp_float(clut_r);
    176 		*dest++ = clamp_float(clut_g);
    177 		*dest++ = clamp_float(clut_b);
    178 	}
    179 }
    180 
    181 static void qcms_transform_module_clut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
    182 {
    183 	size_t i;
    184 	int xy_len = 1;
    185 	int x_len = transform->grid_size;
    186 	int len = x_len * x_len;
    187 	float* r_table = transform->r_clut;
    188 	float* g_table = transform->g_clut;
    189 	float* b_table = transform->b_clut;
    190 	for (i = 0; i < length; i++) {
    191 		float device_r = *src++;
    192 		float device_g = *src++;
    193 		float device_b = *src++;
    194 		float linear_r = lut_interp_linear_float(device_r,
    195 				transform->input_clut_table_r, transform->input_clut_table_length);
    196 		float linear_g = lut_interp_linear_float(device_g,
    197 				transform->input_clut_table_g, transform->input_clut_table_length);
    198 		float linear_b = lut_interp_linear_float(device_b,
    199 				transform->input_clut_table_b, transform->input_clut_table_length);
    200 
    201 		int x = floor(linear_r * (transform->grid_size-1));
    202 		int y = floor(linear_g * (transform->grid_size-1));
    203 		int z = floor(linear_b * (transform->grid_size-1));
    204 		int x_n = ceil(linear_r * (transform->grid_size-1));
    205 		int y_n = ceil(linear_g * (transform->grid_size-1));
    206 		int z_n = ceil(linear_b * (transform->grid_size-1));
    207 		float x_d = linear_r * (transform->grid_size-1) - x;
    208 		float y_d = linear_g * (transform->grid_size-1) - y;
    209 		float z_d = linear_b * (transform->grid_size-1) - z;
    210 
    211 		float r_x1 = lerp(CLU(r_table,x,y,z), CLU(r_table,x_n,y,z), x_d);
    212 		float r_x2 = lerp(CLU(r_table,x,y_n,z), CLU(r_table,x_n,y_n,z), x_d);
    213 		float r_y1 = lerp(r_x1, r_x2, y_d);
    214 		float r_x3 = lerp(CLU(r_table,x,y,z_n), CLU(r_table,x_n,y,z_n), x_d);
    215 		float r_x4 = lerp(CLU(r_table,x,y_n,z_n), CLU(r_table,x_n,y_n,z_n), x_d);
    216 		float r_y2 = lerp(r_x3, r_x4, y_d);
    217 		float clut_r = lerp(r_y1, r_y2, z_d);
    218 
    219 		float g_x1 = lerp(CLU(g_table,x,y,z), CLU(g_table,x_n,y,z), x_d);
    220 		float g_x2 = lerp(CLU(g_table,x,y_n,z), CLU(g_table,x_n,y_n,z), x_d);
    221 		float g_y1 = lerp(g_x1, g_x2, y_d);
    222 		float g_x3 = lerp(CLU(g_table,x,y,z_n), CLU(g_table,x_n,y,z_n), x_d);
    223 		float g_x4 = lerp(CLU(g_table,x,y_n,z_n), CLU(g_table,x_n,y_n,z_n), x_d);
    224 		float g_y2 = lerp(g_x3, g_x4, y_d);
    225 		float clut_g = lerp(g_y1, g_y2, z_d);
    226 
    227 		float b_x1 = lerp(CLU(b_table,x,y,z), CLU(b_table,x_n,y,z), x_d);
    228 		float b_x2 = lerp(CLU(b_table,x,y_n,z), CLU(b_table,x_n,y_n,z), x_d);
    229 		float b_y1 = lerp(b_x1, b_x2, y_d);
    230 		float b_x3 = lerp(CLU(b_table,x,y,z_n), CLU(b_table,x_n,y,z_n), x_d);
    231 		float b_x4 = lerp(CLU(b_table,x,y_n,z_n), CLU(b_table,x_n,y_n,z_n), x_d);
    232 		float b_y2 = lerp(b_x3, b_x4, y_d);
    233 		float clut_b = lerp(b_y1, b_y2, z_d);
    234 
    235 		float pcs_r = lut_interp_linear_float(clut_r,
    236 				transform->output_clut_table_r, transform->output_clut_table_length);
    237 		float pcs_g = lut_interp_linear_float(clut_g,
    238 				transform->output_clut_table_g, transform->output_clut_table_length);
    239 		float pcs_b = lut_interp_linear_float(clut_b,
    240 				transform->output_clut_table_b, transform->output_clut_table_length);
    241 
    242 		*dest++ = clamp_float(pcs_r);
    243 		*dest++ = clamp_float(pcs_g);
    244 		*dest++ = clamp_float(pcs_b);
    245 	}
    246 }
    247 
    248 /* NOT USED
    249 static void qcms_transform_module_tetra_clut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
    250 {
    251 	size_t i;
    252 	int xy_len = 1;
    253 	int x_len = transform->grid_size;
    254 	int len = x_len * x_len;
    255 	float* r_table = transform->r_clut;
    256 	float* g_table = transform->g_clut;
    257 	float* b_table = transform->b_clut;
    258 	float c0_r, c1_r, c2_r, c3_r;
    259 	float c0_g, c1_g, c2_g, c3_g;
    260 	float c0_b, c1_b, c2_b, c3_b;
    261 	float clut_r, clut_g, clut_b;
    262 	float pcs_r, pcs_g, pcs_b;
    263 	for (i = 0; i < length; i++) {
    264 		float device_r = *src++;
    265 		float device_g = *src++;
    266 		float device_b = *src++;
    267 		float linear_r = lut_interp_linear_float(device_r,
    268 				transform->input_clut_table_r, transform->input_clut_table_length);
    269 		float linear_g = lut_interp_linear_float(device_g,
    270 				transform->input_clut_table_g, transform->input_clut_table_length);
    271 		float linear_b = lut_interp_linear_float(device_b,
    272 				transform->input_clut_table_b, transform->input_clut_table_length);
    273 
    274 		int x = floor(linear_r * (transform->grid_size-1));
    275 		int y = floor(linear_g * (transform->grid_size-1));
    276 		int z = floor(linear_b * (transform->grid_size-1));
    277 		int x_n = ceil(linear_r * (transform->grid_size-1));
    278 		int y_n = ceil(linear_g * (transform->grid_size-1));
    279 		int z_n = ceil(linear_b * (transform->grid_size-1));
    280 		float rx = linear_r * (transform->grid_size-1) - x;
    281 		float ry = linear_g * (transform->grid_size-1) - y;
    282 		float rz = linear_b * (transform->grid_size-1) - z;
    283 
    284 		c0_r = CLU(r_table, x, y, z);
    285 		c0_g = CLU(g_table, x, y, z);
    286 		c0_b = CLU(b_table, x, y, z);
    287 		if( rx >= ry ) {
    288 			if (ry >= rz) { //rx >= ry && ry >= rz
    289 				c1_r = CLU(r_table, x_n, y, z) - c0_r;
    290 				c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x_n, y, z);
    291 				c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
    292 				c1_g = CLU(g_table, x_n, y, z) - c0_g;
    293 				c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x_n, y, z);
    294 				c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
    295 				c1_b = CLU(b_table, x_n, y, z) - c0_b;
    296 				c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x_n, y, z);
    297 				c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
    298 			} else {
    299 				if (rx >= rz) { //rx >= rz && rz >= ry
    300 					c1_r = CLU(r_table, x_n, y, z) - c0_r;
    301 					c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
    302 					c3_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x_n, y, z);
    303 					c1_g = CLU(g_table, x_n, y, z) - c0_g;
    304 					c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
    305 					c3_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x_n, y, z);
    306 					c1_b = CLU(b_table, x_n, y, z) - c0_b;
    307 					c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
    308 					c3_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x_n, y, z);
    309 				} else { //rz > rx && rx >= ry
    310 					c1_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x, y, z_n);
    311 					c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
    312 					c3_r = CLU(r_table, x, y, z_n) - c0_r;
    313 					c1_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x, y, z_n);
    314 					c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
    315 					c3_g = CLU(g_table, x, y, z_n) - c0_g;
    316 					c1_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x, y, z_n);
    317 					c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
    318 					c3_b = CLU(b_table, x, y, z_n) - c0_b;
    319 				}
    320 			}
    321 		} else {
    322 			if (rx >= rz) { //ry > rx && rx >= rz
    323 				c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x, y_n, z);
    324 				c2_r = CLU(r_table, x_n, y_n, z) - c0_r;
    325 				c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
    326 				c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x, y_n, z);
    327 				c2_g = CLU(g_table, x_n, y_n, z) - c0_g;
    328 				c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
    329 				c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x, y_n, z);
    330 				c2_b = CLU(b_table, x_n, y_n, z) - c0_b;
    331 				c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
    332 			} else {
    333 				if (ry >= rz) { //ry >= rz && rz > rx
    334 					c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
    335 					c2_r = CLU(r_table, x, y_n, z) - c0_r;
    336 					c3_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y_n, z);
    337 					c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
    338 					c2_g = CLU(g_table, x, y_n, z) - c0_g;
    339 					c3_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y_n, z);
    340 					c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
    341 					c2_b = CLU(b_table, x, y_n, z) - c0_b;
    342 					c3_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y_n, z);
    343 				} else { //rz > ry && ry > rx
    344 					c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
    345 					c2_r = CLU(r_table, x, y_n, z) - c0_r;
    346 					c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
    347 					c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
    348 					c2_g = CLU(g_table, x, y_n, z) - c0_g;
    349 					c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
    350 					c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
    351 					c2_b = CLU(b_table, x, y_n, z) - c0_b;
    352 					c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
    353 				}
    354 			}
    355 		}
    356 
    357 		clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz;
    358 		clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz;
    359 		clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz;
    360 
    361 		pcs_r = lut_interp_linear_float(clut_r,
    362 				transform->output_clut_table_r, transform->output_clut_table_length);
    363 		pcs_g = lut_interp_linear_float(clut_g,
    364 				transform->output_clut_table_g, transform->output_clut_table_length);
    365 		pcs_b = lut_interp_linear_float(clut_b,
    366 				transform->output_clut_table_b, transform->output_clut_table_length);
    367 		*dest++ = clamp_float(pcs_r);
    368 		*dest++ = clamp_float(pcs_g);
    369 		*dest++ = clamp_float(pcs_b);
    370 	}
    371 }
    372 */
    373 
    374 static void qcms_transform_module_gamma_table(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
    375 {
    376 	size_t i;
    377 	float out_r, out_g, out_b;
    378 	for (i = 0; i < length; i++) {
    379 		float in_r = *src++;
    380 		float in_g = *src++;
    381 		float in_b = *src++;
    382 
    383 		out_r = lut_interp_linear_float(in_r, transform->input_clut_table_r, 256);
    384 		out_g = lut_interp_linear_float(in_g, transform->input_clut_table_g, 256);
    385 		out_b = lut_interp_linear_float(in_b, transform->input_clut_table_b, 256);
    386 
    387 		*dest++ = clamp_float(out_r);
    388 		*dest++ = clamp_float(out_g);
    389 		*dest++ = clamp_float(out_b);
    390 	}
    391 }
    392 
    393 static void qcms_transform_module_gamma_lut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
    394 {
    395 	size_t i;
    396 	float out_r, out_g, out_b;
    397 	for (i = 0; i < length; i++) {
    398 		float in_r = *src++;
    399 		float in_g = *src++;
    400 		float in_b = *src++;
    401 
    402 		out_r = lut_interp_linear(in_r,
    403 				transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
    404 		out_g = lut_interp_linear(in_g,
    405 				transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
    406 		out_b = lut_interp_linear(in_b,
    407 				transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
    408 
    409 		*dest++ = clamp_float(out_r);
    410 		*dest++ = clamp_float(out_g);
    411 		*dest++ = clamp_float(out_b);
    412 	}
    413 }
    414 
    415 static void qcms_transform_module_matrix_translate(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
    416 {
    417 	size_t i;
    418 	struct matrix mat;
    419 
    420 	/* store the results in column major mode
    421 	 * this makes doing the multiplication with sse easier */
    422 	mat.m[0][0] = transform->matrix.m[0][0];
    423 	mat.m[1][0] = transform->matrix.m[0][1];
    424 	mat.m[2][0] = transform->matrix.m[0][2];
    425 	mat.m[0][1] = transform->matrix.m[1][0];
    426 	mat.m[1][1] = transform->matrix.m[1][1];
    427 	mat.m[2][1] = transform->matrix.m[1][2];
    428 	mat.m[0][2] = transform->matrix.m[2][0];
    429 	mat.m[1][2] = transform->matrix.m[2][1];
    430 	mat.m[2][2] = transform->matrix.m[2][2];
    431 
    432 	for (i = 0; i < length; i++) {
    433 		float in_r = *src++;
    434 		float in_g = *src++;
    435 		float in_b = *src++;
    436 
    437 		float out_r = mat.m[0][0]*in_r + mat.m[1][0]*in_g + mat.m[2][0]*in_b + transform->tx;
    438 		float out_g = mat.m[0][1]*in_r + mat.m[1][1]*in_g + mat.m[2][1]*in_b + transform->ty;
    439 		float out_b = mat.m[0][2]*in_r + mat.m[1][2]*in_g + mat.m[2][2]*in_b + transform->tz;
    440 
    441 		*dest++ = clamp_float(out_r);
    442 		*dest++ = clamp_float(out_g);
    443 		*dest++ = clamp_float(out_b);
    444 	}
    445 }
    446 
    447 static void qcms_transform_module_matrix(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
    448 {
    449 	size_t i;
    450 	struct matrix mat;
    451 
    452 	/* store the results in column major mode
    453 	 * this makes doing the multiplication with sse easier */
    454 	mat.m[0][0] = transform->matrix.m[0][0];
    455 	mat.m[1][0] = transform->matrix.m[0][1];
    456 	mat.m[2][0] = transform->matrix.m[0][2];
    457 	mat.m[0][1] = transform->matrix.m[1][0];
    458 	mat.m[1][1] = transform->matrix.m[1][1];
    459 	mat.m[2][1] = transform->matrix.m[1][2];
    460 	mat.m[0][2] = transform->matrix.m[2][0];
    461 	mat.m[1][2] = transform->matrix.m[2][1];
    462 	mat.m[2][2] = transform->matrix.m[2][2];
    463 
    464 	for (i = 0; i < length; i++) {
    465 		float in_r = *src++;
    466 		float in_g = *src++;
    467 		float in_b = *src++;
    468 
    469 		float out_r = mat.m[0][0]*in_r + mat.m[1][0]*in_g + mat.m[2][0]*in_b;
    470 		float out_g = mat.m[0][1]*in_r + mat.m[1][1]*in_g + mat.m[2][1]*in_b;
    471 		float out_b = mat.m[0][2]*in_r + mat.m[1][2]*in_g + mat.m[2][2]*in_b;
    472 
    473 		*dest++ = clamp_float(out_r);
    474 		*dest++ = clamp_float(out_g);
    475 		*dest++ = clamp_float(out_b);
    476 	}
    477 }
    478 
    479 static struct qcms_modular_transform* qcms_modular_transform_alloc() {
    480 	return calloc(1, sizeof(struct qcms_modular_transform));
    481 }
    482 
    483 static void qcms_modular_transform_release(struct qcms_modular_transform *transform)
    484 {
    485 	struct qcms_modular_transform *next_transform;
    486 	while (transform != NULL) {
    487 		next_transform = transform->next_transform;
    488 		// clut may use a single block of memory.
    489 		// Perhaps we should remove this to simply the code.
    490 		if (transform->input_clut_table_r + transform->input_clut_table_length == transform->input_clut_table_g && transform->input_clut_table_g + transform->input_clut_table_length == transform->input_clut_table_b) {
    491 			if (transform->input_clut_table_r) free(transform->input_clut_table_r);
    492 		} else {
    493 			if (transform->input_clut_table_r) free(transform->input_clut_table_r);
    494 			if (transform->input_clut_table_g) free(transform->input_clut_table_g);
    495 			if (transform->input_clut_table_b) free(transform->input_clut_table_b);
    496 		}
    497 		if (transform->r_clut + 1 == transform->g_clut && transform->g_clut + 1 == transform->b_clut) {
    498 			if (transform->r_clut) free(transform->r_clut);
    499 		} else {
    500 			if (transform->r_clut) free(transform->r_clut);
    501 			if (transform->g_clut) free(transform->g_clut);
    502 			if (transform->b_clut) free(transform->b_clut);
    503 		}
    504 		if (transform->output_clut_table_r + transform->output_clut_table_length == transform->output_clut_table_g && transform->output_clut_table_g+ transform->output_clut_table_length == transform->output_clut_table_b) {
    505 			if (transform->output_clut_table_r) free(transform->output_clut_table_r);
    506 		} else {
    507 			if (transform->output_clut_table_r) free(transform->output_clut_table_r);
    508 			if (transform->output_clut_table_g) free(transform->output_clut_table_g);
    509 			if (transform->output_clut_table_b) free(transform->output_clut_table_b);
    510 		}
    511 		if (transform->output_gamma_lut_r) free(transform->output_gamma_lut_r);
    512 		if (transform->output_gamma_lut_g) free(transform->output_gamma_lut_g);
    513 		if (transform->output_gamma_lut_b) free(transform->output_gamma_lut_b);
    514 		free(transform);
    515 		transform = next_transform;
    516 	}
    517 }
    518 
    519 /* Set transform to be the next element in the linked list. */
    520 static void append_transform(struct qcms_modular_transform *transform, struct qcms_modular_transform ***next_transform)
    521 {
    522 	**next_transform = transform;
    523 	while (transform) {
    524 		*next_transform = &(transform->next_transform);
    525 		transform = transform->next_transform;
    526 	}
    527 }
    528 
    529 /* reverse the transformation list (used by mBA) */
    530 static struct qcms_modular_transform* reverse_transform(struct qcms_modular_transform *transform)
    531 {
    532 	struct qcms_modular_transform *prev_transform = NULL;
    533 	while (transform != NULL) {
    534 		struct qcms_modular_transform *next_transform = transform->next_transform;
    535 		transform->next_transform = prev_transform;
    536 		prev_transform = transform;
    537 		transform = next_transform;
    538 	}
    539 
    540 	return prev_transform;
    541 }
    542 
    543 #define EMPTY_TRANSFORM_LIST NULL
    544 static struct qcms_modular_transform* qcms_modular_transform_create_mAB(struct lutmABType *lut)
    545 {
    546 	struct qcms_modular_transform *first_transform = NULL;
    547 	struct qcms_modular_transform **next_transform = &first_transform;
    548 	struct qcms_modular_transform *transform = NULL;
    549 
    550 	if (lut->a_curves[0] != NULL) {
    551 		size_t clut_length;
    552 		float *clut;
    553 
    554 		// If the A curve is present this also implies the
    555 		// presence of a CLUT.
    556 		if (!lut->clut_table)
    557 			goto fail;
    558 
    559 		// Prepare A curve.
    560 		transform = qcms_modular_transform_alloc();
    561 		if (!transform)
    562 			goto fail;
    563 		append_transform(transform, &next_transform);
    564 		transform->input_clut_table_r = build_input_gamma_table(lut->a_curves[0]);
    565 		transform->input_clut_table_g = build_input_gamma_table(lut->a_curves[1]);
    566 		transform->input_clut_table_b = build_input_gamma_table(lut->a_curves[2]);
    567 		transform->transform_module_fn = qcms_transform_module_gamma_table;
    568 		if (lut->num_grid_points[0] != lut->num_grid_points[1] ||
    569 			lut->num_grid_points[1] != lut->num_grid_points[2] ) {
    570 			//XXX: We don't currently support clut that are not squared!
    571 			goto fail;
    572 		}
    573 
    574 		// Prepare CLUT
    575 		transform = qcms_modular_transform_alloc();
    576 		if (!transform)
    577 			goto fail;
    578 		append_transform(transform, &next_transform);
    579 		clut_length = sizeof(float)*pow(lut->num_grid_points[0], 3)*3;
    580 		clut = malloc(clut_length);
    581 		if (!clut)
    582 			goto fail;
    583 		memcpy(clut, lut->clut_table, clut_length);
    584 		transform->r_clut = clut + 0;
    585 		transform->g_clut = clut + 1;
    586 		transform->b_clut = clut + 2;
    587 		transform->grid_size = lut->num_grid_points[0];
    588 		transform->transform_module_fn = qcms_transform_module_clut_only;
    589 	}
    590 	if (lut->m_curves[0] != NULL) {
    591 		// M curve imples the presence of a Matrix
    592 
    593 		// Prepare M curve
    594 		transform = qcms_modular_transform_alloc();
    595 		if (!transform)
    596 			goto fail;
    597 		append_transform(transform, &next_transform);
    598 		transform->input_clut_table_r = build_input_gamma_table(lut->m_curves[0]);
    599 		transform->input_clut_table_g = build_input_gamma_table(lut->m_curves[1]);
    600 		transform->input_clut_table_b = build_input_gamma_table(lut->m_curves[2]);
    601 		transform->transform_module_fn = qcms_transform_module_gamma_table;
    602 
    603 		// Prepare Matrix
    604 		transform = qcms_modular_transform_alloc();
    605 		if (!transform)
    606 			goto fail;
    607 		append_transform(transform, &next_transform);
    608 		transform->matrix = build_mAB_matrix(lut);
    609 		if (transform->matrix.invalid)
    610 			goto fail;
    611 		transform->tx = s15Fixed16Number_to_float(lut->e03);
    612 		transform->ty = s15Fixed16Number_to_float(lut->e13);
    613 		transform->tz = s15Fixed16Number_to_float(lut->e23);
    614 		transform->transform_module_fn = qcms_transform_module_matrix_translate;
    615 	}
    616 	if (lut->b_curves[0] != NULL) {
    617 		// Prepare B curve
    618 		transform = qcms_modular_transform_alloc();
    619 		if (!transform)
    620 			goto fail;
    621 		append_transform(transform, &next_transform);
    622 		transform->input_clut_table_r = build_input_gamma_table(lut->b_curves[0]);
    623 		transform->input_clut_table_g = build_input_gamma_table(lut->b_curves[1]);
    624 		transform->input_clut_table_b = build_input_gamma_table(lut->b_curves[2]);
    625 		transform->transform_module_fn = qcms_transform_module_gamma_table;
    626 	} else {
    627 		// B curve is mandatory
    628 		goto fail;
    629 	}
    630 
    631 	if (lut->reversed) {
    632 		// mBA are identical to mAB except that the transformation order
    633 		// is reversed
    634 		first_transform = reverse_transform(first_transform);
    635 	}
    636 
    637 	return first_transform;
    638 fail:
    639 	qcms_modular_transform_release(first_transform);
    640 	return NULL;
    641 }
    642 
    643 static struct qcms_modular_transform* qcms_modular_transform_create_lut(struct lutType *lut)
    644 {
    645 	struct qcms_modular_transform *first_transform = NULL;
    646 	struct qcms_modular_transform **next_transform = &first_transform;
    647 	struct qcms_modular_transform *transform = NULL;
    648 
    649 	size_t in_curve_len, clut_length, out_curve_len;
    650 	float *in_curves, *clut, *out_curves;
    651 
    652 	// Prepare Matrix
    653 	transform = qcms_modular_transform_alloc();
    654 	if (!transform)
    655 		goto fail;
    656 	append_transform(transform, &next_transform);
    657 	transform->matrix = build_lut_matrix(lut);
    658 	if (transform->matrix.invalid)
    659 		goto fail;
    660 	transform->transform_module_fn = qcms_transform_module_matrix;
    661 
    662 	// Prepare input curves
    663 	transform = qcms_modular_transform_alloc();
    664 	if (!transform)
    665 		goto fail;
    666 	append_transform(transform, &next_transform);
    667 	in_curve_len = sizeof(float)*lut->num_input_table_entries * 3;
    668 	in_curves = malloc(in_curve_len);
    669 	if (!in_curves)
    670 		goto fail;
    671 	memcpy(in_curves, lut->input_table, in_curve_len);
    672 	transform->input_clut_table_r = in_curves + lut->num_input_table_entries * 0;
    673 	transform->input_clut_table_g = in_curves + lut->num_input_table_entries * 1;
    674 	transform->input_clut_table_b = in_curves + lut->num_input_table_entries * 2;
    675 	transform->input_clut_table_length = lut->num_input_table_entries;
    676 
    677 	// Prepare table
    678 	clut_length = sizeof(float)*pow(lut->num_clut_grid_points, 3)*3;
    679 	clut = malloc(clut_length);
    680 	if (!clut)
    681 		goto fail;
    682 	memcpy(clut, lut->clut_table, clut_length);
    683 	transform->r_clut = clut + 0;
    684 	transform->g_clut = clut + 1;
    685 	transform->b_clut = clut + 2;
    686 	transform->grid_size = lut->num_clut_grid_points;
    687 
    688 	// Prepare output curves
    689 	out_curve_len = sizeof(float) * lut->num_output_table_entries * 3;
    690 	out_curves = malloc(out_curve_len);
    691 	if (!out_curves)
    692 		goto fail;
    693 	memcpy(out_curves, lut->output_table, out_curve_len);
    694 	transform->output_clut_table_r = out_curves + lut->num_output_table_entries * 0;
    695 	transform->output_clut_table_g = out_curves + lut->num_output_table_entries * 1;
    696 	transform->output_clut_table_b = out_curves + lut->num_output_table_entries * 2;
    697 	transform->output_clut_table_length = lut->num_output_table_entries;
    698 	transform->transform_module_fn = qcms_transform_module_clut;
    699 
    700 	return first_transform;
    701 fail:
    702 	qcms_modular_transform_release(first_transform);
    703 	return NULL;
    704 }
    705 
    706 struct qcms_modular_transform* qcms_modular_transform_create_input(qcms_profile *in)
    707 {
    708 	struct qcms_modular_transform *first_transform = NULL;
    709 	struct qcms_modular_transform **next_transform = &first_transform;
    710 
    711 	if (in->A2B0) {
    712 		struct qcms_modular_transform *lut_transform;
    713 		lut_transform = qcms_modular_transform_create_lut(in->A2B0);
    714 		if (!lut_transform)
    715 			goto fail;
    716 		append_transform(lut_transform, &next_transform);
    717 	} else if (in->mAB && in->mAB->num_in_channels == 3 && in->mAB->num_out_channels == 3) {
    718 		struct qcms_modular_transform *mAB_transform;
    719 		mAB_transform = qcms_modular_transform_create_mAB(in->mAB);
    720 		if (!mAB_transform)
    721 			goto fail;
    722 		append_transform(mAB_transform, &next_transform);
    723 
    724 	} else {
    725 		struct qcms_modular_transform *transform;
    726 
    727 		transform = qcms_modular_transform_alloc();
    728 		if (!transform)
    729 			goto fail;
    730 		append_transform(transform, &next_transform);
    731 		transform->input_clut_table_r = build_input_gamma_table(in->redTRC);
    732 		transform->input_clut_table_g = build_input_gamma_table(in->greenTRC);
    733 		transform->input_clut_table_b = build_input_gamma_table(in->blueTRC);
    734 		transform->transform_module_fn = qcms_transform_module_gamma_table;
    735 		if (!transform->input_clut_table_r || !transform->input_clut_table_g ||
    736 				!transform->input_clut_table_b) {
    737 			goto fail;
    738 		}
    739 
    740 		transform = qcms_modular_transform_alloc();
    741 		if (!transform)
    742 			goto fail;
    743 		append_transform(transform, &next_transform);
    744 		transform->matrix.m[0][0] = 1/1.999969482421875f;
    745 		transform->matrix.m[0][1] = 0.f;
    746 		transform->matrix.m[0][2] = 0.f;
    747 		transform->matrix.m[1][0] = 0.f;
    748 		transform->matrix.m[1][1] = 1/1.999969482421875f;
    749 		transform->matrix.m[1][2] = 0.f;
    750 		transform->matrix.m[2][0] = 0.f;
    751 		transform->matrix.m[2][1] = 0.f;
    752 		transform->matrix.m[2][2] = 1/1.999969482421875f;
    753 		transform->matrix.invalid = false;
    754 		transform->transform_module_fn = qcms_transform_module_matrix;
    755 
    756 		transform = qcms_modular_transform_alloc();
    757 		if (!transform)
    758 			goto fail;
    759 		append_transform(transform, &next_transform);
    760 		transform->matrix = build_colorant_matrix(in);
    761 		transform->transform_module_fn = qcms_transform_module_matrix;
    762 	}
    763 
    764 	return first_transform;
    765 fail:
    766 	qcms_modular_transform_release(first_transform);
    767 	return EMPTY_TRANSFORM_LIST;
    768 }
    769 static struct qcms_modular_transform* qcms_modular_transform_create_output(qcms_profile *out)
    770 {
    771 	struct qcms_modular_transform *first_transform = NULL;
    772 	struct qcms_modular_transform **next_transform = &first_transform;
    773 
    774 	if (out->B2A0) {
    775 		struct qcms_modular_transform *lut_transform;
    776 		lut_transform = qcms_modular_transform_create_lut(out->B2A0);
    777 		if (!lut_transform)
    778 			goto fail;
    779 		append_transform(lut_transform, &next_transform);
    780 	} else if (out->mBA && out->mBA->num_in_channels == 3 && out->mBA->num_out_channels == 3) {
    781 		struct qcms_modular_transform *lut_transform;
    782 		lut_transform = qcms_modular_transform_create_mAB(out->mBA);
    783 		if (!lut_transform)
    784 			goto fail;
    785 		append_transform(lut_transform, &next_transform);
    786 	} else if (out->redTRC && out->greenTRC && out->blueTRC) {
    787 		struct qcms_modular_transform *transform;
    788 
    789 		transform = qcms_modular_transform_alloc();
    790 		if (!transform)
    791 			goto fail;
    792 		append_transform(transform, &next_transform);
    793 		transform->matrix = matrix_invert(build_colorant_matrix(out));
    794 		transform->transform_module_fn = qcms_transform_module_matrix;
    795 
    796 		transform = qcms_modular_transform_alloc();
    797 		if (!transform)
    798 			goto fail;
    799 		append_transform(transform, &next_transform);
    800 		transform->matrix.m[0][0] = 1.999969482421875f;
    801 		transform->matrix.m[0][1] = 0.f;
    802 		transform->matrix.m[0][2] = 0.f;
    803 		transform->matrix.m[1][0] = 0.f;
    804 		transform->matrix.m[1][1] = 1.999969482421875f;
    805 		transform->matrix.m[1][2] = 0.f;
    806 		transform->matrix.m[2][0] = 0.f;
    807 		transform->matrix.m[2][1] = 0.f;
    808 		transform->matrix.m[2][2] = 1.999969482421875f;
    809 		transform->matrix.invalid = false;
    810 		transform->transform_module_fn = qcms_transform_module_matrix;
    811 
    812 		transform = qcms_modular_transform_alloc();
    813 		if (!transform)
    814 			goto fail;
    815 		append_transform(transform, &next_transform);
    816 		build_output_lut(out->redTRC, &transform->output_gamma_lut_r,
    817 			&transform->output_gamma_lut_r_length);
    818 		build_output_lut(out->greenTRC, &transform->output_gamma_lut_g,
    819 			&transform->output_gamma_lut_g_length);
    820 		build_output_lut(out->blueTRC, &transform->output_gamma_lut_b,
    821 			&transform->output_gamma_lut_b_length);
    822 		transform->transform_module_fn = qcms_transform_module_gamma_lut;
    823 
    824 		if (!transform->output_gamma_lut_r || !transform->output_gamma_lut_g ||
    825 				!transform->output_gamma_lut_b) {
    826 			goto fail;
    827 		}
    828 	} else {
    829 		assert(0 && "Unsupported output profile workflow.");
    830 		return NULL;
    831 	}
    832 
    833 	return first_transform;
    834 fail:
    835 	qcms_modular_transform_release(first_transform);
    836 	return EMPTY_TRANSFORM_LIST;
    837 }
    838 
    839 /* Not Completed
    840 // Simplify the transformation chain to an equivalent transformation chain
    841 static struct qcms_modular_transform* qcms_modular_transform_reduce(struct qcms_modular_transform *transform)
    842 {
    843 	struct qcms_modular_transform *first_transform = NULL;
    844 	struct qcms_modular_transform *curr_trans = transform;
    845 	struct qcms_modular_transform *prev_trans = NULL;
    846 	while (curr_trans) {
    847 		struct qcms_modular_transform *next_trans = curr_trans->next_transform;
    848 		if (curr_trans->transform_module_fn == qcms_transform_module_matrix) {
    849 			if (next_trans && next_trans->transform_module_fn == qcms_transform_module_matrix) {
    850 				curr_trans->matrix = matrix_multiply(curr_trans->matrix, next_trans->matrix);
    851 				goto remove_next;
    852 			}
    853 		}
    854 		if (curr_trans->transform_module_fn == qcms_transform_module_gamma_table) {
    855 			bool isLinear = true;
    856 			uint16_t i;
    857 			for (i = 0; isLinear && i < 256; i++) {
    858 				isLinear &= (int)(curr_trans->input_clut_table_r[i] * 255) == i;
    859 				isLinear &= (int)(curr_trans->input_clut_table_g[i] * 255) == i;
    860 				isLinear &= (int)(curr_trans->input_clut_table_b[i] * 255) == i;
    861 			}
    862 			goto remove_current;
    863 		}
    864 
    865 next_transform:
    866 		if (!next_trans) break;
    867 		prev_trans = curr_trans;
    868 		curr_trans = next_trans;
    869 		continue;
    870 remove_current:
    871 		if (curr_trans == transform) {
    872 			//Update head
    873 			transform = next_trans;
    874 		} else {
    875 			prev_trans->next_transform = next_trans;
    876 		}
    877 		curr_trans->next_transform = NULL;
    878 		qcms_modular_transform_release(curr_trans);
    879 		//return transform;
    880 		return qcms_modular_transform_reduce(transform);
    881 remove_next:
    882 		curr_trans->next_transform = next_trans->next_transform;
    883 		next_trans->next_transform = NULL;
    884 		qcms_modular_transform_release(next_trans);
    885 		continue;
    886 	}
    887 	return transform;
    888 }
    889 */
    890 
    891 static struct qcms_modular_transform* qcms_modular_transform_create(qcms_profile *in, qcms_profile *out)
    892 {
    893 	struct qcms_modular_transform *first_transform = NULL;
    894 	struct qcms_modular_transform **next_transform = &first_transform;
    895 
    896 	if (in->color_space == RGB_SIGNATURE) {
    897 		struct qcms_modular_transform* rgb_to_pcs;
    898 		rgb_to_pcs = qcms_modular_transform_create_input(in);
    899 		if (!rgb_to_pcs)
    900 			goto fail;
    901 		append_transform(rgb_to_pcs, &next_transform);
    902 	} else {
    903 		assert(0 && "input color space not supported");
    904 		goto fail;
    905 	}
    906 
    907 	if (in->pcs == LAB_SIGNATURE && out->pcs == XYZ_SIGNATURE) {
    908 		struct qcms_modular_transform* lab_to_pcs;
    909 		lab_to_pcs = qcms_modular_transform_alloc();
    910 		if (!lab_to_pcs)
    911 			goto fail;
    912 		append_transform(lab_to_pcs, &next_transform);
    913 		lab_to_pcs->transform_module_fn = qcms_transform_module_LAB_to_XYZ;
    914 	}
    915 
    916 	// This does not improve accuracy in practice, something is wrong here.
    917 	//if (in->chromaticAdaption.invalid == false) {
    918 	//	struct qcms_modular_transform* chromaticAdaption;
    919 	//	chromaticAdaption = qcms_modular_transform_alloc();
    920 	//	if (!chromaticAdaption)
    921 	//		goto fail;
    922 	//	append_transform(chromaticAdaption, &next_transform);
    923 	//	chromaticAdaption->matrix = matrix_invert(in->chromaticAdaption);
    924 	//	chromaticAdaption->transform_module_fn = qcms_transform_module_matrix;
    925 	//}
    926 
    927         if (in->pcs == XYZ_SIGNATURE && out->pcs == LAB_SIGNATURE) {
    928 		struct qcms_modular_transform* pcs_to_lab;
    929 		pcs_to_lab = qcms_modular_transform_alloc();
    930 		if (!pcs_to_lab)
    931 			goto fail;
    932 		append_transform(pcs_to_lab, &next_transform);
    933 		pcs_to_lab->transform_module_fn = qcms_transform_module_XYZ_to_LAB;
    934 	}
    935 
    936 	if (out->color_space == RGB_SIGNATURE) {
    937 		struct qcms_modular_transform* pcs_to_rgb;
    938 		pcs_to_rgb = qcms_modular_transform_create_output(out);
    939 		if (!pcs_to_rgb)
    940 			goto fail;
    941 		append_transform(pcs_to_rgb, &next_transform);
    942 	} else {
    943 		assert(0 && "output color space not supported");
    944 		goto fail;
    945 	}
    946 	// Not Completed
    947 	//return qcms_modular_transform_reduce(first_transform);
    948 	return first_transform;
    949 fail:
    950 	qcms_modular_transform_release(first_transform);
    951 	return EMPTY_TRANSFORM_LIST;
    952 }
    953 
    954 static float* qcms_modular_transform_data(struct qcms_modular_transform *transform, float *src, float *dest, size_t len)
    955 {
    956         while (transform != NULL) {
    957                 // Keep swaping src/dest when performing a transform to use less memory.
    958                 float *new_src = dest;
    959 		const transform_module_fn_t transform_fn = transform->transform_module_fn;
    960 		if (transform_fn != qcms_transform_module_gamma_table &&
    961 		    transform_fn != qcms_transform_module_gamma_lut &&
    962 		    transform_fn != qcms_transform_module_clut &&
    963 		    transform_fn != qcms_transform_module_clut_only &&
    964 		    transform_fn != qcms_transform_module_matrix &&
    965 		    transform_fn != qcms_transform_module_matrix_translate &&
    966 		    transform_fn != qcms_transform_module_LAB_to_XYZ &&
    967 		    transform_fn != qcms_transform_module_XYZ_to_LAB) {
    968 			assert(0 && "Unsupported transform module");
    969 			return NULL;
    970 		}
    971                 transform->transform_module_fn(transform,src,dest,len);
    972                 dest = src;
    973                 src = new_src;
    974                 transform = transform->next_transform;
    975         }
    976         // The results end up in the src buffer because of the switching
    977         return src;
    978 }
    979 
    980 float* qcms_chain_transform(qcms_profile *in, qcms_profile *out, float *src, float *dest, size_t lutSize)
    981 {
    982 	struct qcms_modular_transform *transform_list = qcms_modular_transform_create(in, out);
    983 	if (transform_list != NULL) {
    984 		float *lut = qcms_modular_transform_data(transform_list, src, dest, lutSize/3);
    985 		qcms_modular_transform_release(transform_list);
    986 		return lut;
    987 	}
    988 	return NULL;
    989 }
    990