1 /* vim: set ts=8 sw=8 noexpandtab: */
3 // Copyright (C) 2009 Mozilla Corporation
4 // Copyright (C) 1998-2007 Marti Maria
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.
27 #include <string.h> //memcpy
29 #include "transform_util.h"
32 static struct matrix
build_lut_matrix(struct lutType
*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;
47 memset(&result
, 0, sizeof(struct matrix
));
48 result
.invalid
= true;
53 static struct matrix
build_mAB_matrix(struct lutmABType
*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;
68 memset(&result
, 0, sizeof(struct matrix
));
69 result
.invalid
= true;
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
)
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
)
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
)
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
)
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
);
253 static void qcms_transform_module_tetra_clut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
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);
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);
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;
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);
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
)
381 float out_r
, out_g
, out_b
;
382 for (i
= 0; i
< length
; i
++) {
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
)
400 float out_r
, out_g
, out_b
;
401 for (i
= 0; i
< length
; i
++) {
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
)
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
++) {
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
)
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
++) {
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
);
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
);
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
);
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
);
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
;
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
) {
558 // If the A curve is present this also implies the
559 // presence of a CLUT.
560 if (!lut
->clut_table
)
564 transform
= qcms_modular_transform_alloc();
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!
579 transform
= qcms_modular_transform_alloc();
582 append_transform(transform
, &next_transform
);
583 clut_length
= sizeof(float)*pow(lut
->num_grid_points
[0], 3)*3;
584 clut
= malloc(clut_length
);
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
598 transform
= qcms_modular_transform_alloc();
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
;
608 transform
= qcms_modular_transform_alloc();
611 append_transform(transform
, &next_transform
);
612 transform
->matrix
= build_mAB_matrix(lut
);
613 if (transform
->matrix
.invalid
)
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
) {
622 transform
= qcms_modular_transform_alloc();
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
;
631 // B curve is mandatory
636 // mBA are identical to mAB except that the transformation order
638 first_transform
= reverse_transform(first_transform
);
641 return first_transform
;
643 qcms_modular_transform_release(first_transform
);
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
;
657 transform
= qcms_modular_transform_alloc();
660 append_transform(transform
, &next_transform
);
661 transform
->matrix
= build_lut_matrix(lut
);
662 if (transform
->matrix
.invalid
)
664 transform
->transform_module_fn
= qcms_transform_module_matrix
;
666 // Prepare input curves
667 transform
= qcms_modular_transform_alloc();
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
);
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
;
682 clut_length
= sizeof(float)*pow(lut
->num_clut_grid_points
, 3)*3;
683 clut
= malloc(clut_length
);
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
);
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
;
706 qcms_modular_transform_release(first_transform
);
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
;
716 struct qcms_modular_transform
*lut_transform
;
717 lut_transform
= qcms_modular_transform_create_lut(in
->A2B0
);
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
);
726 append_transform(mAB_transform
, &next_transform
);
729 struct qcms_modular_transform
*transform
;
731 transform
= qcms_modular_transform_alloc();
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
) {
744 transform
= qcms_modular_transform_alloc();
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();
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
;
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
;
779 struct qcms_modular_transform
*lut_transform
;
780 lut_transform
= qcms_modular_transform_create_lut(out
->B2A0
);
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
);
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();
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();
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();
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
) {
833 assert(0 && "Unsupported output profile workflow.");
837 return first_transform
;
839 qcms_modular_transform_release(first_transform
);
840 return EMPTY_TRANSFORM_LIST
;
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;
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);
858 if (curr_trans->transform_module_fn == qcms_transform_module_gamma_table) {
859 bool isLinear = true;
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;
870 if (!next_trans) break;
871 prev_trans = curr_trans;
872 curr_trans = next_trans;
875 if (curr_trans == transform) {
877 transform = next_trans;
879 prev_trans->next_transform = next_trans;
881 curr_trans->next_transform = NULL;
882 qcms_modular_transform_release(curr_trans);
884 return qcms_modular_transform_reduce(transform);
886 curr_trans->next_transform = next_trans->next_transform;
887 next_trans->next_transform = NULL;
888 qcms_modular_transform_release(next_trans);
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
);
905 append_transform(rgb_to_pcs
, &next_transform
);
907 assert(0 && "input color space not supported");
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();
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)
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();
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
);
945 append_transform(pcs_to_rgb
, &next_transform
);
947 assert(0 && "output color space not supported");
951 //return qcms_modular_transform_reduce(first_transform);
952 return first_transform
;
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");
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");
981 transform
->transform_module_fn(transform
,src
,dest
,len
);
984 transform
= transform
->next_transform
;
986 // The results end up in the src buffer because of the switching
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
);