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