1 : /* vim: set ts=8 sw=8 noexpandtab: */
2 : // qcms
3 : // Copyright (C) 2009 Mozilla Corporation
4 : // Copyright (C) 1998-2007 Marti Maria
5 : //
6 : // Permission is hereby granted, free of charge, to any person obtaining
7 : // a copy of this software and associated documentation files (the "Software"),
8 : // to deal in the Software without restriction, including without limitation
9 : // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 : // and/or sell copies of the Software, and to permit persons to whom the Software
11 : // is furnished to do so, subject to the following conditions:
12 : //
13 : // The above copyright notice and this permission notice shall be included in
14 : // all copies or substantial portions of the Software.
15 : //
16 : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 : // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18 : // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 : // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 : // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 : // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 : // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 :
24 : #include <stdlib.h>
25 : #include <math.h>
26 : #include <assert.h>
27 : #include <string.h> //memcpy
28 : #include "qcmsint.h"
29 : #include "chain.h"
30 : #include "matrix.h"
31 : #include "transform_util.h"
32 :
33 : /* for MSVC, GCC, Intel, and Sun compilers */
34 : #if defined(_M_IX86) || defined(__i386__) || defined(__i386) || defined(_M_AMD64) || defined(__x86_64__) || defined(__x86_64)
35 : #define X86
36 : #endif /* _M_IX86 || __i386__ || __i386 || _M_AMD64 || __x86_64__ || __x86_64 */
37 :
38 : // Build a White point, primary chromas transfer matrix from RGB to CIE XYZ
39 : // This is just an approximation, I am not handling all the non-linear
40 : // aspects of the RGB to XYZ process, and assumming that the gamma correction
41 : // has transitive property in the tranformation chain.
42 : //
43 : // the alghoritm:
44 : //
45 : // - First I build the absolute conversion matrix using
46 : // primaries in XYZ. This matrix is next inverted
47 : // - Then I eval the source white point across this matrix
48 : // obtaining the coeficients of the transformation
49 : // - Then, I apply these coeficients to the original matrix
50 0 : static struct matrix build_RGB_to_XYZ_transfer_matrix(qcms_CIE_xyY white, qcms_CIE_xyYTRIPLE primrs)
51 : {
52 : struct matrix primaries;
53 : struct matrix primaries_invert;
54 : struct matrix result;
55 : struct vector white_point;
56 : struct vector coefs;
57 :
58 : double xn, yn;
59 : double xr, yr;
60 : double xg, yg;
61 : double xb, yb;
62 :
63 0 : xn = white.x;
64 0 : yn = white.y;
65 :
66 0 : if (yn == 0.0)
67 0 : return matrix_invalid();
68 :
69 0 : xr = primrs.red.x;
70 0 : yr = primrs.red.y;
71 0 : xg = primrs.green.x;
72 0 : yg = primrs.green.y;
73 0 : xb = primrs.blue.x;
74 0 : yb = primrs.blue.y;
75 :
76 0 : primaries.m[0][0] = xr;
77 0 : primaries.m[0][1] = xg;
78 0 : primaries.m[0][2] = xb;
79 :
80 0 : primaries.m[1][0] = yr;
81 0 : primaries.m[1][1] = yg;
82 0 : primaries.m[1][2] = yb;
83 :
84 0 : primaries.m[2][0] = 1 - xr - yr;
85 0 : primaries.m[2][1] = 1 - xg - yg;
86 0 : primaries.m[2][2] = 1 - xb - yb;
87 0 : primaries.invalid = false;
88 :
89 0 : white_point.v[0] = xn/yn;
90 0 : white_point.v[1] = 1.;
91 0 : white_point.v[2] = (1.0-xn-yn)/yn;
92 :
93 0 : primaries_invert = matrix_invert(primaries);
94 :
95 0 : coefs = matrix_eval(primaries_invert, white_point);
96 :
97 0 : result.m[0][0] = coefs.v[0]*xr;
98 0 : result.m[0][1] = coefs.v[1]*xg;
99 0 : result.m[0][2] = coefs.v[2]*xb;
100 :
101 0 : result.m[1][0] = coefs.v[0]*yr;
102 0 : result.m[1][1] = coefs.v[1]*yg;
103 0 : result.m[1][2] = coefs.v[2]*yb;
104 :
105 0 : result.m[2][0] = coefs.v[0]*(1.-xr-yr);
106 0 : result.m[2][1] = coefs.v[1]*(1.-xg-yg);
107 0 : result.m[2][2] = coefs.v[2]*(1.-xb-yb);
108 0 : result.invalid = primaries_invert.invalid;
109 :
110 0 : return result;
111 : }
112 :
113 : struct CIE_XYZ {
114 : double X;
115 : double Y;
116 : double Z;
117 : };
118 :
119 : /* CIE Illuminant D50 */
120 : static const struct CIE_XYZ D50_XYZ = {
121 : 0.9642,
122 : 1.0000,
123 : 0.8249
124 : };
125 :
126 : /* from lcms: xyY2XYZ()
127 : * corresponds to argyll: icmYxy2XYZ() */
128 0 : static struct CIE_XYZ xyY2XYZ(qcms_CIE_xyY source)
129 : {
130 : struct CIE_XYZ dest;
131 0 : dest.X = (source.x / source.y) * source.Y;
132 0 : dest.Y = source.Y;
133 0 : dest.Z = ((1 - source.x - source.y) / source.y) * source.Y;
134 0 : return dest;
135 : }
136 :
137 : /* from lcms: ComputeChromaticAdaption */
138 : // Compute chromatic adaption matrix using chad as cone matrix
139 : static struct matrix
140 0 : compute_chromatic_adaption(struct CIE_XYZ source_white_point,
141 : struct CIE_XYZ dest_white_point,
142 : struct matrix chad)
143 : {
144 : struct matrix chad_inv;
145 : struct vector cone_source_XYZ, cone_source_rgb;
146 : struct vector cone_dest_XYZ, cone_dest_rgb;
147 : struct matrix cone, tmp;
148 :
149 0 : tmp = chad;
150 0 : chad_inv = matrix_invert(tmp);
151 :
152 0 : cone_source_XYZ.v[0] = source_white_point.X;
153 0 : cone_source_XYZ.v[1] = source_white_point.Y;
154 0 : cone_source_XYZ.v[2] = source_white_point.Z;
155 :
156 0 : cone_dest_XYZ.v[0] = dest_white_point.X;
157 0 : cone_dest_XYZ.v[1] = dest_white_point.Y;
158 0 : cone_dest_XYZ.v[2] = dest_white_point.Z;
159 :
160 0 : cone_source_rgb = matrix_eval(chad, cone_source_XYZ);
161 0 : cone_dest_rgb = matrix_eval(chad, cone_dest_XYZ);
162 :
163 0 : cone.m[0][0] = cone_dest_rgb.v[0]/cone_source_rgb.v[0];
164 0 : cone.m[0][1] = 0;
165 0 : cone.m[0][2] = 0;
166 0 : cone.m[1][0] = 0;
167 0 : cone.m[1][1] = cone_dest_rgb.v[1]/cone_source_rgb.v[1];
168 0 : cone.m[1][2] = 0;
169 0 : cone.m[2][0] = 0;
170 0 : cone.m[2][1] = 0;
171 0 : cone.m[2][2] = cone_dest_rgb.v[2]/cone_source_rgb.v[2];
172 0 : cone.invalid = false;
173 :
174 : // Normalize
175 0 : return matrix_multiply(chad_inv, matrix_multiply(cone, chad));
176 : }
177 :
178 : /* from lcms: cmsAdaptionMatrix */
179 : // Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll
180 : // Bradford is assumed
181 : static struct matrix
182 0 : adaption_matrix(struct CIE_XYZ source_illumination, struct CIE_XYZ target_illumination)
183 : {
184 0 : struct matrix lam_rigg = {{ // Bradford matrix
185 : { 0.8951, 0.2664, -0.1614 },
186 : { -0.7502, 1.7135, 0.0367 },
187 : { 0.0389, -0.0685, 1.0296 }
188 : }};
189 0 : return compute_chromatic_adaption(source_illumination, target_illumination, lam_rigg);
190 : }
191 :
192 : /* from lcms: cmsAdaptMatrixToD50 */
193 0 : static struct matrix adapt_matrix_to_D50(struct matrix r, qcms_CIE_xyY source_white_pt)
194 : {
195 : struct CIE_XYZ Dn;
196 : struct matrix Bradford;
197 :
198 0 : if (source_white_pt.y == 0.0)
199 0 : return matrix_invalid();
200 :
201 0 : Dn = xyY2XYZ(source_white_pt);
202 :
203 0 : Bradford = adaption_matrix(Dn, D50_XYZ);
204 0 : return matrix_multiply(Bradford, r);
205 : }
206 :
207 0 : qcms_bool set_rgb_colorants(qcms_profile *profile, qcms_CIE_xyY white_point, qcms_CIE_xyYTRIPLE primaries)
208 : {
209 : struct matrix colorants;
210 0 : colorants = build_RGB_to_XYZ_transfer_matrix(white_point, primaries);
211 0 : colorants = adapt_matrix_to_D50(colorants, white_point);
212 :
213 0 : if (colorants.invalid)
214 0 : return false;
215 :
216 : /* note: there's a transpose type of operation going on here */
217 0 : profile->redColorant.X = double_to_s15Fixed16Number(colorants.m[0][0]);
218 0 : profile->redColorant.Y = double_to_s15Fixed16Number(colorants.m[1][0]);
219 0 : profile->redColorant.Z = double_to_s15Fixed16Number(colorants.m[2][0]);
220 :
221 0 : profile->greenColorant.X = double_to_s15Fixed16Number(colorants.m[0][1]);
222 0 : profile->greenColorant.Y = double_to_s15Fixed16Number(colorants.m[1][1]);
223 0 : profile->greenColorant.Z = double_to_s15Fixed16Number(colorants.m[2][1]);
224 :
225 0 : profile->blueColorant.X = double_to_s15Fixed16Number(colorants.m[0][2]);
226 0 : profile->blueColorant.Y = double_to_s15Fixed16Number(colorants.m[1][2]);
227 0 : profile->blueColorant.Z = double_to_s15Fixed16Number(colorants.m[2][2]);
228 :
229 0 : return true;
230 : }
231 :
232 : #if 0
233 : static void qcms_transform_data_rgb_out_pow(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
234 : {
235 : int i;
236 : float (*mat)[4] = transform->matrix;
237 : for (i=0; i<length; i++) {
238 : unsigned char device_r = *src++;
239 : unsigned char device_g = *src++;
240 : unsigned char device_b = *src++;
241 :
242 : float linear_r = transform->input_gamma_table_r[device_r];
243 : float linear_g = transform->input_gamma_table_g[device_g];
244 : float linear_b = transform->input_gamma_table_b[device_b];
245 :
246 : float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
247 : float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
248 : float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
249 :
250 : float out_device_r = pow(out_linear_r, transform->out_gamma_r);
251 : float out_device_g = pow(out_linear_g, transform->out_gamma_g);
252 : float out_device_b = pow(out_linear_b, transform->out_gamma_b);
253 :
254 : *dest++ = clamp_u8(255*out_device_r);
255 : *dest++ = clamp_u8(255*out_device_g);
256 : *dest++ = clamp_u8(255*out_device_b);
257 : }
258 : }
259 : #endif
260 :
261 0 : static void qcms_transform_data_gray_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
262 : {
263 : unsigned int i;
264 0 : for (i = 0; i < length; i++) {
265 : float out_device_r, out_device_g, out_device_b;
266 0 : unsigned char device = *src++;
267 :
268 0 : float linear = transform->input_gamma_table_gray[device];
269 :
270 0 : out_device_r = lut_interp_linear(linear, transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
271 0 : out_device_g = lut_interp_linear(linear, transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
272 0 : out_device_b = lut_interp_linear(linear, transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
273 :
274 0 : *dest++ = clamp_u8(out_device_r*255);
275 0 : *dest++ = clamp_u8(out_device_g*255);
276 0 : *dest++ = clamp_u8(out_device_b*255);
277 : }
278 0 : }
279 :
280 : /* Alpha is not corrected.
281 : A rationale for this is found in Alvy Ray's "Should Alpha Be Nonlinear If
282 : RGB Is?" Tech Memo 17 (December 14, 1998).
283 : See: ftp://ftp.alvyray.com/Acrobat/17_Nonln.pdf
284 : */
285 :
286 0 : static void qcms_transform_data_graya_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
287 : {
288 : unsigned int i;
289 0 : for (i = 0; i < length; i++) {
290 : float out_device_r, out_device_g, out_device_b;
291 0 : unsigned char device = *src++;
292 0 : unsigned char alpha = *src++;
293 :
294 0 : float linear = transform->input_gamma_table_gray[device];
295 :
296 0 : out_device_r = lut_interp_linear(linear, transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
297 0 : out_device_g = lut_interp_linear(linear, transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
298 0 : out_device_b = lut_interp_linear(linear, transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
299 :
300 0 : *dest++ = clamp_u8(out_device_r*255);
301 0 : *dest++ = clamp_u8(out_device_g*255);
302 0 : *dest++ = clamp_u8(out_device_b*255);
303 0 : *dest++ = alpha;
304 : }
305 0 : }
306 :
307 :
308 0 : static void qcms_transform_data_gray_out_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
309 : {
310 : unsigned int i;
311 0 : for (i = 0; i < length; i++) {
312 0 : unsigned char device = *src++;
313 : uint16_t gray;
314 :
315 0 : float linear = transform->input_gamma_table_gray[device];
316 :
317 : /* we could round here... */
318 0 : gray = linear * PRECACHE_OUTPUT_MAX;
319 :
320 0 : *dest++ = transform->output_table_r->data[gray];
321 0 : *dest++ = transform->output_table_g->data[gray];
322 0 : *dest++ = transform->output_table_b->data[gray];
323 : }
324 0 : }
325 :
326 0 : static void qcms_transform_data_graya_out_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
327 : {
328 : unsigned int i;
329 0 : for (i = 0; i < length; i++) {
330 0 : unsigned char device = *src++;
331 0 : unsigned char alpha = *src++;
332 : uint16_t gray;
333 :
334 0 : float linear = transform->input_gamma_table_gray[device];
335 :
336 : /* we could round here... */
337 0 : gray = linear * PRECACHE_OUTPUT_MAX;
338 :
339 0 : *dest++ = transform->output_table_r->data[gray];
340 0 : *dest++ = transform->output_table_g->data[gray];
341 0 : *dest++ = transform->output_table_b->data[gray];
342 0 : *dest++ = alpha;
343 : }
344 0 : }
345 :
346 0 : static void qcms_transform_data_rgb_out_lut_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
347 : {
348 : unsigned int i;
349 0 : float (*mat)[4] = transform->matrix;
350 0 : for (i = 0; i < length; i++) {
351 0 : unsigned char device_r = *src++;
352 0 : unsigned char device_g = *src++;
353 0 : unsigned char device_b = *src++;
354 : uint16_t r, g, b;
355 :
356 0 : float linear_r = transform->input_gamma_table_r[device_r];
357 0 : float linear_g = transform->input_gamma_table_g[device_g];
358 0 : float linear_b = transform->input_gamma_table_b[device_b];
359 :
360 0 : float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
361 0 : float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
362 0 : float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
363 :
364 0 : out_linear_r = clamp_float(out_linear_r);
365 0 : out_linear_g = clamp_float(out_linear_g);
366 0 : out_linear_b = clamp_float(out_linear_b);
367 :
368 : /* we could round here... */
369 0 : r = out_linear_r * PRECACHE_OUTPUT_MAX;
370 0 : g = out_linear_g * PRECACHE_OUTPUT_MAX;
371 0 : b = out_linear_b * PRECACHE_OUTPUT_MAX;
372 :
373 0 : *dest++ = transform->output_table_r->data[r];
374 0 : *dest++ = transform->output_table_g->data[g];
375 0 : *dest++ = transform->output_table_b->data[b];
376 : }
377 0 : }
378 :
379 0 : static void qcms_transform_data_rgba_out_lut_precache(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
380 : {
381 : unsigned int i;
382 0 : float (*mat)[4] = transform->matrix;
383 0 : for (i = 0; i < length; i++) {
384 0 : unsigned char device_r = *src++;
385 0 : unsigned char device_g = *src++;
386 0 : unsigned char device_b = *src++;
387 0 : unsigned char alpha = *src++;
388 : uint16_t r, g, b;
389 :
390 0 : float linear_r = transform->input_gamma_table_r[device_r];
391 0 : float linear_g = transform->input_gamma_table_g[device_g];
392 0 : float linear_b = transform->input_gamma_table_b[device_b];
393 :
394 0 : float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
395 0 : float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
396 0 : float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
397 :
398 0 : out_linear_r = clamp_float(out_linear_r);
399 0 : out_linear_g = clamp_float(out_linear_g);
400 0 : out_linear_b = clamp_float(out_linear_b);
401 :
402 : /* we could round here... */
403 0 : r = out_linear_r * PRECACHE_OUTPUT_MAX;
404 0 : g = out_linear_g * PRECACHE_OUTPUT_MAX;
405 0 : b = out_linear_b * PRECACHE_OUTPUT_MAX;
406 :
407 0 : *dest++ = transform->output_table_r->data[r];
408 0 : *dest++ = transform->output_table_g->data[g];
409 0 : *dest++ = transform->output_table_b->data[b];
410 0 : *dest++ = alpha;
411 : }
412 0 : }
413 :
414 : // Not used
415 : /*
416 : static void qcms_transform_data_clut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) {
417 : unsigned int i;
418 : int xy_len = 1;
419 : int x_len = transform->grid_size;
420 : int len = x_len * x_len;
421 : float* r_table = transform->r_clut;
422 : float* g_table = transform->g_clut;
423 : float* b_table = transform->b_clut;
424 :
425 : for (i = 0; i < length; i++) {
426 : unsigned char in_r = *src++;
427 : unsigned char in_g = *src++;
428 : unsigned char in_b = *src++;
429 : float linear_r = in_r/255.0f, linear_g=in_g/255.0f, linear_b = in_b/255.0f;
430 :
431 : int x = floor(linear_r * (transform->grid_size-1));
432 : int y = floor(linear_g * (transform->grid_size-1));
433 : int z = floor(linear_b * (transform->grid_size-1));
434 : int x_n = ceil(linear_r * (transform->grid_size-1));
435 : int y_n = ceil(linear_g * (transform->grid_size-1));
436 : int z_n = ceil(linear_b * (transform->grid_size-1));
437 : float x_d = linear_r * (transform->grid_size-1) - x;
438 : float y_d = linear_g * (transform->grid_size-1) - y;
439 : float z_d = linear_b * (transform->grid_size-1) - z;
440 :
441 : float r_x1 = lerp(CLU(r_table,x,y,z), CLU(r_table,x_n,y,z), x_d);
442 : float r_x2 = lerp(CLU(r_table,x,y_n,z), CLU(r_table,x_n,y_n,z), x_d);
443 : float r_y1 = lerp(r_x1, r_x2, y_d);
444 : float r_x3 = lerp(CLU(r_table,x,y,z_n), CLU(r_table,x_n,y,z_n), x_d);
445 : float r_x4 = lerp(CLU(r_table,x,y_n,z_n), CLU(r_table,x_n,y_n,z_n), x_d);
446 : float r_y2 = lerp(r_x3, r_x4, y_d);
447 : float clut_r = lerp(r_y1, r_y2, z_d);
448 :
449 : float g_x1 = lerp(CLU(g_table,x,y,z), CLU(g_table,x_n,y,z), x_d);
450 : float g_x2 = lerp(CLU(g_table,x,y_n,z), CLU(g_table,x_n,y_n,z), x_d);
451 : float g_y1 = lerp(g_x1, g_x2, y_d);
452 : float g_x3 = lerp(CLU(g_table,x,y,z_n), CLU(g_table,x_n,y,z_n), x_d);
453 : float g_x4 = lerp(CLU(g_table,x,y_n,z_n), CLU(g_table,x_n,y_n,z_n), x_d);
454 : float g_y2 = lerp(g_x3, g_x4, y_d);
455 : float clut_g = lerp(g_y1, g_y2, z_d);
456 :
457 : float b_x1 = lerp(CLU(b_table,x,y,z), CLU(b_table,x_n,y,z), x_d);
458 : float b_x2 = lerp(CLU(b_table,x,y_n,z), CLU(b_table,x_n,y_n,z), x_d);
459 : float b_y1 = lerp(b_x1, b_x2, y_d);
460 : float b_x3 = lerp(CLU(b_table,x,y,z_n), CLU(b_table,x_n,y,z_n), x_d);
461 : float b_x4 = lerp(CLU(b_table,x,y_n,z_n), CLU(b_table,x_n,y_n,z_n), x_d);
462 : float b_y2 = lerp(b_x3, b_x4, y_d);
463 : float clut_b = lerp(b_y1, b_y2, z_d);
464 :
465 : *dest++ = clamp_u8(clut_r*255.0f);
466 : *dest++ = clamp_u8(clut_g*255.0f);
467 : *dest++ = clamp_u8(clut_b*255.0f);
468 : }
469 : }
470 : */
471 :
472 : // Using lcms' tetra interpolation algorithm.
473 0 : static void qcms_transform_data_tetra_clut_rgba(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) {
474 : unsigned int i;
475 0 : int xy_len = 1;
476 0 : int x_len = transform->grid_size;
477 0 : int len = x_len * x_len;
478 0 : float* r_table = transform->r_clut;
479 0 : float* g_table = transform->g_clut;
480 0 : float* b_table = transform->b_clut;
481 : float c0_r, c1_r, c2_r, c3_r;
482 : float c0_g, c1_g, c2_g, c3_g;
483 : float c0_b, c1_b, c2_b, c3_b;
484 : float clut_r, clut_g, clut_b;
485 0 : for (i = 0; i < length; i++) {
486 0 : unsigned char in_r = *src++;
487 0 : unsigned char in_g = *src++;
488 0 : unsigned char in_b = *src++;
489 0 : unsigned char in_a = *src++;
490 0 : float linear_r = in_r/255.0f, linear_g=in_g/255.0f, linear_b = in_b/255.0f;
491 :
492 0 : int x = floor(linear_r * (transform->grid_size-1));
493 0 : int y = floor(linear_g * (transform->grid_size-1));
494 0 : int z = floor(linear_b * (transform->grid_size-1));
495 0 : int x_n = ceil(linear_r * (transform->grid_size-1));
496 0 : int y_n = ceil(linear_g * (transform->grid_size-1));
497 0 : int z_n = ceil(linear_b * (transform->grid_size-1));
498 0 : float rx = linear_r * (transform->grid_size-1) - x;
499 0 : float ry = linear_g * (transform->grid_size-1) - y;
500 0 : float rz = linear_b * (transform->grid_size-1) - z;
501 :
502 0 : c0_r = CLU(r_table, x, y, z);
503 0 : c0_g = CLU(g_table, x, y, z);
504 0 : c0_b = CLU(b_table, x, y, z);
505 :
506 0 : if( rx >= ry ) {
507 0 : if (ry >= rz) { //rx >= ry && ry >= rz
508 0 : c1_r = CLU(r_table, x_n, y, z) - c0_r;
509 0 : c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x_n, y, z);
510 0 : c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
511 0 : c1_g = CLU(g_table, x_n, y, z) - c0_g;
512 0 : c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x_n, y, z);
513 0 : c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
514 0 : c1_b = CLU(b_table, x_n, y, z) - c0_b;
515 0 : c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x_n, y, z);
516 0 : c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
517 : } else {
518 0 : if (rx >= rz) { //rx >= rz && rz >= ry
519 0 : c1_r = CLU(r_table, x_n, y, z) - c0_r;
520 0 : c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
521 0 : c3_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x_n, y, z);
522 0 : c1_g = CLU(g_table, x_n, y, z) - c0_g;
523 0 : c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
524 0 : c3_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x_n, y, z);
525 0 : c1_b = CLU(b_table, x_n, y, z) - c0_b;
526 0 : c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
527 0 : c3_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x_n, y, z);
528 : } else { //rz > rx && rx >= ry
529 0 : c1_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x, y, z_n);
530 0 : c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
531 0 : c3_r = CLU(r_table, x, y, z_n) - c0_r;
532 0 : c1_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x, y, z_n);
533 0 : c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
534 0 : c3_g = CLU(g_table, x, y, z_n) - c0_g;
535 0 : c1_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x, y, z_n);
536 0 : c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
537 0 : c3_b = CLU(b_table, x, y, z_n) - c0_b;
538 : }
539 : }
540 : } else {
541 0 : if (rx >= rz) { //ry > rx && rx >= rz
542 0 : c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x, y_n, z);
543 0 : c2_r = CLU(r_table, x, y_n, z) - c0_r;
544 0 : c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
545 0 : c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x, y_n, z);
546 0 : c2_g = CLU(g_table, x, y_n, z) - c0_g;
547 0 : c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
548 0 : c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x, y_n, z);
549 0 : c2_b = CLU(b_table, x, y_n, z) - c0_b;
550 0 : c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
551 : } else {
552 0 : if (ry >= rz) { //ry >= rz && rz > rx
553 0 : c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
554 0 : c2_r = CLU(r_table, x, y_n, z) - c0_r;
555 0 : c3_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y_n, z);
556 0 : c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
557 0 : c2_g = CLU(g_table, x, y_n, z) - c0_g;
558 0 : c3_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y_n, z);
559 0 : c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
560 0 : c2_b = CLU(b_table, x, y_n, z) - c0_b;
561 0 : c3_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y_n, z);
562 : } else { //rz > ry && ry > rx
563 0 : c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
564 0 : c2_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y, z_n);
565 0 : c3_r = CLU(r_table, x, y, z_n) - c0_r;
566 0 : c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
567 0 : c2_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y, z_n);
568 0 : c3_g = CLU(g_table, x, y, z_n) - c0_g;
569 0 : c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
570 0 : c2_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y, z_n);
571 0 : c3_b = CLU(b_table, x, y, z_n) - c0_b;
572 : }
573 : }
574 : }
575 :
576 0 : clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz;
577 0 : clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz;
578 0 : clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz;
579 :
580 0 : *dest++ = clamp_u8(clut_r*255.0f);
581 0 : *dest++ = clamp_u8(clut_g*255.0f);
582 0 : *dest++ = clamp_u8(clut_b*255.0f);
583 0 : *dest++ = in_a;
584 : }
585 0 : }
586 :
587 : // Using lcms' tetra interpolation code.
588 0 : static void qcms_transform_data_tetra_clut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length) {
589 : unsigned int i;
590 0 : int xy_len = 1;
591 0 : int x_len = transform->grid_size;
592 0 : int len = x_len * x_len;
593 0 : float* r_table = transform->r_clut;
594 0 : float* g_table = transform->g_clut;
595 0 : float* b_table = transform->b_clut;
596 : float c0_r, c1_r, c2_r, c3_r;
597 : float c0_g, c1_g, c2_g, c3_g;
598 : float c0_b, c1_b, c2_b, c3_b;
599 : float clut_r, clut_g, clut_b;
600 0 : for (i = 0; i < length; i++) {
601 0 : unsigned char in_r = *src++;
602 0 : unsigned char in_g = *src++;
603 0 : unsigned char in_b = *src++;
604 0 : float linear_r = in_r/255.0f, linear_g=in_g/255.0f, linear_b = in_b/255.0f;
605 :
606 0 : int x = floor(linear_r * (transform->grid_size-1));
607 0 : int y = floor(linear_g * (transform->grid_size-1));
608 0 : int z = floor(linear_b * (transform->grid_size-1));
609 0 : int x_n = ceil(linear_r * (transform->grid_size-1));
610 0 : int y_n = ceil(linear_g * (transform->grid_size-1));
611 0 : int z_n = ceil(linear_b * (transform->grid_size-1));
612 0 : float rx = linear_r * (transform->grid_size-1) - x;
613 0 : float ry = linear_g * (transform->grid_size-1) - y;
614 0 : float rz = linear_b * (transform->grid_size-1) - z;
615 :
616 0 : c0_r = CLU(r_table, x, y, z);
617 0 : c0_g = CLU(g_table, x, y, z);
618 0 : c0_b = CLU(b_table, x, y, z);
619 :
620 0 : if( rx >= ry ) {
621 0 : if (ry >= rz) { //rx >= ry && ry >= rz
622 0 : c1_r = CLU(r_table, x_n, y, z) - c0_r;
623 0 : c2_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x_n, y, z);
624 0 : c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
625 0 : c1_g = CLU(g_table, x_n, y, z) - c0_g;
626 0 : c2_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x_n, y, z);
627 0 : c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
628 0 : c1_b = CLU(b_table, x_n, y, z) - c0_b;
629 0 : c2_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x_n, y, z);
630 0 : c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
631 : } else {
632 0 : if (rx >= rz) { //rx >= rz && rz >= ry
633 0 : c1_r = CLU(r_table, x_n, y, z) - c0_r;
634 0 : c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
635 0 : c3_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x_n, y, z);
636 0 : c1_g = CLU(g_table, x_n, y, z) - c0_g;
637 0 : c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
638 0 : c3_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x_n, y, z);
639 0 : c1_b = CLU(b_table, x_n, y, z) - c0_b;
640 0 : c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
641 0 : c3_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x_n, y, z);
642 : } else { //rz > rx && rx >= ry
643 0 : c1_r = CLU(r_table, x_n, y, z_n) - CLU(r_table, x, y, z_n);
644 0 : c2_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y, z_n);
645 0 : c3_r = CLU(r_table, x, y, z_n) - c0_r;
646 0 : c1_g = CLU(g_table, x_n, y, z_n) - CLU(g_table, x, y, z_n);
647 0 : c2_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y, z_n);
648 0 : c3_g = CLU(g_table, x, y, z_n) - c0_g;
649 0 : c1_b = CLU(b_table, x_n, y, z_n) - CLU(b_table, x, y, z_n);
650 0 : c2_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y, z_n);
651 0 : c3_b = CLU(b_table, x, y, z_n) - c0_b;
652 : }
653 : }
654 : } else {
655 0 : if (rx >= rz) { //ry > rx && rx >= rz
656 0 : c1_r = CLU(r_table, x_n, y_n, z) - CLU(r_table, x, y_n, z);
657 0 : c2_r = CLU(r_table, x, y_n, z) - c0_r;
658 0 : c3_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x_n, y_n, z);
659 0 : c1_g = CLU(g_table, x_n, y_n, z) - CLU(g_table, x, y_n, z);
660 0 : c2_g = CLU(g_table, x, y_n, z) - c0_g;
661 0 : c3_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x_n, y_n, z);
662 0 : c1_b = CLU(b_table, x_n, y_n, z) - CLU(b_table, x, y_n, z);
663 0 : c2_b = CLU(b_table, x, y_n, z) - c0_b;
664 0 : c3_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x_n, y_n, z);
665 : } else {
666 0 : if (ry >= rz) { //ry >= rz && rz > rx
667 0 : c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
668 0 : c2_r = CLU(r_table, x, y_n, z) - c0_r;
669 0 : c3_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y_n, z);
670 0 : c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
671 0 : c2_g = CLU(g_table, x, y_n, z) - c0_g;
672 0 : c3_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y_n, z);
673 0 : c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
674 0 : c2_b = CLU(b_table, x, y_n, z) - c0_b;
675 0 : c3_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y_n, z);
676 : } else { //rz > ry && ry > rx
677 0 : c1_r = CLU(r_table, x_n, y_n, z_n) - CLU(r_table, x, y_n, z_n);
678 0 : c2_r = CLU(r_table, x, y_n, z_n) - CLU(r_table, x, y, z_n);
679 0 : c3_r = CLU(r_table, x, y, z_n) - c0_r;
680 0 : c1_g = CLU(g_table, x_n, y_n, z_n) - CLU(g_table, x, y_n, z_n);
681 0 : c2_g = CLU(g_table, x, y_n, z_n) - CLU(g_table, x, y, z_n);
682 0 : c3_g = CLU(g_table, x, y, z_n) - c0_g;
683 0 : c1_b = CLU(b_table, x_n, y_n, z_n) - CLU(b_table, x, y_n, z_n);
684 0 : c2_b = CLU(b_table, x, y_n, z_n) - CLU(b_table, x, y, z_n);
685 0 : c3_b = CLU(b_table, x, y, z_n) - c0_b;
686 : }
687 : }
688 : }
689 :
690 0 : clut_r = c0_r + c1_r*rx + c2_r*ry + c3_r*rz;
691 0 : clut_g = c0_g + c1_g*rx + c2_g*ry + c3_g*rz;
692 0 : clut_b = c0_b + c1_b*rx + c2_b*ry + c3_b*rz;
693 :
694 0 : *dest++ = clamp_u8(clut_r*255.0f);
695 0 : *dest++ = clamp_u8(clut_g*255.0f);
696 0 : *dest++ = clamp_u8(clut_b*255.0f);
697 : }
698 0 : }
699 :
700 0 : static void qcms_transform_data_rgb_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
701 : {
702 : unsigned int i;
703 0 : float (*mat)[4] = transform->matrix;
704 0 : for (i = 0; i < length; i++) {
705 0 : unsigned char device_r = *src++;
706 0 : unsigned char device_g = *src++;
707 0 : unsigned char device_b = *src++;
708 : float out_device_r, out_device_g, out_device_b;
709 :
710 0 : float linear_r = transform->input_gamma_table_r[device_r];
711 0 : float linear_g = transform->input_gamma_table_g[device_g];
712 0 : float linear_b = transform->input_gamma_table_b[device_b];
713 :
714 0 : float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
715 0 : float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
716 0 : float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
717 :
718 0 : out_linear_r = clamp_float(out_linear_r);
719 0 : out_linear_g = clamp_float(out_linear_g);
720 0 : out_linear_b = clamp_float(out_linear_b);
721 :
722 0 : out_device_r = lut_interp_linear(out_linear_r,
723 0 : transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
724 0 : out_device_g = lut_interp_linear(out_linear_g,
725 0 : transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
726 0 : out_device_b = lut_interp_linear(out_linear_b,
727 0 : transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
728 :
729 0 : *dest++ = clamp_u8(out_device_r*255);
730 0 : *dest++ = clamp_u8(out_device_g*255);
731 0 : *dest++ = clamp_u8(out_device_b*255);
732 : }
733 0 : }
734 :
735 0 : static void qcms_transform_data_rgba_out_lut(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
736 : {
737 : unsigned int i;
738 0 : float (*mat)[4] = transform->matrix;
739 0 : for (i = 0; i < length; i++) {
740 0 : unsigned char device_r = *src++;
741 0 : unsigned char device_g = *src++;
742 0 : unsigned char device_b = *src++;
743 0 : unsigned char alpha = *src++;
744 : float out_device_r, out_device_g, out_device_b;
745 :
746 0 : float linear_r = transform->input_gamma_table_r[device_r];
747 0 : float linear_g = transform->input_gamma_table_g[device_g];
748 0 : float linear_b = transform->input_gamma_table_b[device_b];
749 :
750 0 : float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
751 0 : float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
752 0 : float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
753 :
754 0 : out_linear_r = clamp_float(out_linear_r);
755 0 : out_linear_g = clamp_float(out_linear_g);
756 0 : out_linear_b = clamp_float(out_linear_b);
757 :
758 0 : out_device_r = lut_interp_linear(out_linear_r,
759 0 : transform->output_gamma_lut_r, transform->output_gamma_lut_r_length);
760 0 : out_device_g = lut_interp_linear(out_linear_g,
761 0 : transform->output_gamma_lut_g, transform->output_gamma_lut_g_length);
762 0 : out_device_b = lut_interp_linear(out_linear_b,
763 0 : transform->output_gamma_lut_b, transform->output_gamma_lut_b_length);
764 :
765 0 : *dest++ = clamp_u8(out_device_r*255);
766 0 : *dest++ = clamp_u8(out_device_g*255);
767 0 : *dest++ = clamp_u8(out_device_b*255);
768 0 : *dest++ = alpha;
769 : }
770 0 : }
771 :
772 : #if 0
773 : static void qcms_transform_data_rgb_out_linear(qcms_transform *transform, unsigned char *src, unsigned char *dest, size_t length)
774 : {
775 : int i;
776 : float (*mat)[4] = transform->matrix;
777 : for (i = 0; i < length; i++) {
778 : unsigned char device_r = *src++;
779 : unsigned char device_g = *src++;
780 : unsigned char device_b = *src++;
781 :
782 : float linear_r = transform->input_gamma_table_r[device_r];
783 : float linear_g = transform->input_gamma_table_g[device_g];
784 : float linear_b = transform->input_gamma_table_b[device_b];
785 :
786 : float out_linear_r = mat[0][0]*linear_r + mat[1][0]*linear_g + mat[2][0]*linear_b;
787 : float out_linear_g = mat[0][1]*linear_r + mat[1][1]*linear_g + mat[2][1]*linear_b;
788 : float out_linear_b = mat[0][2]*linear_r + mat[1][2]*linear_g + mat[2][2]*linear_b;
789 :
790 : *dest++ = clamp_u8(out_linear_r*255);
791 : *dest++ = clamp_u8(out_linear_g*255);
792 : *dest++ = clamp_u8(out_linear_b*255);
793 : }
794 : }
795 : #endif
796 :
797 0 : static struct precache_output *precache_reference(struct precache_output *p)
798 : {
799 0 : p->ref_count++;
800 0 : return p;
801 : }
802 :
803 0 : static struct precache_output *precache_create()
804 : {
805 0 : struct precache_output *p = malloc(sizeof(struct precache_output));
806 0 : if (p)
807 0 : p->ref_count = 1;
808 0 : return p;
809 : }
810 :
811 0 : void precache_release(struct precache_output *p)
812 : {
813 0 : if (--p->ref_count == 0) {
814 0 : free(p);
815 : }
816 0 : }
817 :
818 : #ifdef HAS_POSIX_MEMALIGN
819 : static qcms_transform *transform_alloc(void)
820 : {
821 : qcms_transform *t;
822 : if (!posix_memalign(&t, 16, sizeof(*t))) {
823 : return t;
824 : } else {
825 : return NULL;
826 : }
827 : }
828 : static void transform_free(qcms_transform *t)
829 : {
830 : free(t);
831 : }
832 : #else
833 0 : static qcms_transform *transform_alloc(void)
834 : {
835 : /* transform needs to be aligned on a 16byte boundrary */
836 0 : char *original_block = calloc(sizeof(qcms_transform) + sizeof(void*) + 16, 1);
837 : /* make room for a pointer to the block returned by calloc */
838 0 : void *transform_start = original_block + sizeof(void*);
839 : /* align transform_start */
840 0 : qcms_transform *transform_aligned = (qcms_transform*)(((uintptr_t)transform_start + 15) & ~0xf);
841 :
842 : /* store a pointer to the block returned by calloc so that we can free it later */
843 0 : void **(original_block_ptr) = (void**)transform_aligned;
844 0 : if (!original_block)
845 0 : return NULL;
846 0 : original_block_ptr--;
847 0 : *original_block_ptr = original_block;
848 :
849 0 : return transform_aligned;
850 : }
851 0 : static void transform_free(qcms_transform *t)
852 : {
853 : /* get at the pointer to the unaligned block returned by calloc */
854 0 : void **p = (void**)t;
855 0 : p--;
856 0 : free(*p);
857 0 : }
858 : #endif
859 :
860 0 : void qcms_transform_release(qcms_transform *t)
861 : {
862 : /* ensure we only free the gamma tables once even if there are
863 : * multiple references to the same data */
864 :
865 0 : if (t->output_table_r)
866 0 : precache_release(t->output_table_r);
867 0 : if (t->output_table_g)
868 0 : precache_release(t->output_table_g);
869 0 : if (t->output_table_b)
870 0 : precache_release(t->output_table_b);
871 :
872 0 : free(t->input_gamma_table_r);
873 0 : if (t->input_gamma_table_g != t->input_gamma_table_r)
874 0 : free(t->input_gamma_table_g);
875 0 : if (t->input_gamma_table_g != t->input_gamma_table_r &&
876 0 : t->input_gamma_table_g != t->input_gamma_table_b)
877 0 : free(t->input_gamma_table_b);
878 :
879 0 : free(t->input_gamma_table_gray);
880 :
881 0 : free(t->output_gamma_lut_r);
882 0 : free(t->output_gamma_lut_g);
883 0 : free(t->output_gamma_lut_b);
884 :
885 0 : transform_free(t);
886 0 : }
887 :
888 : #ifdef X86
889 : // Determine if we can build with SSE2 (this was partly copied from jmorecfg.h in
890 : // mozilla/jpeg)
891 : // -------------------------------------------------------------------------
892 : #if defined(_M_IX86) && defined(_MSC_VER)
893 : #define HAS_CPUID
894 : /* Get us a CPUID function. Avoid clobbering EBX because sometimes it's the PIC
895 : register - I'm not sure if that ever happens on windows, but cpuid isn't
896 : on the critical path so we just preserve the register to be safe and to be
897 : consistent with the non-windows version. */
898 : static void cpuid(uint32_t fxn, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) {
899 : uint32_t a_, b_, c_, d_;
900 : __asm {
901 : xchg ebx, esi
902 : mov eax, fxn
903 : cpuid
904 : mov a_, eax
905 : mov b_, ebx
906 : mov c_, ecx
907 : mov d_, edx
908 : xchg ebx, esi
909 : }
910 : *a = a_;
911 : *b = b_;
912 : *c = c_;
913 : *d = d_;
914 : }
915 : #elif (defined(__GNUC__) || defined(__SUNPRO_C)) && (defined(__i386__) || defined(__i386))
916 : #define HAS_CPUID
917 : /* Get us a CPUID function. We can't use ebx because it's the PIC register on
918 : some platforms, so we use ESI instead and save ebx to avoid clobbering it. */
919 0 : static void cpuid(uint32_t fxn, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d) {
920 :
921 : uint32_t a_, b_, c_, d_;
922 0 : __asm__ __volatile__ ("xchgl %%ebx, %%esi; cpuid; xchgl %%ebx, %%esi;"
923 : : "=a" (a_), "=S" (b_), "=c" (c_), "=d" (d_) : "a" (fxn));
924 0 : *a = a_;
925 0 : *b = b_;
926 0 : *c = c_;
927 0 : *d = d_;
928 0 : }
929 : #endif
930 :
931 : // -------------------------Runtime SSEx Detection-----------------------------
932 :
933 : /* MMX is always supported per
934 : * Gecko v1.9.1 minimum CPU requirements */
935 : #define SSE1_EDX_MASK (1UL << 25)
936 : #define SSE2_EDX_MASK (1UL << 26)
937 : #define SSE3_ECX_MASK (1UL << 0)
938 :
939 0 : static int sse_version_available(void)
940 : {
941 : #if defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64)
942 : /* we know at build time that 64-bit CPUs always have SSE2
943 : * this tells the compiler that non-SSE2 branches will never be
944 : * taken (i.e. OK to optimze away the SSE1 and non-SIMD code */
945 : return 2;
946 : #elif defined(HAS_CPUID)
947 : static int sse_version = -1;
948 : uint32_t a, b, c, d;
949 0 : uint32_t function = 0x00000001;
950 :
951 0 : if (sse_version == -1) {
952 0 : sse_version = 0;
953 0 : cpuid(function, &a, &b, &c, &d);
954 0 : if (c & SSE3_ECX_MASK)
955 0 : sse_version = 3;
956 0 : else if (d & SSE2_EDX_MASK)
957 0 : sse_version = 2;
958 0 : else if (d & SSE1_EDX_MASK)
959 0 : sse_version = 1;
960 : }
961 :
962 0 : return sse_version;
963 : #else
964 : return 0;
965 : #endif
966 : }
967 : #endif
968 :
969 : static const struct matrix bradford_matrix = {{ { 0.8951f, 0.2664f,-0.1614f},
970 : {-0.7502f, 1.7135f, 0.0367f},
971 : { 0.0389f,-0.0685f, 1.0296f}},
972 : false};
973 :
974 : static const struct matrix bradford_matrix_inv = {{ { 0.9869929f,-0.1470543f, 0.1599627f},
975 : { 0.4323053f, 0.5183603f, 0.0492912f},
976 : {-0.0085287f, 0.0400428f, 0.9684867f}},
977 : false};
978 :
979 : // See ICCv4 E.3
980 0 : struct matrix compute_whitepoint_adaption(float X, float Y, float Z) {
981 0 : float p = (0.96422f*bradford_matrix.m[0][0] + 1.000f*bradford_matrix.m[1][0] + 0.82521f*bradford_matrix.m[2][0]) /
982 0 : (X*bradford_matrix.m[0][0] + Y*bradford_matrix.m[1][0] + Z*bradford_matrix.m[2][0] );
983 0 : float y = (0.96422f*bradford_matrix.m[0][1] + 1.000f*bradford_matrix.m[1][1] + 0.82521f*bradford_matrix.m[2][1]) /
984 0 : (X*bradford_matrix.m[0][1] + Y*bradford_matrix.m[1][1] + Z*bradford_matrix.m[2][1] );
985 0 : float b = (0.96422f*bradford_matrix.m[0][2] + 1.000f*bradford_matrix.m[1][2] + 0.82521f*bradford_matrix.m[2][2]) /
986 0 : (X*bradford_matrix.m[0][2] + Y*bradford_matrix.m[1][2] + Z*bradford_matrix.m[2][2] );
987 0 : struct matrix white_adaption = {{ {p,0,0}, {0,y,0}, {0,0,b}}, false};
988 0 : return matrix_multiply( bradford_matrix_inv, matrix_multiply(white_adaption, bradford_matrix) );
989 : }
990 :
991 0 : void qcms_profile_precache_output_transform(qcms_profile *profile)
992 : {
993 : /* we only support precaching on rgb profiles */
994 0 : if (profile->color_space != RGB_SIGNATURE)
995 0 : return;
996 :
997 : /* don't precache since we will use the B2A LUT */
998 0 : if (profile->B2A0)
999 0 : return;
1000 :
1001 : /* don't precache since we will use the mBA LUT */
1002 0 : if (profile->mBA)
1003 0 : return;
1004 :
1005 : /* don't precache if we do not have the TRC curves */
1006 0 : if (!profile->redTRC || !profile->greenTRC || !profile->blueTRC)
1007 0 : return;
1008 :
1009 0 : if (!profile->output_table_r) {
1010 0 : profile->output_table_r = precache_create();
1011 0 : if (profile->output_table_r &&
1012 0 : !compute_precache(profile->redTRC, profile->output_table_r->data)) {
1013 0 : precache_release(profile->output_table_r);
1014 0 : profile->output_table_r = NULL;
1015 : }
1016 : }
1017 0 : if (!profile->output_table_g) {
1018 0 : profile->output_table_g = precache_create();
1019 0 : if (profile->output_table_g &&
1020 0 : !compute_precache(profile->greenTRC, profile->output_table_g->data)) {
1021 0 : precache_release(profile->output_table_g);
1022 0 : profile->output_table_g = NULL;
1023 : }
1024 : }
1025 0 : if (!profile->output_table_b) {
1026 0 : profile->output_table_b = precache_create();
1027 0 : if (profile->output_table_b &&
1028 0 : !compute_precache(profile->blueTRC, profile->output_table_b->data)) {
1029 0 : precache_release(profile->output_table_b);
1030 0 : profile->output_table_b = NULL;
1031 : }
1032 : }
1033 : }
1034 :
1035 : /* Replace the current transformation with a LUT transformation using a given number of sample points */
1036 0 : qcms_transform* qcms_transform_precacheLUT_float(qcms_transform *transform, qcms_profile *in, qcms_profile *out,
1037 : int samples, qcms_data_type in_type)
1038 : {
1039 : /* The range between which 2 consecutive sample points can be used to interpolate */
1040 : uint16_t x,y,z;
1041 : uint32_t l;
1042 0 : uint32_t lutSize = 3 * samples * samples * samples;
1043 0 : float* src = NULL;
1044 0 : float* dest = NULL;
1045 0 : float* lut = NULL;
1046 :
1047 0 : src = malloc(lutSize*sizeof(float));
1048 0 : dest = malloc(lutSize*sizeof(float));
1049 :
1050 0 : if (src && dest) {
1051 : /* Prepare a list of points we want to sample */
1052 0 : l = 0;
1053 0 : for (x = 0; x < samples; x++) {
1054 0 : for (y = 0; y < samples; y++) {
1055 0 : for (z = 0; z < samples; z++) {
1056 0 : src[l++] = x / (float)(samples-1);
1057 0 : src[l++] = y / (float)(samples-1);
1058 0 : src[l++] = z / (float)(samples-1);
1059 : }
1060 : }
1061 : }
1062 :
1063 0 : lut = qcms_chain_transform(in, out, src, dest, lutSize);
1064 0 : if (lut) {
1065 0 : transform->r_clut = &lut[0];
1066 0 : transform->g_clut = &lut[1];
1067 0 : transform->b_clut = &lut[2];
1068 0 : transform->grid_size = samples;
1069 0 : if (in_type == QCMS_DATA_RGBA_8) {
1070 0 : transform->transform_fn = qcms_transform_data_tetra_clut_rgba;
1071 : } else {
1072 0 : transform->transform_fn = qcms_transform_data_tetra_clut;
1073 : }
1074 : }
1075 : }
1076 :
1077 :
1078 : //XXX: qcms_modular_transform_data may return either the src or dest buffer. If so it must not be free-ed
1079 0 : if (src && lut != src) {
1080 0 : free(src);
1081 : }
1082 0 : if (dest && lut != dest) {
1083 0 : free(dest);
1084 : }
1085 :
1086 0 : if (lut == NULL) {
1087 0 : return NULL;
1088 : }
1089 0 : return transform;
1090 : }
1091 :
1092 : #define NO_MEM_TRANSFORM NULL
1093 :
1094 0 : qcms_transform* qcms_transform_create(
1095 : qcms_profile *in, qcms_data_type in_type,
1096 : qcms_profile *out, qcms_data_type out_type,
1097 : qcms_intent intent)
1098 : {
1099 0 : bool precache = false;
1100 :
1101 0 : qcms_transform *transform = transform_alloc();
1102 0 : if (!transform) {
1103 0 : return NULL;
1104 : }
1105 0 : if (out_type != QCMS_DATA_RGB_8 &&
1106 : out_type != QCMS_DATA_RGBA_8) {
1107 0 : assert(0 && "output type");
1108 : transform_free(transform);
1109 : return NULL;
1110 : }
1111 :
1112 0 : if (out->output_table_r &&
1113 0 : out->output_table_g &&
1114 0 : out->output_table_b) {
1115 0 : precache = true;
1116 : }
1117 :
1118 0 : if (qcms_supports_iccv4 && (in->A2B0 || out->B2A0 || in->mAB || out->mAB)) {
1119 : // Precache the transformation to a CLUT 33x33x33 in size.
1120 : // 33 is used by many profiles and works well in pratice.
1121 : // This evenly divides 256 into blocks of 8x8x8.
1122 : // TODO For transforming small data sets of about 200x200 or less
1123 : // precaching should be avoided.
1124 0 : qcms_transform *result = qcms_transform_precacheLUT_float(transform, in, out, 33, in_type);
1125 0 : if (!result) {
1126 0 : assert(0 && "precacheLUT failed");
1127 : transform_free(transform);
1128 : return NULL;
1129 : }
1130 0 : return result;
1131 : }
1132 :
1133 0 : if (precache) {
1134 0 : transform->output_table_r = precache_reference(out->output_table_r);
1135 0 : transform->output_table_g = precache_reference(out->output_table_g);
1136 0 : transform->output_table_b = precache_reference(out->output_table_b);
1137 : } else {
1138 0 : if (!out->redTRC || !out->greenTRC || !out->blueTRC) {
1139 0 : qcms_transform_release(transform);
1140 0 : return NO_MEM_TRANSFORM;
1141 : }
1142 0 : build_output_lut(out->redTRC, &transform->output_gamma_lut_r, &transform->output_gamma_lut_r_length);
1143 0 : build_output_lut(out->greenTRC, &transform->output_gamma_lut_g, &transform->output_gamma_lut_g_length);
1144 0 : build_output_lut(out->blueTRC, &transform->output_gamma_lut_b, &transform->output_gamma_lut_b_length);
1145 0 : if (!transform->output_gamma_lut_r || !transform->output_gamma_lut_g || !transform->output_gamma_lut_b) {
1146 0 : qcms_transform_release(transform);
1147 0 : return NO_MEM_TRANSFORM;
1148 : }
1149 : }
1150 :
1151 0 : if (in->color_space == RGB_SIGNATURE) {
1152 : struct matrix in_matrix, out_matrix, result;
1153 :
1154 0 : if (in_type != QCMS_DATA_RGB_8 &&
1155 : in_type != QCMS_DATA_RGBA_8){
1156 0 : assert(0 && "input type");
1157 : transform_free(transform);
1158 : return NULL;
1159 : }
1160 0 : if (precache) {
1161 : #ifdef X86
1162 0 : if (sse_version_available() >= 2) {
1163 0 : if (in_type == QCMS_DATA_RGB_8)
1164 0 : transform->transform_fn = qcms_transform_data_rgb_out_lut_sse2;
1165 : else
1166 0 : transform->transform_fn = qcms_transform_data_rgba_out_lut_sse2;
1167 :
1168 : #if !(defined(_MSC_VER) && defined(_M_AMD64))
1169 : /* Microsoft Compiler for x64 doesn't support MMX.
1170 : * SSE code uses MMX so that we disable on x64 */
1171 : } else
1172 0 : if (sse_version_available() >= 1) {
1173 0 : if (in_type == QCMS_DATA_RGB_8)
1174 0 : transform->transform_fn = qcms_transform_data_rgb_out_lut_sse1;
1175 : else
1176 0 : transform->transform_fn = qcms_transform_data_rgba_out_lut_sse1;
1177 : #endif
1178 : } else
1179 : #endif
1180 : {
1181 0 : if (in_type == QCMS_DATA_RGB_8)
1182 0 : transform->transform_fn = qcms_transform_data_rgb_out_lut_precache;
1183 : else
1184 0 : transform->transform_fn = qcms_transform_data_rgba_out_lut_precache;
1185 : }
1186 : } else {
1187 0 : if (in_type == QCMS_DATA_RGB_8)
1188 0 : transform->transform_fn = qcms_transform_data_rgb_out_lut;
1189 : else
1190 0 : transform->transform_fn = qcms_transform_data_rgba_out_lut;
1191 : }
1192 :
1193 : //XXX: avoid duplicating tables if we can
1194 0 : transform->input_gamma_table_r = build_input_gamma_table(in->redTRC);
1195 0 : transform->input_gamma_table_g = build_input_gamma_table(in->greenTRC);
1196 0 : transform->input_gamma_table_b = build_input_gamma_table(in->blueTRC);
1197 0 : if (!transform->input_gamma_table_r || !transform->input_gamma_table_g || !transform->input_gamma_table_b) {
1198 0 : qcms_transform_release(transform);
1199 0 : return NO_MEM_TRANSFORM;
1200 : }
1201 :
1202 :
1203 : /* build combined colorant matrix */
1204 0 : in_matrix = build_colorant_matrix(in);
1205 0 : out_matrix = build_colorant_matrix(out);
1206 0 : out_matrix = matrix_invert(out_matrix);
1207 0 : if (out_matrix.invalid) {
1208 0 : qcms_transform_release(transform);
1209 0 : return NULL;
1210 : }
1211 0 : result = matrix_multiply(out_matrix, in_matrix);
1212 :
1213 : /* store the results in column major mode
1214 : * this makes doing the multiplication with sse easier */
1215 0 : transform->matrix[0][0] = result.m[0][0];
1216 0 : transform->matrix[1][0] = result.m[0][1];
1217 0 : transform->matrix[2][0] = result.m[0][2];
1218 0 : transform->matrix[0][1] = result.m[1][0];
1219 0 : transform->matrix[1][1] = result.m[1][1];
1220 0 : transform->matrix[2][1] = result.m[1][2];
1221 0 : transform->matrix[0][2] = result.m[2][0];
1222 0 : transform->matrix[1][2] = result.m[2][1];
1223 0 : transform->matrix[2][2] = result.m[2][2];
1224 :
1225 0 : } else if (in->color_space == GRAY_SIGNATURE) {
1226 0 : if (in_type != QCMS_DATA_GRAY_8 &&
1227 : in_type != QCMS_DATA_GRAYA_8){
1228 0 : assert(0 && "input type");
1229 : transform_free(transform);
1230 : return NULL;
1231 : }
1232 :
1233 0 : transform->input_gamma_table_gray = build_input_gamma_table(in->grayTRC);
1234 0 : if (!transform->input_gamma_table_gray) {
1235 0 : qcms_transform_release(transform);
1236 0 : return NO_MEM_TRANSFORM;
1237 : }
1238 :
1239 0 : if (precache) {
1240 0 : if (in_type == QCMS_DATA_GRAY_8) {
1241 0 : transform->transform_fn = qcms_transform_data_gray_out_precache;
1242 : } else {
1243 0 : transform->transform_fn = qcms_transform_data_graya_out_precache;
1244 : }
1245 : } else {
1246 0 : if (in_type == QCMS_DATA_GRAY_8) {
1247 0 : transform->transform_fn = qcms_transform_data_gray_out_lut;
1248 : } else {
1249 0 : transform->transform_fn = qcms_transform_data_graya_out_lut;
1250 : }
1251 : }
1252 : } else {
1253 0 : assert(0 && "unexpected colorspace");
1254 : transform_free(transform);
1255 : return NULL;
1256 : }
1257 0 : return transform;
1258 : }
1259 :
1260 : #if defined(__GNUC__) && !defined(__x86_64__) && !defined(__amd64__)
1261 : /* we need this to avoid crashes when gcc assumes the stack is 128bit aligned */
1262 : __attribute__((__force_align_arg_pointer__))
1263 : #endif
1264 0 : void qcms_transform_data(qcms_transform *transform, void *src, void *dest, size_t length)
1265 : {
1266 0 : transform->transform_fn(transform, src, dest, length);
1267 0 : }
1268 :
1269 : qcms_bool qcms_supports_iccv4;
1270 0 : void qcms_enable_iccv4()
1271 : {
1272 0 : qcms_supports_iccv4 = true;
1273 0 : }
|