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 Software
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 #ifdef USE_LIBFUZZER
36 #define ASSERT(x)
37 #else
38 #define ASSERT(x) assert(x)
39 #endif
40
41 #define PARAMETRIC_CURVE_TYPE 0x70617261 //'para'
42
43 /* value must be a value between 0 and 1 */
44 //XXX: is the above a good restriction to have?
45 // the output range of this function is 0..1
lut_interp_linear(double input_value,uint16_t * table,size_t length)46 float lut_interp_linear(double input_value, uint16_t *table, size_t length)
47 {
48 int upper, lower;
49 float value;
50 input_value = input_value * (length - 1); // scale to length of the array
51 upper = ceil(input_value);
52 lower = floor(input_value);
53 //XXX: can we be more performant here?
54 value = table[upper]*(1. - (upper - input_value)) + table[lower]*(upper - input_value);
55 /* scale the value */
56 return value * (1.f/65535.f);
57 }
58
59 /* same as above but takes and returns a uint16_t value representing a range from 0..1 */
lut_interp_linear16(uint16_t input_value,uint16_t * table,size_t length)60 uint16_t lut_interp_linear16(uint16_t input_value, uint16_t *table, size_t length)
61 {
62 /* Start scaling input_value to the length of the array: 65535*(length-1).
63 * We'll divide out the 65535 next */
64 uintptr_t value = (input_value * (length - 1));
65 uint32_t upper = (value + 65534) / 65535; /* equivalent to ceil(value/65535) */
66 uint32_t lower = value / 65535; /* equivalent to floor(value/65535) */
67 /* interp is the distance from upper to value scaled to 0..65535 */
68 uint32_t interp = value % 65535;
69
70 value = (table[upper]*(interp) + table[lower]*(65535 - interp))/65535; // 0..65535*65535
71
72 return value;
73 }
74
75 /* same as above but takes an input_value from 0..PRECACHE_OUTPUT_MAX
76 * and returns a uint8_t value representing a range from 0..1 */
77 static
lut_interp_linear_precache_output(uint32_t input_value,uint16_t * table,size_t length)78 uint8_t lut_interp_linear_precache_output(uint32_t input_value, uint16_t *table, size_t length)
79 {
80 /* Start scaling input_value to the length of the array: PRECACHE_OUTPUT_MAX*(length-1).
81 * We'll divide out the PRECACHE_OUTPUT_MAX next */
82 uintptr_t value = (input_value * (length - 1));
83
84 /* equivalent to ceil(value/PRECACHE_OUTPUT_MAX) */
85 uint32_t upper = (value + PRECACHE_OUTPUT_MAX-1) / PRECACHE_OUTPUT_MAX;
86 /* equivalent to floor(value/PRECACHE_OUTPUT_MAX) */
87 uint32_t lower = value / PRECACHE_OUTPUT_MAX;
88 /* interp is the distance from upper to value scaled to 0..PRECACHE_OUTPUT_MAX */
89 uint32_t interp = value % PRECACHE_OUTPUT_MAX;
90
91 /* the table values range from 0..65535 */
92 value = (table[upper]*(interp) + table[lower]*(PRECACHE_OUTPUT_MAX - interp)); // 0..(65535*PRECACHE_OUTPUT_MAX)
93
94 /* round and scale */
95 value += (PRECACHE_OUTPUT_MAX*65535/255)/2;
96 value /= (PRECACHE_OUTPUT_MAX*65535/255); // scale to 0..255
97 return value;
98 }
99
100 /* value must be a value between 0 and 1 */
101 //XXX: is the above a good restriction to have?
lut_interp_linear_float(float value,float * table,size_t length)102 float lut_interp_linear_float(float value, float *table, size_t length)
103 {
104 int upper, lower;
105 value = value * (length - 1);
106 upper = ceil(value);
107 lower = floor(value);
108 //XXX: can we be more performant here?
109 value = table[upper]*(1. - (upper - value)) + table[lower]*(upper - value);
110 /* scale the value */
111 return value;
112 }
113
114 #if 0
115 /* if we use a different representation i.e. one that goes from 0 to 0x1000 we can be more efficient
116 * because we can avoid the divisions and use a shifting instead */
117 /* same as above but takes and returns a uint16_t value representing a range from 0..1 */
118 uint16_t lut_interp_linear16(uint16_t input_value, uint16_t *table, int length)
119 {
120 uint32_t value = (input_value * (length - 1));
121 uint32_t upper = (value + 4095) / 4096; /* equivalent to ceil(value/4096) */
122 uint32_t lower = value / 4096; /* equivalent to floor(value/4096) */
123 uint32_t interp = value % 4096;
124
125 value = (table[upper]*(interp) + table[lower]*(4096 - interp))/4096; // 0..4096*4096
126
127 return value;
128 }
129 #endif
130
compute_curve_gamma_table_type1(float gamma_table[256],uint16_t gamma)131 void compute_curve_gamma_table_type1(float gamma_table[256], uint16_t gamma)
132 {
133 unsigned int i;
134 float gamma_float = u8Fixed8Number_to_float(gamma);
135 for (i = 0; i < 256; i++) {
136 // 0..1^(0..255 + 255/256) will always be between 0 and 1
137 gamma_table[i] = pow(i/255., gamma_float);
138 }
139 }
140
compute_curve_gamma_table_type2(float gamma_table[256],uint16_t * table,size_t length)141 void compute_curve_gamma_table_type2(float gamma_table[256], uint16_t *table, size_t length)
142 {
143 unsigned int i;
144 for (i = 0; i < 256; i++) {
145 gamma_table[i] = lut_interp_linear(i/255., table, length);
146 }
147 }
148
compute_curve_gamma_table_type_parametric(float gamma_table[256],float parameter[7],int count)149 void compute_curve_gamma_table_type_parametric(float gamma_table[256], float parameter[7], int count)
150 {
151 size_t X;
152 float interval;
153 float a, b, c, e, f;
154 float y = parameter[0];
155 if (count == 0) {
156 a = 1;
157 b = 0;
158 c = 0;
159 e = 0;
160 f = 0;
161 interval = -INFINITY;
162 } else if(count == 1) {
163 a = parameter[1];
164 b = parameter[2];
165 c = 0;
166 e = 0;
167 f = 0;
168 interval = -1 * parameter[2] / parameter[1];
169 } else if(count == 2) {
170 a = parameter[1];
171 b = parameter[2];
172 c = 0;
173 e = parameter[3];
174 f = parameter[3];
175 interval = -1 * parameter[2] / parameter[1];
176 } else if(count == 3) {
177 a = parameter[1];
178 b = parameter[2];
179 c = parameter[3];
180 e = -c;
181 f = 0;
182 interval = parameter[4];
183 } else if(count == 4) {
184 a = parameter[1];
185 b = parameter[2];
186 c = parameter[3];
187 e = parameter[5] - c;
188 f = parameter[6];
189 interval = parameter[4];
190 } else {
191 ASSERT(0 && "invalid parametric function type.");
192 a = 1;
193 b = 0;
194 c = 0;
195 e = 0;
196 f = 0;
197 interval = -INFINITY;
198 }
199 for (X = 0; X < 256; X++) {
200 float x = X / 255.0;
201 if (x >= interval) {
202 // XXX The equations are not exactly as definied in the spec but are
203 // algebraic equivilent.
204 // TODO Should division by 255 be for the whole expression.
205 gamma_table[X] = clamp_float(powf(a * x + b, y) + (c + e));
206 } else {
207 gamma_table[X] = clamp_float(c * x + f);
208 }
209 }
210 }
211
compute_curve_gamma_table_type0(float gamma_table[256])212 void compute_curve_gamma_table_type0(float gamma_table[256])
213 {
214 unsigned int i;
215 for (i = 0; i < 256; i++) {
216 gamma_table[i] = i/255.;
217 }
218 }
219
clamp_float(float a)220 float clamp_float(float a)
221 {
222 /* One would naturally write this function as the following:
223 if (a > 1.)
224 return 1.;
225 else if (a < 0)
226 return 0;
227 else
228 return a;
229
230 However, that version will let NaNs pass through which is undesirable
231 for most consumers.
232 */
233
234 if (a > 1.)
235 return 1.;
236 else if (a >= 0)
237 return a;
238 else // a < 0 or a is NaN
239 return 0;
240 }
241
clamp_u8(float v)242 unsigned char clamp_u8(float v)
243 {
244 if (v > 255.)
245 return 255;
246 else if (v < 0)
247 return 0;
248 else
249 return floor(v+.5);
250 }
251
u8Fixed8Number_to_float(uint16_t x)252 float u8Fixed8Number_to_float(uint16_t x)
253 {
254 // 0x0000 = 0.
255 // 0x0100 = 1.
256 // 0xffff = 255 + 255/256
257 return x/256.;
258 }
259
260 /* The SSE2 code uses min & max which let NaNs pass through.
261 We want to try to prevent that here by ensuring that
262 gamma table is within expected values. */
validate_gamma_table(float gamma_table[256])263 void validate_gamma_table(float gamma_table[256])
264 {
265 int i;
266 for (i = 0; i < 256; i++) {
267 // Note: we check that the gamma is not in range
268 // instead of out of range so that we catch NaNs
269 if (!(gamma_table[i] >= 0.f && gamma_table[i] <= 1.f)) {
270 gamma_table[i] = 0.f;
271 }
272 }
273 }
274
build_input_gamma_table(struct curveType * TRC)275 float *build_input_gamma_table(struct curveType *TRC)
276 {
277 float *gamma_table;
278
279 if (!TRC) return NULL;
280 gamma_table = malloc(sizeof(float)*256);
281 if (gamma_table) {
282 if (TRC->type == PARAMETRIC_CURVE_TYPE) {
283 compute_curve_gamma_table_type_parametric(gamma_table, TRC->parameter, TRC->count);
284 } else {
285 if (TRC->count == 0) {
286 compute_curve_gamma_table_type0(gamma_table);
287 } else if (TRC->count == 1) {
288 compute_curve_gamma_table_type1(gamma_table, TRC->data[0]);
289 } else {
290 compute_curve_gamma_table_type2(gamma_table, TRC->data, TRC->count);
291 }
292 }
293 }
294
295 validate_gamma_table(gamma_table);
296
297 return gamma_table;
298 }
299
build_colorant_matrix(qcms_profile * p)300 struct matrix build_colorant_matrix(qcms_profile *p)
301 {
302 struct matrix result;
303 result.m[0][0] = s15Fixed16Number_to_float(p->redColorant.X);
304 result.m[0][1] = s15Fixed16Number_to_float(p->greenColorant.X);
305 result.m[0][2] = s15Fixed16Number_to_float(p->blueColorant.X);
306 result.m[1][0] = s15Fixed16Number_to_float(p->redColorant.Y);
307 result.m[1][1] = s15Fixed16Number_to_float(p->greenColorant.Y);
308 result.m[1][2] = s15Fixed16Number_to_float(p->blueColorant.Y);
309 result.m[2][0] = s15Fixed16Number_to_float(p->redColorant.Z);
310 result.m[2][1] = s15Fixed16Number_to_float(p->greenColorant.Z);
311 result.m[2][2] = s15Fixed16Number_to_float(p->blueColorant.Z);
312 result.invalid = false;
313 return result;
314 }
315
316 /* The following code is copied nearly directly from lcms.
317 * I think it could be much better. For example, Argyll seems to have better code in
318 * icmTable_lookup_bwd and icmTable_setup_bwd. However, for now this is a quick way
319 * to a working solution and allows for easy comparing with lcms. */
lut_inverse_interp16(uint16_t Value,uint16_t LutTable[],int length,int NumZeroes,int NumPoles)320 uint16_fract_t lut_inverse_interp16(uint16_t Value, uint16_t LutTable[], int length, int NumZeroes, int NumPoles)
321 {
322 int l = 1;
323 int r = 0x10000;
324 int x = 0, res; // 'int' Give spacing for negative values
325 int cell0, cell1;
326 double val2;
327 double y0, y1, x0, x1;
328 double a, b, f;
329
330 // July/27 2001 - Expanded to handle degenerated curves with an arbitrary
331 // number of elements containing 0 at the beginning of the table (Zeroes)
332 // and another arbitrary number of poles (FFFFh) at the end.
333
334 // There are no zeros at the beginning and we are trying to find a zero, so
335 // return anything. It seems zero would be the less destructive choice
336 /* I'm not sure that this makes sense, but oh well... */
337 if (NumZeroes == 0 && Value == 0)
338 return 0;
339
340 // Does the curve belong to this case?
341 if (NumZeroes > 1 || NumPoles > 1)
342 {
343 float a, b;
344 int sample;
345
346 // Identify if value fall downto 0 or FFFF zone
347 if (Value == 0) return 0;
348 // if (Value == 0xFFFF) return 0xFFFF;
349 sample = (length-1) * ((double) Value * (1./65535.));
350 if (LutTable[sample] == 0xffff)
351 return 0xffff;
352
353 // else restrict to valid zone
354
355 a = ((NumZeroes-1) * 65535.f) / (length-1);
356 b = ((length-1 - NumPoles) * 65535.f) / (length-1);
357
358 l = ((int)a) - 1;
359 r = ((int)b) + 1;
360
361 // Ensure a valid binary search range
362
363 if (l < 1)
364 l = 1;
365 if (r > 0x10000)
366 r = 0x10000;
367
368 // If the search range is inverted due to degeneracy,
369 // deem LutTable non-invertible in this search range.
370 // Refer to https://bugzil.la/1132467
371
372 if (r <= l)
373 return 0;
374 }
375
376 // For input 0, return that to maintain black level. Note the binary search
377 // does not. For example, it inverts the standard sRGB gamma curve to 7 at
378 // the origin, causing a black level error.
379
380 if (Value == 0 && NumZeroes) {
381 return 0;
382 }
383
384 // Seems not a degenerated case... apply binary search
385
386 while (r > l) {
387
388 x = (l + r) / 2;
389
390 res = (int) lut_interp_linear16((uint16_fract_t) (x-1), LutTable, length);
391
392 if (res == Value) {
393
394 // Found exact match.
395
396 return (uint16_fract_t) (x - 1);
397 }
398
399 if (res > Value) r = x - 1;
400 else l = x + 1;
401 }
402
403 // Not found, should we interpolate?
404
405 // Get surrounding nodes
406
407 assert(x >= 1);
408
409 val2 = (length-1) * ((double) (x - 1) / 65535.0);
410
411 cell0 = (int) floor(val2);
412 cell1 = (int) ceil(val2);
413
414 assert(cell0 >= 0);
415 assert(cell1 >= 0);
416 assert(cell0 < length);
417 assert(cell1 < length);
418
419 if (cell0 == cell1) return (uint16_fract_t) x;
420
421 y0 = LutTable[cell0] ;
422 x0 = (65535.0 * cell0) / (length-1);
423
424 y1 = LutTable[cell1] ;
425 x1 = (65535.0 * cell1) / (length-1);
426
427 a = (y1 - y0) / (x1 - x0);
428 b = y0 - a * x0;
429
430 if (fabs(a) < 0.01) return (uint16_fract_t) x;
431
432 f = ((Value - b) / a);
433
434 if (f < 0.0) return (uint16_fract_t) 0;
435 if (f >= 65535.0) return (uint16_fract_t) 0xFFFF;
436
437 return (uint16_fract_t) floor(f + 0.5);
438 }
439
440 // December/16 2015 - Moved this code out of lut_inverse_interp16
441 // in order to save computation in invert_lut loop.
count_zeroes_and_poles(uint16_t * LutTable,int length,int * NumZeroes,int * NumPoles)442 static void count_zeroes_and_poles(uint16_t *LutTable, int length, int *NumZeroes, int *NumPoles)
443 {
444 int z = 0, p = 0;
445
446 while (LutTable[z] == 0 && z < length - 1)
447 z++;
448 *NumZeroes = z;
449
450 while (LutTable[length - 1 - p] == 0xFFFF && p < length - 1)
451 p++;
452 *NumPoles = p;
453 }
454
455 /*
456 The number of entries needed to invert a lookup table should not
457 necessarily be the same as the original number of entries. This is
458 especially true of lookup tables that have a small number of entries.
459
460 For example:
461 Using a table like:
462 {0, 3104, 14263, 34802, 65535}
463 invert_lut will produce an inverse of:
464 {3, 34459, 47529, 56801, 65535}
465 which has an maximum error of about 9855 (pixel difference of ~38.346)
466
467 For now, we punt the decision of output size to the caller. */
invert_lut(uint16_t * table,int length,size_t out_length)468 static uint16_t *invert_lut(uint16_t *table, int length, size_t out_length)
469 {
470 int NumZeroes;
471 int NumPoles;
472 int i;
473 /* for now we invert the lut by creating a lut of size out_length
474 * and attempting to lookup a value for each entry using lut_inverse_interp16 */
475 uint16_t *output = malloc(sizeof(uint16_t)*out_length);
476 if (!output)
477 return NULL;
478
479 // December/16 2015 - Compute the input curve zero and pole extents outside
480 // the loop and pass them to lut_inverse_interp16.
481 count_zeroes_and_poles(table, length, &NumZeroes, &NumPoles);
482
483 for (i = 0; i < out_length; i++) {
484 double x = ((double) i * 65535.) / (double) (out_length - 1);
485 uint16_fract_t input = floor(x + .5);
486 output[i] = lut_inverse_interp16(input, table, length, NumZeroes, NumPoles);
487 }
488
489 return output;
490 }
491
compute_precache_pow(uint8_t * output,float gamma)492 static void compute_precache_pow(uint8_t *output, float gamma)
493 {
494 uint32_t v = 0;
495 for (v = 0; v < PRECACHE_OUTPUT_SIZE; v++) {
496 //XXX: don't do integer/float conversion... and round?
497 output[v] = 255. * pow(v/(double)PRECACHE_OUTPUT_MAX, gamma);
498 }
499 }
500
compute_precache_lut(uint8_t * output,uint16_t * table,int length)501 void compute_precache_lut(uint8_t *output, uint16_t *table, int length)
502 {
503 uint32_t v = 0;
504 for (v = 0; v < PRECACHE_OUTPUT_SIZE; v++) {
505 output[v] = lut_interp_linear_precache_output(v, table, length);
506 }
507 }
508
compute_precache_linear(uint8_t * output)509 void compute_precache_linear(uint8_t *output)
510 {
511 uint32_t v = 0;
512 for (v = 0; v < PRECACHE_OUTPUT_SIZE; v++) {
513 //XXX: round?
514 output[v] = v / (PRECACHE_OUTPUT_SIZE/256);
515 }
516 }
517
compute_precache(struct curveType * trc,uint8_t * output)518 qcms_bool compute_precache(struct curveType *trc, uint8_t *output)
519 {
520
521 if (trc->type == PARAMETRIC_CURVE_TYPE) {
522 float gamma_table[256];
523 uint16_t gamma_table_uint[256];
524 uint16_t i;
525 uint16_t *inverted;
526 int inverted_size = 256;
527
528 compute_curve_gamma_table_type_parametric(gamma_table, trc->parameter, trc->count);
529 for(i = 0; i < 256; i++) {
530 gamma_table_uint[i] = (uint16_t)(gamma_table[i] * 65535);
531 }
532
533 //XXX: the choice of a minimum of 256 here is not backed by any theory,
534 // measurement or data, howeve r it is what lcms uses.
535 // the maximum number we would need is 65535 because that's the
536 // accuracy used for computing the pre cache table
537 if (inverted_size < 256)
538 inverted_size = 256;
539
540 inverted = invert_lut(gamma_table_uint, 256, inverted_size);
541 if (!inverted)
542 return false;
543 compute_precache_lut(output, inverted, inverted_size);
544 free(inverted);
545 } else {
546 if (trc->count == 0) {
547 compute_precache_linear(output);
548 } else if (trc->count == 1) {
549 compute_precache_pow(output, 1./u8Fixed8Number_to_float(trc->data[0]));
550 } else {
551 uint16_t *inverted;
552 int inverted_size = trc->count;
553 //XXX: the choice of a minimum of 256 here is not backed by any theory,
554 // measurement or data, howeve r it is what lcms uses.
555 // the maximum number we would need is 65535 because that's the
556 // accuracy used for computing the pre cache table
557 if (inverted_size < 256)
558 inverted_size = 256;
559
560 inverted = invert_lut(trc->data, trc->count, inverted_size);
561 if (!inverted)
562 return false;
563 compute_precache_lut(output, inverted, inverted_size);
564 free(inverted);
565 }
566 }
567 return true;
568 }
569
570
build_linear_table(int length)571 static uint16_t *build_linear_table(int length)
572 {
573 int i;
574 uint16_t *output = malloc(sizeof(uint16_t)*length);
575 if (!output)
576 return NULL;
577
578 for (i = 0; i < length; i++) {
579 double x = ((double) i * 65535.) / (double) (length - 1);
580 uint16_fract_t input = floor(x + .5);
581 output[i] = input;
582 }
583 return output;
584 }
585
build_pow_table(float gamma,int length)586 static uint16_t *build_pow_table(float gamma, int length)
587 {
588 int i;
589 uint16_t *output = malloc(sizeof(uint16_t)*length);
590 if (!output)
591 return NULL;
592
593 for (i = 0; i < length; i++) {
594 uint16_fract_t result;
595 double x = ((double) i) / (double) (length - 1);
596 x = pow(x, gamma); //XXX turn this conversion into a function
597 result = floor(x*65535. + .5);
598 output[i] = result;
599 }
600 return output;
601 }
602
build_output_lut(struct curveType * trc,uint16_t ** output_gamma_lut,size_t * output_gamma_lut_length)603 void build_output_lut(struct curveType *trc,
604 uint16_t **output_gamma_lut, size_t *output_gamma_lut_length)
605 {
606 if (trc->type == PARAMETRIC_CURVE_TYPE) {
607 float gamma_table[256];
608 uint16_t gamma_table_uint[256];
609 uint16_t i;
610 uint16_t *inverted;
611 int inverted_size = 4096;
612
613 compute_curve_gamma_table_type_parametric(gamma_table, trc->parameter, trc->count);
614 for(i = 0; i < 256; i++) {
615 gamma_table_uint[i] = (uint16_t)(gamma_table[i] * 65535);
616 }
617
618 //XXX: the choice of a minimum of 256 here is not backed by any theory,
619 // measurement or data, however it is what lcms uses.
620 // the maximum number we would need is 65535 because that's the
621 // accuracy used for computing the pre cache table
622 inverted = invert_lut(gamma_table_uint, 256, inverted_size);
623 if (!inverted)
624 return;
625 *output_gamma_lut = inverted;
626 *output_gamma_lut_length = inverted_size;
627 } else {
628 if (trc->count == 0) {
629 *output_gamma_lut = build_linear_table(4096);
630 *output_gamma_lut_length = 4096;
631 } else if (trc->count == 1) {
632 float gamma = 1./u8Fixed8Number_to_float(trc->data[0]);
633 *output_gamma_lut = build_pow_table(gamma, 4096);
634 *output_gamma_lut_length = 4096;
635 } else {
636 //XXX: the choice of a minimum of 256 here is not backed by any theory,
637 // measurement or data, however it is what lcms uses.
638 *output_gamma_lut_length = trc->count;
639 if (*output_gamma_lut_length < 256)
640 *output_gamma_lut_length = 256;
641
642 *output_gamma_lut = invert_lut(trc->data, trc->count, *output_gamma_lut_length);
643 }
644 }
645
646 }
647
qcms_profile_get_parametric_curve(qcms_profile * profile,qcms_trc_channel channel,float data[7])648 size_t qcms_profile_get_parametric_curve(qcms_profile *profile, qcms_trc_channel channel, float data[7])
649 {
650 static const uint32_t COUNT_TO_LENGTH[5] = {1, 3, 4, 5, 7};
651 struct curveType *curve = NULL;
652 size_t size;
653
654 if (profile->color_space != RGB_SIGNATURE)
655 return 0;
656
657 switch(channel) {
658 case QCMS_TRC_RED:
659 curve = profile->redTRC;
660 break;
661 case QCMS_TRC_GREEN:
662 curve = profile->greenTRC;
663 break;
664 case QCMS_TRC_BLUE:
665 curve = profile->blueTRC;
666 break;
667 default:
668 return 0;
669 }
670
671 if (!curve || curve->type != PARAMETRIC_CURVE_TYPE)
672 return 0;
673
674 size = COUNT_TO_LENGTH[curve->count];
675
676 if (data)
677 memcpy(data, curve->parameter, size * sizeof(float));
678
679 return size;
680 }
681