Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: third_party/qcms/src/transform_util.c

Issue 2014023003: Add exact version of qcms used by Chrome for testing and comparison (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/qcms/src/transform_util.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // qcms
2 // Copyright (C) 2009 Mozilla Foundation
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the Softwar e
9 // is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
16 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 #define _ISOC99_SOURCE /* for INFINITY */
23
24 #include <math.h>
25 #include <assert.h>
26 #include <string.h> //memcpy
27 #include "qcmsint.h"
28 #include "transform_util.h"
29 #include "matrix.h"
30
31 #if !defined(INFINITY)
32 #define INFINITY HUGE_VAL
33 #endif
34
35 #define PARAMETRIC_CURVE_TYPE 0x70617261 //'para'
36
37 /* value must be a value between 0 and 1 */
38 //XXX: is the above a good restriction to have?
39 // the output range of this function is 0..1
40 float lut_interp_linear(double input_value, uint16_t *table, size_t length)
41 {
42 int upper, lower;
43 float value;
44 input_value = input_value * (length - 1); // scale to length of the arra y
45 upper = ceil(input_value);
46 lower = floor(input_value);
47 //XXX: can we be more performant here?
48 value = table[upper]*(1. - (upper - input_value)) + table[lower]*(upper - input_value);
49 /* scale the value */
50 return value * (1.f/65535.f);
51 }
52
53 /* same as above but takes and returns a uint16_t value representing a range fro m 0..1 */
54 uint16_t lut_interp_linear16(uint16_t input_value, uint16_t *table, size_t lengt h)
55 {
56 /* Start scaling input_value to the length of the array: 65535*(length-1 ).
57 * We'll divide out the 65535 next */
58 uintptr_t value = (input_value * (length - 1));
59 uint32_t upper = (value + 65534) / 65535; /* equivalent to ceil(value/65 535) */
60 uint32_t lower = value / 65535; /* equivalent to floor(value/6 5535) */
61 /* interp is the distance from upper to value scaled to 0..65535 */
62 uint32_t interp = value % 65535;
63
64 value = (table[upper]*(interp) + table[lower]*(65535 - interp))/65535; / / 0..65535*65535
65
66 return value;
67 }
68
69 /* same as above but takes an input_value from 0..PRECACHE_OUTPUT_MAX
70 * and returns a uint8_t value representing a range from 0..1 */
71 static
72 uint8_t lut_interp_linear_precache_output(uint32_t input_value, uint16_t *table, size_t length)
73 {
74 /* Start scaling input_value to the length of the array: PRECACHE_OUTPUT _MAX*(length-1).
75 * We'll divide out the PRECACHE_OUTPUT_MAX next */
76 uintptr_t value = (input_value * (length - 1));
77
78 /* equivalent to ceil(value/PRECACHE_OUTPUT_MAX) */
79 uint32_t upper = (value + PRECACHE_OUTPUT_MAX-1) / PRECACHE_OUTPUT_MAX;
80 /* equivalent to floor(value/PRECACHE_OUTPUT_MAX) */
81 uint32_t lower = value / PRECACHE_OUTPUT_MAX;
82 /* interp is the distance from upper to value scaled to 0..PRECACHE_OUTP UT_MAX */
83 uint32_t interp = value % PRECACHE_OUTPUT_MAX;
84
85 /* the table values range from 0..65535 */
86 value = (table[upper]*(interp) + table[lower]*(PRECACHE_OUTPUT_MAX - int erp)); // 0..(65535*PRECACHE_OUTPUT_MAX)
87
88 /* round and scale */
89 value += (PRECACHE_OUTPUT_MAX*65535/255)/2;
90 value /= (PRECACHE_OUTPUT_MAX*65535/255); // scale to 0..255
91 return value;
92 }
93
94 /* value must be a value between 0 and 1 */
95 //XXX: is the above a good restriction to have?
96 float lut_interp_linear_float(float value, float *table, size_t length)
97 {
98 int upper, lower;
99 value = value * (length - 1);
100 upper = ceil(value);
101 lower = floor(value);
102 //XXX: can we be more performant here?
103 value = table[upper]*(1. - (upper - value)) + table[lower]*(upper - valu e);
104 /* scale the value */
105 return value;
106 }
107
108 #if 0
109 /* if we use a different representation i.e. one that goes from 0 to 0x1000 we c an be more efficient
110 * because we can avoid the divisions and use a shifting instead */
111 /* same as above but takes and returns a uint16_t value representing a range fro m 0..1 */
112 uint16_t lut_interp_linear16(uint16_t input_value, uint16_t *table, int length)
113 {
114 uint32_t value = (input_value * (length - 1));
115 uint32_t upper = (value + 4095) / 4096; /* equivalent to ceil(value/4096 ) */
116 uint32_t lower = value / 4096; /* equivalent to floor(value/40 96) */
117 uint32_t interp = value % 4096;
118
119 value = (table[upper]*(interp) + table[lower]*(4096 - interp))/4096; // 0..4096*4096
120
121 return value;
122 }
123 #endif
124
125 void compute_curve_gamma_table_type1(float gamma_table[256], uint16_t gamma)
126 {
127 unsigned int i;
128 float gamma_float = u8Fixed8Number_to_float(gamma);
129 for (i = 0; i < 256; i++) {
130 // 0..1^(0..255 + 255/256) will always be between 0 and 1
131 gamma_table[i] = pow(i/255., gamma_float);
132 }
133 }
134
135 void compute_curve_gamma_table_type2(float gamma_table[256], uint16_t *table, si ze_t length)
136 {
137 unsigned int i;
138 for (i = 0; i < 256; i++) {
139 gamma_table[i] = lut_interp_linear(i/255., table, length);
140 }
141 }
142
143 void compute_curve_gamma_table_type_parametric(float gamma_table[256], float par ameter[7], int count)
144 {
145 size_t X;
146 float interval;
147 float a, b, c, e, f;
148 float y = parameter[0];
149 if (count == 0) {
150 a = 1;
151 b = 0;
152 c = 0;
153 e = 0;
154 f = 0;
155 interval = -INFINITY;
156 } else if(count == 1) {
157 a = parameter[1];
158 b = parameter[2];
159 c = 0;
160 e = 0;
161 f = 0;
162 interval = -1 * parameter[2] / parameter[1];
163 } else if(count == 2) {
164 a = parameter[1];
165 b = parameter[2];
166 c = 0;
167 e = parameter[3];
168 f = parameter[3];
169 interval = -1 * parameter[2] / parameter[1];
170 } else if(count == 3) {
171 a = parameter[1];
172 b = parameter[2];
173 c = parameter[3];
174 e = -c;
175 f = 0;
176 interval = parameter[4];
177 } else if(count == 4) {
178 a = parameter[1];
179 b = parameter[2];
180 c = parameter[3];
181 e = parameter[5] - c;
182 f = parameter[6];
183 interval = parameter[4];
184 } else {
185 assert(0 && "invalid parametric function type.");
186 a = 1;
187 b = 0;
188 c = 0;
189 e = 0;
190 f = 0;
191 interval = -INFINITY;
192 }
193 for (X = 0; X < 256; X++) {
194 float x = X / 255.0;
195 if (x >= interval) {
196 // XXX The equations are not exactly as definied in the spec but are
197 // algebraic equivilent.
198 // TODO Should division by 255 be for the whole expressi on.
199 gamma_table[X] = clamp_float(pow(a * x + b, y) + c + e);
200 } else {
201 gamma_table[X] = clamp_float(c * x + f);
202 }
203 }
204 }
205
206 void compute_curve_gamma_table_type0(float gamma_table[256])
207 {
208 unsigned int i;
209 for (i = 0; i < 256; i++) {
210 gamma_table[i] = i/255.;
211 }
212 }
213
214 float clamp_float(float a)
215 {
216 /* One would naturally write this function as the following:
217 if (a > 1.)
218 return 1.;
219 else if (a < 0)
220 return 0;
221 else
222 return a;
223
224 However, that version will let NaNs pass through which is undesirable
225 for most consumers.
226 */
227
228 if (a > 1.)
229 return 1.;
230 else if (a >= 0)
231 return a;
232 else // a < 0 or a is NaN
233 return 0;
234 }
235
236 unsigned char clamp_u8(float v)
237 {
238 if (v > 255.)
239 return 255;
240 else if (v < 0)
241 return 0;
242 else
243 return floor(v+.5);
244 }
245
246 float u8Fixed8Number_to_float(uint16_t x)
247 {
248 // 0x0000 = 0.
249 // 0x0100 = 1.
250 // 0xffff = 255 + 255/256
251 return x/256.;
252 }
253
254 /* The SSE2 code uses min & max which let NaNs pass through.
255 We want to try to prevent that here by ensuring that
256 gamma table is within expected values. */
257 void validate_gamma_table(float gamma_table[256])
258 {
259 int i;
260 for (i = 0; i < 256; i++) {
261 // Note: we check that the gamma is not in range
262 // instead of out of range so that we catch NaNs
263 if (!(gamma_table[i] >= 0.f && gamma_table[i] <= 1.f)) {
264 gamma_table[i] = 0.f;
265 }
266 }
267 }
268
269 float *build_input_gamma_table(struct curveType *TRC)
270 {
271 float *gamma_table;
272
273 if (!TRC) return NULL;
274 gamma_table = malloc(sizeof(float)*256);
275 if (gamma_table) {
276 if (TRC->type == PARAMETRIC_CURVE_TYPE) {
277 compute_curve_gamma_table_type_parametric(gamma_table, T RC->parameter, TRC->count);
278 } else {
279 if (TRC->count == 0) {
280 compute_curve_gamma_table_type0(gamma_table);
281 } else if (TRC->count == 1) {
282 compute_curve_gamma_table_type1(gamma_table, TRC ->data[0]);
283 } else {
284 compute_curve_gamma_table_type2(gamma_table, TRC ->data, TRC->count);
285 }
286 }
287 }
288
289 validate_gamma_table(gamma_table);
290
291 return gamma_table;
292 }
293
294 struct matrix build_colorant_matrix(qcms_profile *p)
295 {
296 struct matrix result;
297 result.m[0][0] = s15Fixed16Number_to_float(p->redColorant.X);
298 result.m[0][1] = s15Fixed16Number_to_float(p->greenColorant.X);
299 result.m[0][2] = s15Fixed16Number_to_float(p->blueColorant.X);
300 result.m[1][0] = s15Fixed16Number_to_float(p->redColorant.Y);
301 result.m[1][1] = s15Fixed16Number_to_float(p->greenColorant.Y);
302 result.m[1][2] = s15Fixed16Number_to_float(p->blueColorant.Y);
303 result.m[2][0] = s15Fixed16Number_to_float(p->redColorant.Z);
304 result.m[2][1] = s15Fixed16Number_to_float(p->greenColorant.Z);
305 result.m[2][2] = s15Fixed16Number_to_float(p->blueColorant.Z);
306 result.invalid = false;
307 return result;
308 }
309
310 /* The following code is copied nearly directly from lcms.
311 * I think it could be much better. For example, Argyll seems to have better cod e in
312 * icmTable_lookup_bwd and icmTable_setup_bwd. However, for now this is a quick way
313 * to a working solution and allows for easy comparing with lcms. */
314 uint16_fract_t lut_inverse_interp16(uint16_t Value, uint16_t LutTable[], int len gth, int NumZeroes, int NumPoles)
315 {
316 int l = 1;
317 int r = 0x10000;
318 int x = 0, res; // 'int' Give spacing for negative values
319 int cell0, cell1;
320 double val2;
321 double y0, y1, x0, x1;
322 double a, b, f;
323
324 // July/27 2001 - Expanded to handle degenerated curves with an arbitrar y
325 // number of elements containing 0 at the beginning of the table (Zeroes )
326 // and another arbitrary number of poles (FFFFh) at the end.
327
328 // There are no zeros at the beginning and we are trying to find a zero, so
329 // return anything. It seems zero would be the less destructive choice
330 /* I'm not sure that this makes sense, but oh well... */
331 if (NumZeroes == 0 && Value == 0)
332 return 0;
333
334 // Does the curve belong to this case?
335 if (NumZeroes > 1 || NumPoles > 1)
336 {
337 int a, b, sample;
338
339 // Identify if value fall downto 0 or FFFF zone
340 if (Value == 0) return 0;
341 // if (Value == 0xFFFF) return 0xFFFF;
342 sample = (length-1) * ((double) Value * (1./65535.));
343 if (LutTable[sample] == 0xffff)
344 return 0xffff;
345
346 // else restrict to valid zone
347
348 a = ((NumZeroes-1) * 0xFFFF) / (length-1);
349 b = ((length-1 - NumPoles) * 0xFFFF) / (length-1);
350
351 l = a - 1;
352 r = b + 1;
353
354 // Ensure a valid binary search range
355
356 if (l < 1)
357 l = 1;
358 if (r > 0x10000)
359 r = 0x10000;
360
361 // If the search range is inverted due to degeneracy,
362 // deem LutTable non-invertible in this search range.
363 // Refer to https://bugzil.la/1132467
364
365 if (r <= l)
366 return 0;
367 }
368
369 // For input 0, return that to maintain black level. Note the binary sea rch
370 // does not. For example, it inverts the standard sRGB gamma curve to 7 at
371 // the origin, causing a black level error.
372
373 if (Value == 0 && NumZeroes) {
374 return 0;
375 }
376
377 // Seems not a degenerated case... apply binary search
378
379 while (r > l) {
380
381 x = (l + r) / 2;
382
383 res = (int) lut_interp_linear16((uint16_fract_t) (x-1), LutTable , length);
384
385 if (res == Value) {
386
387 // Found exact match.
388
389 return (uint16_fract_t) (x - 1);
390 }
391
392 if (res > Value) r = x - 1;
393 else l = x + 1;
394 }
395
396 // Not found, should we interpolate?
397
398 // Get surrounding nodes
399
400 assert(x >= 1);
401
402 val2 = (length-1) * ((double) (x - 1) / 65535.0);
403
404 cell0 = (int) floor(val2);
405 cell1 = (int) ceil(val2);
406
407 assert(cell0 >= 0);
408 assert(cell1 >= 0);
409 assert(cell0 < length);
410 assert(cell1 < length);
411
412 if (cell0 == cell1) return (uint16_fract_t) x;
413
414 y0 = LutTable[cell0] ;
415 x0 = (65535.0 * cell0) / (length-1);
416
417 y1 = LutTable[cell1] ;
418 x1 = (65535.0 * cell1) / (length-1);
419
420 a = (y1 - y0) / (x1 - x0);
421 b = y0 - a * x0;
422
423 if (fabs(a) < 0.01) return (uint16_fract_t) x;
424
425 f = ((Value - b) / a);
426
427 if (f < 0.0) return (uint16_fract_t) 0;
428 if (f >= 65535.0) return (uint16_fract_t) 0xFFFF;
429
430 return (uint16_fract_t) floor(f + 0.5);
431 }
432
433 // December/16 2015 - Moved this code out of lut_inverse_interp16
434 // in order to save computation in invert_lut loop.
435 static void count_zeroes_and_poles(uint16_t *LutTable, int length, int *NumZeroe s, int *NumPoles)
436 {
437 int z = 0, p = 0;
438
439 while (LutTable[z] == 0 && z < length - 1)
440 z++;
441 *NumZeroes = z;
442
443 while (LutTable[length - 1 - p] == 0xFFFF && p < length - 1)
444 p++;
445 *NumPoles = p;
446 }
447
448 /*
449 The number of entries needed to invert a lookup table should not
450 necessarily be the same as the original number of entries. This is
451 especially true of lookup tables that have a small number of entries.
452
453 For example:
454 Using a table like:
455 {0, 3104, 14263, 34802, 65535}
456 invert_lut will produce an inverse of:
457 {3, 34459, 47529, 56801, 65535}
458 which has an maximum error of about 9855 (pixel difference of ~38.346)
459
460 For now, we punt the decision of output size to the caller. */
461 static uint16_t *invert_lut(uint16_t *table, int length, size_t out_length)
462 {
463 int NumZeroes;
464 int NumPoles;
465 int i;
466 /* for now we invert the lut by creating a lut of size out_length
467 * and attempting to lookup a value for each entry using lut_inverse_int erp16 */
468 uint16_t *output = malloc(sizeof(uint16_t)*out_length);
469 if (!output)
470 return NULL;
471
472 // December/16 2015 - Compute the input curve zero and pole extents outs ide
473 // the loop and pass them to lut_inverse_interp16.
474 count_zeroes_and_poles(table, length, &NumZeroes, &NumPoles);
475
476 for (i = 0; i < out_length; i++) {
477 double x = ((double) i * 65535.) / (double) (out_length - 1);
478 uint16_fract_t input = floor(x + .5);
479 output[i] = lut_inverse_interp16(input, table, length, NumZeroes , NumPoles);
480 }
481
482 return output;
483 }
484
485 static void compute_precache_pow(uint8_t *output, float gamma)
486 {
487 uint32_t v = 0;
488 for (v = 0; v < PRECACHE_OUTPUT_SIZE; v++) {
489 //XXX: don't do integer/float conversion... and round?
490 output[v] = 255. * pow(v/(double)PRECACHE_OUTPUT_MAX, gamma);
491 }
492 }
493
494 void compute_precache_lut(uint8_t *output, uint16_t *table, int length)
495 {
496 uint32_t v = 0;
497 for (v = 0; v < PRECACHE_OUTPUT_SIZE; v++) {
498 output[v] = lut_interp_linear_precache_output(v, table, length);
499 }
500 }
501
502 void compute_precache_linear(uint8_t *output)
503 {
504 uint32_t v = 0;
505 for (v = 0; v < PRECACHE_OUTPUT_SIZE; v++) {
506 //XXX: round?
507 output[v] = v / (PRECACHE_OUTPUT_SIZE/256);
508 }
509 }
510
511 qcms_bool compute_precache(struct curveType *trc, uint8_t *output)
512 {
513
514 if (trc->type == PARAMETRIC_CURVE_TYPE) {
515 float gamma_table[256];
516 uint16_t gamma_table_uint[256];
517 uint16_t i;
518 uint16_t *inverted;
519 int inverted_size = 256;
520
521 compute_curve_gamma_table_type_parametric(gamma_table, t rc->parameter, trc->count);
522 for(i = 0; i < 256; i++) {
523 gamma_table_uint[i] = (uint16_t)(gamma_table[i] * 65535);
524 }
525
526 //XXX: the choice of a minimum of 256 here is not backed by any theory,
527 // measurement or data, howeve r it is what lcms use s.
528 // the maximum number we would need is 65535 because that's the
529 // accuracy used for computing the pre cache table
530 if (inverted_size < 256)
531 inverted_size = 256;
532
533 inverted = invert_lut(gamma_table_uint, 256, inverted_si ze);
534 if (!inverted)
535 return false;
536 compute_precache_lut(output, inverted, inverted_size);
537 free(inverted);
538 } else {
539 if (trc->count == 0) {
540 compute_precache_linear(output);
541 } else if (trc->count == 1) {
542 compute_precache_pow(output, 1./u8Fixed8Number_to_float( trc->data[0]));
543 } else {
544 uint16_t *inverted;
545 int inverted_size = trc->count;
546 //XXX: the choice of a minimum of 256 here is not backed by any theory,
547 // measurement or data, howeve r it is what lcms use s.
548 // the maximum number we would need is 65535 because that's the
549 // accuracy used for computing the pre cache table
550 if (inverted_size < 256)
551 inverted_size = 256;
552
553 inverted = invert_lut(trc->data, trc->count, inverted_si ze);
554 if (!inverted)
555 return false;
556 compute_precache_lut(output, inverted, inverted_size);
557 free(inverted);
558 }
559 }
560 return true;
561 }
562
563
564 static uint16_t *build_linear_table(int length)
565 {
566 int i;
567 uint16_t *output = malloc(sizeof(uint16_t)*length);
568 if (!output)
569 return NULL;
570
571 for (i = 0; i < length; i++) {
572 double x = ((double) i * 65535.) / (double) (length - 1);
573 uint16_fract_t input = floor(x + .5);
574 output[i] = input;
575 }
576 return output;
577 }
578
579 static uint16_t *build_pow_table(float gamma, int length)
580 {
581 int i;
582 uint16_t *output = malloc(sizeof(uint16_t)*length);
583 if (!output)
584 return NULL;
585
586 for (i = 0; i < length; i++) {
587 uint16_fract_t result;
588 double x = ((double) i) / (double) (length - 1);
589 x = pow(x, gamma); //XXX turn this conversion int o a function
590 result = floor(x*65535. + .5);
591 output[i] = result;
592 }
593 return output;
594 }
595
596 void build_output_lut(struct curveType *trc,
597 uint16_t **output_gamma_lut, size_t *output_gamma_lut_length)
598 {
599 if (trc->type == PARAMETRIC_CURVE_TYPE) {
600 float gamma_table[256];
601 uint16_t gamma_table_uint[256];
602 uint16_t i;
603 uint16_t *inverted;
604 int inverted_size = 4096;
605
606 compute_curve_gamma_table_type_parametric(gamma_table, trc->para meter, trc->count);
607 for(i = 0; i < 256; i++) {
608 gamma_table_uint[i] = (uint16_t)(gamma_table[i] * 65535) ;
609 }
610
611 //XXX: the choice of a minimum of 256 here is not backed by any theory,
612 // measurement or data, however it is what lcms uses.
613 // the maximum number we would need is 65535 because that's the
614 // accuracy used for computing the pre cache table
615 inverted = invert_lut(gamma_table_uint, 256, inverted_size);
616 if (!inverted)
617 return;
618 *output_gamma_lut = inverted;
619 *output_gamma_lut_length = inverted_size;
620 } else {
621 if (trc->count == 0) {
622 *output_gamma_lut = build_linear_table(4096);
623 *output_gamma_lut_length = 4096;
624 } else if (trc->count == 1) {
625 float gamma = 1./u8Fixed8Number_to_float(trc->data[0]);
626 *output_gamma_lut = build_pow_table(gamma, 4096);
627 *output_gamma_lut_length = 4096;
628 } else {
629 //XXX: the choice of a minimum of 256 here is not backed by any theory,
630 // measurement or data, however it is what lcms uses .
631 *output_gamma_lut_length = trc->count;
632 if (*output_gamma_lut_length < 256)
633 *output_gamma_lut_length = 256;
634
635 *output_gamma_lut = invert_lut(trc->data, trc->count, *o utput_gamma_lut_length);
636 }
637 }
638
639 }
OLDNEW
« no previous file with comments | « third_party/qcms/src/transform_util.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698