1 : #define _ISOC99_SOURCE /* for INFINITY */
2 :
3 : #include <math.h>
4 : #include <assert.h>
5 : #include <string.h> //memcpy
6 : #include "qcmsint.h"
7 : #include "transform_util.h"
8 : #include "matrix.h"
9 :
10 : #if !defined(INFINITY)
11 : #define INFINITY HUGE_VAL
12 : #endif
13 :
14 : #define PARAMETRIC_CURVE_TYPE 0x70617261 //'para'
15 :
16 : /* value must be a value between 0 and 1 */
17 : //XXX: is the above a good restriction to have?
18 0 : float lut_interp_linear(double value, uint16_t *table, int length)
19 : {
20 : int upper, lower;
21 0 : value = value * (length - 1); // scale to length of the array
22 0 : upper = ceil(value);
23 0 : lower = floor(value);
24 : //XXX: can we be more performant here?
25 0 : value = table[upper]*(1. - (upper - value)) + table[lower]*(upper - value);
26 : /* scale the value */
27 0 : return value * (1./65535.);
28 : }
29 :
30 : /* same as above but takes and returns a uint16_t value representing a range from 0..1 */
31 0 : uint16_t lut_interp_linear16(uint16_t input_value, uint16_t *table, int length)
32 : {
33 : /* Start scaling input_value to the length of the array: 65535*(length-1).
34 : * We'll divide out the 65535 next */
35 0 : uint32_t value = (input_value * (length - 1));
36 0 : uint32_t upper = (value + 65534) / 65535; /* equivalent to ceil(value/65535) */
37 0 : uint32_t lower = value / 65535; /* equivalent to floor(value/65535) */
38 : /* interp is the distance from upper to value scaled to 0..65535 */
39 0 : uint32_t interp = value % 65535;
40 :
41 0 : value = (table[upper]*(interp) + table[lower]*(65535 - interp))/65535; // 0..65535*65535
42 :
43 0 : return value;
44 : }
45 :
46 : /* same as above but takes an input_value from 0..PRECACHE_OUTPUT_MAX
47 : * and returns a uint8_t value representing a range from 0..1 */
48 : static
49 0 : uint8_t lut_interp_linear_precache_output(uint32_t input_value, uint16_t *table, int length)
50 : {
51 : /* Start scaling input_value to the length of the array: PRECACHE_OUTPUT_MAX*(length-1).
52 : * We'll divide out the PRECACHE_OUTPUT_MAX next */
53 0 : uint32_t value = (input_value * (length - 1));
54 :
55 : /* equivalent to ceil(value/PRECACHE_OUTPUT_MAX) */
56 0 : uint32_t upper = (value + PRECACHE_OUTPUT_MAX-1) / PRECACHE_OUTPUT_MAX;
57 : /* equivalent to floor(value/PRECACHE_OUTPUT_MAX) */
58 0 : uint32_t lower = value / PRECACHE_OUTPUT_MAX;
59 : /* interp is the distance from upper to value scaled to 0..PRECACHE_OUTPUT_MAX */
60 0 : uint32_t interp = value % PRECACHE_OUTPUT_MAX;
61 :
62 : /* the table values range from 0..65535 */
63 0 : value = (table[upper]*(interp) + table[lower]*(PRECACHE_OUTPUT_MAX - interp)); // 0..(65535*PRECACHE_OUTPUT_MAX)
64 :
65 : /* round and scale */
66 0 : value += (PRECACHE_OUTPUT_MAX*65535/255)/2;
67 0 : value /= (PRECACHE_OUTPUT_MAX*65535/255); // scale to 0..255
68 0 : return value;
69 : }
70 :
71 : /* value must be a value between 0 and 1 */
72 : //XXX: is the above a good restriction to have?
73 0 : float lut_interp_linear_float(float value, float *table, int length)
74 : {
75 : int upper, lower;
76 0 : value = value * (length - 1);
77 0 : upper = ceil(value);
78 0 : lower = floor(value);
79 : //XXX: can we be more performant here?
80 0 : value = table[upper]*(1. - (upper - value)) + table[lower]*(upper - value);
81 : /* scale the value */
82 0 : return value;
83 : }
84 :
85 : #if 0
86 : /* if we use a different representation i.e. one that goes from 0 to 0x1000 we can be more efficient
87 : * because we can avoid the divisions and use a shifting instead */
88 : /* same as above but takes and returns a uint16_t value representing a range from 0..1 */
89 : uint16_t lut_interp_linear16(uint16_t input_value, uint16_t *table, int length)
90 : {
91 : uint32_t value = (input_value * (length - 1));
92 : uint32_t upper = (value + 4095) / 4096; /* equivalent to ceil(value/4096) */
93 : uint32_t lower = value / 4096; /* equivalent to floor(value/4096) */
94 : uint32_t interp = value % 4096;
95 :
96 : value = (table[upper]*(interp) + table[lower]*(4096 - interp))/4096; // 0..4096*4096
97 :
98 : return value;
99 : }
100 : #endif
101 :
102 0 : void compute_curve_gamma_table_type1(float gamma_table[256], double gamma)
103 : {
104 : unsigned int i;
105 0 : for (i = 0; i < 256; i++) {
106 0 : gamma_table[i] = pow(i/255., gamma);
107 : }
108 0 : }
109 :
110 0 : void compute_curve_gamma_table_type2(float gamma_table[256], uint16_t *table, int length)
111 : {
112 : unsigned int i;
113 0 : for (i = 0; i < 256; i++) {
114 0 : gamma_table[i] = lut_interp_linear(i/255., table, length);
115 : }
116 0 : }
117 :
118 0 : void compute_curve_gamma_table_type_parametric(float gamma_table[256], float parameter[7], int count)
119 : {
120 : size_t X;
121 : float interval;
122 : float a, b, c, e, f;
123 0 : float y = parameter[0];
124 0 : if (count == 0) {
125 0 : a = 1;
126 0 : b = 0;
127 0 : c = 0;
128 0 : e = 0;
129 0 : f = 0;
130 0 : interval = -INFINITY;
131 0 : } else if(count == 1) {
132 0 : a = parameter[1];
133 0 : b = parameter[2];
134 0 : c = 0;
135 0 : e = 0;
136 0 : f = 0;
137 0 : interval = -1 * parameter[2] / parameter[1];
138 0 : } else if(count == 2) {
139 0 : a = parameter[1];
140 0 : b = parameter[2];
141 0 : c = 0;
142 0 : e = parameter[3];
143 0 : f = parameter[3];
144 0 : interval = -1 * parameter[2] / parameter[1];
145 0 : } else if(count == 3) {
146 0 : a = parameter[1];
147 0 : b = parameter[2];
148 0 : c = parameter[3];
149 0 : e = -c;
150 0 : f = 0;
151 0 : interval = parameter[4];
152 0 : } else if(count == 4) {
153 0 : a = parameter[1];
154 0 : b = parameter[2];
155 0 : c = parameter[3];
156 0 : e = parameter[5] - c;
157 0 : f = parameter[6];
158 0 : interval = parameter[4];
159 : } else {
160 0 : assert(0 && "invalid parametric function type.");
161 : a = 1;
162 : b = 0;
163 : c = 0;
164 : e = 0;
165 : f = 0;
166 : interval = -INFINITY;
167 : }
168 0 : for (X = 0; X < 256; X++) {
169 0 : if (X >= interval) {
170 : // XXX The equations are not exactly as definied in the spec but are
171 : // algebraic equivilent.
172 : // TODO Should division by 255 be for the whole expression.
173 0 : gamma_table[X] = pow(a * X / 255. + b, y) + c + e;
174 : } else {
175 0 : gamma_table[X] = c * X / 255. + f;
176 : }
177 : }
178 0 : }
179 :
180 0 : void compute_curve_gamma_table_type0(float gamma_table[256])
181 : {
182 : unsigned int i;
183 0 : for (i = 0; i < 256; i++) {
184 0 : gamma_table[i] = i/255.;
185 : }
186 0 : }
187 :
188 :
189 0 : float clamp_float(float a)
190 : {
191 0 : if (a > 1.)
192 0 : return 1.;
193 0 : else if (a < 0)
194 0 : return 0;
195 : else
196 0 : return a;
197 : }
198 :
199 0 : unsigned char clamp_u8(float v)
200 : {
201 0 : if (v > 255.)
202 0 : return 255;
203 0 : else if (v < 0)
204 0 : return 0;
205 : else
206 0 : return floor(v+.5);
207 : }
208 :
209 0 : float u8Fixed8Number_to_float(uint16_t x)
210 : {
211 : // 0x0000 = 0.
212 : // 0x0100 = 1.
213 : // 0xffff = 255 + 255/256
214 0 : return x/256.;
215 : }
216 :
217 0 : float *build_input_gamma_table(struct curveType *TRC)
218 : {
219 : float *gamma_table;
220 :
221 0 : if (!TRC) return NULL;
222 0 : gamma_table = malloc(sizeof(float)*256);
223 0 : if (gamma_table) {
224 0 : if (TRC->type == PARAMETRIC_CURVE_TYPE) {
225 0 : compute_curve_gamma_table_type_parametric(gamma_table, TRC->parameter, TRC->count);
226 : } else {
227 0 : if (TRC->count == 0) {
228 0 : compute_curve_gamma_table_type0(gamma_table);
229 0 : } else if (TRC->count == 1) {
230 0 : compute_curve_gamma_table_type1(gamma_table, u8Fixed8Number_to_float(TRC->data[0]));
231 : } else {
232 0 : compute_curve_gamma_table_type2(gamma_table, TRC->data, TRC->count);
233 : }
234 : }
235 : }
236 0 : return gamma_table;
237 : }
238 :
239 0 : struct matrix build_colorant_matrix(qcms_profile *p)
240 : {
241 : struct matrix result;
242 0 : result.m[0][0] = s15Fixed16Number_to_float(p->redColorant.X);
243 0 : result.m[0][1] = s15Fixed16Number_to_float(p->greenColorant.X);
244 0 : result.m[0][2] = s15Fixed16Number_to_float(p->blueColorant.X);
245 0 : result.m[1][0] = s15Fixed16Number_to_float(p->redColorant.Y);
246 0 : result.m[1][1] = s15Fixed16Number_to_float(p->greenColorant.Y);
247 0 : result.m[1][2] = s15Fixed16Number_to_float(p->blueColorant.Y);
248 0 : result.m[2][0] = s15Fixed16Number_to_float(p->redColorant.Z);
249 0 : result.m[2][1] = s15Fixed16Number_to_float(p->greenColorant.Z);
250 0 : result.m[2][2] = s15Fixed16Number_to_float(p->blueColorant.Z);
251 0 : result.invalid = false;
252 0 : return result;
253 : }
254 :
255 : /* The following code is copied nearly directly from lcms.
256 : * I think it could be much better. For example, Argyll seems to have better code in
257 : * icmTable_lookup_bwd and icmTable_setup_bwd. However, for now this is a quick way
258 : * to a working solution and allows for easy comparing with lcms. */
259 0 : uint16_fract_t lut_inverse_interp16(uint16_t Value, uint16_t LutTable[], int length)
260 : {
261 0 : int l = 1;
262 0 : int r = 0x10000;
263 0 : int x = 0, res; // 'int' Give spacing for negative values
264 : int NumZeroes, NumPoles;
265 : int cell0, cell1;
266 : double val2;
267 : double y0, y1, x0, x1;
268 : double a, b, f;
269 :
270 : // July/27 2001 - Expanded to handle degenerated curves with an arbitrary
271 : // number of elements containing 0 at the begining of the table (Zeroes)
272 : // and another arbitrary number of poles (FFFFh) at the end.
273 : // First the zero and pole extents are computed, then value is compared.
274 :
275 0 : NumZeroes = 0;
276 0 : while (LutTable[NumZeroes] == 0 && NumZeroes < length-1)
277 0 : NumZeroes++;
278 :
279 : // There are no zeros at the beginning and we are trying to find a zero, so
280 : // return anything. It seems zero would be the less destructive choice
281 : /* I'm not sure that this makes sense, but oh well... */
282 0 : if (NumZeroes == 0 && Value == 0)
283 0 : return 0;
284 :
285 0 : NumPoles = 0;
286 0 : while (LutTable[length-1- NumPoles] == 0xFFFF && NumPoles < length-1)
287 0 : NumPoles++;
288 :
289 : // Does the curve belong to this case?
290 0 : if (NumZeroes > 1 || NumPoles > 1)
291 : {
292 : int a, b;
293 :
294 : // Identify if value fall downto 0 or FFFF zone
295 0 : if (Value == 0) return 0;
296 : // if (Value == 0xFFFF) return 0xFFFF;
297 :
298 : // else restrict to valid zone
299 :
300 0 : a = ((NumZeroes-1) * 0xFFFF) / (length-1);
301 0 : b = ((length-1 - NumPoles) * 0xFFFF) / (length-1);
302 :
303 0 : l = a - 1;
304 0 : r = b + 1;
305 : }
306 :
307 :
308 : // Seems not a degenerated case... apply binary search
309 :
310 0 : while (r > l) {
311 :
312 0 : x = (l + r) / 2;
313 :
314 0 : res = (int) lut_interp_linear16((uint16_fract_t) (x-1), LutTable, length);
315 :
316 0 : if (res == Value) {
317 :
318 : // Found exact match.
319 :
320 0 : return (uint16_fract_t) (x - 1);
321 : }
322 :
323 0 : if (res > Value) r = x - 1;
324 0 : else l = x + 1;
325 : }
326 :
327 : // Not found, should we interpolate?
328 :
329 :
330 : // Get surrounding nodes
331 :
332 0 : val2 = (length-1) * ((double) (x - 1) / 65535.0);
333 :
334 0 : cell0 = (int) floor(val2);
335 0 : cell1 = (int) ceil(val2);
336 :
337 0 : if (cell0 == cell1) return (uint16_fract_t) x;
338 :
339 0 : y0 = LutTable[cell0] ;
340 0 : x0 = (65535.0 * cell0) / (length-1);
341 :
342 0 : y1 = LutTable[cell1] ;
343 0 : x1 = (65535.0 * cell1) / (length-1);
344 :
345 0 : a = (y1 - y0) / (x1 - x0);
346 0 : b = y0 - a * x0;
347 :
348 0 : if (fabs(a) < 0.01) return (uint16_fract_t) x;
349 :
350 0 : f = ((Value - b) / a);
351 :
352 0 : if (f < 0.0) return (uint16_fract_t) 0;
353 0 : if (f >= 65535.0) return (uint16_fract_t) 0xFFFF;
354 :
355 0 : return (uint16_fract_t) floor(f + 0.5);
356 :
357 : }
358 :
359 : /*
360 : The number of entries needed to invert a lookup table should not
361 : necessarily be the same as the original number of entries. This is
362 : especially true of lookup tables that have a small number of entries.
363 :
364 : For example:
365 : Using a table like:
366 : {0, 3104, 14263, 34802, 65535}
367 : invert_lut will produce an inverse of:
368 : {3, 34459, 47529, 56801, 65535}
369 : which has an maximum error of about 9855 (pixel difference of ~38.346)
370 :
371 : For now, we punt the decision of output size to the caller. */
372 0 : static uint16_t *invert_lut(uint16_t *table, int length, int out_length)
373 : {
374 : int i;
375 : /* for now we invert the lut by creating a lut of size out_length
376 : * and attempting to lookup a value for each entry using lut_inverse_interp16 */
377 0 : uint16_t *output = malloc(sizeof(uint16_t)*out_length);
378 0 : if (!output)
379 0 : return NULL;
380 :
381 0 : for (i = 0; i < out_length; i++) {
382 0 : double x = ((double) i * 65535.) / (double) (out_length - 1);
383 0 : uint16_fract_t input = floor(x + .5);
384 0 : output[i] = lut_inverse_interp16(input, table, length);
385 : }
386 0 : return output;
387 : }
388 :
389 0 : static void compute_precache_pow(uint8_t *output, float gamma)
390 : {
391 0 : uint32_t v = 0;
392 0 : for (v = 0; v < PRECACHE_OUTPUT_SIZE; v++) {
393 : //XXX: don't do integer/float conversion... and round?
394 0 : output[v] = 255. * pow(v/(double)PRECACHE_OUTPUT_MAX, gamma);
395 : }
396 0 : }
397 :
398 0 : void compute_precache_lut(uint8_t *output, uint16_t *table, int length)
399 : {
400 0 : uint32_t v = 0;
401 0 : for (v = 0; v < PRECACHE_OUTPUT_SIZE; v++) {
402 0 : output[v] = lut_interp_linear_precache_output(v, table, length);
403 : }
404 0 : }
405 :
406 0 : void compute_precache_linear(uint8_t *output)
407 : {
408 0 : uint32_t v = 0;
409 0 : for (v = 0; v < PRECACHE_OUTPUT_SIZE; v++) {
410 : //XXX: round?
411 0 : output[v] = v / (PRECACHE_OUTPUT_SIZE/256);
412 : }
413 0 : }
414 :
415 0 : qcms_bool compute_precache(struct curveType *trc, uint8_t *output)
416 : {
417 :
418 0 : if (trc->type == PARAMETRIC_CURVE_TYPE) {
419 : float gamma_table[256];
420 : uint16_t gamma_table_uint[256];
421 : uint16_t i;
422 : uint16_t *inverted;
423 0 : int inverted_size = 256;
424 :
425 0 : compute_curve_gamma_table_type_parametric(gamma_table, trc->parameter, trc->count);
426 0 : for(i = 0; i < 256; i++) {
427 0 : gamma_table_uint[i] = (uint16_t)(gamma_table[i] * 65535);
428 : }
429 :
430 : //XXX: the choice of a minimum of 256 here is not backed by any theory,
431 : // measurement or data, howeve r it is what lcms uses.
432 : // the maximum number we would need is 65535 because that's the
433 : // accuracy used for computing the pre cache table
434 0 : if (inverted_size < 256)
435 0 : inverted_size = 256;
436 :
437 0 : inverted = invert_lut(gamma_table_uint, 256, inverted_size);
438 0 : if (!inverted)
439 0 : return false;
440 0 : compute_precache_lut(output, inverted, inverted_size);
441 0 : free(inverted);
442 : } else {
443 0 : if (trc->count == 0) {
444 0 : compute_precache_linear(output);
445 0 : } else if (trc->count == 1) {
446 0 : compute_precache_pow(output, 1./u8Fixed8Number_to_float(trc->data[0]));
447 : } else {
448 : uint16_t *inverted;
449 0 : int inverted_size = trc->count;
450 : //XXX: the choice of a minimum of 256 here is not backed by any theory,
451 : // measurement or data, howeve r it is what lcms uses.
452 : // the maximum number we would need is 65535 because that's the
453 : // accuracy used for computing the pre cache table
454 0 : if (inverted_size < 256)
455 0 : inverted_size = 256;
456 :
457 0 : inverted = invert_lut(trc->data, trc->count, inverted_size);
458 0 : if (!inverted)
459 0 : return false;
460 0 : compute_precache_lut(output, inverted, inverted_size);
461 0 : free(inverted);
462 : }
463 : }
464 0 : return true;
465 : }
466 :
467 :
468 0 : static uint16_t *build_linear_table(int length)
469 : {
470 : int i;
471 0 : uint16_t *output = malloc(sizeof(uint16_t)*length);
472 0 : if (!output)
473 0 : return NULL;
474 :
475 0 : for (i = 0; i < length; i++) {
476 0 : double x = ((double) i * 65535.) / (double) (length - 1);
477 0 : uint16_fract_t input = floor(x + .5);
478 0 : output[i] = input;
479 : }
480 0 : return output;
481 : }
482 :
483 0 : static uint16_t *build_pow_table(float gamma, int length)
484 : {
485 : int i;
486 0 : uint16_t *output = malloc(sizeof(uint16_t)*length);
487 0 : if (!output)
488 0 : return NULL;
489 :
490 0 : for (i = 0; i < length; i++) {
491 : uint16_fract_t result;
492 0 : double x = ((double) i) / (double) (length - 1);
493 0 : x = pow(x, gamma); //XXX turn this conversion into a function
494 0 : result = floor(x*65535. + .5);
495 0 : output[i] = result;
496 : }
497 0 : return output;
498 : }
499 :
500 0 : void build_output_lut(struct curveType *trc,
501 : uint16_t **output_gamma_lut, size_t *output_gamma_lut_length)
502 : {
503 0 : if (trc->type == PARAMETRIC_CURVE_TYPE) {
504 : float gamma_table[256];
505 : uint16_t i;
506 0 : uint16_t *output = malloc(sizeof(uint16_t)*256);
507 :
508 0 : if (!output) {
509 0 : *output_gamma_lut = NULL;
510 0 : return;
511 : }
512 :
513 0 : compute_curve_gamma_table_type_parametric(gamma_table, trc->parameter, trc->count);
514 0 : *output_gamma_lut_length = 256;
515 0 : for(i = 0; i < 256; i++) {
516 0 : output[i] = (uint16_t)(gamma_table[i] * 65535);
517 : }
518 0 : *output_gamma_lut = output;
519 : } else {
520 0 : if (trc->count == 0) {
521 0 : *output_gamma_lut = build_linear_table(4096);
522 0 : *output_gamma_lut_length = 4096;
523 0 : } else if (trc->count == 1) {
524 0 : float gamma = 1./u8Fixed8Number_to_float(trc->data[0]);
525 0 : *output_gamma_lut = build_pow_table(gamma, 4096);
526 0 : *output_gamma_lut_length = 4096;
527 : } else {
528 : //XXX: the choice of a minimum of 256 here is not backed by any theory,
529 : // measurement or data, however it is what lcms uses.
530 0 : *output_gamma_lut_length = trc->count;
531 0 : if (*output_gamma_lut_length < 256)
532 0 : *output_gamma_lut_length = 256;
533 :
534 0 : *output_gamma_lut = invert_lut(trc->data, trc->count, *output_gamma_lut_length);
535 : }
536 : }
537 :
538 : }
539 :
|