- add sources.
[platform/framework/web/crosswalk.git] / src / third_party / qcms / src / chain.c
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 }