OLD | NEW |
| (Empty) |
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 Softwar
e | |
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 | |
37 #if 0 | |
38 not used yet | |
39 /* __builtin_bswap isn't available in older gccs | |
40 * so open code it for now */ | |
41 static be32 cpu_to_be32(int32_t v) | |
42 { | |
43 #ifdef IS_LITTLE_ENDIAN | |
44 return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8)
| ((v & 0xff000000) >> 24); | |
45 //return __builtin_bswap32(v); | |
46 return v; | |
47 #endif | |
48 } | |
49 #endif | |
50 | |
51 static uint32_t be32_to_cpu(be32 v) | |
52 { | |
53 #ifdef IS_LITTLE_ENDIAN | |
54 return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8)
| ((v & 0xff000000) >> 24); | |
55 //return __builtin_bswap32(v); | |
56 #else | |
57 return v; | |
58 #endif | |
59 } | |
60 | |
61 static uint16_t be16_to_cpu(be16 v) | |
62 { | |
63 #ifdef IS_LITTLE_ENDIAN | |
64 return ((v & 0xff) << 8) | ((v & 0xff00) >> 8); | |
65 #else | |
66 return v; | |
67 #endif | |
68 } | |
69 | |
70 /* a wrapper around the memory that we are going to parse | |
71 * into a qcms_profile */ | |
72 struct mem_source | |
73 { | |
74 const unsigned char *buf; | |
75 size_t size; | |
76 qcms_bool valid; | |
77 const char *invalid_reason; | |
78 }; | |
79 | |
80 static void invalid_source(struct mem_source *mem, const char *reason) | |
81 { | |
82 mem->valid = false; | |
83 mem->invalid_reason = reason; | |
84 } | |
85 | |
86 static uint32_t read_u32(struct mem_source *mem, size_t offset) | |
87 { | |
88 /* Subtract from mem->size instead of the more intuitive adding to offse
t. | |
89 * This avoids overflowing offset. The subtraction is safe because | |
90 * mem->size is guaranteed to be > 4 */ | |
91 if (offset > mem->size - 4) { | |
92 invalid_source(mem, "Invalid offset"); | |
93 return 0; | |
94 } else { | |
95 be32 k; | |
96 memcpy(&k, mem->buf + offset, sizeof(k)); | |
97 return be32_to_cpu(k); | |
98 } | |
99 } | |
100 | |
101 static uint16_t read_u16(struct mem_source *mem, size_t offset) | |
102 { | |
103 if (offset > mem->size - 2) { | |
104 invalid_source(mem, "Invalid offset"); | |
105 return 0; | |
106 } else { | |
107 be16 k; | |
108 memcpy(&k, mem->buf + offset, sizeof(k)); | |
109 return be16_to_cpu(k); | |
110 } | |
111 } | |
112 | |
113 static uint8_t read_u8(struct mem_source *mem, size_t offset) | |
114 { | |
115 if (offset > mem->size - 1) { | |
116 invalid_source(mem, "Invalid offset"); | |
117 return 0; | |
118 } else { | |
119 return *(uint8_t*)(mem->buf + offset); | |
120 } | |
121 } | |
122 | |
123 static s15Fixed16Number read_s15Fixed16Number(struct mem_source *mem, size_t off
set) | |
124 { | |
125 return read_u32(mem, offset); | |
126 } | |
127 | |
128 static uInt8Number read_uInt8Number(struct mem_source *mem, size_t offset) | |
129 { | |
130 return read_u8(mem, offset); | |
131 } | |
132 | |
133 static uInt16Number read_uInt16Number(struct mem_source *mem, size_t offset) | |
134 { | |
135 return read_u16(mem, offset); | |
136 } | |
137 | |
138 #define BAD_VALUE_PROFILE NULL | |
139 #define INVALID_PROFILE NULL | |
140 #define NO_MEM_PROFILE NULL | |
141 | |
142 /* An arbitrary 4MB limit on profile size */ | |
143 #define MAX_PROFILE_SIZE 1024*1024*4 | |
144 #define MAX_TAG_COUNT 1024 | |
145 | |
146 static void check_CMM_type_signature(struct mem_source *src) | |
147 { | |
148 //uint32_t CMM_type_signature = read_u32(src, 4); | |
149 //TODO: do the check? | |
150 | |
151 } | |
152 | |
153 static void check_profile_version(struct mem_source *src) | |
154 { | |
155 | |
156 /* | |
157 uint8_t major_revision = read_u8(src, 8 + 0); | |
158 uint8_t minor_revision = read_u8(src, 8 + 1); | |
159 */ | |
160 uint8_t reserved1 = read_u8(src, 8 + 2); | |
161 uint8_t reserved2 = read_u8(src, 8 + 3); | |
162 /* Checking the version doesn't buy us anything | |
163 if (major_revision != 0x4) { | |
164 if (major_revision > 0x2) | |
165 invalid_source(src, "Unsupported major revision"); | |
166 if (minor_revision > 0x40) | |
167 invalid_source(src, "Unsupported minor revision"); | |
168 } | |
169 */ | |
170 if (reserved1 != 0 || reserved2 != 0) | |
171 invalid_source(src, "Invalid reserved bytes"); | |
172 } | |
173 | |
174 #define INPUT_DEVICE_PROFILE 0x73636e72 // 'scnr' | |
175 #define DISPLAY_DEVICE_PROFILE 0x6d6e7472 // 'mntr' | |
176 #define OUTPUT_DEVICE_PROFILE 0x70727472 // 'prtr' | |
177 #define DEVICE_LINK_PROFILE 0x6c696e6b // 'link' | |
178 #define COLOR_SPACE_PROFILE 0x73706163 // 'spac' | |
179 #define ABSTRACT_PROFILE 0x61627374 // 'abst' | |
180 #define NAMED_COLOR_PROFILE 0x6e6d636c // 'nmcl' | |
181 | |
182 static void read_class_signature(qcms_profile *profile, struct mem_source *mem) | |
183 { | |
184 profile->class = read_u32(mem, 12); | |
185 switch (profile->class) { | |
186 case DISPLAY_DEVICE_PROFILE: | |
187 case INPUT_DEVICE_PROFILE: | |
188 case OUTPUT_DEVICE_PROFILE: | |
189 case COLOR_SPACE_PROFILE: | |
190 break; | |
191 default: | |
192 invalid_source(mem, "Invalid Profile/Device Class signa
ture"); | |
193 } | |
194 } | |
195 | |
196 static void read_color_space(qcms_profile *profile, struct mem_source *mem) | |
197 { | |
198 profile->color_space = read_u32(mem, 16); | |
199 switch (profile->color_space) { | |
200 case RGB_SIGNATURE: | |
201 case GRAY_SIGNATURE: | |
202 break; | |
203 default: | |
204 invalid_source(mem, "Unsupported colorspace"); | |
205 } | |
206 } | |
207 | |
208 static void read_pcs(qcms_profile *profile, struct mem_source *mem) | |
209 { | |
210 profile->pcs = read_u32(mem, 20); | |
211 switch (profile->pcs) { | |
212 case XYZ_SIGNATURE: | |
213 case LAB_SIGNATURE: | |
214 break; | |
215 default: | |
216 invalid_source(mem, "Unsupported pcs"); | |
217 } | |
218 } | |
219 | |
220 struct tag | |
221 { | |
222 uint32_t signature; | |
223 uint32_t offset; | |
224 uint32_t size; | |
225 }; | |
226 | |
227 struct tag_index { | |
228 uint32_t count; | |
229 struct tag *tags; | |
230 }; | |
231 | |
232 static struct tag_index read_tag_table(qcms_profile *profile, struct mem_source
*mem) | |
233 { | |
234 struct tag_index index = {0, NULL}; | |
235 unsigned int i; | |
236 | |
237 index.count = read_u32(mem, 128); | |
238 if (index.count > MAX_TAG_COUNT) { | |
239 invalid_source(mem, "max number of tags exceeded"); | |
240 return index; | |
241 } | |
242 | |
243 index.tags = malloc(sizeof(struct tag)*index.count); | |
244 if (index.tags) { | |
245 for (i = 0; i < index.count; i++) { | |
246 index.tags[i].signature = read_u32(mem, 128 + 4 + 4*i*3)
; | |
247 index.tags[i].offset = read_u32(mem, 128 + 4 + 4*i*3
+ 4); | |
248 index.tags[i].size = read_u32(mem, 128 + 4 + 4*i*3
+ 8); | |
249 } | |
250 } | |
251 | |
252 return index; | |
253 } | |
254 | |
255 // Checks a profile for obvious inconsistencies and returns | |
256 // true if the profile looks bogus and should probably be | |
257 // ignored. | |
258 qcms_bool qcms_profile_is_bogus(qcms_profile *profile) | |
259 { | |
260 float sum[3], target[3], tolerance[3]; | |
261 float rX, rY, rZ, gX, gY, gZ, bX, bY, bZ; | |
262 bool negative; | |
263 unsigned i; | |
264 | |
265 // We currently only check the bogosity of RGB profiles | |
266 if (profile->color_space != RGB_SIGNATURE) | |
267 return false; | |
268 | |
269 if (profile->A2B0 || profile->B2A0) | |
270 return false; | |
271 | |
272 rX = s15Fixed16Number_to_float(profile->redColorant.X); | |
273 rY = s15Fixed16Number_to_float(profile->redColorant.Y); | |
274 rZ = s15Fixed16Number_to_float(profile->redColorant.Z); | |
275 | |
276 gX = s15Fixed16Number_to_float(profile->greenColorant.X); | |
277 gY = s15Fixed16Number_to_float(profile->greenColorant.Y); | |
278 gZ = s15Fixed16Number_to_float(profile->greenColorant.Z); | |
279 | |
280 bX = s15Fixed16Number_to_float(profile->blueColorant.X); | |
281 bY = s15Fixed16Number_to_float(profile->blueColorant.Y); | |
282 bZ = s15Fixed16Number_to_float(profile->blueColorant.Z); | |
283 | |
284 // Check if any of the XYZ values are negative (see mozilla bug 498245) | |
285 // CIEXYZ tristimulus values cannot be negative according to the spec. | |
286 negative = | |
287 (rX < 0) || (rY < 0) || (rZ < 0) || | |
288 (gX < 0) || (gY < 0) || (gZ < 0) || | |
289 (bX < 0) || (bY < 0) || (bZ < 0); | |
290 | |
291 if (negative) | |
292 return true; | |
293 | |
294 | |
295 // Sum the values; they should add up to something close to white | |
296 sum[0] = rX + gX + bX; | |
297 sum[1] = rY + gY + bY; | |
298 sum[2] = rZ + gZ + bZ; | |
299 | |
300 // Build our target vector (see mozilla bug 460629) | |
301 target[0] = 0.96420; | |
302 target[1] = 1.00000; | |
303 target[2] = 0.82491; | |
304 | |
305 // Our tolerance vector - Recommended by Chris Murphy based on | |
306 // conversion from the LAB space criterion of no more than 3 in any one | |
307 // channel. This is similar to, but slightly more tolerant than Adobe's | |
308 // criterion. | |
309 tolerance[0] = 0.02; | |
310 tolerance[1] = 0.02; | |
311 tolerance[2] = 0.04; | |
312 | |
313 // Compare with our tolerance | |
314 for (i = 0; i < 3; ++i) { | |
315 if (!(((sum[i] - tolerance[i]) <= target[i]) && | |
316 ((sum[i] + tolerance[i]) >= target[i]))) | |
317 return true; | |
318 } | |
319 | |
320 // All Good | |
321 return false; | |
322 } | |
323 | |
324 #define TAG_bXYZ 0x6258595a | |
325 #define TAG_gXYZ 0x6758595a | |
326 #define TAG_rXYZ 0x7258595a | |
327 #define TAG_rTRC 0x72545243 | |
328 #define TAG_bTRC 0x62545243 | |
329 #define TAG_gTRC 0x67545243 | |
330 #define TAG_kTRC 0x6b545243 | |
331 #define TAG_A2B0 0x41324230 | |
332 #define TAG_B2A0 0x42324130 | |
333 #define TAG_CHAD 0x63686164 | |
334 | |
335 static struct tag *find_tag(struct tag_index index, uint32_t tag_id) | |
336 { | |
337 unsigned int i; | |
338 struct tag *tag = NULL; | |
339 for (i = 0; i < index.count; i++) { | |
340 if (index.tags[i].signature == tag_id) { | |
341 return &index.tags[i]; | |
342 } | |
343 } | |
344 return tag; | |
345 } | |
346 | |
347 #define XYZ_TYPE 0x58595a20 // 'XYZ ' | |
348 #define CURVE_TYPE 0x63757276 // 'curv' | |
349 #define PARAMETRIC_CURVE_TYPE 0x70617261 // 'para' | |
350 #define LUT16_TYPE 0x6d667432 // 'mft2' | |
351 #define LUT8_TYPE 0x6d667431 // 'mft1' | |
352 #define LUT_MAB_TYPE 0x6d414220 // 'mAB ' | |
353 #define LUT_MBA_TYPE 0x6d424120 // 'mBA ' | |
354 #define CHROMATIC_TYPE 0x73663332 // 'sf32' | |
355 | |
356 static struct matrix read_tag_s15Fixed16ArrayType(struct mem_source *src, struct
tag_index index, uint32_t tag_id) | |
357 { | |
358 struct tag *tag = find_tag(index, tag_id); | |
359 struct matrix matrix; | |
360 if (tag) { | |
361 uint8_t i; | |
362 uint32_t offset = tag->offset; | |
363 uint32_t type = read_u32(src, offset); | |
364 | |
365 // Check mandatory type signature for s16Fixed16ArrayType | |
366 if (type != CHROMATIC_TYPE) { | |
367 invalid_source(src, "unexpected type, expected 'sf32'"); | |
368 } | |
369 | |
370 for (i = 0; i < 9; i++) { | |
371 matrix.m[i/3][i%3] = s15Fixed16Number_to_float(read_s15F
ixed16Number(src, offset+8+i*4)); | |
372 } | |
373 matrix.invalid = false; | |
374 } else { | |
375 matrix.invalid = true; | |
376 invalid_source(src, "missing sf32tag"); | |
377 } | |
378 return matrix; | |
379 } | |
380 | |
381 static struct XYZNumber read_tag_XYZType(struct mem_source *src, struct tag_inde
x index, uint32_t tag_id) | |
382 { | |
383 struct XYZNumber num = {0, 0, 0}; | |
384 struct tag *tag = find_tag(index, tag_id); | |
385 if (tag) { | |
386 uint32_t offset = tag->offset; | |
387 | |
388 uint32_t type = read_u32(src, offset); | |
389 if (type != XYZ_TYPE) | |
390 invalid_source(src, "unexpected type, expected XYZ"); | |
391 num.X = read_s15Fixed16Number(src, offset+8); | |
392 num.Y = read_s15Fixed16Number(src, offset+12); | |
393 num.Z = read_s15Fixed16Number(src, offset+16); | |
394 } else { | |
395 invalid_source(src, "missing xyztag"); | |
396 } | |
397 return num; | |
398 } | |
399 | |
400 // Read the tag at a given offset rather then the tag_index. | |
401 // This method is used when reading mAB tags where nested curveType are | |
402 // present that are not part of the tag_index. | |
403 static struct curveType *read_curveType(struct mem_source *src, uint32_t offset,
uint32_t *len) | |
404 { | |
405 static const size_t COUNT_TO_LENGTH[5] = {1, 3, 4, 5, 7}; | |
406 struct curveType *curve = NULL; | |
407 uint32_t type = read_u32(src, offset); | |
408 uint32_t count; | |
409 int i; | |
410 | |
411 if (type != CURVE_TYPE && type != PARAMETRIC_CURVE_TYPE) { | |
412 invalid_source(src, "unexpected type, expected CURV or PARA"); | |
413 return NULL; | |
414 } | |
415 | |
416 if (type == CURVE_TYPE) { | |
417 count = read_u32(src, offset+8); | |
418 | |
419 #define MAX_CURVE_ENTRIES 40000 //arbitrary | |
420 if (count > MAX_CURVE_ENTRIES) { | |
421 invalid_source(src, "curve size too large"); | |
422 return NULL; | |
423 } | |
424 curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*c
ount); | |
425 if (!curve) | |
426 return NULL; | |
427 | |
428 curve->count = count; | |
429 curve->type = type; | |
430 | |
431 for (i=0; i<count; i++) { | |
432 curve->data[i] = read_u16(src, offset + 12 + i*2); | |
433 } | |
434 *len = 12 + count * 2; | |
435 } else { //PARAMETRIC_CURVE_TYPE | |
436 count = read_u16(src, offset+8); | |
437 | |
438 if (count > 4) { | |
439 invalid_source(src, "parametric function type not suppor
ted."); | |
440 return NULL; | |
441 } | |
442 | |
443 curve = malloc(sizeof(struct curveType)); | |
444 if (!curve) | |
445 return NULL; | |
446 | |
447 curve->count = count; | |
448 curve->type = type; | |
449 | |
450 for (i=0; i < COUNT_TO_LENGTH[count]; i++) { | |
451 curve->parameter[i] = s15Fixed16Number_to_float(read_s15
Fixed16Number(src, offset + 12 + i*4)); | |
452 } | |
453 *len = 12 + COUNT_TO_LENGTH[count] * 4; | |
454 | |
455 if ((count == 1 || count == 2)) { | |
456 /* we have a type 1 or type 2 function that has a divisi
on by 'a' */ | |
457 float a = curve->parameter[1]; | |
458 if (a == 0.f) | |
459 invalid_source(src, "parametricCurve definition
causes division by zero."); | |
460 } | |
461 } | |
462 | |
463 return curve; | |
464 } | |
465 | |
466 static struct curveType *read_tag_curveType(struct mem_source *src, struct tag_i
ndex index, uint32_t tag_id) | |
467 { | |
468 struct tag *tag = find_tag(index, tag_id); | |
469 struct curveType *curve = NULL; | |
470 if (tag) { | |
471 uint32_t len; | |
472 return read_curveType(src, tag->offset, &len); | |
473 } else { | |
474 invalid_source(src, "missing curvetag"); | |
475 } | |
476 | |
477 return curve; | |
478 } | |
479 | |
480 #define MAX_CLUT_SIZE 500000 // arbitrary | |
481 #define MAX_CHANNELS 10 // arbitrary | |
482 static void read_nested_curveType(struct mem_source *src, struct curveType *(*cu
rveArray)[MAX_CHANNELS], uint8_t num_channels, uint32_t curve_offset) | |
483 { | |
484 uint32_t channel_offset = 0; | |
485 int i; | |
486 for (i = 0; i < num_channels; i++) { | |
487 uint32_t tag_len; | |
488 | |
489 (*curveArray)[i] = read_curveType(src, curve_offset + channel_of
fset, &tag_len); | |
490 if (!(*curveArray)[i]) { | |
491 invalid_source(src, "invalid nested curveType curve"); | |
492 } | |
493 | |
494 channel_offset += tag_len; | |
495 // 4 byte aligned | |
496 if ((tag_len % 4) != 0) | |
497 channel_offset += 4 - (tag_len % 4); | |
498 } | |
499 | |
500 } | |
501 | |
502 static void mAB_release(struct lutmABType *lut) | |
503 { | |
504 uint8_t i; | |
505 | |
506 for (i = 0; i < lut->num_in_channels; i++){ | |
507 free(lut->a_curves[i]); | |
508 } | |
509 for (i = 0; i < lut->num_out_channels; i++){ | |
510 free(lut->b_curves[i]); | |
511 free(lut->m_curves[i]); | |
512 } | |
513 free(lut); | |
514 } | |
515 | |
516 /* See section 10.10 for specs */ | |
517 static struct lutmABType *read_tag_lutmABType(struct mem_source *src, struct tag
_index index, uint32_t tag_id) | |
518 { | |
519 struct tag *tag = find_tag(index, tag_id); | |
520 uint32_t offset = tag->offset; | |
521 uint32_t a_curve_offset, b_curve_offset, m_curve_offset; | |
522 uint32_t matrix_offset; | |
523 uint32_t clut_offset; | |
524 uint32_t clut_size = 1; | |
525 uint8_t clut_precision; | |
526 uint32_t type = read_u32(src, offset); | |
527 uint8_t num_in_channels, num_out_channels; | |
528 struct lutmABType *lut; | |
529 int i; | |
530 | |
531 if (type != LUT_MAB_TYPE && type != LUT_MBA_TYPE) { | |
532 return NULL; | |
533 } | |
534 | |
535 num_in_channels = read_u8(src, offset + 8); | |
536 num_out_channels = read_u8(src, offset + 8); | |
537 if (num_in_channels > MAX_CHANNELS || num_out_channels > MAX_CHANNELS) | |
538 return NULL; | |
539 | |
540 // We require 3in/out channels since we only support RGB->XYZ (or RGB->L
AB) | |
541 // XXX: If we remove this restriction make sure that the number of chann
els | |
542 // is less or equal to the maximum number of mAB curves in qcmsint.
h | |
543 // also check for clut_size overflow. | |
544 if (num_in_channels != 3 || num_out_channels != 3) | |
545 return NULL; | |
546 | |
547 // some of this data is optional and is denoted by a zero offset | |
548 // we also use this to track their existance | |
549 a_curve_offset = read_u32(src, offset + 28); | |
550 clut_offset = read_u32(src, offset + 24); | |
551 m_curve_offset = read_u32(src, offset + 20); | |
552 matrix_offset = read_u32(src, offset + 16); | |
553 b_curve_offset = read_u32(src, offset + 12); | |
554 | |
555 // Convert offsets relative to the tag to relative to the profile | |
556 // preserve zero for optional fields | |
557 if (a_curve_offset) | |
558 a_curve_offset += offset; | |
559 if (clut_offset) | |
560 clut_offset += offset; | |
561 if (m_curve_offset) | |
562 m_curve_offset += offset; | |
563 if (matrix_offset) | |
564 matrix_offset += offset; | |
565 if (b_curve_offset) | |
566 b_curve_offset += offset; | |
567 | |
568 if (clut_offset) { | |
569 assert (num_in_channels == 3); | |
570 // clut_size can not overflow since lg(256^num_in_channels) = 24
bits. | |
571 for (i = 0; i < num_in_channels; i++) { | |
572 clut_size *= read_u8(src, clut_offset + i); | |
573 } | |
574 } else { | |
575 clut_size = 0; | |
576 } | |
577 | |
578 // 24bits * 3 won't overflow either | |
579 clut_size = clut_size * num_out_channels; | |
580 | |
581 if (clut_size > MAX_CLUT_SIZE) | |
582 return NULL; | |
583 | |
584 lut = malloc(sizeof(struct lutmABType) + (clut_size) * sizeof(float)); | |
585 if (!lut) | |
586 return NULL; | |
587 // we'll fill in the rest below | |
588 memset(lut, 0, sizeof(struct lutmABType)); | |
589 lut->clut_table = &lut->clut_table_data[0]; | |
590 | |
591 for (i = 0; i < num_in_channels; i++) { | |
592 lut->num_grid_points[i] = read_u8(src, clut_offset + i); | |
593 } | |
594 | |
595 // Reverse the processing of transformation elements for mBA type. | |
596 lut->reversed = (type == LUT_MBA_TYPE); | |
597 | |
598 lut->num_in_channels = num_in_channels; | |
599 lut->num_out_channels = num_out_channels; | |
600 | |
601 if (matrix_offset) { | |
602 // read the matrix if we have it | |
603 lut->e00 = read_s15Fixed16Number(src, matrix_offset+4*0); | |
604 lut->e01 = read_s15Fixed16Number(src, matrix_offset+4*1); | |
605 lut->e02 = read_s15Fixed16Number(src, matrix_offset+4*2); | |
606 lut->e10 = read_s15Fixed16Number(src, matrix_offset+4*3); | |
607 lut->e11 = read_s15Fixed16Number(src, matrix_offset+4*4); | |
608 lut->e12 = read_s15Fixed16Number(src, matrix_offset+4*5); | |
609 lut->e20 = read_s15Fixed16Number(src, matrix_offset+4*6); | |
610 lut->e21 = read_s15Fixed16Number(src, matrix_offset+4*7); | |
611 lut->e22 = read_s15Fixed16Number(src, matrix_offset+4*8); | |
612 lut->e03 = read_s15Fixed16Number(src, matrix_offset+4*9); | |
613 lut->e13 = read_s15Fixed16Number(src, matrix_offset+4*10); | |
614 lut->e23 = read_s15Fixed16Number(src, matrix_offset+4*11); | |
615 } | |
616 | |
617 if (a_curve_offset) { | |
618 read_nested_curveType(src, &lut->a_curves, num_in_channels, a_cu
rve_offset); | |
619 } | |
620 if (m_curve_offset) { | |
621 read_nested_curveType(src, &lut->m_curves, num_out_channels, m_c
urve_offset); | |
622 } | |
623 if (b_curve_offset) { | |
624 read_nested_curveType(src, &lut->b_curves, num_out_channels, b_c
urve_offset); | |
625 } else { | |
626 invalid_source(src, "B curves required"); | |
627 } | |
628 | |
629 if (clut_offset) { | |
630 clut_precision = read_u8(src, clut_offset + 16); | |
631 if (clut_precision == 1) { | |
632 for (i = 0; i < clut_size; i++) { | |
633 lut->clut_table[i] = uInt8Number_to_float(read_u
Int8Number(src, clut_offset + 20 + i*1)); | |
634 } | |
635 } else if (clut_precision == 2) { | |
636 for (i = 0; i < clut_size; i++) { | |
637 lut->clut_table[i] = uInt16Number_to_float(read_
uInt16Number(src, clut_offset + 20 + i*2)); | |
638 } | |
639 } else { | |
640 invalid_source(src, "Invalid clut precision"); | |
641 } | |
642 } | |
643 | |
644 if (!src->valid) { | |
645 mAB_release(lut); | |
646 return NULL; | |
647 } | |
648 | |
649 return lut; | |
650 } | |
651 | |
652 static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index
index, uint32_t tag_id) | |
653 { | |
654 struct tag *tag = find_tag(index, tag_id); | |
655 uint32_t offset = tag->offset; | |
656 uint32_t type = read_u32(src, offset); | |
657 uint16_t num_input_table_entries; | |
658 uint16_t num_output_table_entries; | |
659 uint8_t in_chan, grid_points, out_chan; | |
660 uint32_t clut_offset, output_offset; | |
661 uint32_t clut_size; | |
662 size_t entry_size; | |
663 struct lutType *lut; | |
664 int i; | |
665 | |
666 /* I'm not sure why the spec specifies a fixed number of entries for LUT
8 tables even though | |
667 * they have room for the num_entries fields */ | |
668 if (type == LUT8_TYPE) { | |
669 num_input_table_entries = 256; | |
670 num_output_table_entries = 256; | |
671 entry_size = 1; | |
672 } else if (type == LUT16_TYPE) { | |
673 num_input_table_entries = read_u16(src, offset + 48); | |
674 num_output_table_entries = read_u16(src, offset + 50); | |
675 entry_size = 2; | |
676 } else { | |
677 assert(0); // the caller checks that this doesn't happen | |
678 invalid_source(src, "Unexpected lut type"); | |
679 return NULL; | |
680 } | |
681 | |
682 in_chan = read_u8(src, offset + 8); | |
683 out_chan = read_u8(src, offset + 9); | |
684 grid_points = read_u8(src, offset + 10); | |
685 | |
686 clut_size = pow(grid_points, in_chan); | |
687 if (clut_size > MAX_CLUT_SIZE) { | |
688 return NULL; | |
689 } | |
690 | |
691 if (in_chan != 3 || out_chan != 3) { | |
692 return NULL; | |
693 } | |
694 | |
695 lut = malloc(sizeof(struct lutType) + (num_input_table_entries * in_chan
+ clut_size*out_chan + num_output_table_entries * out_chan)*sizeof(float)); | |
696 if (!lut) { | |
697 return NULL; | |
698 } | |
699 | |
700 /* compute the offsets of tables */ | |
701 lut->input_table = &lut->table_data[0]; | |
702 lut->clut_table = &lut->table_data[in_chan*num_input_table_entries]; | |
703 lut->output_table = &lut->table_data[in_chan*num_input_table_entries + c
lut_size*out_chan]; | |
704 | |
705 lut->num_input_table_entries = num_input_table_entries; | |
706 lut->num_output_table_entries = num_output_table_entries; | |
707 lut->num_input_channels = read_u8(src, offset + 8); | |
708 lut->num_output_channels = read_u8(src, offset + 9); | |
709 lut->num_clut_grid_points = read_u8(src, offset + 10); | |
710 lut->e00 = read_s15Fixed16Number(src, offset+12); | |
711 lut->e01 = read_s15Fixed16Number(src, offset+16); | |
712 lut->e02 = read_s15Fixed16Number(src, offset+20); | |
713 lut->e10 = read_s15Fixed16Number(src, offset+24); | |
714 lut->e11 = read_s15Fixed16Number(src, offset+28); | |
715 lut->e12 = read_s15Fixed16Number(src, offset+32); | |
716 lut->e20 = read_s15Fixed16Number(src, offset+36); | |
717 lut->e21 = read_s15Fixed16Number(src, offset+40); | |
718 lut->e22 = read_s15Fixed16Number(src, offset+44); | |
719 | |
720 for (i = 0; i < lut->num_input_table_entries * in_chan; i++) { | |
721 if (type == LUT8_TYPE) { | |
722 lut->input_table[i] = uInt8Number_to_float(read_uInt8Num
ber(src, offset + 52 + i * entry_size)); | |
723 } else { | |
724 lut->input_table[i] = uInt16Number_to_float(read_uInt16N
umber(src, offset + 52 + i * entry_size)); | |
725 } | |
726 } | |
727 | |
728 clut_offset = offset + 52 + lut->num_input_table_entries * in_chan * ent
ry_size; | |
729 for (i = 0; i < clut_size * out_chan; i+=3) { | |
730 if (type == LUT8_TYPE) { | |
731 lut->clut_table[i+0] = uInt8Number_to_float(read_uInt8Nu
mber(src, clut_offset + i*entry_size + 0)); | |
732 lut->clut_table[i+1] = uInt8Number_to_float(read_uInt8Nu
mber(src, clut_offset + i*entry_size + 1)); | |
733 lut->clut_table[i+2] = uInt8Number_to_float(read_uInt8Nu
mber(src, clut_offset + i*entry_size + 2)); | |
734 } else { | |
735 lut->clut_table[i+0] = uInt16Number_to_float(read_uInt16
Number(src, clut_offset + i*entry_size + 0)); | |
736 lut->clut_table[i+1] = uInt16Number_to_float(read_uInt16
Number(src, clut_offset + i*entry_size + 2)); | |
737 lut->clut_table[i+2] = uInt16Number_to_float(read_uInt16
Number(src, clut_offset + i*entry_size + 4)); | |
738 } | |
739 } | |
740 | |
741 output_offset = clut_offset + clut_size * out_chan * entry_size; | |
742 for (i = 0; i < lut->num_output_table_entries * out_chan; i++) { | |
743 if (type == LUT8_TYPE) { | |
744 lut->output_table[i] = uInt8Number_to_float(read_uInt8Nu
mber(src, output_offset + i*entry_size)); | |
745 } else { | |
746 lut->output_table[i] = uInt16Number_to_float(read_uInt16
Number(src, output_offset + i*entry_size)); | |
747 } | |
748 } | |
749 | |
750 return lut; | |
751 } | |
752 | |
753 static void read_rendering_intent(qcms_profile *profile, struct mem_source *src) | |
754 { | |
755 profile->rendering_intent = read_u32(src, 64); | |
756 switch (profile->rendering_intent) { | |
757 case QCMS_INTENT_PERCEPTUAL: | |
758 case QCMS_INTENT_SATURATION: | |
759 case QCMS_INTENT_RELATIVE_COLORIMETRIC: | |
760 case QCMS_INTENT_ABSOLUTE_COLORIMETRIC: | |
761 break; | |
762 default: | |
763 invalid_source(src, "unknown rendering intent"); | |
764 } | |
765 } | |
766 | |
767 qcms_profile *qcms_profile_create(void) | |
768 { | |
769 return calloc(sizeof(qcms_profile), 1); | |
770 } | |
771 | |
772 | |
773 | |
774 /* build sRGB gamma table */ | |
775 /* based on cmsBuildParametricGamma() */ | |
776 static uint16_t *build_sRGB_gamma_table(int num_entries) | |
777 { | |
778 int i; | |
779 /* taken from lcms: Build_sRGBGamma() */ | |
780 double gamma = 2.4; | |
781 double a = 1./1.055; | |
782 double b = 0.055/1.055; | |
783 double c = 1./12.92; | |
784 double d = 0.04045; | |
785 | |
786 uint16_t *table = malloc(sizeof(uint16_t) * num_entries); | |
787 if (!table) | |
788 return NULL; | |
789 | |
790 for (i=0; i<num_entries; i++) { | |
791 double x = (double)i / (num_entries-1); | |
792 double y, output; | |
793 // IEC 61966-2.1 (sRGB) | |
794 // Y = (aX + b)^Gamma | X >= d | |
795 // Y = cX | X < d | |
796 if (x >= d) { | |
797 double e = (a*x + b); | |
798 if (e > 0) | |
799 y = pow(e, gamma); | |
800 else | |
801 y = 0; | |
802 } else { | |
803 y = c*x; | |
804 } | |
805 | |
806 // Saturate -- this could likely move to a separate function | |
807 output = y * 65535. + .5; | |
808 if (output > 65535.) | |
809 output = 65535; | |
810 if (output < 0) | |
811 output = 0; | |
812 table[i] = (uint16_t)floor(output); | |
813 } | |
814 return table; | |
815 } | |
816 | |
817 static struct curveType *curve_from_table(uint16_t *table, int num_entries) | |
818 { | |
819 struct curveType *curve; | |
820 int i; | |
821 curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*num_entri
es); | |
822 if (!curve) | |
823 return NULL; | |
824 curve->type = CURVE_TYPE; | |
825 curve->count = num_entries; | |
826 for (i = 0; i < num_entries; i++) { | |
827 curve->data[i] = table[i]; | |
828 } | |
829 return curve; | |
830 } | |
831 | |
832 static uint16_t float_to_u8Fixed8Number(float a) | |
833 { | |
834 if (a > (255.f + 255.f/256)) | |
835 return 0xffff; | |
836 else if (a < 0.f) | |
837 return 0; | |
838 else | |
839 return floor(a*256.f + .5f); | |
840 } | |
841 | |
842 static struct curveType *curve_from_gamma(float gamma) | |
843 { | |
844 struct curveType *curve; | |
845 int num_entries = 1; | |
846 curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*num_entri
es); | |
847 if (!curve) | |
848 return NULL; | |
849 curve->count = num_entries; | |
850 curve->data[0] = float_to_u8Fixed8Number(gamma); | |
851 return curve; | |
852 } | |
853 | |
854 | |
855 //XXX: it would be nice if we had a way of ensuring | |
856 // everything in a profile was initialized regardless of how it was created | |
857 | |
858 //XXX: should this also be taking a black_point? | |
859 /* similar to CGColorSpaceCreateCalibratedRGB */ | |
860 qcms_profile* qcms_profile_create_rgb_with_gamma( | |
861 qcms_CIE_xyY white_point, | |
862 qcms_CIE_xyYTRIPLE primaries, | |
863 float gamma) | |
864 { | |
865 qcms_profile* profile = qcms_profile_create(); | |
866 if (!profile) | |
867 return NO_MEM_PROFILE; | |
868 | |
869 //XXX: should store the whitepoint | |
870 if (!set_rgb_colorants(profile, white_point, primaries)) { | |
871 qcms_profile_release(profile); | |
872 return INVALID_PROFILE; | |
873 } | |
874 | |
875 profile->redTRC = curve_from_gamma(gamma); | |
876 profile->blueTRC = curve_from_gamma(gamma); | |
877 profile->greenTRC = curve_from_gamma(gamma); | |
878 | |
879 if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC) { | |
880 qcms_profile_release(profile); | |
881 return NO_MEM_PROFILE; | |
882 } | |
883 profile->class = DISPLAY_DEVICE_PROFILE; | |
884 profile->rendering_intent = QCMS_INTENT_PERCEPTUAL; | |
885 profile->color_space = RGB_SIGNATURE; | |
886 return profile; | |
887 } | |
888 | |
889 qcms_profile* qcms_profile_create_rgb_with_table( | |
890 qcms_CIE_xyY white_point, | |
891 qcms_CIE_xyYTRIPLE primaries, | |
892 uint16_t *table, int num_entries) | |
893 { | |
894 qcms_profile* profile = qcms_profile_create(); | |
895 if (!profile) | |
896 return NO_MEM_PROFILE; | |
897 | |
898 //XXX: should store the whitepoint | |
899 if (!set_rgb_colorants(profile, white_point, primaries)) { | |
900 qcms_profile_release(profile); | |
901 return INVALID_PROFILE; | |
902 } | |
903 | |
904 profile->redTRC = curve_from_table(table, num_entries); | |
905 profile->blueTRC = curve_from_table(table, num_entries); | |
906 profile->greenTRC = curve_from_table(table, num_entries); | |
907 | |
908 if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC) { | |
909 qcms_profile_release(profile); | |
910 return NO_MEM_PROFILE; | |
911 } | |
912 profile->class = DISPLAY_DEVICE_PROFILE; | |
913 profile->rendering_intent = QCMS_INTENT_PERCEPTUAL; | |
914 profile->color_space = RGB_SIGNATURE; | |
915 return profile; | |
916 } | |
917 | |
918 /* from lcms: cmsWhitePointFromTemp */ | |
919 /* tempK must be >= 4000. and <= 25000. | |
920 * similar to argyll: icx_DTEMP2XYZ() */ | |
921 static qcms_CIE_xyY white_point_from_temp(int temp_K) | |
922 { | |
923 qcms_CIE_xyY white_point; | |
924 double x, y; | |
925 double T, T2, T3; | |
926 // double M1, M2; | |
927 | |
928 // No optimization provided. | |
929 T = temp_K; | |
930 T2 = T*T; // Square | |
931 T3 = T2*T; // Cube | |
932 | |
933 // For correlated color temperature (T) between 4000K and 7000K: | |
934 if (T >= 4000. && T <= 7000.) { | |
935 x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244
063; | |
936 } else { | |
937 // or for correlated color temperature (T) between 7000K and 250
00K: | |
938 if (T > 7000.0 && T <= 25000.0) { | |
939 x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T)
+ 0.237040; | |
940 } else { | |
941 assert(0 && "invalid temp"); | |
942 } | |
943 } | |
944 | |
945 // Obtain y(x) | |
946 | |
947 y = -3.000*(x*x) + 2.870*x - 0.275; | |
948 | |
949 // wave factors (not used, but here for futures extensions) | |
950 | |
951 // M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y); | |
952 // M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y); | |
953 | |
954 // Fill white_point struct | |
955 white_point.x = x; | |
956 white_point.y = y; | |
957 white_point.Y = 1.0; | |
958 | |
959 return white_point; | |
960 } | |
961 | |
962 qcms_profile* qcms_profile_sRGB(void) | |
963 { | |
964 qcms_profile *profile; | |
965 uint16_t *table; | |
966 | |
967 qcms_CIE_xyYTRIPLE Rec709Primaries = { | |
968 {0.6400, 0.3300, 1.0}, | |
969 {0.3000, 0.6000, 1.0}, | |
970 {0.1500, 0.0600, 1.0} | |
971 }; | |
972 qcms_CIE_xyY D65; | |
973 | |
974 D65 = white_point_from_temp(6504); | |
975 | |
976 table = build_sRGB_gamma_table(1024); | |
977 | |
978 if (!table) | |
979 return NO_MEM_PROFILE; | |
980 | |
981 profile = qcms_profile_create_rgb_with_table(D65, Rec709Primaries, table
, 1024); | |
982 free(table); | |
983 return profile; | |
984 } | |
985 | |
986 | |
987 /* qcms_profile_from_memory does not hold a reference to the memory passed in */ | |
988 qcms_profile* qcms_profile_from_memory(const void *mem, size_t size) | |
989 { | |
990 uint32_t length; | |
991 struct mem_source source; | |
992 struct mem_source *src = &source; | |
993 struct tag_index index; | |
994 qcms_profile *profile; | |
995 | |
996 source.buf = mem; | |
997 source.size = size; | |
998 source.valid = true; | |
999 | |
1000 length = read_u32(src, 0); | |
1001 if (length <= size) { | |
1002 // shrink the area that we can read if appropriate | |
1003 source.size = length; | |
1004 } else { | |
1005 return INVALID_PROFILE; | |
1006 } | |
1007 | |
1008 /* ensure that the profile size is sane so it's easier to reason about *
/ | |
1009 if (source.size <= 64 || source.size >= MAX_PROFILE_SIZE) | |
1010 return INVALID_PROFILE; | |
1011 | |
1012 profile = qcms_profile_create(); | |
1013 if (!profile) | |
1014 return NO_MEM_PROFILE; | |
1015 | |
1016 check_CMM_type_signature(src); | |
1017 check_profile_version(src); | |
1018 read_class_signature(profile, src); | |
1019 read_rendering_intent(profile, src); | |
1020 read_color_space(profile, src); | |
1021 read_pcs(profile, src); | |
1022 //TODO read rest of profile stuff | |
1023 | |
1024 if (!src->valid) | |
1025 goto invalid_profile; | |
1026 | |
1027 index = read_tag_table(profile, src); | |
1028 if (!src->valid || !index.tags) | |
1029 goto invalid_tag_table; | |
1030 | |
1031 if (find_tag(index, TAG_CHAD)) { | |
1032 profile->chromaticAdaption = read_tag_s15Fixed16ArrayType(src, i
ndex, TAG_CHAD); | |
1033 } else { | |
1034 profile->chromaticAdaption.invalid = true; //Signal the data is
not present | |
1035 } | |
1036 | |
1037 if (profile->class == DISPLAY_DEVICE_PROFILE || profile->class == INPUT_
DEVICE_PROFILE || | |
1038 profile->class == OUTPUT_DEVICE_PROFILE || profile->class == COLOR_
SPACE_PROFILE) { | |
1039 if (profile->color_space == RGB_SIGNATURE) { | |
1040 if (find_tag(index, TAG_A2B0)) { | |
1041 if (read_u32(src, find_tag(index, TAG_A2B0)->off
set) == LUT8_TYPE || | |
1042 read_u32(src, find_tag(index, TAG_A2B0)->off
set) == LUT16_TYPE) { | |
1043 profile->A2B0 = read_tag_lutType(src, in
dex, TAG_A2B0); | |
1044 } else if (read_u32(src, find_tag(index, TAG_A2B
0)->offset) == LUT_MAB_TYPE) { | |
1045 profile->mAB = read_tag_lutmABType(src,
index, TAG_A2B0); | |
1046 } | |
1047 } | |
1048 if (find_tag(index, TAG_B2A0)) { | |
1049 if (read_u32(src, find_tag(index, TAG_B2A0)->off
set) == LUT8_TYPE || | |
1050 read_u32(src, find_tag(index, TAG_B2A0)->off
set) == LUT16_TYPE) { | |
1051 profile->B2A0 = read_tag_lutType(src, in
dex, TAG_B2A0); | |
1052 } else if (read_u32(src, find_tag(index, TAG_B2A
0)->offset) == LUT_MBA_TYPE) { | |
1053 profile->mBA = read_tag_lutmABType(src,
index, TAG_B2A0); | |
1054 } | |
1055 } | |
1056 if (find_tag(index, TAG_rXYZ) || !qcms_supports_iccv4) { | |
1057 profile->redColorant = read_tag_XYZType(src, ind
ex, TAG_rXYZ); | |
1058 profile->greenColorant = read_tag_XYZType(src, i
ndex, TAG_gXYZ); | |
1059 profile->blueColorant = read_tag_XYZType(src, in
dex, TAG_bXYZ); | |
1060 } | |
1061 | |
1062 if (!src->valid) | |
1063 goto invalid_tag_table; | |
1064 | |
1065 if (find_tag(index, TAG_rTRC) || !qcms_supports_iccv4) { | |
1066 profile->redTRC = read_tag_curveType(src, index,
TAG_rTRC); | |
1067 profile->greenTRC = read_tag_curveType(src, inde
x, TAG_gTRC); | |
1068 profile->blueTRC = read_tag_curveType(src, index
, TAG_bTRC); | |
1069 | |
1070 if (!profile->redTRC || !profile->blueTRC || !pr
ofile->greenTRC) | |
1071 goto invalid_tag_table; | |
1072 } | |
1073 } else if (profile->color_space == GRAY_SIGNATURE) { | |
1074 | |
1075 profile->grayTRC = read_tag_curveType(src, index, TAG_kT
RC); | |
1076 if (!profile->grayTRC) | |
1077 goto invalid_tag_table; | |
1078 | |
1079 } else { | |
1080 assert(0 && "read_color_space protects against entering
here"); | |
1081 goto invalid_tag_table; | |
1082 } | |
1083 } else { | |
1084 goto invalid_tag_table; | |
1085 } | |
1086 | |
1087 if (!src->valid) | |
1088 goto invalid_tag_table; | |
1089 | |
1090 free(index.tags); | |
1091 | |
1092 return profile; | |
1093 | |
1094 invalid_tag_table: | |
1095 free(index.tags); | |
1096 invalid_profile: | |
1097 qcms_profile_release(profile); | |
1098 return INVALID_PROFILE; | |
1099 } | |
1100 | |
1101 qcms_intent qcms_profile_get_rendering_intent(qcms_profile *profile) | |
1102 { | |
1103 return profile->rendering_intent; | |
1104 } | |
1105 | |
1106 icColorSpaceSignature | |
1107 qcms_profile_get_color_space(qcms_profile *profile) | |
1108 { | |
1109 return profile->color_space; | |
1110 } | |
1111 | |
1112 static void lut_release(struct lutType *lut) | |
1113 { | |
1114 free(lut); | |
1115 } | |
1116 | |
1117 void qcms_profile_release(qcms_profile *profile) | |
1118 { | |
1119 if (profile->output_table_r) | |
1120 precache_release(profile->output_table_r); | |
1121 if (profile->output_table_g) | |
1122 precache_release(profile->output_table_g); | |
1123 if (profile->output_table_b) | |
1124 precache_release(profile->output_table_b); | |
1125 | |
1126 if (profile->A2B0) | |
1127 lut_release(profile->A2B0); | |
1128 if (profile->B2A0) | |
1129 lut_release(profile->B2A0); | |
1130 | |
1131 if (profile->mAB) | |
1132 mAB_release(profile->mAB); | |
1133 if (profile->mBA) | |
1134 mAB_release(profile->mBA); | |
1135 | |
1136 free(profile->redTRC); | |
1137 free(profile->blueTRC); | |
1138 free(profile->greenTRC); | |
1139 free(profile->grayTRC); | |
1140 free(profile); | |
1141 } | |
1142 | |
1143 | |
1144 #include <stdio.h> | |
1145 qcms_profile* qcms_profile_from_file(FILE *file) | |
1146 { | |
1147 uint32_t length, remaining_length; | |
1148 qcms_profile *profile; | |
1149 size_t read_length; | |
1150 be32 length_be; | |
1151 void *data; | |
1152 | |
1153 if (fread(&length_be, 1, sizeof(length_be), file) != sizeof(length_be)) | |
1154 return BAD_VALUE_PROFILE; | |
1155 | |
1156 length = be32_to_cpu(length_be); | |
1157 if (length > MAX_PROFILE_SIZE || length < sizeof(length_be)) | |
1158 return BAD_VALUE_PROFILE; | |
1159 | |
1160 /* allocate room for the entire profile */ | |
1161 data = malloc(length); | |
1162 if (!data) | |
1163 return NO_MEM_PROFILE; | |
1164 | |
1165 /* copy in length to the front so that the buffer will contain the entir
e profile */ | |
1166 *((be32*)data) = length_be; | |
1167 remaining_length = length - sizeof(length_be); | |
1168 | |
1169 /* read the rest profile */ | |
1170 read_length = fread((unsigned char*)data + sizeof(length_be), 1, remaini
ng_length, file); | |
1171 if (read_length != remaining_length) { | |
1172 free(data); | |
1173 return INVALID_PROFILE; | |
1174 } | |
1175 | |
1176 profile = qcms_profile_from_memory(data, length); | |
1177 free(data); | |
1178 return profile; | |
1179 } | |
1180 | |
1181 qcms_profile* qcms_profile_from_path(const char *path) | |
1182 { | |
1183 qcms_profile *profile = NULL; | |
1184 FILE *file = fopen(path, "rb"); | |
1185 if (file) { | |
1186 profile = qcms_profile_from_file(file); | |
1187 fclose(file); | |
1188 } | |
1189 return profile; | |
1190 } | |
1191 | |
1192 #ifdef _WIN32 | |
1193 /* Unicode path version */ | |
1194 qcms_profile* qcms_profile_from_unicode_path(const wchar_t *path) | |
1195 { | |
1196 qcms_profile *profile = NULL; | |
1197 FILE *file = _wfopen(path, L"rb"); | |
1198 if (file) { | |
1199 profile = qcms_profile_from_file(file); | |
1200 fclose(file); | |
1201 } | |
1202 return profile; | |
1203 } | |
1204 #endif | |
OLD | NEW |