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