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 assert(transform
->grid_size
>= 1);
138 for (i
= 0; i
< length
; i
++) {
139 float linear_r
= *src
++;
140 float linear_g
= *src
++;
141 float linear_b
= *src
++;
143 int x
= floor(linear_r
* (transform
->grid_size
-1));
144 int y
= floor(linear_g
* (transform
->grid_size
-1));
145 int z
= floor(linear_b
* (transform
->grid_size
-1));
146 int x_n
= ceil(linear_r
* (transform
->grid_size
-1));
147 int y_n
= ceil(linear_g
* (transform
->grid_size
-1));
148 int z_n
= ceil(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
;
193 assert(transform
->grid_size
>= 1);
195 for (i
= 0; i
< length
; i
++) {
196 float device_r
= *src
++;
197 float device_g
= *src
++;
198 float device_b
= *src
++;
199 float linear_r
= lut_interp_linear_float(device_r
,
200 transform
->input_clut_table_r
, transform
->input_clut_table_length
);
201 float linear_g
= lut_interp_linear_float(device_g
,
202 transform
->input_clut_table_g
, transform
->input_clut_table_length
);
203 float linear_b
= lut_interp_linear_float(device_b
,
204 transform
->input_clut_table_b
, transform
->input_clut_table_length
);
206 int x
= floor(linear_r
* (transform
->grid_size
-1));
207 int y
= floor(linear_g
* (transform
->grid_size
-1));
208 int z
= floor(linear_b
* (transform
->grid_size
-1));
209 int x_n
= ceil(linear_r
* (transform
->grid_size
-1));
210 int y_n
= ceil(linear_g
* (transform
->grid_size
-1));
211 int z_n
= ceil(linear_b
* (transform
->grid_size
-1));
212 float x_d
= linear_r
* (transform
->grid_size
-1) - x
;
213 float y_d
= linear_g
* (transform
->grid_size
-1) - y
;
214 float z_d
= linear_b
* (transform
->grid_size
-1) - z
;
216 float r_x1
= lerp(CLU(r_table
,x
,y
,z
), CLU(r_table
,x_n
,y
,z
), x_d
);
217 float r_x2
= lerp(CLU(r_table
,x
,y_n
,z
), CLU(r_table
,x_n
,y_n
,z
), x_d
);
218 float r_y1
= lerp(r_x1
, r_x2
, y_d
);
219 float r_x3
= lerp(CLU(r_table
,x
,y
,z_n
), CLU(r_table
,x_n
,y
,z_n
), x_d
);
220 float r_x4
= lerp(CLU(r_table
,x
,y_n
,z_n
), CLU(r_table
,x_n
,y_n
,z_n
), x_d
);
221 float r_y2
= lerp(r_x3
, r_x4
, y_d
);
222 float clut_r
= lerp(r_y1
, r_y2
, z_d
);
224 float g_x1
= lerp(CLU(g_table
,x
,y
,z
), CLU(g_table
,x_n
,y
,z
), x_d
);
225 float g_x2
= lerp(CLU(g_table
,x
,y_n
,z
), CLU(g_table
,x_n
,y_n
,z
), x_d
);
226 float g_y1
= lerp(g_x1
, g_x2
, y_d
);
227 float g_x3
= lerp(CLU(g_table
,x
,y
,z_n
), CLU(g_table
,x_n
,y
,z_n
), x_d
);
228 float g_x4
= lerp(CLU(g_table
,x
,y_n
,z_n
), CLU(g_table
,x_n
,y_n
,z_n
), x_d
);
229 float g_y2
= lerp(g_x3
, g_x4
, y_d
);
230 float clut_g
= lerp(g_y1
, g_y2
, z_d
);
232 float b_x1
= lerp(CLU(b_table
,x
,y
,z
), CLU(b_table
,x_n
,y
,z
), x_d
);
233 float b_x2
= lerp(CLU(b_table
,x
,y_n
,z
), CLU(b_table
,x_n
,y_n
,z
), x_d
);
234 float b_y1
= lerp(b_x1
, b_x2
, y_d
);
235 float b_x3
= lerp(CLU(b_table
,x
,y
,z_n
), CLU(b_table
,x_n
,y
,z_n
), x_d
);
236 float b_x4
= lerp(CLU(b_table
,x
,y_n
,z_n
), CLU(b_table
,x_n
,y_n
,z_n
), x_d
);
237 float b_y2
= lerp(b_x3
, b_x4
, y_d
);
238 float clut_b
= lerp(b_y1
, b_y2
, z_d
);
240 float pcs_r
= lut_interp_linear_float(clut_r
,
241 transform
->output_clut_table_r
, transform
->output_clut_table_length
);
242 float pcs_g
= lut_interp_linear_float(clut_g
,
243 transform
->output_clut_table_g
, transform
->output_clut_table_length
);
244 float pcs_b
= lut_interp_linear_float(clut_b
,
245 transform
->output_clut_table_b
, transform
->output_clut_table_length
);
247 *dest
++ = clamp_float(pcs_r
);
248 *dest
++ = clamp_float(pcs_g
);
249 *dest
++ = clamp_float(pcs_b
);
254 static void qcms_transform_module_tetra_clut(struct qcms_modular_transform *transform, float *src, float *dest, size_t length)
258 int x_len = transform->grid_size;
259 int len = x_len * x_len;
260 float* r_table = transform->r_clut;
261 float* g_table = transform->g_clut;
262 float* b_table = transform->b_clut;
263 float c0_r, c1_r, c2_r, c3_r;
264 float c0_g, c1_g, c2_g, c3_g;
265 float c0_b, c1_b, c2_b, c3_b;
266 float clut_r, clut_g, clut_b;
267 float pcs_r, pcs_g, pcs_b;
268 for (i = 0; i < length; i++) {
269 float device_r = *src++;
270 float device_g = *src++;
271 float device_b = *src++;
272 float linear_r = lut_interp_linear_float(device_r,
273 transform->input_clut_table_r, transform->input_clut_table_length);
274 float linear_g = lut_interp_linear_float(device_g,
275 transform->input_clut_table_g, transform->input_clut_table_length);
276 float linear_b = lut_interp_linear_float(device_b,
277 transform->input_clut_table_b, transform->input_clut_table_length);
279 int x = floor(linear_r * (transform->grid_size-1));
280 int y = floor(linear_g * (transform->grid_size-1));
281 int z = floor(linear_b * (transform->grid_size-1));
282 int x_n = ceil(linear_r * (transform->grid_size-1));
283 int y_n = ceil(linear_g * (transform->grid_size-1));
284 int z_n = ceil(linear_b * (transform->grid_size-1));
285 float rx = linear_r * (transform->grid_size-1) - x;
286 float ry = linear_g * (transform->grid_size-1) - y;
287 float rz = linear_b * (transform->grid_size-1) - z;
289 c0_r = CLU(r_table, x, y, z);
290 c0_g = CLU(g_table, x, y, z);
291 c0_b = CLU(b_table, x, y, z);
293 if (ry >= rz) { //rx >= ry && ry >= rz
294 c1_r = CLU(r_table, x_n, y, z) - c0_r;
295 c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x_n, y, z);
296 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
297 c1_g = CLU(g_table, x_n, y, z) - c0_g;
298 c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x_n, y, z);
299 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
300 c1_b = CLU(b_table, x_n, y, z) - c0_b;
301 c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x_n, y, z);
302 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
304 if (rx >= rz) { //rx >= rz && rz >= ry
305 c1_r = CLU(r_table, x_n, y, z) - c0_r;
306 c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
307 c3_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x_n, y, z);
308 c1_g = CLU(g_table, x_n, y, z) - c0_g;
309 c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
310 c3_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x_n, y, z);
311 c1_b = CLU(b_table, x_n, y, z) - c0_b;
312 c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
313 c3_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x_n, y, z);
314 } else { //rz > rx && rx >= ry
315 c1_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x, y, z_n);
316 c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
317 c3_r = CLU(r_table, x, y, z_n) - c0_r;
318 c1_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x, y, z_n);
319 c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
320 c3_g = CLU(g_table, x, y, z_n) - c0_g;
321 c1_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x, y, z_n);
322 c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
323 c3_b = CLU(b_table, x, y, z_n) - c0_b;
327 if (rx >= rz) { //ry > rx && rx >= rz
328 c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x, y_n, z);
329 c2_r = CLU(r_table, x_n, y_n, z) - c0_r;
330 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
331 c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x, y_n, z);
332 c2_g = CLU(g_table, x_n, y_n, z) - c0_g;
333 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
334 c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x, y_n, z);
335 c2_b = CLU(b_table, x_n, y_n, z) - c0_b;
336 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
338 if (ry >= rz) { //ry >= rz && rz > rx
339 c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
340 c2_r = CLU(r_table, x, y_n, z) - c0_r;
341 c3_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y_n, z);
342 c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
343 c2_g = CLU(g_table, x, y_n, z) - c0_g;
344 c3_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y_n, z);
345 c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
346 c2_b = CLU(b_table, x, y_n, z) - c0_b;
347 c3_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y_n, z);
348 } else { //rz > ry && ry > rx
349 c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
350 c2_r = CLU(r_table, x, y_n, z) - c0_r;
351 c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
352 c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
353 c2_g = CLU(g_table, x, y_n, z) - c0_g;
354 c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
355 c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
356 c2_b = CLU(b_table, x, y_n, z) - c0_b;
357 c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
362 clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz;
363 clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz;
364 clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz;
366 pcs_r = lut_interp_linear_float(clut_r,
367 transform->output_clut_table_r, transform->output_clut_table_length);
368 pcs_g = lut_interp_linear_float(clut_g,
369 transform->output_clut_table_g, transform->output_clut_table_length);
370 pcs_b = lut_interp_linear_float(clut_b,
371 transform->output_clut_table_b, transform->output_clut_table_length);
372 *dest++ = clamp_float(pcs_r);
373 *dest++ = clamp_float(pcs_g);
374 *dest++ = clamp_float(pcs_b);
379 static void qcms_transform_module_gamma_table(struct qcms_modular_transform
*transform
, float *src
, float *dest
, size_t length
)
382 float out_r
, out_g
, out_b
;
383 for (i
= 0; i
< length
; i
++) {
388 out_r
= lut_interp_linear_float(in_r
, transform
->input_clut_table_r
, 256);
389 out_g
= lut_interp_linear_float(in_g
, transform
->input_clut_table_g
, 256);
390 out_b
= lut_interp_linear_float(in_b
, transform
->input_clut_table_b
, 256);
392 *dest
++ = clamp_float(out_r
);
393 *dest
++ = clamp_float(out_g
);
394 *dest
++ = clamp_float(out_b
);
398 static void qcms_transform_module_gamma_lut(struct qcms_modular_transform
*transform
, float *src
, float *dest
, size_t length
)
401 float out_r
, out_g
, out_b
;
402 for (i
= 0; i
< length
; i
++) {
407 out_r
= lut_interp_linear(in_r
,
408 transform
->output_gamma_lut_r
, transform
->output_gamma_lut_r_length
);
409 out_g
= lut_interp_linear(in_g
,
410 transform
->output_gamma_lut_g
, transform
->output_gamma_lut_g_length
);
411 out_b
= lut_interp_linear(in_b
,
412 transform
->output_gamma_lut_b
, transform
->output_gamma_lut_b_length
);
414 *dest
++ = clamp_float(out_r
);
415 *dest
++ = clamp_float(out_g
);
416 *dest
++ = clamp_float(out_b
);
420 static void qcms_transform_module_matrix_translate(struct qcms_modular_transform
*transform
, float *src
, float *dest
, size_t length
)
425 /* store the results in column major mode
426 * this makes doing the multiplication with sse easier */
427 mat
.m
[0][0] = transform
->matrix
.m
[0][0];
428 mat
.m
[1][0] = transform
->matrix
.m
[0][1];
429 mat
.m
[2][0] = transform
->matrix
.m
[0][2];
430 mat
.m
[0][1] = transform
->matrix
.m
[1][0];
431 mat
.m
[1][1] = transform
->matrix
.m
[1][1];
432 mat
.m
[2][1] = transform
->matrix
.m
[1][2];
433 mat
.m
[0][2] = transform
->matrix
.m
[2][0];
434 mat
.m
[1][2] = transform
->matrix
.m
[2][1];
435 mat
.m
[2][2] = transform
->matrix
.m
[2][2];
437 for (i
= 0; i
< length
; i
++) {
442 float out_r
= mat
.m
[0][0]*in_r
+ mat
.m
[1][0]*in_g
+ mat
.m
[2][0]*in_b
+ transform
->tx
;
443 float out_g
= mat
.m
[0][1]*in_r
+ mat
.m
[1][1]*in_g
+ mat
.m
[2][1]*in_b
+ transform
->ty
;
444 float out_b
= mat
.m
[0][2]*in_r
+ mat
.m
[1][2]*in_g
+ mat
.m
[2][2]*in_b
+ transform
->tz
;
446 *dest
++ = clamp_float(out_r
);
447 *dest
++ = clamp_float(out_g
);
448 *dest
++ = clamp_float(out_b
);
452 static void qcms_transform_module_matrix(struct qcms_modular_transform
*transform
, float *src
, float *dest
, size_t length
)
457 /* store the results in column major mode
458 * this makes doing the multiplication with sse easier */
459 mat
.m
[0][0] = transform
->matrix
.m
[0][0];
460 mat
.m
[1][0] = transform
->matrix
.m
[0][1];
461 mat
.m
[2][0] = transform
->matrix
.m
[0][2];
462 mat
.m
[0][1] = transform
->matrix
.m
[1][0];
463 mat
.m
[1][1] = transform
->matrix
.m
[1][1];
464 mat
.m
[2][1] = transform
->matrix
.m
[1][2];
465 mat
.m
[0][2] = transform
->matrix
.m
[2][0];
466 mat
.m
[1][2] = transform
->matrix
.m
[2][1];
467 mat
.m
[2][2] = transform
->matrix
.m
[2][2];
469 for (i
= 0; i
< length
; i
++) {
474 float out_r
= mat
.m
[0][0]*in_r
+ mat
.m
[1][0]*in_g
+ mat
.m
[2][0]*in_b
;
475 float out_g
= mat
.m
[0][1]*in_r
+ mat
.m
[1][1]*in_g
+ mat
.m
[2][1]*in_b
;
476 float out_b
= mat
.m
[0][2]*in_r
+ mat
.m
[1][2]*in_g
+ mat
.m
[2][2]*in_b
;
478 *dest
++ = clamp_float(out_r
);
479 *dest
++ = clamp_float(out_g
);
480 *dest
++ = clamp_float(out_b
);
484 static struct qcms_modular_transform
* qcms_modular_transform_alloc() {
485 return calloc(1, sizeof(struct qcms_modular_transform
));
488 static void qcms_modular_transform_release(struct qcms_modular_transform
*transform
)
490 struct qcms_modular_transform
*next_transform
;
491 while (transform
!= NULL
) {
492 next_transform
= transform
->next_transform
;
493 // clut may use a single block of memory.
494 // Perhaps we should remove this to simply the code.
495 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
) {
496 if (transform
->input_clut_table_r
) free(transform
->input_clut_table_r
);
498 if (transform
->input_clut_table_r
) free(transform
->input_clut_table_r
);
499 if (transform
->input_clut_table_g
) free(transform
->input_clut_table_g
);
500 if (transform
->input_clut_table_b
) free(transform
->input_clut_table_b
);
502 if (transform
->r_clut
+ 1 == transform
->g_clut
&& transform
->g_clut
+ 1 == transform
->b_clut
) {
503 if (transform
->r_clut
) free(transform
->r_clut
);
505 if (transform
->r_clut
) free(transform
->r_clut
);
506 if (transform
->g_clut
) free(transform
->g_clut
);
507 if (transform
->b_clut
) free(transform
->b_clut
);
509 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
) {
510 if (transform
->output_clut_table_r
) free(transform
->output_clut_table_r
);
512 if (transform
->output_clut_table_r
) free(transform
->output_clut_table_r
);
513 if (transform
->output_clut_table_g
) free(transform
->output_clut_table_g
);
514 if (transform
->output_clut_table_b
) free(transform
->output_clut_table_b
);
516 if (transform
->output_gamma_lut_r
) free(transform
->output_gamma_lut_r
);
517 if (transform
->output_gamma_lut_g
) free(transform
->output_gamma_lut_g
);
518 if (transform
->output_gamma_lut_b
) free(transform
->output_gamma_lut_b
);
520 transform
= next_transform
;
524 /* Set transform to be the next element in the linked list. */
525 static void append_transform(struct qcms_modular_transform
*transform
, struct qcms_modular_transform
***next_transform
)
527 **next_transform
= transform
;
529 *next_transform
= &(transform
->next_transform
);
530 transform
= transform
->next_transform
;
534 /* reverse the transformation list (used by mBA) */
535 static struct qcms_modular_transform
* reverse_transform(struct qcms_modular_transform
*transform
)
537 struct qcms_modular_transform
*prev_transform
= NULL
;
538 while (transform
!= NULL
) {
539 struct qcms_modular_transform
*next_transform
= transform
->next_transform
;
540 transform
->next_transform
= prev_transform
;
541 prev_transform
= transform
;
542 transform
= next_transform
;
545 return prev_transform
;
548 #define EMPTY_TRANSFORM_LIST NULL
549 static struct qcms_modular_transform
* qcms_modular_transform_create_mAB(struct lutmABType
*lut
)
551 struct qcms_modular_transform
*first_transform
= NULL
;
552 struct qcms_modular_transform
**next_transform
= &first_transform
;
553 struct qcms_modular_transform
*transform
= NULL
;
555 if (lut
->a_curves
[0] != NULL
) {
559 // If the A curve is present this also implies the
560 // presence of a CLUT.
561 if (!lut
->clut_table
)
565 transform
= qcms_modular_transform_alloc();
568 append_transform(transform
, &next_transform
);
569 transform
->input_clut_table_r
= build_input_gamma_table(lut
->a_curves
[0]);
570 transform
->input_clut_table_g
= build_input_gamma_table(lut
->a_curves
[1]);
571 transform
->input_clut_table_b
= build_input_gamma_table(lut
->a_curves
[2]);
572 transform
->transform_module_fn
= qcms_transform_module_gamma_table
;
573 if (lut
->num_grid_points
[0] != lut
->num_grid_points
[1] ||
574 lut
->num_grid_points
[1] != lut
->num_grid_points
[2] ) {
575 //XXX: We don't currently support clut that are not squared!
580 transform
= qcms_modular_transform_alloc();
583 append_transform(transform
, &next_transform
);
584 clut_length
= sizeof(float)*pow(lut
->num_grid_points
[0], 3)*3;
585 clut
= malloc(clut_length
);
588 memcpy(clut
, lut
->clut_table
, clut_length
);
589 transform
->r_clut
= clut
+ 0;
590 transform
->g_clut
= clut
+ 1;
591 transform
->b_clut
= clut
+ 2;
592 transform
->grid_size
= lut
->num_grid_points
[0];
593 transform
->transform_module_fn
= qcms_transform_module_clut_only
;
595 if (lut
->m_curves
[0] != NULL
) {
596 // M curve imples the presence of a Matrix
599 transform
= qcms_modular_transform_alloc();
602 append_transform(transform
, &next_transform
);
603 transform
->input_clut_table_r
= build_input_gamma_table(lut
->m_curves
[0]);
604 transform
->input_clut_table_g
= build_input_gamma_table(lut
->m_curves
[1]);
605 transform
->input_clut_table_b
= build_input_gamma_table(lut
->m_curves
[2]);
606 transform
->transform_module_fn
= qcms_transform_module_gamma_table
;
609 transform
= qcms_modular_transform_alloc();
612 append_transform(transform
, &next_transform
);
613 transform
->matrix
= build_mAB_matrix(lut
);
614 if (transform
->matrix
.invalid
)
616 transform
->tx
= s15Fixed16Number_to_float(lut
->e03
);
617 transform
->ty
= s15Fixed16Number_to_float(lut
->e13
);
618 transform
->tz
= s15Fixed16Number_to_float(lut
->e23
);
619 transform
->transform_module_fn
= qcms_transform_module_matrix_translate
;
621 if (lut
->b_curves
[0] != NULL
) {
623 transform
= qcms_modular_transform_alloc();
626 append_transform(transform
, &next_transform
);
627 transform
->input_clut_table_r
= build_input_gamma_table(lut
->b_curves
[0]);
628 transform
->input_clut_table_g
= build_input_gamma_table(lut
->b_curves
[1]);
629 transform
->input_clut_table_b
= build_input_gamma_table(lut
->b_curves
[2]);
630 transform
->transform_module_fn
= qcms_transform_module_gamma_table
;
632 // B curve is mandatory
637 // mBA are identical to mAB except that the transformation order
639 first_transform
= reverse_transform(first_transform
);
642 return first_transform
;
644 qcms_modular_transform_release(first_transform
);
648 static struct qcms_modular_transform
* qcms_modular_transform_create_lut(struct lutType
*lut
)
650 struct qcms_modular_transform
*first_transform
= NULL
;
651 struct qcms_modular_transform
**next_transform
= &first_transform
;
652 struct qcms_modular_transform
*transform
= NULL
;
654 size_t in_curve_len
, clut_length
, out_curve_len
;
655 float *in_curves
, *clut
, *out_curves
;
658 transform
= qcms_modular_transform_alloc();
661 append_transform(transform
, &next_transform
);
662 transform
->matrix
= build_lut_matrix(lut
);
663 if (transform
->matrix
.invalid
)
665 transform
->transform_module_fn
= qcms_transform_module_matrix
;
667 // Prepare input curves
668 transform
= qcms_modular_transform_alloc();
671 append_transform(transform
, &next_transform
);
672 in_curve_len
= sizeof(float)*lut
->num_input_table_entries
* 3;
673 in_curves
= malloc(in_curve_len
);
676 memcpy(in_curves
, lut
->input_table
, in_curve_len
);
677 transform
->input_clut_table_r
= in_curves
+ lut
->num_input_table_entries
* 0;
678 transform
->input_clut_table_g
= in_curves
+ lut
->num_input_table_entries
* 1;
679 transform
->input_clut_table_b
= in_curves
+ lut
->num_input_table_entries
* 2;
680 transform
->input_clut_table_length
= lut
->num_input_table_entries
;
683 clut_length
= sizeof(float)*pow(lut
->num_clut_grid_points
, 3)*3;
684 clut
= malloc(clut_length
);
687 memcpy(clut
, lut
->clut_table
, clut_length
);
688 transform
->r_clut
= clut
+ 0;
689 transform
->g_clut
= clut
+ 1;
690 transform
->b_clut
= clut
+ 2;
691 transform
->grid_size
= lut
->num_clut_grid_points
;
693 // Prepare output curves
694 out_curve_len
= sizeof(float) * lut
->num_output_table_entries
* 3;
695 out_curves
= malloc(out_curve_len
);
698 memcpy(out_curves
, lut
->output_table
, out_curve_len
);
699 transform
->output_clut_table_r
= out_curves
+ lut
->num_output_table_entries
* 0;
700 transform
->output_clut_table_g
= out_curves
+ lut
->num_output_table_entries
* 1;
701 transform
->output_clut_table_b
= out_curves
+ lut
->num_output_table_entries
* 2;
702 transform
->output_clut_table_length
= lut
->num_output_table_entries
;
703 transform
->transform_module_fn
= qcms_transform_module_clut
;
705 return first_transform
;
707 qcms_modular_transform_release(first_transform
);
711 struct qcms_modular_transform
* qcms_modular_transform_create_input(qcms_profile
*in
)
713 struct qcms_modular_transform
*first_transform
= NULL
;
714 struct qcms_modular_transform
**next_transform
= &first_transform
;
717 struct qcms_modular_transform
*lut_transform
;
718 lut_transform
= qcms_modular_transform_create_lut(in
->A2B0
);
721 append_transform(lut_transform
, &next_transform
);
722 } else if (in
->mAB
&& in
->mAB
->num_in_channels
== 3 && in
->mAB
->num_out_channels
== 3) {
723 struct qcms_modular_transform
*mAB_transform
;
724 mAB_transform
= qcms_modular_transform_create_mAB(in
->mAB
);
727 append_transform(mAB_transform
, &next_transform
);
730 struct qcms_modular_transform
*transform
;
732 transform
= qcms_modular_transform_alloc();
735 append_transform(transform
, &next_transform
);
736 transform
->input_clut_table_r
= build_input_gamma_table(in
->redTRC
);
737 transform
->input_clut_table_g
= build_input_gamma_table(in
->greenTRC
);
738 transform
->input_clut_table_b
= build_input_gamma_table(in
->blueTRC
);
739 transform
->transform_module_fn
= qcms_transform_module_gamma_table
;
740 if (!transform
->input_clut_table_r
|| !transform
->input_clut_table_g
||
741 !transform
->input_clut_table_b
) {
745 transform
= qcms_modular_transform_alloc();
748 append_transform(transform
, &next_transform
);
749 transform
->matrix
.m
[0][0] = 1/1.999969482421875f
;
750 transform
->matrix
.m
[0][1] = 0.f
;
751 transform
->matrix
.m
[0][2] = 0.f
;
752 transform
->matrix
.m
[1][0] = 0.f
;
753 transform
->matrix
.m
[1][1] = 1/1.999969482421875f
;
754 transform
->matrix
.m
[1][2] = 0.f
;
755 transform
->matrix
.m
[2][0] = 0.f
;
756 transform
->matrix
.m
[2][1] = 0.f
;
757 transform
->matrix
.m
[2][2] = 1/1.999969482421875f
;
758 transform
->matrix
.invalid
= false;
759 transform
->transform_module_fn
= qcms_transform_module_matrix
;
761 transform
= qcms_modular_transform_alloc();
764 append_transform(transform
, &next_transform
);
765 transform
->matrix
= build_colorant_matrix(in
);
766 transform
->transform_module_fn
= qcms_transform_module_matrix
;
769 return first_transform
;
771 qcms_modular_transform_release(first_transform
);
772 return EMPTY_TRANSFORM_LIST
;
774 static struct qcms_modular_transform
* qcms_modular_transform_create_output(qcms_profile
*out
)
776 struct qcms_modular_transform
*first_transform
= NULL
;
777 struct qcms_modular_transform
**next_transform
= &first_transform
;
780 struct qcms_modular_transform
*lut_transform
;
781 lut_transform
= qcms_modular_transform_create_lut(out
->B2A0
);
784 append_transform(lut_transform
, &next_transform
);
785 } else if (out
->mBA
&& out
->mBA
->num_in_channels
== 3 && out
->mBA
->num_out_channels
== 3) {
786 struct qcms_modular_transform
*lut_transform
;
787 lut_transform
= qcms_modular_transform_create_mAB(out
->mBA
);
790 append_transform(lut_transform
, &next_transform
);
791 } else if (out
->redTRC
&& out
->greenTRC
&& out
->blueTRC
) {
792 struct qcms_modular_transform
*transform
;
794 transform
= qcms_modular_transform_alloc();
797 append_transform(transform
, &next_transform
);
798 transform
->matrix
= matrix_invert(build_colorant_matrix(out
));
799 transform
->transform_module_fn
= qcms_transform_module_matrix
;
801 transform
= qcms_modular_transform_alloc();
804 append_transform(transform
, &next_transform
);
805 transform
->matrix
.m
[0][0] = 1.999969482421875f
;
806 transform
->matrix
.m
[0][1] = 0.f
;
807 transform
->matrix
.m
[0][2] = 0.f
;
808 transform
->matrix
.m
[1][0] = 0.f
;
809 transform
->matrix
.m
[1][1] = 1.999969482421875f
;
810 transform
->matrix
.m
[1][2] = 0.f
;
811 transform
->matrix
.m
[2][0] = 0.f
;
812 transform
->matrix
.m
[2][1] = 0.f
;
813 transform
->matrix
.m
[2][2] = 1.999969482421875f
;
814 transform
->matrix
.invalid
= false;
815 transform
->transform_module_fn
= qcms_transform_module_matrix
;
817 transform
= qcms_modular_transform_alloc();
820 append_transform(transform
, &next_transform
);
821 build_output_lut(out
->redTRC
, &transform
->output_gamma_lut_r
,
822 &transform
->output_gamma_lut_r_length
);
823 build_output_lut(out
->greenTRC
, &transform
->output_gamma_lut_g
,
824 &transform
->output_gamma_lut_g_length
);
825 build_output_lut(out
->blueTRC
, &transform
->output_gamma_lut_b
,
826 &transform
->output_gamma_lut_b_length
);
827 transform
->transform_module_fn
= qcms_transform_module_gamma_lut
;
829 if (!transform
->output_gamma_lut_r
|| !transform
->output_gamma_lut_g
||
830 !transform
->output_gamma_lut_b
) {
834 assert(0 && "Unsupported output profile workflow.");
838 return first_transform
;
840 qcms_modular_transform_release(first_transform
);
841 return EMPTY_TRANSFORM_LIST
;
845 // Simplify the transformation chain to an equivalent transformation chain
846 static struct qcms_modular_transform* qcms_modular_transform_reduce(struct qcms_modular_transform *transform)
848 struct qcms_modular_transform *first_transform = NULL;
849 struct qcms_modular_transform *curr_trans = transform;
850 struct qcms_modular_transform *prev_trans = NULL;
852 struct qcms_modular_transform *next_trans = curr_trans->next_transform;
853 if (curr_trans->transform_module_fn == qcms_transform_module_matrix) {
854 if (next_trans && next_trans->transform_module_fn == qcms_transform_module_matrix) {
855 curr_trans->matrix = matrix_multiply(curr_trans->matrix, next_trans->matrix);
859 if (curr_trans->transform_module_fn == qcms_transform_module_gamma_table) {
860 bool isLinear = true;
862 for (i = 0; isLinear && i < 256; i++) {
863 isLinear &= (int)(curr_trans->input_clut_table_r[i] * 255) == i;
864 isLinear &= (int)(curr_trans->input_clut_table_g[i] * 255) == i;
865 isLinear &= (int)(curr_trans->input_clut_table_b[i] * 255) == i;
871 if (!next_trans) break;
872 prev_trans = curr_trans;
873 curr_trans = next_trans;
876 if (curr_trans == transform) {
878 transform = next_trans;
880 prev_trans->next_transform = next_trans;
882 curr_trans->next_transform = NULL;
883 qcms_modular_transform_release(curr_trans);
885 return qcms_modular_transform_reduce(transform);
887 curr_trans->next_transform = next_trans->next_transform;
888 next_trans->next_transform = NULL;
889 qcms_modular_transform_release(next_trans);
896 static struct qcms_modular_transform
* qcms_modular_transform_create(qcms_profile
*in
, qcms_profile
*out
)
898 struct qcms_modular_transform
*first_transform
= NULL
;
899 struct qcms_modular_transform
**next_transform
= &first_transform
;
901 if (in
->color_space
== RGB_SIGNATURE
) {
902 struct qcms_modular_transform
* rgb_to_pcs
;
903 rgb_to_pcs
= qcms_modular_transform_create_input(in
);
906 append_transform(rgb_to_pcs
, &next_transform
);
908 assert(0 && "input color space not supported");
912 if (in
->pcs
== LAB_SIGNATURE
&& out
->pcs
== XYZ_SIGNATURE
) {
913 struct qcms_modular_transform
* lab_to_pcs
;
914 lab_to_pcs
= qcms_modular_transform_alloc();
917 append_transform(lab_to_pcs
, &next_transform
);
918 lab_to_pcs
->transform_module_fn
= qcms_transform_module_LAB_to_XYZ
;
921 // This does not improve accuracy in practice, something is wrong here.
922 //if (in->chromaticAdaption.invalid == false) {
923 // struct qcms_modular_transform* chromaticAdaption;
924 // chromaticAdaption = qcms_modular_transform_alloc();
925 // if (!chromaticAdaption)
927 // append_transform(chromaticAdaption, &next_transform);
928 // chromaticAdaption->matrix = matrix_invert(in->chromaticAdaption);
929 // chromaticAdaption->transform_module_fn = qcms_transform_module_matrix;
932 if (in
->pcs
== XYZ_SIGNATURE
&& out
->pcs
== LAB_SIGNATURE
) {
933 struct qcms_modular_transform
* pcs_to_lab
;
934 pcs_to_lab
= qcms_modular_transform_alloc();
937 append_transform(pcs_to_lab
, &next_transform
);
938 pcs_to_lab
->transform_module_fn
= qcms_transform_module_XYZ_to_LAB
;
941 if (out
->color_space
== RGB_SIGNATURE
) {
942 struct qcms_modular_transform
* pcs_to_rgb
;
943 pcs_to_rgb
= qcms_modular_transform_create_output(out
);
946 append_transform(pcs_to_rgb
, &next_transform
);
948 assert(0 && "output color space not supported");
952 //return qcms_modular_transform_reduce(first_transform);
953 return first_transform
;
955 qcms_modular_transform_release(first_transform
);
956 return EMPTY_TRANSFORM_LIST
;
959 static float* qcms_modular_transform_data(struct qcms_modular_transform
*transform
, float *src
, float *dest
, size_t len
)
961 while (transform
!= NULL
) {
962 // Keep swaping src/dest when performing a transform to use less memory.
963 float *new_src
= dest
;
964 const transform_module_fn_t transform_fn
= transform
->transform_module_fn
;
965 if (transform_fn
!= qcms_transform_module_gamma_table
&&
966 transform_fn
!= qcms_transform_module_gamma_lut
&&
967 transform_fn
!= qcms_transform_module_clut
&&
968 transform_fn
!= qcms_transform_module_clut_only
&&
969 transform_fn
!= qcms_transform_module_matrix
&&
970 transform_fn
!= qcms_transform_module_matrix_translate
&&
971 transform_fn
!= qcms_transform_module_LAB_to_XYZ
&&
972 transform_fn
!= qcms_transform_module_XYZ_to_LAB
) {
973 assert(0 && "Unsupported transform module");
976 transform
->transform_module_fn(transform
,src
,dest
,len
);
979 transform
= transform
->next_transform
;
981 // The results end up in the src buffer because of the switching
985 float* qcms_chain_transform(qcms_profile
*in
, qcms_profile
*out
, float *src
, float *dest
, size_t lutSize
)
987 struct qcms_modular_transform
*transform_list
= qcms_modular_transform_create(in
, out
);
988 if (transform_list
!= NULL
) {
989 float *lut
= qcms_modular_transform_data(transform_list
, src
, dest
, lutSize
/3);
990 qcms_modular_transform_release(transform_list
);