Bug 1639153 - Part 6.4: Add tls dependency for WasmTruncateToInt64 and Int64ToFloatin...
[gecko.git] / gfx / qcms / chain.c
blobdbae183789e799cc25b89222c14f1ab2e307d5cd
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:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
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.
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"
32 static struct matrix build_lut_matrix(struct lutType *lut)
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;
50 return result;
53 static struct matrix build_mAB_matrix(struct lutmABType *lut)
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;
71 return result;
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)
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;
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);
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)
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;
112 float fx = f(device_x);
113 float fy = f(device_y);
114 float fz = f(device_z);
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;
126 static void qcms_transform_module_clut_only(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
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;
136 for (i = 0; i < length; i++) {
137 assert(transform->grid_size >= 1);
139 float linear_r = *src++;
140 float linear_g = *src++;
141 float linear_b = *src++;
143 int x = floorf(linear_r * (transform->grid_size-1));
144 int y = floorf(linear_g * (transform->grid_size-1));
145 int z = floorf(linear_b * (transform->grid_size-1));
146 int x_n = ceilf(linear_r * (transform->grid_size-1));
147 int y_n = ceilf(linear_g * (transform->grid_size-1));
148 int z_n = ceilf(linear_b * (transform->grid_size-1));
149 float x_d = linear_r * (transform->grid_size-1) - x;
150 float y_d = linear_g * (transform->grid_size-1) - y;
151 float z_d = linear_b * (transform->grid_size-1) - z;
153 float r_x1 = lerp(CLU(r_table,x,y,z), CLU(r_table,x_n,y,z), x_d);
154 float r_x2 = lerp(CLU(r_table,x,y_n,z), CLU(r_table,x_n,y_n,z), x_d);
155 float r_y1 = lerp(r_x1, r_x2, y_d);
156 float r_x3 = lerp(CLU(r_table,x,y,z_n), CLU(r_table,x_n,y,z_n), x_d);
157 float r_x4 = lerp(CLU(r_table,x,y_n,z_n), CLU(r_table,x_n,y_n,z_n), x_d);
158 float r_y2 = lerp(r_x3, r_x4, y_d);
159 float clut_r = lerp(r_y1, r_y2, z_d);
161 float g_x1 = lerp(CLU(g_table,x,y,z), CLU(g_table,x_n,y,z), x_d);
162 float g_x2 = lerp(CLU(g_table,x,y_n,z), CLU(g_table,x_n,y_n,z), x_d);
163 float g_y1 = lerp(g_x1, g_x2, y_d);
164 float g_x3 = lerp(CLU(g_table,x,y,z_n), CLU(g_table,x_n,y,z_n), x_d);
165 float g_x4 = lerp(CLU(g_table,x,y_n,z_n), CLU(g_table,x_n,y_n,z_n), x_d);
166 float g_y2 = lerp(g_x3, g_x4, y_d);
167 float clut_g = lerp(g_y1, g_y2, z_d);
169 float b_x1 = lerp(CLU(b_table,x,y,z), CLU(b_table,x_n,y,z), x_d);
170 float b_x2 = lerp(CLU(b_table,x,y_n,z), CLU(b_table,x_n,y_n,z), x_d);
171 float b_y1 = lerp(b_x1, b_x2, y_d);
172 float b_x3 = lerp(CLU(b_table,x,y,z_n), CLU(b_table,x_n,y,z_n), x_d);
173 float b_x4 = lerp(CLU(b_table,x,y_n,z_n), CLU(b_table,x_n,y_n,z_n), x_d);
174 float b_y2 = lerp(b_x3, b_x4, y_d);
175 float clut_b = lerp(b_y1, b_y2, z_d);
177 *dest++ = clamp_float(clut_r);
178 *dest++ = clamp_float(clut_g);
179 *dest++ = clamp_float(clut_b);
183 static void qcms_transform_module_clut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
185 size_t i;
186 int xy_len = 1;
187 int x_len = transform->grid_size;
188 int len = x_len * x_len;
189 float* r_table = transform->r_clut;
190 float* g_table = transform->g_clut;
191 float* b_table = transform->b_clut;
192 for (i = 0; i < length; i++) {
193 assert(transform->grid_size >= 1);
195 float device_r = *src++;
196 float device_g = *src++;
197 float device_b = *src++;
198 float linear_r = lut_interp_linear_float(device_r,
199 transform->input_clut_table_r, transform->input_clut_table_length);
200 float linear_g = lut_interp_linear_float(device_g,
201 transform->input_clut_table_g, transform->input_clut_table_length);
202 float linear_b = lut_interp_linear_float(device_b,
203 transform->input_clut_table_b, transform->input_clut_table_length);
205 int x = floorf(linear_r * (transform->grid_size-1));
206 int y = floorf(linear_g * (transform->grid_size-1));
207 int z = floorf(linear_b * (transform->grid_size-1));
208 int x_n = ceilf(linear_r * (transform->grid_size-1));
209 int y_n = ceilf(linear_g * (transform->grid_size-1));
210 int z_n = ceilf(linear_b * (transform->grid_size-1));
211 float x_d = linear_r * (transform->grid_size-1) - x;
212 float y_d = linear_g * (transform->grid_size-1) - y;
213 float z_d = linear_b * (transform->grid_size-1) - z;
215 float r_x1 = lerp(CLU(r_table,x,y,z), CLU(r_table,x_n,y,z), x_d);
216 float r_x2 = lerp(CLU(r_table,x,y_n,z), CLU(r_table,x_n,y_n,z), x_d);
217 float r_y1 = lerp(r_x1, r_x2, y_d);
218 float r_x3 = lerp(CLU(r_table,x,y,z_n), CLU(r_table,x_n,y,z_n), x_d);
219 float r_x4 = lerp(CLU(r_table,x,y_n,z_n), CLU(r_table,x_n,y_n,z_n), x_d);
220 float r_y2 = lerp(r_x3, r_x4, y_d);
221 float clut_r = lerp(r_y1, r_y2, z_d);
223 float g_x1 = lerp(CLU(g_table,x,y,z), CLU(g_table,x_n,y,z), x_d);
224 float g_x2 = lerp(CLU(g_table,x,y_n,z), CLU(g_table,x_n,y_n,z), x_d);
225 float g_y1 = lerp(g_x1, g_x2, y_d);
226 float g_x3 = lerp(CLU(g_table,x,y,z_n), CLU(g_table,x_n,y,z_n), x_d);
227 float g_x4 = lerp(CLU(g_table,x,y_n,z_n), CLU(g_table,x_n,y_n,z_n), x_d);
228 float g_y2 = lerp(g_x3, g_x4, y_d);
229 float clut_g = lerp(g_y1, g_y2, z_d);
231 float b_x1 = lerp(CLU(b_table,x,y,z), CLU(b_table,x_n,y,z), x_d);
232 float b_x2 = lerp(CLU(b_table,x,y_n,z), CLU(b_table,x_n,y_n,z), x_d);
233 float b_y1 = lerp(b_x1, b_x2, y_d);
234 float b_x3 = lerp(CLU(b_table,x,y,z_n), CLU(b_table,x_n,y,z_n), x_d);
235 float b_x4 = lerp(CLU(b_table,x,y_n,z_n), CLU(b_table,x_n,y_n,z_n), x_d);
236 float b_y2 = lerp(b_x3, b_x4, y_d);
237 float clut_b = lerp(b_y1, b_y2, z_d);
239 float pcs_r = lut_interp_linear_float(clut_r,
240 transform->output_clut_table_r, transform->output_clut_table_length);
241 float pcs_g = lut_interp_linear_float(clut_g,
242 transform->output_clut_table_g, transform->output_clut_table_length);
243 float pcs_b = lut_interp_linear_float(clut_b,
244 transform->output_clut_table_b, transform->output_clut_table_length);
246 *dest++ = clamp_float(pcs_r);
247 *dest++ = clamp_float(pcs_g);
248 *dest++ = clamp_float(pcs_b);
252 /* NOT USED
253 static void qcms_transform_module_tetra_clut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
255 size_t i;
256 int xy_len = 1;
257 int x_len = transform->grid_size;
258 int len = x_len * x_len;
259 float* r_table = transform->r_clut;
260 float* g_table = transform->g_clut;
261 float* b_table = transform->b_clut;
262 float c0_r, c1_r, c2_r, c3_r;
263 float c0_g, c1_g, c2_g, c3_g;
264 float c0_b, c1_b, c2_b, c3_b;
265 float clut_r, clut_g, clut_b;
266 float pcs_r, pcs_g, pcs_b;
267 for (i = 0; i < length; i++) {
268 float device_r = *src++;
269 float device_g = *src++;
270 float device_b = *src++;
271 float linear_r = lut_interp_linear_float(device_r,
272 transform->input_clut_table_r, transform->input_clut_table_length);
273 float linear_g = lut_interp_linear_float(device_g,
274 transform->input_clut_table_g, transform->input_clut_table_length);
275 float linear_b = lut_interp_linear_float(device_b,
276 transform->input_clut_table_b, transform->input_clut_table_length);
278 int x = floorf(linear_r * (transform->grid_size-1));
279 int y = floorf(linear_g * (transform->grid_size-1));
280 int z = floorf(linear_b * (transform->grid_size-1));
281 int x_n = ceilf(linear_r * (transform->grid_size-1));
282 int y_n = ceilf(linear_g * (transform->grid_size-1));
283 int z_n = ceilf(linear_b * (transform->grid_size-1));
284 float rx = linear_r * (transform->grid_size-1) - x;
285 float ry = linear_g * (transform->grid_size-1) - y;
286 float rz = linear_b * (transform->grid_size-1) - z;
288 c0_r = CLU(r_table, x, y, z);
289 c0_g = CLU(g_table, x, y, z);
290 c0_b = CLU(b_table, x, y, z);
291 if( rx >= ry ) {
292 if (ry >= rz) { //rx >= ry && ry >= rz
293 c1_r = CLU(r_table, x_n, y, z) - c0_r;
294 c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x_n, y, z);
295 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
296 c1_g = CLU(g_table, x_n, y, z) - c0_g;
297 c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x_n, y, z);
298 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
299 c1_b = CLU(b_table, x_n, y, z) - c0_b;
300 c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x_n, y, z);
301 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
302 } else {
303 if (rx >= rz) { //rx >= rz && rz >= ry
304 c1_r = CLU(r_table, x_n, y, z) - c0_r;
305 c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
306 c3_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x_n, y, z);
307 c1_g = CLU(g_table, x_n, y, z) - c0_g;
308 c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
309 c3_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x_n, y, z);
310 c1_b = CLU(b_table, x_n, y, z) - c0_b;
311 c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
312 c3_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x_n, y, z);
313 } else { //rz > rx && rx >= ry
314 c1_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x, y, z_n);
315 c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
316 c3_r = CLU(r_table, x, y, z_n) - c0_r;
317 c1_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x, y, z_n);
318 c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
319 c3_g = CLU(g_table, x, y, z_n) - c0_g;
320 c1_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x, y, z_n);
321 c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
322 c3_b = CLU(b_table, x, y, z_n) - c0_b;
325 } else {
326 if (rx >= rz) { //ry > rx && rx >= rz
327 c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x, y_n, z);
328 c2_r = CLU(r_table, x_n, y_n, z) - c0_r;
329 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
330 c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x, y_n, z);
331 c2_g = CLU(g_table, x_n, y_n, z) - c0_g;
332 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
333 c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x, y_n, z);
334 c2_b = CLU(b_table, x_n, y_n, z) - c0_b;
335 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
336 } else {
337 if (ry >= rz) { //ry >= rz && rz > rx
338 c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
339 c2_r = CLU(r_table, x, y_n, z) - c0_r;
340 c3_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y_n, z);
341 c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
342 c2_g = CLU(g_table, x, y_n, z) - c0_g;
343 c3_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y_n, z);
344 c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
345 c2_b = CLU(b_table, x, y_n, z) - c0_b;
346 c3_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y_n, z);
347 } else { //rz > ry && ry > rx
348 c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
349 c2_r = CLU(r_table, x, y_n, z) - c0_r;
350 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
351 c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
352 c2_g = CLU(g_table, x, y_n, z) - c0_g;
353 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
354 c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
355 c2_b = CLU(b_table, x, y_n, z) - c0_b;
356 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
361 clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz;
362 clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz;
363 clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz;
365 pcs_r = lut_interp_linear_float(clut_r,
366 transform->output_clut_table_r, transform->output_clut_table_length);
367 pcs_g = lut_interp_linear_float(clut_g,
368 transform->output_clut_table_g, transform->output_clut_table_length);
369 pcs_b = lut_interp_linear_float(clut_b,
370 transform->output_clut_table_b, transform->output_clut_table_length);
371 *dest++ = clamp_float(pcs_r);
372 *dest++ = clamp_float(pcs_g);
373 *dest++ = clamp_float(pcs_b);
378 static void qcms_transform_module_gamma_table(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
380 size_t i;
381 float out_r, out_g, out_b;
382 for (i = 0; i < length; i++) {
383 float in_r = *src++;
384 float in_g = *src++;
385 float in_b = *src++;
387 out_r = lut_interp_linear_float(in_r, transform->input_clut_table_r, 256);
388 out_g = lut_interp_linear_float(in_g, transform->input_clut_table_g, 256);
389 out_b = lut_interp_linear_float(in_b, transform->input_clut_table_b, 256);
391 *dest++ = clamp_float(out_r);
392 *dest++ = clamp_float(out_g);
393 *dest++ = clamp_float(out_b);
397 static void qcms_transform_module_gamma_lut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
399 size_t i;
400 float out_r, out_g, out_b;
401 for (i = 0; i < length; i++) {
402 float in_r = *src++;
403 float in_g = *src++;
404 float in_b = *src++;
406 out_r = lut_interp_linear(in_r,
407 transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
408 out_g = lut_interp_linear(in_g,
409 transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
410 out_b = lut_interp_linear(in_b,
411 transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
413 *dest++ = clamp_float(out_r);
414 *dest++ = clamp_float(out_g);
415 *dest++ = clamp_float(out_b);
419 static void qcms_transform_module_matrix_translate(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
421 size_t i;
422 struct matrix mat;
424 /* store the results in column major mode
425 * this makes doing the multiplication with sse easier */
426 mat.m[0][0] = transform->matrix.m[0][0];
427 mat.m[1][0] = transform->matrix.m[0][1];
428 mat.m[2][0] = transform->matrix.m[0][2];
429 mat.m[0][1] = transform->matrix.m[1][0];
430 mat.m[1][1] = transform->matrix.m[1][1];
431 mat.m[2][1] = transform->matrix.m[1][2];
432 mat.m[0][2] = transform->matrix.m[2][0];
433 mat.m[1][2] = transform->matrix.m[2][1];
434 mat.m[2][2] = transform->matrix.m[2][2];
436 for (i = 0; i < length; i++) {
437 float in_r = *src++;
438 float in_g = *src++;
439 float in_b = *src++;
441 float out_r = mat.m[0][0]*in_r + mat.m[1][0]*in_g + mat.m[2][0]*in_b + transform->tx;
442 float out_g = mat.m[0][1]*in_r + mat.m[1][1]*in_g + mat.m[2][1]*in_b + transform->ty;
443 float out_b = mat.m[0][2]*in_r + mat.m[1][2]*in_g + mat.m[2][2]*in_b + transform->tz;
445 *dest++ = clamp_float(out_r);
446 *dest++ = clamp_float(out_g);
447 *dest++ = clamp_float(out_b);
451 static void qcms_transform_module_matrix(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
453 size_t i;
454 struct matrix mat;
456 /* store the results in column major mode
457 * this makes doing the multiplication with sse easier */
458 mat.m[0][0] = transform->matrix.m[0][0];
459 mat.m[1][0] = transform->matrix.m[0][1];
460 mat.m[2][0] = transform->matrix.m[0][2];
461 mat.m[0][1] = transform->matrix.m[1][0];
462 mat.m[1][1] = transform->matrix.m[1][1];
463 mat.m[2][1] = transform->matrix.m[1][2];
464 mat.m[0][2] = transform->matrix.m[2][0];
465 mat.m[1][2] = transform->matrix.m[2][1];
466 mat.m[2][2] = transform->matrix.m[2][2];
468 for (i = 0; i < length; i++) {
469 float in_r = *src++;
470 float in_g = *src++;
471 float in_b = *src++;
473 float out_r = mat.m[0][0]*in_r + mat.m[1][0]*in_g + mat.m[2][0]*in_b;
474 float out_g = mat.m[0][1]*in_r + mat.m[1][1]*in_g + mat.m[2][1]*in_b;
475 float out_b = mat.m[0][2]*in_r + mat.m[1][2]*in_g + mat.m[2][2]*in_b;
477 *dest++ = clamp_float(out_r);
478 *dest++ = clamp_float(out_g);
479 *dest++ = clamp_float(out_b);
483 static struct qcms_modular_transform* qcms_modular_transform_alloc() {
484 return calloc(1, sizeof(struct qcms_modular_transform));
487 static void qcms_modular_transform_release(struct qcms_modular_transform *transform)
489 struct qcms_modular_transform *next_transform;
490 while (transform != NULL) {
491 next_transform = transform->next_transform;
492 // clut may use a single block of memory.
493 // Perhaps we should remove this to simply the code.
494 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) {
495 if (transform->input_clut_table_r) free(transform->input_clut_table_r);
496 } else {
497 if (transform->input_clut_table_r) free(transform->input_clut_table_r);
498 if (transform->input_clut_table_g) free(transform->input_clut_table_g);
499 if (transform->input_clut_table_b) free(transform->input_clut_table_b);
501 if (transform->r_clut + 1 == transform->g_clut && transform->g_clut + 1 == transform->b_clut) {
502 if (transform->r_clut) free(transform->r_clut);
503 } else {
504 if (transform->r_clut) free(transform->r_clut);
505 if (transform->g_clut) free(transform->g_clut);
506 if (transform->b_clut) free(transform->b_clut);
508 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) {
509 if (transform->output_clut_table_r) free(transform->output_clut_table_r);
510 } else {
511 if (transform->output_clut_table_r) free(transform->output_clut_table_r);
512 if (transform->output_clut_table_g) free(transform->output_clut_table_g);
513 if (transform->output_clut_table_b) free(transform->output_clut_table_b);
515 if (transform->output_gamma_lut_r) free(transform->output_gamma_lut_r);
516 if (transform->output_gamma_lut_g) free(transform->output_gamma_lut_g);
517 if (transform->output_gamma_lut_b) free(transform->output_gamma_lut_b);
518 free(transform);
519 transform = next_transform;
523 /* Set transform to be the next element in the linked list. */
524 static void append_transform(struct qcms_modular_transform *transform, struct qcms_modular_transform ***next_transform)
526 **next_transform = transform;
527 while (transform) {
528 *next_transform = &(transform->next_transform);
529 transform = transform->next_transform;
533 /* reverse the transformation list (used by mBA) */
534 static struct qcms_modular_transform* reverse_transform(struct qcms_modular_transform *transform)
536 struct qcms_modular_transform *prev_transform = NULL;
537 while (transform != NULL) {
538 struct qcms_modular_transform *next_transform = transform->next_transform;
539 transform->next_transform = prev_transform;
540 prev_transform = transform;
541 transform = next_transform;
544 return prev_transform;
547 #define EMPTY_TRANSFORM_LIST NULL
548 static struct qcms_modular_transform* qcms_modular_transform_create_mAB(struct lutmABType *lut)
550 struct qcms_modular_transform *first_transform = NULL;
551 struct qcms_modular_transform **next_transform = &first_transform;
552 struct qcms_modular_transform *transform = NULL;
554 if (lut->a_curves[0] != NULL) {
555 size_t clut_length;
556 float *clut;
558 // If the A curve is present this also implies the
559 // presence of a CLUT.
560 if (!lut->clut_table)
561 goto fail;
563 // Prepare A curve.
564 transform = qcms_modular_transform_alloc();
565 if (!transform)
566 goto fail;
567 append_transform(transform, &next_transform);
568 transform->input_clut_table_r = build_input_gamma_table(lut->a_curves[0]);
569 transform->input_clut_table_g = build_input_gamma_table(lut->a_curves[1]);
570 transform->input_clut_table_b = build_input_gamma_table(lut->a_curves[2]);
571 transform->transform_module_fn = qcms_transform_module_gamma_table;
572 if (lut->num_grid_points[0] != lut->num_grid_points[1] ||
573 lut->num_grid_points[1] != lut->num_grid_points[2] ) {
574 //XXX: We don't currently support clut that are not squared!
575 goto fail;
578 // Prepare CLUT
579 transform = qcms_modular_transform_alloc();
580 if (!transform)
581 goto fail;
582 append_transform(transform, &next_transform);
583 clut_length = sizeof(float)*pow(lut->num_grid_points[0], 3)*3;
584 clut = malloc(clut_length);
585 if (!clut)
586 goto fail;
587 memcpy(clut, lut->clut_table, clut_length);
588 transform->r_clut = clut + 0;
589 transform->g_clut = clut + 1;
590 transform->b_clut = clut + 2;
591 transform->grid_size = lut->num_grid_points[0];
592 transform->transform_module_fn = qcms_transform_module_clut_only;
594 if (lut->m_curves[0] != NULL) {
595 // M curve imples the presence of a Matrix
597 // Prepare M curve
598 transform = qcms_modular_transform_alloc();
599 if (!transform)
600 goto fail;
601 append_transform(transform, &next_transform);
602 transform->input_clut_table_r = build_input_gamma_table(lut->m_curves[0]);
603 transform->input_clut_table_g = build_input_gamma_table(lut->m_curves[1]);
604 transform->input_clut_table_b = build_input_gamma_table(lut->m_curves[2]);
605 transform->transform_module_fn = qcms_transform_module_gamma_table;
607 // Prepare Matrix
608 transform = qcms_modular_transform_alloc();
609 if (!transform)
610 goto fail;
611 append_transform(transform, &next_transform);
612 transform->matrix = build_mAB_matrix(lut);
613 if (transform->matrix.invalid)
614 goto fail;
615 transform->tx = s15Fixed16Number_to_float(lut->e03);
616 transform->ty = s15Fixed16Number_to_float(lut->e13);
617 transform->tz = s15Fixed16Number_to_float(lut->e23);
618 transform->transform_module_fn = qcms_transform_module_matrix_translate;
620 if (lut->b_curves[0] != NULL) {
621 // Prepare B curve
622 transform = qcms_modular_transform_alloc();
623 if (!transform)
624 goto fail;
625 append_transform(transform, &next_transform);
626 transform->input_clut_table_r = build_input_gamma_table(lut->b_curves[0]);
627 transform->input_clut_table_g = build_input_gamma_table(lut->b_curves[1]);
628 transform->input_clut_table_b = build_input_gamma_table(lut->b_curves[2]);
629 transform->transform_module_fn = qcms_transform_module_gamma_table;
630 } else {
631 // B curve is mandatory
632 goto fail;
635 if (lut->reversed) {
636 // mBA are identical to mAB except that the transformation order
637 // is reversed
638 first_transform = reverse_transform(first_transform);
641 return first_transform;
642 fail:
643 qcms_modular_transform_release(first_transform);
644 return NULL;
647 static struct qcms_modular_transform* qcms_modular_transform_create_lut(struct lutType *lut)
649 struct qcms_modular_transform *first_transform = NULL;
650 struct qcms_modular_transform **next_transform = &first_transform;
651 struct qcms_modular_transform *transform = NULL;
653 size_t in_curve_len, clut_length, out_curve_len;
654 float *in_curves, *clut, *out_curves;
656 // Prepare Matrix
657 transform = qcms_modular_transform_alloc();
658 if (!transform)
659 goto fail;
660 append_transform(transform, &next_transform);
661 transform->matrix = build_lut_matrix(lut);
662 if (transform->matrix.invalid)
663 goto fail;
664 transform->transform_module_fn = qcms_transform_module_matrix;
666 // Prepare input curves
667 transform = qcms_modular_transform_alloc();
668 if (!transform)
669 goto fail;
670 append_transform(transform, &next_transform);
671 in_curve_len = sizeof(float)*lut->num_input_table_entries * 3;
672 in_curves = malloc(in_curve_len);
673 if (!in_curves)
674 goto fail;
675 memcpy(in_curves, lut->input_table, in_curve_len);
676 transform->input_clut_table_r = in_curves + lut->num_input_table_entries * 0;
677 transform->input_clut_table_g = in_curves + lut->num_input_table_entries * 1;
678 transform->input_clut_table_b = in_curves + lut->num_input_table_entries * 2;
679 transform->input_clut_table_length = lut->num_input_table_entries;
681 // Prepare table
682 clut_length = sizeof(float)*pow(lut->num_clut_grid_points, 3)*3;
683 clut = malloc(clut_length);
684 if (!clut)
685 goto fail;
686 memcpy(clut, lut->clut_table, clut_length);
687 transform->r_clut = clut + 0;
688 transform->g_clut = clut + 1;
689 transform->b_clut = clut + 2;
690 transform->grid_size = lut->num_clut_grid_points;
692 // Prepare output curves
693 out_curve_len = sizeof(float) * lut->num_output_table_entries * 3;
694 out_curves = malloc(out_curve_len);
695 if (!out_curves)
696 goto fail;
697 memcpy(out_curves, lut->output_table, out_curve_len);
698 transform->output_clut_table_r = out_curves + lut->num_output_table_entries * 0;
699 transform->output_clut_table_g = out_curves + lut->num_output_table_entries * 1;
700 transform->output_clut_table_b = out_curves + lut->num_output_table_entries * 2;
701 transform->output_clut_table_length = lut->num_output_table_entries;
702 transform->transform_module_fn = qcms_transform_module_clut;
704 return first_transform;
705 fail:
706 qcms_modular_transform_release(first_transform);
707 return NULL;
710 struct qcms_modular_transform* qcms_modular_transform_create_input(qcms_profile *in)
712 struct qcms_modular_transform *first_transform = NULL;
713 struct qcms_modular_transform **next_transform = &first_transform;
715 if (in->A2B0) {
716 struct qcms_modular_transform *lut_transform;
717 lut_transform = qcms_modular_transform_create_lut(in->A2B0);
718 if (!lut_transform)
719 goto fail;
720 append_transform(lut_transform, &next_transform);
721 } else if (in->mAB && in->mAB->num_in_channels == 3 && in->mAB->num_out_channels == 3) {
722 struct qcms_modular_transform *mAB_transform;
723 mAB_transform = qcms_modular_transform_create_mAB(in->mAB);
724 if (!mAB_transform)
725 goto fail;
726 append_transform(mAB_transform, &next_transform);
728 } else {
729 struct qcms_modular_transform *transform;
731 transform = qcms_modular_transform_alloc();
732 if (!transform)
733 goto fail;
734 append_transform(transform, &next_transform);
735 transform->input_clut_table_r = build_input_gamma_table(in->redTRC);
736 transform->input_clut_table_g = build_input_gamma_table(in->greenTRC);
737 transform->input_clut_table_b = build_input_gamma_table(in->blueTRC);
738 transform->transform_module_fn = qcms_transform_module_gamma_table;
739 if (!transform->input_clut_table_r || !transform->input_clut_table_g ||
740 !transform->input_clut_table_b) {
741 goto fail;
744 transform = qcms_modular_transform_alloc();
745 if (!transform)
746 goto fail;
747 append_transform(transform, &next_transform);
748 transform->matrix.m[0][0] = 1/1.999969482421875f;
749 transform->matrix.m[0][1] = 0.f;
750 transform->matrix.m[0][2] = 0.f;
751 transform->matrix.m[1][0] = 0.f;
752 transform->matrix.m[1][1] = 1/1.999969482421875f;
753 transform->matrix.m[1][2] = 0.f;
754 transform->matrix.m[2][0] = 0.f;
755 transform->matrix.m[2][1] = 0.f;
756 transform->matrix.m[2][2] = 1/1.999969482421875f;
757 transform->matrix.invalid = false;
758 transform->transform_module_fn = qcms_transform_module_matrix;
760 transform = qcms_modular_transform_alloc();
761 if (!transform)
762 goto fail;
763 append_transform(transform, &next_transform);
764 transform->matrix = build_colorant_matrix(in);
765 transform->transform_module_fn = qcms_transform_module_matrix;
768 return first_transform;
769 fail:
770 qcms_modular_transform_release(first_transform);
771 return EMPTY_TRANSFORM_LIST;
773 static struct qcms_modular_transform* qcms_modular_transform_create_output(qcms_profile *out)
775 struct qcms_modular_transform *first_transform = NULL;
776 struct qcms_modular_transform **next_transform = &first_transform;
778 if (out->B2A0) {
779 struct qcms_modular_transform *lut_transform;
780 lut_transform = qcms_modular_transform_create_lut(out->B2A0);
781 if (!lut_transform)
782 goto fail;
783 append_transform(lut_transform, &next_transform);
784 } else if (out->mBA && out->mBA->num_in_channels == 3 && out->mBA->num_out_channels == 3) {
785 struct qcms_modular_transform *lut_transform;
786 lut_transform = qcms_modular_transform_create_mAB(out->mBA);
787 if (!lut_transform)
788 goto fail;
789 append_transform(lut_transform, &next_transform);
790 } else if (out->redTRC && out->greenTRC && out->blueTRC) {
791 struct qcms_modular_transform *transform;
793 transform = qcms_modular_transform_alloc();
794 if (!transform)
795 goto fail;
796 append_transform(transform, &next_transform);
797 transform->matrix = matrix_invert(build_colorant_matrix(out));
798 transform->transform_module_fn = qcms_transform_module_matrix;
800 transform = qcms_modular_transform_alloc();
801 if (!transform)
802 goto fail;
803 append_transform(transform, &next_transform);
804 transform->matrix.m[0][0] = 1.999969482421875f;
805 transform->matrix.m[0][1] = 0.f;
806 transform->matrix.m[0][2] = 0.f;
807 transform->matrix.m[1][0] = 0.f;
808 transform->matrix.m[1][1] = 1.999969482421875f;
809 transform->matrix.m[1][2] = 0.f;
810 transform->matrix.m[2][0] = 0.f;
811 transform->matrix.m[2][1] = 0.f;
812 transform->matrix.m[2][2] = 1.999969482421875f;
813 transform->matrix.invalid = false;
814 transform->transform_module_fn = qcms_transform_module_matrix;
816 transform = qcms_modular_transform_alloc();
817 if (!transform)
818 goto fail;
819 append_transform(transform, &next_transform);
820 build_output_lut(out->redTRC, &transform->output_gamma_lut_r,
821 &transform->output_gamma_lut_r_length);
822 build_output_lut(out->greenTRC, &transform->output_gamma_lut_g,
823 &transform->output_gamma_lut_g_length);
824 build_output_lut(out->blueTRC, &transform->output_gamma_lut_b,
825 &transform->output_gamma_lut_b_length);
826 transform->transform_module_fn = qcms_transform_module_gamma_lut;
828 if (!transform->output_gamma_lut_r || !transform->output_gamma_lut_g ||
829 !transform->output_gamma_lut_b) {
830 goto fail;
832 } else {
833 assert(0 && "Unsupported output profile workflow.");
834 return NULL;
837 return first_transform;
838 fail:
839 qcms_modular_transform_release(first_transform);
840 return EMPTY_TRANSFORM_LIST;
843 /* Not Completed
844 // Simplify the transformation chain to an equivalent transformation chain
845 static struct qcms_modular_transform* qcms_modular_transform_reduce(struct qcms_modular_transform *transform)
847 struct qcms_modular_transform *first_transform = NULL;
848 struct qcms_modular_transform *curr_trans = transform;
849 struct qcms_modular_transform *prev_trans = NULL;
850 while (curr_trans) {
851 struct qcms_modular_transform *next_trans = curr_trans->next_transform;
852 if (curr_trans->transform_module_fn == qcms_transform_module_matrix) {
853 if (next_trans && next_trans->transform_module_fn == qcms_transform_module_matrix) {
854 curr_trans->matrix = matrix_multiply(curr_trans->matrix, next_trans->matrix);
855 goto remove_next;
858 if (curr_trans->transform_module_fn == qcms_transform_module_gamma_table) {
859 bool isLinear = true;
860 uint16_t i;
861 for (i = 0; isLinear && i < 256; i++) {
862 isLinear &= (int)(curr_trans->input_clut_table_r[i] * 255) == i;
863 isLinear &= (int)(curr_trans->input_clut_table_g[i] * 255) == i;
864 isLinear &= (int)(curr_trans->input_clut_table_b[i] * 255) == i;
866 goto remove_current;
869 next_transform:
870 if (!next_trans) break;
871 prev_trans = curr_trans;
872 curr_trans = next_trans;
873 continue;
874 remove_current:
875 if (curr_trans == transform) {
876 //Update head
877 transform = next_trans;
878 } else {
879 prev_trans->next_transform = next_trans;
881 curr_trans->next_transform = NULL;
882 qcms_modular_transform_release(curr_trans);
883 //return transform;
884 return qcms_modular_transform_reduce(transform);
885 remove_next:
886 curr_trans->next_transform = next_trans->next_transform;
887 next_trans->next_transform = NULL;
888 qcms_modular_transform_release(next_trans);
889 continue;
891 return transform;
895 static struct qcms_modular_transform* qcms_modular_transform_create(qcms_profile *in, qcms_profile *out)
897 struct qcms_modular_transform *first_transform = NULL;
898 struct qcms_modular_transform **next_transform = &first_transform;
900 if (in->color_space == RGB_SIGNATURE) {
901 struct qcms_modular_transform* rgb_to_pcs;
902 rgb_to_pcs = qcms_modular_transform_create_input(in);
903 if (!rgb_to_pcs)
904 goto fail;
905 append_transform(rgb_to_pcs, &next_transform);
906 } else {
907 assert(0 && "input color space not supported");
908 goto fail;
911 if (in->pcs == LAB_SIGNATURE && out->pcs == XYZ_SIGNATURE) {
912 struct qcms_modular_transform* lab_to_pcs;
913 lab_to_pcs = qcms_modular_transform_alloc();
914 if (!lab_to_pcs)
915 goto fail;
916 append_transform(lab_to_pcs, &next_transform);
917 lab_to_pcs->transform_module_fn = qcms_transform_module_LAB_to_XYZ;
920 // This does not improve accuracy in practice, something is wrong here.
921 //if (in->chromaticAdaption.invalid == false) {
922 // struct qcms_modular_transform* chromaticAdaption;
923 // chromaticAdaption = qcms_modular_transform_alloc();
924 // if (!chromaticAdaption)
925 // goto fail;
926 // append_transform(chromaticAdaption, &next_transform);
927 // chromaticAdaption->matrix = matrix_invert(in->chromaticAdaption);
928 // chromaticAdaption->transform_module_fn = qcms_transform_module_matrix;
931 if (in->pcs == XYZ_SIGNATURE && out->pcs == LAB_SIGNATURE) {
932 struct qcms_modular_transform* pcs_to_lab;
933 pcs_to_lab = qcms_modular_transform_alloc();
934 if (!pcs_to_lab)
935 goto fail;
936 append_transform(pcs_to_lab, &next_transform);
937 pcs_to_lab->transform_module_fn = qcms_transform_module_XYZ_to_LAB;
940 if (out->color_space == RGB_SIGNATURE) {
941 struct qcms_modular_transform* pcs_to_rgb;
942 pcs_to_rgb = qcms_modular_transform_create_output(out);
943 if (!pcs_to_rgb)
944 goto fail;
945 append_transform(pcs_to_rgb, &next_transform);
946 } else {
947 assert(0 && "output color space not supported");
948 goto fail;
950 // Not Completed
951 //return qcms_modular_transform_reduce(first_transform);
952 return first_transform;
953 fail:
954 qcms_modular_transform_release(first_transform);
955 return EMPTY_TRANSFORM_LIST;
958 static float* qcms_modular_transform_data(struct qcms_modular_transform *transform, float *src, float *dest, size_t len)
960 while (transform != NULL) {
961 // Keep swaping src/dest when performing a transform to use less memory.
962 float *new_src = dest;
963 const transform_module_fn_t transform_fn = transform->transform_module_fn;
964 if (transform_fn != qcms_transform_module_gamma_table &&
965 transform_fn != qcms_transform_module_gamma_lut &&
966 transform_fn != qcms_transform_module_clut &&
967 transform_fn != qcms_transform_module_clut_only &&
968 transform_fn != qcms_transform_module_matrix &&
969 transform_fn != qcms_transform_module_matrix_translate &&
970 transform_fn != qcms_transform_module_LAB_to_XYZ &&
971 transform_fn != qcms_transform_module_XYZ_to_LAB) {
972 assert(0 && "Unsupported transform module");
973 return NULL;
975 if (transform->grid_size <= 0 &&
976 (transform_fn == qcms_transform_module_clut ||
977 transform_fn == qcms_transform_module_clut_only)) {
978 assert(0 && "Invalid transform");
979 return NULL;
981 transform->transform_module_fn(transform,src,dest,len);
982 dest = src;
983 src = new_src;
984 transform = transform->next_transform;
986 // The results end up in the src buffer because of the switching
987 return src;
990 float* qcms_chain_transform(qcms_profile *in, qcms_profile *out, float *src, float *dest, size_t lutSize)
992 struct qcms_modular_transform *transform_list = qcms_modular_transform_create(in, out);
993 if (transform_list != NULL) {
994 float *lut = qcms_modular_transform_data(transform_list, src, dest, lutSize/3);
995 qcms_modular_transform_release(transform_list);
996 return lut;
998 return NULL;