1 /* vim: set ts=8 sw=8 noexpandtab: */
2 //  qcms
3 //  Copyright (C) 2009 Mozilla Foundation
4 //  Copyright (C) 1998-2007 Marti Maria
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the Software
11 // is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 
24 #include <math.h>
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <string.h> //memset
28 #include "qcmsint.h"
29 
30 /* It might be worth having a unified limit on content controlled
31  * allocation per profile. This would remove the need for many
32  * of the arbitrary limits that we used */
33 
34 typedef uint32_t be32;
35 typedef uint16_t be16;
36 
cpu_to_be32(uint32_t v)37 static be32 cpu_to_be32(uint32_t v)
38 {
39 #ifdef IS_LITTLE_ENDIAN
40 	return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | ((v & 0xff000000) >> 24);
41 #else
42 	return v;
43 #endif
44 }
45 
cpu_to_be16(uint16_t v)46 static be16 cpu_to_be16(uint16_t v)
47 {
48 #ifdef IS_LITTLE_ENDIAN
49 	return ((v & 0xff) << 8) | ((v & 0xff00) >> 8);
50 #else
51 	return v;
52 #endif
53 }
54 
be32_to_cpu(be32 v)55 static uint32_t be32_to_cpu(be32 v)
56 {
57 #ifdef IS_LITTLE_ENDIAN
58 	return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | ((v & 0xff000000) >> 24);
59 	//return __builtin_bswap32(v);
60 #else
61 	return v;
62 #endif
63 }
64 
be16_to_cpu(be16 v)65 static uint16_t be16_to_cpu(be16 v)
66 {
67 #ifdef IS_LITTLE_ENDIAN
68 	return ((v & 0xff) << 8) | ((v & 0xff00) >> 8);
69 #else
70 	return v;
71 #endif
72 }
73 
74 /* a wrapper around the memory that we are going to parse
75  * into a qcms_profile */
76 struct mem_source
77 {
78 	const unsigned char *buf;
79 	size_t size;
80 	qcms_bool valid;
81 	const char *invalid_reason;
82 };
83 
invalid_source(struct mem_source * mem,const char * reason)84 static void invalid_source(struct mem_source *mem, const char *reason)
85 {
86 	mem->valid = false;
87 	mem->invalid_reason = reason;
88 }
89 
read_u32(struct mem_source * mem,size_t offset)90 static uint32_t read_u32(struct mem_source *mem, size_t offset)
91 {
92 	/* Subtract from mem->size instead of the more intuitive adding to offset.
93 	 * This avoids overflowing offset. The subtraction is safe because
94 	 * mem->size is guaranteed to be > 4 */
95 	if (offset > mem->size - 4) {
96 		invalid_source(mem, "Invalid offset");
97 		return 0;
98 	} else {
99 		be32 k;
100 		memcpy(&k, mem->buf + offset, sizeof(k));
101 		return be32_to_cpu(k);
102 	}
103 }
104 
read_u16(struct mem_source * mem,size_t offset)105 static uint16_t read_u16(struct mem_source *mem, size_t offset)
106 {
107 	if (offset > mem->size - 2) {
108 		invalid_source(mem, "Invalid offset");
109 		return 0;
110 	} else {
111 		be16 k;
112 		memcpy(&k, mem->buf + offset, sizeof(k));
113 		return be16_to_cpu(k);
114 	}
115 }
116 
read_u8(struct mem_source * mem,size_t offset)117 static uint8_t read_u8(struct mem_source *mem, size_t offset)
118 {
119 	if (offset > mem->size - 1) {
120 		invalid_source(mem, "Invalid offset");
121 		return 0;
122 	} else {
123 		return *(uint8_t*)(mem->buf + offset);
124 	}
125 }
126 
read_s15Fixed16Number(struct mem_source * mem,size_t offset)127 static s15Fixed16Number read_s15Fixed16Number(struct mem_source *mem, size_t offset)
128 {
129 	return read_u32(mem, offset);
130 }
131 
read_uInt8Number(struct mem_source * mem,size_t offset)132 static uInt8Number read_uInt8Number(struct mem_source *mem, size_t offset)
133 {
134 	return read_u8(mem, offset);
135 }
136 
read_uInt16Number(struct mem_source * mem,size_t offset)137 static uInt16Number read_uInt16Number(struct mem_source *mem, size_t offset)
138 {
139 	return read_u16(mem, offset);
140 }
141 
write_u32(void * mem,size_t offset,uint32_t value)142 static void write_u32(void *mem, size_t offset, uint32_t value)
143 {
144     *((uint32_t *)((unsigned char*)mem + offset)) = cpu_to_be32(value);
145 }
146 
write_u16(void * mem,size_t offset,uint16_t value)147 static void write_u16(void *mem, size_t offset, uint16_t value)
148 {
149     *((uint16_t *)((unsigned char*)mem + offset)) = cpu_to_be16(value);
150 }
151 
152 #define BAD_VALUE_PROFILE NULL
153 #define INVALID_PROFILE NULL
154 #define NO_MEM_PROFILE NULL
155 
156 /* An arbitrary 4MB limit on profile size */
157 #define MAX_PROFILE_SIZE 1024*1024*4
158 #define MAX_TAG_COUNT 1024
159 
check_CMM_type_signature(struct mem_source * src)160 static void check_CMM_type_signature(struct mem_source *src)
161 {
162 	//uint32_t CMM_type_signature = read_u32(src, 4);
163 	//TODO: do the check?
164 
165 }
166 
check_profile_version(struct mem_source * src)167 static void check_profile_version(struct mem_source *src)
168 {
169 
170 	/*
171 	uint8_t major_revision = read_u8(src, 8 + 0);
172 	uint8_t minor_revision = read_u8(src, 8 + 1);
173 	*/
174 	uint8_t reserved1      = read_u8(src, 8 + 2);
175 	uint8_t reserved2      = read_u8(src, 8 + 3);
176 	/* Checking the version doesn't buy us anything
177 	if (major_revision != 0x4) {
178 		if (major_revision > 0x2)
179 			invalid_source(src, "Unsupported major revision");
180 		if (minor_revision > 0x40)
181 			invalid_source(src, "Unsupported minor revision");
182 	}
183 	*/
184 	if (reserved1 != 0 || reserved2 != 0)
185 		invalid_source(src, "Invalid reserved bytes");
186 }
187 
188 #define INPUT_DEVICE_PROFILE   0x73636e72 // 'scnr'
189 #define DISPLAY_DEVICE_PROFILE 0x6d6e7472 // 'mntr'
190 #define OUTPUT_DEVICE_PROFILE  0x70727472 // 'prtr'
191 #define DEVICE_LINK_PROFILE    0x6c696e6b // 'link'
192 #define COLOR_SPACE_PROFILE    0x73706163 // 'spac'
193 #define ABSTRACT_PROFILE       0x61627374 // 'abst'
194 #define NAMED_COLOR_PROFILE    0x6e6d636c // 'nmcl'
195 
read_class_signature(qcms_profile * profile,struct mem_source * mem)196 static void read_class_signature(qcms_profile *profile, struct mem_source *mem)
197 {
198 	profile->class = read_u32(mem, 12);
199 	switch (profile->class) {
200 		case DISPLAY_DEVICE_PROFILE:
201 		case INPUT_DEVICE_PROFILE:
202 		case OUTPUT_DEVICE_PROFILE:
203 		case COLOR_SPACE_PROFILE:
204 			break;
205 		default:
206 			invalid_source(mem, "Invalid  Profile/Device Class signature");
207 	}
208 }
209 
read_color_space(qcms_profile * profile,struct mem_source * mem)210 static void read_color_space(qcms_profile *profile, struct mem_source *mem)
211 {
212 	profile->color_space = read_u32(mem, 16);
213 	switch (profile->color_space) {
214 		case RGB_SIGNATURE:
215 		case GRAY_SIGNATURE:
216 			break;
217 		default:
218 			invalid_source(mem, "Unsupported colorspace");
219 	}
220 }
221 
read_pcs(qcms_profile * profile,struct mem_source * mem)222 static void read_pcs(qcms_profile *profile, struct mem_source *mem)
223 {
224 	profile->pcs = read_u32(mem, 20);
225 	switch (profile->pcs) {
226 		case XYZ_SIGNATURE:
227 		case LAB_SIGNATURE:
228 			break;
229 		default:
230 			invalid_source(mem, "Unsupported pcs");
231 	}
232 }
233 
234 struct tag
235 {
236 	uint32_t signature;
237 	uint32_t offset;
238 	uint32_t size;
239 };
240 
241 struct tag_index {
242 	uint32_t count;
243 	struct tag *tags;
244 };
245 
read_tag_table(qcms_profile * profile,struct mem_source * mem)246 static struct tag_index read_tag_table(qcms_profile *profile, struct mem_source *mem)
247 {
248 	struct tag_index index = {0, NULL};
249 	unsigned int i;
250 
251 	index.count = read_u32(mem, 128);
252 	if (index.count > MAX_TAG_COUNT) {
253 		invalid_source(mem, "max number of tags exceeded");
254 		return index;
255 	}
256 
257 	index.tags = malloc(sizeof(struct tag)*index.count);
258 	if (index.tags) {
259 		for (i = 0; i < index.count; i++) {
260 			index.tags[i].signature = read_u32(mem, 128 + 4 + 4*i*3);
261 			index.tags[i].offset    = read_u32(mem, 128 + 4 + 4*i*3 + 4);
262 			index.tags[i].size      = read_u32(mem, 128 + 4 + 4*i*3 + 8);
263 		}
264 	}
265 
266 	return index;
267 }
268 
269 // Checks a profile for obvious inconsistencies and returns
270 // true if the profile looks bogus and should probably be
271 // ignored.
qcms_profile_is_bogus(qcms_profile * profile)272 qcms_bool qcms_profile_is_bogus(qcms_profile *profile)
273 {
274        float sum[3], target[3], tolerance[3];
275        float rX, rY, rZ, gX, gY, gZ, bX, bY, bZ;
276        bool negative;
277        unsigned i;
278 
279        // We currently only check the bogosity of RGB profiles
280        if (profile->color_space != RGB_SIGNATURE)
281 	       return false;
282 
283        if (profile->A2B0 || profile->B2A0)
284                return false;
285 
286        rX = s15Fixed16Number_to_float(profile->redColorant.X);
287        rY = s15Fixed16Number_to_float(profile->redColorant.Y);
288        rZ = s15Fixed16Number_to_float(profile->redColorant.Z);
289 
290        gX = s15Fixed16Number_to_float(profile->greenColorant.X);
291        gY = s15Fixed16Number_to_float(profile->greenColorant.Y);
292        gZ = s15Fixed16Number_to_float(profile->greenColorant.Z);
293 
294        bX = s15Fixed16Number_to_float(profile->blueColorant.X);
295        bY = s15Fixed16Number_to_float(profile->blueColorant.Y);
296        bZ = s15Fixed16Number_to_float(profile->blueColorant.Z);
297 
298        // Sum the values; they should add up to something close to white
299        sum[0] = rX + gX + bX;
300        sum[1] = rY + gY + bY;
301        sum[2] = rZ + gZ + bZ;
302 
303        // Build our target vector (see mozilla bug 460629)
304        target[0] = 0.96420f;
305        target[1] = 1.00000f;
306        target[2] = 0.82491f;
307 
308        // Our tolerance vector - Recommended by Chris Murphy based on
309        // conversion from the LAB space criterion of no more than 3 in any one
310        // channel. This is similar to, but slightly more tolerant than Adobe's
311        // criterion.
312        tolerance[0] = 0.02f;
313        tolerance[1] = 0.02f;
314        tolerance[2] = 0.04f;
315 
316        // Compare with our tolerance
317        for (i = 0; i < 3; ++i) {
318            if (!(((sum[i] - tolerance[i]) <= target[i]) &&
319                  ((sum[i] + tolerance[i]) >= target[i])))
320                return true;
321        }
322 
323 #ifndef __APPLE__
324        // Check if any of the XYZ values are negative (see mozilla bug 498245)
325        // CIEXYZ tristimulus values cannot be negative according to the spec.
326 
327        negative =
328 	       (rX < 0) || (rY < 0) || (rZ < 0) ||
329 	       (gX < 0) || (gY < 0) || (gZ < 0) ||
330 	       (bX < 0) || (bY < 0) || (bZ < 0);
331 
332 #else
333        // Chromatic adaption to D50 can result in negative XYZ, but the white
334        // point D50 tolerance test has passed. Accept negative values herein.
335        // See https://bugzilla.mozilla.org/show_bug.cgi?id=498245#c18 onwards
336        // for discussion about whether profile XYZ can or cannot be negative,
337        // per the spec. Also the https://bugzil.la/450923 user report.
338 
339        // FIXME: allow this relaxation on all ports?
340        negative = false;
341 #endif
342        if (negative)
343 	       return true; // bogus
344 
345        // All Good
346        return false;
347 }
348 
349 #define TAG_bXYZ 0x6258595a
350 #define TAG_gXYZ 0x6758595a
351 #define TAG_rXYZ 0x7258595a
352 #define TAG_rTRC 0x72545243
353 #define TAG_bTRC 0x62545243
354 #define TAG_gTRC 0x67545243
355 #define TAG_kTRC 0x6b545243
356 #define TAG_A2B0 0x41324230
357 #define TAG_B2A0 0x42324130
358 #define TAG_CHAD 0x63686164
359 
find_tag(struct tag_index index,uint32_t tag_id)360 static struct tag *find_tag(struct tag_index index, uint32_t tag_id)
361 {
362 	unsigned int i;
363 	struct tag *tag = NULL;
364 	for (i = 0; i < index.count; i++) {
365 		if (index.tags[i].signature == tag_id) {
366 			return &index.tags[i];
367 		}
368 	}
369 	return tag;
370 }
371 
372 #define XYZ_TYPE		0x58595a20 // 'XYZ '
373 #define CURVE_TYPE		0x63757276 // 'curv'
374 #define PARAMETRIC_CURVE_TYPE	0x70617261 // 'para'
375 #define LUT16_TYPE		0x6d667432 // 'mft2'
376 #define LUT8_TYPE		0x6d667431 // 'mft1'
377 #define LUT_MAB_TYPE		0x6d414220 // 'mAB '
378 #define LUT_MBA_TYPE		0x6d424120 // 'mBA '
379 #define CHROMATIC_TYPE		0x73663332 // 'sf32'
380 
read_tag_s15Fixed16ArrayType(struct mem_source * src,struct tag_index index,uint32_t tag_id)381 static struct matrix read_tag_s15Fixed16ArrayType(struct mem_source *src, struct tag_index index, uint32_t tag_id)
382 {
383 	struct tag *tag = find_tag(index, tag_id);
384 	struct matrix matrix;
385 	if (tag) {
386 		uint8_t i;
387 		uint32_t offset = tag->offset;
388 		uint32_t type = read_u32(src, offset);
389 
390 		// Check mandatory type signature for s16Fixed16ArrayType
391 		if (type != CHROMATIC_TYPE) {
392 			invalid_source(src, "unexpected type, expected 'sf32'");
393 		}
394 
395 		for (i = 0; i < 9; i++) {
396 			matrix.m[i/3][i%3] = s15Fixed16Number_to_float(read_s15Fixed16Number(src, offset+8+i*4));
397 		}
398 		matrix.invalid = false;
399 	} else {
400 		matrix.invalid = true;
401 		invalid_source(src, "missing sf32tag");
402 	}
403 	return matrix;
404 }
405 
read_tag_XYZType(struct mem_source * src,struct tag_index index,uint32_t tag_id)406 static struct XYZNumber read_tag_XYZType(struct mem_source *src, struct tag_index index, uint32_t tag_id)
407 {
408 	struct XYZNumber num = {0, 0, 0};
409 	struct tag *tag = find_tag(index, tag_id);
410 	if (tag) {
411 		uint32_t offset = tag->offset;
412 
413 		uint32_t type = read_u32(src, offset);
414 		if (type != XYZ_TYPE)
415 			invalid_source(src, "unexpected type, expected XYZ");
416 		num.X = read_s15Fixed16Number(src, offset+8);
417 		num.Y = read_s15Fixed16Number(src, offset+12);
418 		num.Z = read_s15Fixed16Number(src, offset+16);
419 	} else {
420 		invalid_source(src, "missing xyztag");
421 	}
422 	return num;
423 }
424 
425 // Read the tag at a given offset rather then the tag_index.
426 // This method is used when reading mAB tags where nested curveType are
427 // present that are not part of the tag_index.
read_curveType(struct mem_source * src,uint32_t offset,uint32_t * len)428 static struct curveType *read_curveType(struct mem_source *src, uint32_t offset, uint32_t *len)
429 {
430 	static const uint32_t COUNT_TO_LENGTH[5] = {1, 3, 4, 5, 7};
431 	struct curveType *curve = NULL;
432 	uint32_t type = read_u32(src, offset);
433 	uint32_t count;
434 	uint32_t i;
435 
436 	if (type != CURVE_TYPE && type != PARAMETRIC_CURVE_TYPE) {
437 		invalid_source(src, "unexpected type, expected CURV or PARA");
438 		return NULL;
439 	}
440 
441 	if (type == CURVE_TYPE) {
442 		count = read_u32(src, offset+8);
443 
444 #define MAX_CURVE_ENTRIES 40000 //arbitrary
445 		if (count > MAX_CURVE_ENTRIES) {
446 			invalid_source(src, "curve size too large");
447 			return NULL;
448 		}
449 		curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*count);
450 		if (!curve)
451 			return NULL;
452 
453 		curve->count = count;
454 		curve->type = CURVE_TYPE;
455 
456 		for (i=0; i<count; i++) {
457 			curve->data[i] = read_u16(src, offset + 12 + i*2);
458 		}
459 		*len = 12 + count * 2;
460 	} else { //PARAMETRIC_CURVE_TYPE
461 		count = read_u16(src, offset+8);
462 
463 		if (count > 4) {
464 			invalid_source(src, "parametric function type not supported.");
465 			return NULL;
466 		}
467 
468 		curve = malloc(sizeof(struct curveType));
469 		if (!curve)
470 			return NULL;
471 
472 		curve->count = count;
473 		curve->type = PARAMETRIC_CURVE_TYPE;
474 
475 		for (i=0; i < COUNT_TO_LENGTH[count]; i++) {
476 			curve->parameter[i] = s15Fixed16Number_to_float(read_s15Fixed16Number(src, offset + 12 + i*4));
477 		}
478 		*len = 12 + COUNT_TO_LENGTH[count] * 4;
479 
480 		if ((count == 1 || count == 2)) {
481 			/* we have a type 1 or type 2 function that has a division by 'a' */
482 			float a = curve->parameter[1];
483 			if (a == 0.f)
484 				invalid_source(src, "parametricCurve definition causes division by zero.");
485 		}
486 	}
487 
488 	return curve;
489 }
490 
read_tag_curveType(struct mem_source * src,struct tag_index index,uint32_t tag_id)491 static struct curveType *read_tag_curveType(struct mem_source *src, struct tag_index index, uint32_t tag_id)
492 {
493 	struct tag *tag = find_tag(index, tag_id);
494 	struct curveType *curve = NULL;
495 	if (tag) {
496 		uint32_t len;
497 		return read_curveType(src, tag->offset, &len);
498 	} else {
499 		invalid_source(src, "missing curvetag");
500 	}
501 
502 	return curve;
503 }
504 
505 #define MAX_CLUT_SIZE 500000 // arbitrary
506 #define MAX_CHANNELS 10 // arbitrary
read_nested_curveType(struct mem_source * src,struct curveType * (* curveArray)[MAX_CHANNELS],uint8_t num_channels,uint32_t curve_offset)507 static void read_nested_curveType(struct mem_source *src, struct curveType *(*curveArray)[MAX_CHANNELS], uint8_t num_channels, uint32_t curve_offset)
508 {
509 	uint32_t channel_offset = 0;
510 	int i;
511 	for (i = 0; i < num_channels; i++) {
512 		uint32_t tag_len;
513 
514 		(*curveArray)[i] = read_curveType(src, curve_offset + channel_offset, &tag_len);
515 		if (!(*curveArray)[i]) {
516 			invalid_source(src, "invalid nested curveType curve");
517 		}
518 
519 		channel_offset += tag_len;
520 		// 4 byte aligned
521 		if ((tag_len % 4) != 0)
522 			channel_offset += 4 - (tag_len % 4);
523 	}
524 
525 }
526 
mAB_release(struct lutmABType * lut)527 static void mAB_release(struct lutmABType *lut)
528 {
529 	uint8_t i;
530 
531 	for (i = 0; i < lut->num_in_channels; i++){
532 		free(lut->a_curves[i]);
533 	}
534 	for (i = 0; i < lut->num_out_channels; i++){
535 		free(lut->b_curves[i]);
536 		free(lut->m_curves[i]);
537 	}
538 	free(lut);
539 }
540 
541 /* See section 10.10 for specs */
read_tag_lutmABType(struct mem_source * src,struct tag_index index,uint32_t tag_id)542 static struct lutmABType *read_tag_lutmABType(struct mem_source *src, struct tag_index index, uint32_t tag_id)
543 {
544 	struct tag *tag = find_tag(index, tag_id);
545 	uint32_t offset = tag->offset;
546 	uint32_t a_curve_offset, b_curve_offset, m_curve_offset;
547 	uint32_t matrix_offset;
548 	uint32_t clut_offset;
549 	uint32_t clut_size = 1;
550 	uint8_t clut_precision;
551 	uint32_t type = read_u32(src, offset);
552 	uint8_t num_in_channels, num_out_channels;
553 	struct lutmABType *lut;
554 	uint32_t i;
555 
556 	if (type != LUT_MAB_TYPE && type != LUT_MBA_TYPE) {
557 		return NULL;
558 	}
559 
560 	num_in_channels = read_u8(src, offset + 8);
561 	num_out_channels = read_u8(src, offset + 9);
562 	if (num_in_channels > MAX_CHANNELS || num_out_channels > MAX_CHANNELS)
563 		return NULL;
564 
565 	// We require 3in/out channels since we only support RGB->XYZ (or RGB->LAB)
566 	// XXX: If we remove this restriction make sure that the number of channels
567 	//      is less or equal to the maximum number of mAB curves in qcmsint.h
568 	//      also check for clut_size overflow. Also make sure it's != 0
569 	if (num_in_channels != 3 || num_out_channels != 3)
570 		return NULL;
571 
572 	// some of this data is optional and is denoted by a zero offset
573 	// we also use this to track their existance
574 	a_curve_offset = read_u32(src, offset + 28);
575 	clut_offset = read_u32(src, offset + 24);
576 	m_curve_offset = read_u32(src, offset + 20);
577 	matrix_offset = read_u32(src, offset + 16);
578 	b_curve_offset = read_u32(src, offset + 12);
579 
580 	// Convert offsets relative to the tag to relative to the profile
581 	// preserve zero for optional fields
582 	if (a_curve_offset)
583 		a_curve_offset += offset;
584 	if (clut_offset)
585 		clut_offset += offset;
586 	if (m_curve_offset)
587 		m_curve_offset += offset;
588 	if (matrix_offset)
589 		matrix_offset += offset;
590 	if (b_curve_offset)
591 		b_curve_offset += offset;
592 
593 	if (clut_offset) {
594 		assert (num_in_channels == 3);
595 		// clut_size can not overflow since lg(256^num_in_channels) = 24 bits.
596 		for (i = 0; i < num_in_channels; i++) {
597 			clut_size *= read_u8(src, clut_offset + i);
598 			if (clut_size == 0) {
599 				invalid_source(src, "bad clut_size");
600 			}
601 		}
602 	} else {
603 		clut_size = 0;
604 	}
605 
606 	// 24bits * 3 won't overflow either
607 	clut_size = clut_size * num_out_channels;
608 
609 	if (clut_size > MAX_CLUT_SIZE)
610 		return NULL;
611 
612 	lut = malloc(sizeof(struct lutmABType) + (clut_size) * sizeof(float));
613 	if (!lut)
614 		return NULL;
615 	// we'll fill in the rest below
616 	memset(lut, 0, sizeof(struct lutmABType));
617 	lut->clut_table   = &lut->clut_table_data[0];
618 
619         if (clut_offset) {
620 		for (i = 0; i < num_in_channels; i++) {
621 			lut->num_grid_points[i] = read_u8(src, clut_offset + i);
622 			if (lut->num_grid_points[i] == 0) {
623 				invalid_source(src, "bad grid_points");
624 			}
625 		}
626         }
627 
628 	// Reverse the processing of transformation elements for mBA type.
629 	lut->reversed = (type == LUT_MBA_TYPE);
630 
631 	lut->num_in_channels = num_in_channels;
632 	lut->num_out_channels = num_out_channels;
633 
634 	if (matrix_offset) {
635 		// read the matrix if we have it
636 		lut->e00 = read_s15Fixed16Number(src, matrix_offset+4*0);
637 		lut->e01 = read_s15Fixed16Number(src, matrix_offset+4*1);
638 		lut->e02 = read_s15Fixed16Number(src, matrix_offset+4*2);
639 		lut->e10 = read_s15Fixed16Number(src, matrix_offset+4*3);
640 		lut->e11 = read_s15Fixed16Number(src, matrix_offset+4*4);
641 		lut->e12 = read_s15Fixed16Number(src, matrix_offset+4*5);
642 		lut->e20 = read_s15Fixed16Number(src, matrix_offset+4*6);
643 		lut->e21 = read_s15Fixed16Number(src, matrix_offset+4*7);
644 		lut->e22 = read_s15Fixed16Number(src, matrix_offset+4*8);
645 		lut->e03 = read_s15Fixed16Number(src, matrix_offset+4*9);
646 		lut->e13 = read_s15Fixed16Number(src, matrix_offset+4*10);
647 		lut->e23 = read_s15Fixed16Number(src, matrix_offset+4*11);
648 	}
649 
650 	if (a_curve_offset) {
651 		read_nested_curveType(src, &lut->a_curves, num_in_channels, a_curve_offset);
652 	}
653 	if (m_curve_offset) {
654 		read_nested_curveType(src, &lut->m_curves, num_out_channels, m_curve_offset);
655 	}
656 	if (b_curve_offset) {
657 		read_nested_curveType(src, &lut->b_curves, num_out_channels, b_curve_offset);
658 	} else {
659 		invalid_source(src, "B curves required");
660 	}
661 
662 	if (clut_offset) {
663 		clut_precision = read_u8(src, clut_offset + 16);
664 		if (clut_precision == 1) {
665 			for (i = 0; i < clut_size; i++) {
666 				lut->clut_table[i] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + 20 + i*1));
667 			}
668 		} else if (clut_precision == 2) {
669 			for (i = 0; i < clut_size; i++) {
670 				lut->clut_table[i] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + 20 + i*2));
671 			}
672 		} else {
673 			invalid_source(src, "Invalid clut precision");
674 		}
675 	}
676 
677 	if (!src->valid) {
678 		mAB_release(lut);
679 		return NULL;
680 	}
681 
682 	return lut;
683 }
684 
read_tag_lutType(struct mem_source * src,struct tag_index index,uint32_t tag_id)685 static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index index, uint32_t tag_id)
686 {
687 	struct tag *tag = find_tag(index, tag_id);
688 	uint32_t offset = tag->offset;
689 	uint32_t type = read_u32(src, offset);
690 	uint16_t num_input_table_entries;
691 	uint16_t num_output_table_entries;
692 	uint8_t in_chan, grid_points, out_chan;
693 	uint32_t clut_offset, output_offset;
694 	uint32_t clut_size;
695 	size_t entry_size;
696 	struct lutType *lut;
697 	uint32_t i;
698 
699 	/* I'm not sure why the spec specifies a fixed number of entries for LUT8 tables even though
700 	 * they have room for the num_entries fields */
701 	if (type == LUT8_TYPE) {
702 		num_input_table_entries = 256;
703 		num_output_table_entries = 256;
704 		entry_size = 1;
705 	} else if (type == LUT16_TYPE) {
706 		num_input_table_entries  = read_u16(src, offset + 48);
707 		num_output_table_entries = read_u16(src, offset + 50);
708 		if (num_input_table_entries == 0 || num_output_table_entries == 0) {
709 			invalid_source(src, "Bad channel count");
710 			return NULL;
711 		}
712 		entry_size = 2;
713 	} else {
714 		assert(0); // the caller checks that this doesn't happen
715 		invalid_source(src, "Unexpected lut type");
716 		return NULL;
717 	}
718 
719 	in_chan     = read_u8(src, offset + 8);
720 	out_chan    = read_u8(src, offset + 9);
721 	grid_points = read_u8(src, offset + 10);
722 
723 	clut_size = pow(grid_points, in_chan);
724 	if (clut_size > MAX_CLUT_SIZE) {
725 		invalid_source(src, "CLUT too large");
726 		return NULL;
727 	}
728 
729 	if (clut_size <= 0) {
730 		invalid_source(src, "CLUT must not be empty.");
731 		return NULL;
732 	}
733 
734 	if (in_chan != 3 || out_chan != 3) {
735 		invalid_source(src, "CLUT only supports RGB");
736 		return NULL;
737 	}
738 
739 	lut = malloc(sizeof(struct lutType) + (num_input_table_entries * in_chan + clut_size*out_chan + num_output_table_entries * out_chan)*sizeof(float));
740 	if (!lut) {
741 		invalid_source(src, "CLUT too large");
742 		return NULL;
743 	}
744 
745 	/* compute the offsets of tables */
746 	lut->input_table  = &lut->table_data[0];
747 	lut->clut_table   = &lut->table_data[in_chan*num_input_table_entries];
748 	lut->output_table = &lut->table_data[in_chan*num_input_table_entries + clut_size*out_chan];
749 
750 	lut->num_input_table_entries  = num_input_table_entries;
751 	lut->num_output_table_entries = num_output_table_entries;
752 	lut->num_input_channels   = in_chan;
753 	lut->num_output_channels  = out_chan;
754 	lut->num_clut_grid_points = grid_points;
755 	lut->e00 = read_s15Fixed16Number(src, offset+12);
756 	lut->e01 = read_s15Fixed16Number(src, offset+16);
757 	lut->e02 = read_s15Fixed16Number(src, offset+20);
758 	lut->e10 = read_s15Fixed16Number(src, offset+24);
759 	lut->e11 = read_s15Fixed16Number(src, offset+28);
760 	lut->e12 = read_s15Fixed16Number(src, offset+32);
761 	lut->e20 = read_s15Fixed16Number(src, offset+36);
762 	lut->e21 = read_s15Fixed16Number(src, offset+40);
763 	lut->e22 = read_s15Fixed16Number(src, offset+44);
764 
765 	for (i = 0; i < (uint32_t)(lut->num_input_table_entries * in_chan); i++) {
766 		if (type == LUT8_TYPE) {
767 			lut->input_table[i] = uInt8Number_to_float(read_uInt8Number(src, offset + 52 + i * entry_size));
768 		} else {
769 			lut->input_table[i] = uInt16Number_to_float(read_uInt16Number(src, offset + 52 + i * entry_size));
770 		}
771 	}
772 
773 	clut_offset = offset + 52 + lut->num_input_table_entries * in_chan * entry_size;
774 	for (i = 0; i < clut_size * out_chan; i+=3) {
775 		if (type == LUT8_TYPE) {
776 			lut->clut_table[i+0] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 0));
777 			lut->clut_table[i+1] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 1));
778 			lut->clut_table[i+2] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 2));
779 		} else {
780 			lut->clut_table[i+0] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 0));
781 			lut->clut_table[i+1] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 2));
782 			lut->clut_table[i+2] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 4));
783 		}
784 	}
785 
786 	output_offset = clut_offset + clut_size * out_chan * entry_size;
787 	for (i = 0; i < (uint32_t)(lut->num_output_table_entries * out_chan); i++) {
788 		if (type == LUT8_TYPE) {
789 			lut->output_table[i] = uInt8Number_to_float(read_uInt8Number(src, output_offset + i*entry_size));
790 		} else {
791 			lut->output_table[i] = uInt16Number_to_float(read_uInt16Number(src, output_offset + i*entry_size));
792 		}
793 	}
794 
795 	return lut;
796 }
797 
read_rendering_intent(qcms_profile * profile,struct mem_source * src)798 static void read_rendering_intent(qcms_profile *profile, struct mem_source *src)
799 {
800 	profile->rendering_intent = read_u32(src, 64);
801 	switch (profile->rendering_intent) {
802 		case QCMS_INTENT_PERCEPTUAL:
803 		case QCMS_INTENT_SATURATION:
804 		case QCMS_INTENT_RELATIVE_COLORIMETRIC:
805 		case QCMS_INTENT_ABSOLUTE_COLORIMETRIC:
806 			break;
807 		default:
808 			invalid_source(src, "unknown rendering intent");
809 	}
810 }
811 
qcms_profile_create(void)812 qcms_profile *qcms_profile_create(void)
813 {
814 	return calloc(sizeof(qcms_profile), 1);
815 }
816 
817 
818 
819 /* build sRGB gamma table */
820 /* based on cmsBuildParametricGamma() */
build_sRGB_gamma_table(int num_entries)821 static uint16_t *build_sRGB_gamma_table(int num_entries)
822 {
823 	int i;
824 	/* taken from lcms: Build_sRGBGamma() */
825 	double gamma = 2.4;
826 	double a = 1./1.055;
827 	double b = 0.055/1.055;
828 	double c = 1./12.92;
829 	double d = 0.04045;
830 
831 	uint16_t *table = malloc(sizeof(uint16_t) * num_entries);
832 	if (!table)
833 		return NULL;
834 
835 	for (i=0; i<num_entries; i++) {
836 		double x = (double)i / (num_entries-1);
837 		double y, output;
838 		// IEC 61966-2.1 (sRGB)
839 		// Y = (aX + b)^Gamma | X >= d
840 		// Y = cX             | X < d
841 		if (x >= d) {
842 			double e = (a*x + b);
843 			if (e > 0)
844 				y = pow(e, gamma);
845 			else
846 				y = 0;
847 		} else {
848 			y = c*x;
849 		}
850 
851 		// Saturate -- this could likely move to a separate function
852 		output = y * 65535. + .5;
853 		if (output > 65535.)
854 			output = 65535;
855 		if (output < 0)
856 			output = 0;
857 		table[i] = (uint16_t)floor(output);
858 	}
859 	return table;
860 }
861 
curve_from_table(uint16_t * table,int num_entries)862 static struct curveType *curve_from_table(uint16_t *table, int num_entries)
863 {
864 	struct curveType *curve;
865 	int i;
866 	curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*num_entries);
867 	if (!curve)
868 		return NULL;
869 	curve->type = CURVE_TYPE;
870 	curve->count = num_entries;
871 	for (i = 0; i < num_entries; i++) {
872 		curve->data[i] = table[i];
873 	}
874 	return curve;
875 }
876 
float_to_u8Fixed8Number(float a)877 static uint16_t float_to_u8Fixed8Number(float a)
878 {
879 	if (a > (255.f + 255.f/256))
880 		return 0xffff;
881 	else if (a < 0.f)
882 		return 0;
883 	else
884 		return floorf(a*256.f + .5f);
885 }
886 
curve_from_gamma(float gamma)887 static struct curveType *curve_from_gamma(float gamma)
888 {
889 	struct curveType *curve;
890 	int num_entries = 1;
891 	curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*num_entries);
892 	if (!curve)
893 		return NULL;
894 	curve->count = num_entries;
895 	curve->data[0] = float_to_u8Fixed8Number(gamma);
896   	curve->type = CURVE_TYPE;
897 	return curve;
898 }
899 
900 //XXX: it would be nice if we had a way of ensuring
901 // everything in a profile was initialized regardless of how it was created
902 
903 //XXX: should this also be taking a black_point?
904 /* similar to CGColorSpaceCreateCalibratedRGB */
qcms_profile_create_rgb_with_gamma(qcms_CIE_xyY white_point,qcms_CIE_xyYTRIPLE primaries,float gamma)905 qcms_profile* qcms_profile_create_rgb_with_gamma(
906 		qcms_CIE_xyY white_point,
907 		qcms_CIE_xyYTRIPLE primaries,
908 		float gamma)
909 {
910 	qcms_profile* profile = qcms_profile_create();
911 	if (!profile)
912 		return NO_MEM_PROFILE;
913 
914 	//XXX: should store the whitepoint
915 	if (!set_rgb_colorants(profile, white_point, primaries)) {
916 		qcms_profile_release(profile);
917 		return INVALID_PROFILE;
918 	}
919 
920 	profile->redTRC = curve_from_gamma(gamma);
921 	profile->blueTRC = curve_from_gamma(gamma);
922 	profile->greenTRC = curve_from_gamma(gamma);
923 
924 	if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC) {
925 		qcms_profile_release(profile);
926 		return NO_MEM_PROFILE;
927 	}
928 	profile->class = DISPLAY_DEVICE_PROFILE;
929 	profile->rendering_intent = QCMS_INTENT_PERCEPTUAL;
930 	profile->color_space = RGB_SIGNATURE;
931 	return profile;
932 }
933 
qcms_profile_create_rgb_with_table(qcms_CIE_xyY white_point,qcms_CIE_xyYTRIPLE primaries,uint16_t * table,int num_entries)934 qcms_profile* qcms_profile_create_rgb_with_table(
935 		qcms_CIE_xyY white_point,
936 		qcms_CIE_xyYTRIPLE primaries,
937 		uint16_t *table, int num_entries)
938 {
939 	qcms_profile* profile = qcms_profile_create();
940 	if (!profile)
941 		return NO_MEM_PROFILE;
942 
943 	//XXX: should store the whitepoint
944 	if (!set_rgb_colorants(profile, white_point, primaries)) {
945 		qcms_profile_release(profile);
946 		return INVALID_PROFILE;
947 	}
948 
949 	profile->redTRC = curve_from_table(table, num_entries);
950 	profile->blueTRC = curve_from_table(table, num_entries);
951 	profile->greenTRC = curve_from_table(table, num_entries);
952 
953 	if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC) {
954 		qcms_profile_release(profile);
955 		return NO_MEM_PROFILE;
956 	}
957 	profile->class = DISPLAY_DEVICE_PROFILE;
958 	profile->rendering_intent = QCMS_INTENT_PERCEPTUAL;
959 	profile->color_space = RGB_SIGNATURE;
960 	return profile;
961 }
962 
963 /* from lcms: cmsWhitePointFromTemp */
964 /* tempK must be >= 4000. and <= 25000.
965  * Invalid values of tempK will return
966  * (x,y,Y) = (-1.0, -1.0, -1.0)
967  * similar to argyll: icx_DTEMP2XYZ() */
white_point_from_temp(int temp_K)968 static qcms_CIE_xyY white_point_from_temp(int temp_K)
969 {
970 	qcms_CIE_xyY white_point;
971 	double x, y;
972 	double T, T2, T3;
973 	// double M1, M2;
974 
975 	// No optimization provided.
976 	T = temp_K;
977 	T2 = T*T;            // Square
978 	T3 = T2*T;           // Cube
979 
980 	// For correlated color temperature (T) between 4000K and 7000K:
981 	if (T >= 4000. && T <= 7000.) {
982 		x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063;
983 	} else {
984 		// or for correlated color temperature (T) between 7000K and 25000K:
985 		if (T > 7000.0 && T <= 25000.0) {
986 			x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040;
987 		} else {
988 			// Invalid tempK
989 			white_point.x = -1.0;
990 			white_point.y = -1.0;
991 			white_point.Y = -1.0;
992 
993 			assert(0 && "invalid temp");
994 
995 			return white_point;
996 		}
997 	}
998 
999 	// Obtain y(x)
1000 
1001 	y = -3.000*(x*x) + 2.870*x - 0.275;
1002 
1003 	// wave factors (not used, but here for futures extensions)
1004 
1005 	// M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y);
1006 	// M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y);
1007 
1008 	// Fill white_point struct
1009 	white_point.x = x;
1010 	white_point.y = y;
1011 	white_point.Y = 1.0;
1012 
1013 	return white_point;
1014 }
1015 
qcms_profile_sRGB(void)1016 qcms_profile* qcms_profile_sRGB(void)
1017 {
1018 	qcms_profile *profile;
1019 	uint16_t *table;
1020 
1021 	qcms_CIE_xyYTRIPLE Rec709Primaries = {
1022 		{0.6400, 0.3300, 1.0},
1023 		{0.3000, 0.6000, 1.0},
1024 		{0.1500, 0.0600, 1.0}
1025 	};
1026 	qcms_CIE_xyY D65;
1027 
1028 	D65 = white_point_from_temp(6504);
1029 
1030 	table = build_sRGB_gamma_table(1024);
1031 
1032 	if (!table)
1033 		return NO_MEM_PROFILE;
1034 
1035 	profile = qcms_profile_create_rgb_with_table(D65, Rec709Primaries, table, 1024);
1036 	free(table);
1037 	return profile;
1038 }
1039 
1040 
1041 /* qcms_profile_from_memory does not hold a reference to the memory passed in */
qcms_profile_from_memory(const void * mem,size_t size)1042 qcms_profile* qcms_profile_from_memory(const void *mem, size_t size)
1043 {
1044 	uint32_t length;
1045 	struct mem_source source;
1046 	struct mem_source *src = &source;
1047 	struct tag_index index;
1048 	qcms_profile *profile;
1049 
1050 	source.buf = mem;
1051 	source.size = size;
1052 	source.valid = true;
1053 
1054 	if (size < 4)
1055 		return INVALID_PROFILE;
1056 
1057 	length = read_u32(src, 0);
1058 	if (length <= size) {
1059 		// shrink the area that we can read if appropriate
1060 		source.size = length;
1061 	} else {
1062 		return INVALID_PROFILE;
1063 	}
1064 
1065 	/* ensure that the profile size is sane so it's easier to reason about */
1066 	if (source.size <= 64 || source.size >= MAX_PROFILE_SIZE)
1067 		return INVALID_PROFILE;
1068 
1069 	profile = qcms_profile_create();
1070 	if (!profile)
1071 		return NO_MEM_PROFILE;
1072 
1073 	check_CMM_type_signature(src);
1074 	check_profile_version(src);
1075 	read_class_signature(profile, src);
1076 	read_rendering_intent(profile, src);
1077 	read_color_space(profile, src);
1078 	read_pcs(profile, src);
1079 	//TODO read rest of profile stuff
1080 
1081 	if (!src->valid)
1082 		goto invalid_profile;
1083 
1084 	index = read_tag_table(profile, src);
1085 	if (!src->valid || !index.tags)
1086 		goto invalid_tag_table;
1087 
1088 	if (find_tag(index, TAG_CHAD)) {
1089 		profile->chromaticAdaption = read_tag_s15Fixed16ArrayType(src, index, TAG_CHAD);
1090 	} else {
1091 		profile->chromaticAdaption.invalid = true; //Signal the data is not present
1092 	}
1093 
1094 	if (profile->class == DISPLAY_DEVICE_PROFILE || profile->class == INPUT_DEVICE_PROFILE ||
1095             profile->class == OUTPUT_DEVICE_PROFILE  || profile->class == COLOR_SPACE_PROFILE) {
1096 		if (profile->color_space == RGB_SIGNATURE) {
1097 			if (find_tag(index, TAG_A2B0)) {
1098 				if (read_u32(src, find_tag(index, TAG_A2B0)->offset) == LUT8_TYPE ||
1099 				    read_u32(src, find_tag(index, TAG_A2B0)->offset) == LUT16_TYPE) {
1100 					profile->A2B0 = read_tag_lutType(src, index, TAG_A2B0);
1101 				} else if (read_u32(src, find_tag(index, TAG_A2B0)->offset) == LUT_MAB_TYPE) {
1102 					profile->mAB = read_tag_lutmABType(src, index, TAG_A2B0);
1103 				}
1104 			}
1105 			if (find_tag(index, TAG_B2A0)) {
1106 				if (read_u32(src, find_tag(index, TAG_B2A0)->offset) == LUT8_TYPE ||
1107 				    read_u32(src, find_tag(index, TAG_B2A0)->offset) == LUT16_TYPE) {
1108 					profile->B2A0 = read_tag_lutType(src, index, TAG_B2A0);
1109 				} else if (read_u32(src, find_tag(index, TAG_B2A0)->offset) == LUT_MBA_TYPE) {
1110 					profile->mBA = read_tag_lutmABType(src, index, TAG_B2A0);
1111 				}
1112 			}
1113 			if (find_tag(index, TAG_rXYZ) || !qcms_supports_iccv4) {
1114 				profile->redColorant = read_tag_XYZType(src, index, TAG_rXYZ);
1115 				profile->greenColorant = read_tag_XYZType(src, index, TAG_gXYZ);
1116 				profile->blueColorant = read_tag_XYZType(src, index, TAG_bXYZ);
1117 			}
1118 
1119 			if (!src->valid)
1120 				goto invalid_tag_table;
1121 
1122 			if (find_tag(index, TAG_rTRC) || !qcms_supports_iccv4) {
1123 				profile->redTRC = read_tag_curveType(src, index, TAG_rTRC);
1124 				profile->greenTRC = read_tag_curveType(src, index, TAG_gTRC);
1125 				profile->blueTRC = read_tag_curveType(src, index, TAG_bTRC);
1126 
1127 				if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC)
1128 					goto invalid_tag_table;
1129 			}
1130 		} else if (profile->color_space == GRAY_SIGNATURE) {
1131 
1132 			profile->grayTRC = read_tag_curveType(src, index, TAG_kTRC);
1133 			if (!profile->grayTRC)
1134 				goto invalid_tag_table;
1135 
1136 		} else {
1137 			assert(0 && "read_color_space protects against entering here");
1138 			goto invalid_tag_table;
1139 		}
1140 	} else {
1141 		goto invalid_tag_table;
1142 	}
1143 
1144 	if (!src->valid)
1145 		goto invalid_tag_table;
1146 
1147 	free(index.tags);
1148 
1149 	return profile;
1150 
1151 invalid_tag_table:
1152 	free(index.tags);
1153 invalid_profile:
1154 	qcms_profile_release(profile);
1155 	return INVALID_PROFILE;
1156 }
1157 
qcms_profile_get_rendering_intent(qcms_profile * profile)1158 qcms_intent qcms_profile_get_rendering_intent(qcms_profile *profile)
1159 {
1160 	return profile->rendering_intent;
1161 }
1162 
1163 icColorSpaceSignature
qcms_profile_get_color_space(qcms_profile * profile)1164 qcms_profile_get_color_space(qcms_profile *profile)
1165 {
1166 	return profile->color_space;
1167 }
1168 
lut_release(struct lutType * lut)1169 static void lut_release(struct lutType *lut)
1170 {
1171 	free(lut);
1172 }
1173 
qcms_profile_release(qcms_profile * profile)1174 void qcms_profile_release(qcms_profile *profile)
1175 {
1176 	if (profile->output_table_r)
1177 		precache_release(profile->output_table_r);
1178 	if (profile->output_table_g)
1179 		precache_release(profile->output_table_g);
1180 	if (profile->output_table_b)
1181 		precache_release(profile->output_table_b);
1182 
1183 	if (profile->A2B0)
1184 		lut_release(profile->A2B0);
1185 	if (profile->B2A0)
1186 		lut_release(profile->B2A0);
1187 
1188 	if (profile->mAB)
1189 		mAB_release(profile->mAB);
1190 	if (profile->mBA)
1191 		mAB_release(profile->mBA);
1192 
1193 	free(profile->redTRC);
1194 	free(profile->blueTRC);
1195 	free(profile->greenTRC);
1196 	free(profile->grayTRC);
1197 	free(profile);
1198 }
1199 
1200 
1201 #include <stdio.h>
qcms_data_from_file(FILE * file,void ** mem,size_t * size)1202 static void qcms_data_from_file(FILE *file, void **mem, size_t *size)
1203 {
1204 	uint32_t length, remaining_length;
1205 	size_t read_length;
1206 	be32 length_be;
1207 	void *data;
1208 
1209 	*mem = NULL;
1210 	*size = 0;
1211 
1212 	if (fread(&length_be, 1, sizeof(length_be), file) != sizeof(length_be))
1213 		return;
1214 
1215 	length = be32_to_cpu(length_be);
1216 	if (length > MAX_PROFILE_SIZE || length < sizeof(length_be))
1217 		return;
1218 
1219 	/* allocate room for the entire profile */
1220 	data = malloc(length);
1221 	if (!data)
1222 		return;
1223 
1224 	/* copy in length to the front so that the buffer will contain the entire profile */
1225 	*((be32*)data) = length_be;
1226 	remaining_length = length - sizeof(length_be);
1227 
1228 	/* read the rest profile */
1229 	read_length = fread((unsigned char*)data + sizeof(length_be), 1, remaining_length, file);
1230 	if (read_length != remaining_length) {
1231 		free(data);
1232 		return;
1233 	}
1234 
1235 	/* successfully get the profile.*/
1236 	*mem = data;
1237 	*size = length;
1238 }
1239 
qcms_profile_from_file(FILE * file)1240 qcms_profile* qcms_profile_from_file(FILE *file)
1241 {
1242 	size_t length;
1243 	qcms_profile *profile;
1244 	void *data;
1245 
1246 	qcms_data_from_file(file, &data, &length);
1247 	if ((data == NULL) || (length == 0))
1248 		return INVALID_PROFILE;
1249 
1250 	profile = qcms_profile_from_memory(data, length);
1251 	free(data);
1252 	return profile;
1253 }
1254 
qcms_profile_from_path(const char * path)1255 qcms_profile* qcms_profile_from_path(const char *path)
1256 {
1257 	qcms_profile *profile = NULL;
1258 	FILE *file = fopen(path, "rb");
1259 	if (file) {
1260 		profile = qcms_profile_from_file(file);
1261 		fclose(file);
1262 	}
1263 	return profile;
1264 }
1265 
qcms_data_from_path(const char * path,void ** mem,size_t * size)1266 void qcms_data_from_path(const char *path, void **mem, size_t *size)
1267 {
1268 	FILE *file = NULL;
1269 	*mem = NULL;
1270 	*size  = 0;
1271 
1272 	file = fopen(path, "rb");
1273 	if (file) {
1274 		qcms_data_from_file(file, mem, size);
1275 		fclose(file);
1276 	}
1277 }
1278 
1279 #ifdef _WIN32
1280 /* Unicode path version */
qcms_profile_from_unicode_path(const wchar_t * path)1281 qcms_profile* qcms_profile_from_unicode_path(const wchar_t *path)
1282 {
1283 	qcms_profile *profile = NULL;
1284 	FILE *file = _wfopen(path, L"rb");
1285 	if (file) {
1286 		profile = qcms_profile_from_file(file);
1287 		fclose(file);
1288 	}
1289 	return profile;
1290 }
1291 
qcms_data_from_unicode_path(const wchar_t * path,void ** mem,size_t * size)1292 void qcms_data_from_unicode_path(const wchar_t *path, void **mem, size_t *size)
1293 {
1294 	FILE *file = NULL;
1295 	*mem = NULL;
1296 	*size  = 0;
1297 
1298 	file = _wfopen(path, L"rb");
1299 	if (file) {
1300 		qcms_data_from_file(file, mem, size);
1301 		fclose(file);
1302 	}
1303 }
1304 #endif
1305 
1306 /*
1307 * This function constructs an ICC profile memory with given header and tag data,
1308 * which can be read via qcms_profile_from_memory(). that means, we must satisfy
1309 * the profiler header type check (which seems not complete till now) and proper
1310 * information to read data from the tag table and tag data elements memory.
1311 *
1312 * To construct a valid ICC profile, its divided into three steps :
1313 *	(1) construct the r/g/bXYZ part
1314 *	(2) construct the r/g/bTRC part
1315 *	(3) construct the profile header
1316 * this is a hardcode step just for "create_rgb_with_gamma", it is the only
1317 * requirement till now, maybe we can make this method more general in future,
1318 *
1319 * NOTE : some of the parameters below are hardcode, please refer to the ICC documentation.
1320 */
1321 #define ICC_PROFILE_HEADER_LENGTH 128
qcms_data_create_rgb_with_gamma(qcms_CIE_xyY white_point,qcms_CIE_xyYTRIPLE primaries,float gamma,void ** mem,size_t * size)1322 void qcms_data_create_rgb_with_gamma(qcms_CIE_xyY white_point, qcms_CIE_xyYTRIPLE primaries, float gamma, void **mem, size_t *size)
1323 {
1324 	uint32_t length, index, xyz_count, trc_count;
1325 	size_t tag_table_offset, tag_data_offset;
1326 	void *data;
1327 	struct matrix colorants;
1328 
1329 	uint32_t TAG_XYZ[3] = {TAG_rXYZ, TAG_gXYZ, TAG_bXYZ};
1330 	uint32_t TAG_TRC[3] = {TAG_rTRC, TAG_gTRC, TAG_bTRC};
1331 
1332 	if ((mem == NULL) || (size == NULL))
1333 		return;
1334 
1335 	*mem = NULL;
1336 	*size = 0;
1337 
1338 	/*
1339 	* total length = icc profile header(128) + tag count(4) +
1340 	* (tag table item (12) * total tag (6 = 3 rTRC + 3 rXYZ)) + rTRC elements data (3 * 20)
1341 	* + rXYZ elements data (3*16), and all tag data elements must start at the 4-byte boundary.
1342 	*/
1343 	xyz_count = 3; // rXYZ, gXYZ, bXYZ
1344 	trc_count = 3; // rTRC, gTRC, bTRC
1345 	length =  ICC_PROFILE_HEADER_LENGTH + 4 + (12 * (xyz_count + trc_count)) + (xyz_count * 20) + (trc_count * 16);
1346 
1347 	// reserve the total memory.
1348 	data = malloc(length);
1349 	if (!data)
1350 		return;
1351 	memset(data, 0, length);
1352 
1353 	// Part1 : write rXYZ, gXYZ and bXYZ
1354 	if (!get_rgb_colorants(&colorants, white_point, primaries)) {
1355 		free(data);
1356 		return;
1357 	}
1358 
1359 	 // the position of first tag's signature in tag table
1360 	tag_table_offset = ICC_PROFILE_HEADER_LENGTH + 4;
1361 	tag_data_offset = ICC_PROFILE_HEADER_LENGTH + 4 +
1362 	   (12 * (xyz_count + trc_count)); // the start of tag data elements.
1363 
1364 	for (index = 0; index < xyz_count; ++index) {
1365 		// tag table
1366 		write_u32(data, tag_table_offset, TAG_XYZ[index]);
1367 		write_u32(data, tag_table_offset+4, tag_data_offset);
1368 		write_u32(data, tag_table_offset+8, 20); // 20 bytes per TAG_(r/g/b)XYZ tag element
1369 
1370 		// tag data element
1371 		write_u32(data, tag_data_offset, XYZ_TYPE);
1372 		// reserved 4 bytes.
1373 		write_u32(data, tag_data_offset+8, double_to_s15Fixed16Number(colorants.m[0][index]));
1374 		write_u32(data, tag_data_offset+12, double_to_s15Fixed16Number(colorants.m[1][index]));
1375 		write_u32(data, tag_data_offset+16, double_to_s15Fixed16Number(colorants.m[2][index]));
1376 
1377 		tag_table_offset += 12;
1378 		tag_data_offset += 20;
1379 	}
1380 
1381 	// Part2 : write rTRC, gTRC and bTRC
1382 	for (index = 0; index < trc_count; ++index) {
1383 		// tag table
1384 		write_u32(data, tag_table_offset, TAG_TRC[index]);
1385 		write_u32(data, tag_table_offset+4, tag_data_offset);
1386 		write_u32(data, tag_table_offset+8, 14); // 14 bytes per TAG_(r/g/b)TRC element
1387 
1388 		// tag data element
1389 		write_u32(data, tag_data_offset, CURVE_TYPE);
1390 		// reserved 4 bytes.
1391 		write_u32(data, tag_data_offset+8, 1); // count
1392 		write_u16(data, tag_data_offset+12, float_to_u8Fixed8Number(gamma));
1393 
1394 		tag_table_offset += 12;
1395 		tag_data_offset += 16;
1396 	}
1397 
1398 	/* Part3 : write profile header
1399 	 *
1400 	 * Important header fields are left empty. This generates a profile for internal use only.
1401 	 * We should be generating: Profile version (04300000h), Profile signature (acsp),
1402 	 * PCS illumiant field. Likewise mandatory profile tags are omitted.
1403 	 */
1404 	write_u32(data, 0, length); // the total length of this memory
1405 	write_u32(data, 12, DISPLAY_DEVICE_PROFILE); // profile->class
1406 	write_u32(data, 16, RGB_SIGNATURE); // profile->color_space
1407 	write_u32(data, 20, XYZ_SIGNATURE); // profile->pcs
1408 	write_u32(data, 64, QCMS_INTENT_PERCEPTUAL); // profile->rendering_intent
1409 
1410 	write_u32(data, ICC_PROFILE_HEADER_LENGTH, 6); // total tag count
1411 
1412 	// prepare the result
1413 	*mem = data;
1414 	*size = length;
1415 }
1416