Coverage Report

Created: 2018-09-25 14:53

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