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 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 offset. 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 offset) 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 signature"); 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 (qcms_supports_iccv4 && (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 #if defined (_MSC_VER) 301 #pragma warning(push) 302 /* Disable double to float truncation warning 4305 */ 303 #pragma warning(disable:4305) 304 #endif 305 // Build our target vector (see mozilla bug 460629) 306 target[0] = 0.96420; 307 target[1] = 1.00000; 308 target[2] = 0.82491; 309 310 // Our tolerance vector - Recommended by Chris Murphy based on 311 // conversion from the LAB space criterion of no more than 3 in any one 312 // channel. This is similar to, but slightly more tolerant than Adobe's 313 // criterion. 314 tolerance[0] = 0.02; 315 tolerance[1] = 0.02; 316 tolerance[2] = 0.04; 317 318 #if defined (_MSC_VER) 319 /* Restore warnings */ 320 #pragma warning(pop) 321 #endif 322 // Compare with our tolerance 323 for (i = 0; i < 3; ++i) { 324 if (!(((sum[i] - tolerance[i]) <= target[i]) && 325 ((sum[i] + tolerance[i]) >= target[i]))) 326 return true; 327 } 328 329 // All Good 330 return false; 331 } 332 333 #define TAG_bXYZ 0x6258595a 334 #define TAG_gXYZ 0x6758595a 335 #define TAG_rXYZ 0x7258595a 336 #define TAG_rTRC 0x72545243 337 #define TAG_bTRC 0x62545243 338 #define TAG_gTRC 0x67545243 339 #define TAG_kTRC 0x6b545243 340 #define TAG_A2B0 0x41324230 341 #define TAG_B2A0 0x42324130 342 #define TAG_CHAD 0x63686164 343 #define TAG_desc 0x64657363 344 345 static struct tag *find_tag(struct tag_index index, uint32_t tag_id) 346 { 347 unsigned int i; 348 struct tag *tag = NULL; 349 for (i = 0; i < index.count; i++) { 350 if (index.tags[i].signature == tag_id) { 351 return &index.tags[i]; 352 } 353 } 354 return tag; 355 } 356 357 #define DESC_TYPE 0x64657363 // 'desc' 358 #define MLUC_TYPE 0x6d6c7563 // 'mluc' 359 360 static bool read_tag_descType(qcms_profile *profile, struct mem_source *src, struct tag_index index, uint32_t tag_id) 361 { 362 struct tag *tag = find_tag(index, tag_id); 363 if (tag) { 364 const uint32_t limit = sizeof profile->description; 365 uint32_t offset = tag->offset; 366 uint32_t type = read_u32(src, offset); 367 uint32_t length = read_u32(src, offset+8); 368 uint32_t i, description; 369 if (length && type == MLUC_TYPE) { 370 length = read_u32(src, offset+20); 371 if (!length || (length & 1) || (read_u32(src, offset+12) != 12)) 372 goto invalid_desc_tag; 373 description = offset + read_u32(src, offset+24); 374 if (!src->valid) 375 goto invalid_desc_tag; 376 } else if (length && type == DESC_TYPE) { 377 description = offset + 12; 378 } else { 379 goto invalid_desc_tag; 380 } 381 if (length >= limit) 382 length = limit - 1; 383 for (i = 0; i < length; ++i) 384 profile->description[i] = read_u8(src, description+i); 385 profile->description[length] = 0; 386 } else { 387 goto invalid_desc_tag; 388 } 389 390 if (src->valid) 391 return true; 392 393 invalid_desc_tag: 394 invalid_source(src, "invalid description"); 395 return false; 396 } 397 398 #define XYZ_TYPE 0x58595a20 // 'XYZ ' 399 #define CURVE_TYPE 0x63757276 // 'curv' 400 #define PARAMETRIC_CURVE_TYPE 0x70617261 // 'para' 401 #define LUT16_TYPE 0x6d667432 // 'mft2' 402 #define LUT8_TYPE 0x6d667431 // 'mft1' 403 #define LUT_MAB_TYPE 0x6d414220 // 'mAB ' 404 #define LUT_MBA_TYPE 0x6d424120 // 'mBA ' 405 #define CHROMATIC_TYPE 0x73663332 // 'sf32' 406 407 static struct matrix read_tag_s15Fixed16ArrayType(struct mem_source *src, struct tag_index index, uint32_t tag_id) 408 { 409 struct tag *tag = find_tag(index, tag_id); 410 struct matrix matrix; 411 if (tag) { 412 uint8_t i; 413 uint32_t offset = tag->offset; 414 uint32_t type = read_u32(src, offset); 415 416 // Check mandatory type signature for s16Fixed16ArrayType 417 if (type != CHROMATIC_TYPE) { 418 invalid_source(src, "unexpected type, expected 'sf32'"); 419 } 420 421 for (i = 0; i < 9; i++) { 422 matrix.m[i/3][i%3] = s15Fixed16Number_to_float(read_s15Fixed16Number(src, offset+8+i*4)); 423 } 424 matrix.invalid = false; 425 } else { 426 matrix.invalid = true; 427 invalid_source(src, "missing sf32tag"); 428 } 429 return matrix; 430 } 431 432 static struct XYZNumber read_tag_XYZType(struct mem_source *src, struct tag_index index, uint32_t tag_id) 433 { 434 struct XYZNumber num = {0, 0, 0}; 435 struct tag *tag = find_tag(index, tag_id); 436 if (tag) { 437 uint32_t offset = tag->offset; 438 439 uint32_t type = read_u32(src, offset); 440 if (type != XYZ_TYPE) 441 invalid_source(src, "unexpected type, expected XYZ"); 442 num.X = read_s15Fixed16Number(src, offset+8); 443 num.Y = read_s15Fixed16Number(src, offset+12); 444 num.Z = read_s15Fixed16Number(src, offset+16); 445 } else { 446 invalid_source(src, "missing xyztag"); 447 } 448 return num; 449 } 450 451 // Read the tag at a given offset rather then the tag_index. 452 // This method is used when reading mAB tags where nested curveType are 453 // present that are not part of the tag_index. 454 static struct curveType *read_curveType(struct mem_source *src, uint32_t offset, uint32_t *len) 455 { 456 static const uint32_t COUNT_TO_LENGTH[5] = {1, 3, 4, 5, 7}; 457 struct curveType *curve = NULL; 458 uint32_t type = read_u32(src, offset); 459 uint32_t count; 460 int i; 461 462 if (type != CURVE_TYPE && type != PARAMETRIC_CURVE_TYPE) { 463 invalid_source(src, "unexpected type, expected CURV or PARA"); 464 return NULL; 465 } 466 467 if (type == CURVE_TYPE) { 468 count = read_u32(src, offset+8); 469 470 #define MAX_CURVE_ENTRIES 40000 //arbitrary 471 if (count > MAX_CURVE_ENTRIES) { 472 invalid_source(src, "curve size too large"); 473 return NULL; 474 } 475 curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*count); 476 if (!curve) 477 return NULL; 478 479 curve->count = count; 480 curve->type = type; 481 482 for (i=0; i<count; i++) { 483 curve->data[i] = read_u16(src, offset + 12 + i*2); 484 } 485 *len = 12 + count * 2; 486 } else { //PARAMETRIC_CURVE_TYPE 487 count = read_u16(src, offset+8); 488 489 if (count > 4) { 490 invalid_source(src, "parametric function type not supported."); 491 return NULL; 492 } 493 494 curve = malloc(sizeof(struct curveType)); 495 if (!curve) 496 return NULL; 497 498 curve->count = count; 499 curve->type = type; 500 501 for (i=0; i < COUNT_TO_LENGTH[count]; i++) { 502 curve->parameter[i] = s15Fixed16Number_to_float(read_s15Fixed16Number(src, offset + 12 + i*4)); 503 } 504 *len = 12 + COUNT_TO_LENGTH[count] * 4; 505 506 if ((count == 1 || count == 2)) { 507 /* we have a type 1 or type 2 function that has a division by 'a' */ 508 float a = curve->parameter[1]; 509 if (a == 0.f) 510 invalid_source(src, "parametricCurve definition causes division by zero."); 511 } 512 } 513 514 return curve; 515 } 516 517 static struct curveType *read_tag_curveType(struct mem_source *src, struct tag_index index, uint32_t tag_id) 518 { 519 struct tag *tag = find_tag(index, tag_id); 520 struct curveType *curve = NULL; 521 if (tag) { 522 uint32_t len; 523 return read_curveType(src, tag->offset, &len); 524 } else { 525 invalid_source(src, "missing curvetag"); 526 } 527 528 return curve; 529 } 530 531 #define MAX_CLUT_SIZE 500000 // arbitrary 532 #define MAX_CHANNELS 10 // arbitrary 533 static void read_nested_curveType(struct mem_source *src, struct curveType *(*curveArray)[MAX_CHANNELS], uint8_t num_channels, uint32_t curve_offset) 534 { 535 uint32_t channel_offset = 0; 536 int i; 537 for (i = 0; i < num_channels; i++) { 538 uint32_t tag_len = ~0; 539 540 (*curveArray)[i] = read_curveType(src, curve_offset + channel_offset, &tag_len); 541 if (!(*curveArray)[i]) { 542 invalid_source(src, "invalid nested curveType curve"); 543 } 544 545 if (tag_len == ~0) { 546 invalid_source(src, "invalid nested curveType tag length"); 547 return; 548 } 549 550 channel_offset += tag_len; 551 // 4 byte aligned 552 if ((tag_len % 4) != 0) 553 channel_offset += 4 - (tag_len % 4); 554 } 555 } 556 557 static void mAB_release(struct lutmABType *lut) 558 { 559 uint8_t i; 560 561 for (i = 0; i < lut->num_in_channels; i++){ 562 free(lut->a_curves[i]); 563 } 564 for (i = 0; i < lut->num_out_channels; i++){ 565 free(lut->b_curves[i]); 566 free(lut->m_curves[i]); 567 } 568 free(lut); 569 } 570 571 /* See section 10.10 for specs */ 572 static struct lutmABType *read_tag_lutmABType(struct mem_source *src, struct tag_index index, uint32_t tag_id) 573 { 574 struct tag *tag = find_tag(index, tag_id); 575 uint32_t offset = tag->offset; 576 uint32_t a_curve_offset, b_curve_offset, m_curve_offset; 577 uint32_t matrix_offset; 578 uint32_t clut_offset; 579 uint32_t clut_size = 1; 580 uint8_t clut_precision; 581 uint32_t type = read_u32(src, offset); 582 uint8_t num_in_channels, num_out_channels; 583 struct lutmABType *lut; 584 int i; 585 586 if (type != LUT_MAB_TYPE && type != LUT_MBA_TYPE) { 587 return NULL; 588 } 589 590 num_in_channels = read_u8(src, offset + 8); 591 num_out_channels = read_u8(src, offset + 8); 592 if (num_in_channels > MAX_CHANNELS || num_out_channels > MAX_CHANNELS) 593 return NULL; 594 595 // We require 3in/out channels since we only support RGB->XYZ (or RGB->LAB) 596 // XXX: If we remove this restriction make sure that the number of channels 597 // is less or equal to the maximum number of mAB curves in qcmsint.h 598 // also check for clut_size overflow. 599 if (num_in_channels != 3 || num_out_channels != 3) 600 return NULL; 601 602 // some of this data is optional and is denoted by a zero offset 603 // we also use this to track their existance 604 a_curve_offset = read_u32(src, offset + 28); 605 clut_offset = read_u32(src, offset + 24); 606 m_curve_offset = read_u32(src, offset + 20); 607 matrix_offset = read_u32(src, offset + 16); 608 b_curve_offset = read_u32(src, offset + 12); 609 610 // Convert offsets relative to the tag to relative to the profile 611 // preserve zero for optional fields 612 if (a_curve_offset) 613 a_curve_offset += offset; 614 if (clut_offset) 615 clut_offset += offset; 616 if (m_curve_offset) 617 m_curve_offset += offset; 618 if (matrix_offset) 619 matrix_offset += offset; 620 if (b_curve_offset) 621 b_curve_offset += offset; 622 623 if (clut_offset) { 624 assert (num_in_channels == 3); 625 // clut_size can not overflow since lg(256^num_in_channels) = 24 bits. 626 for (i = 0; i < num_in_channels; i++) { 627 clut_size *= read_u8(src, clut_offset + i); 628 } 629 } else { 630 clut_size = 0; 631 } 632 633 // 24bits * 3 won't overflow either 634 clut_size = clut_size * num_out_channels; 635 636 if (clut_size > MAX_CLUT_SIZE) 637 return NULL; 638 639 lut = malloc(sizeof(struct lutmABType) + (clut_size) * sizeof(float)); 640 if (!lut) 641 return NULL; 642 // we'll fill in the rest below 643 memset(lut, 0, sizeof(struct lutmABType)); 644 lut->clut_table = &lut->clut_table_data[0]; 645 646 for (i = 0; i < num_in_channels; i++) { 647 lut->num_grid_points[i] = read_u8(src, clut_offset + i); 648 } 649 650 // Reverse the processing of transformation elements for mBA type. 651 lut->reversed = (type == LUT_MBA_TYPE); 652 653 lut->num_in_channels = num_in_channels; 654 lut->num_out_channels = num_out_channels; 655 656 if (matrix_offset) { 657 // read the matrix if we have it 658 lut->e00 = read_s15Fixed16Number(src, matrix_offset+4*0); 659 lut->e01 = read_s15Fixed16Number(src, matrix_offset+4*1); 660 lut->e02 = read_s15Fixed16Number(src, matrix_offset+4*2); 661 lut->e10 = read_s15Fixed16Number(src, matrix_offset+4*3); 662 lut->e11 = read_s15Fixed16Number(src, matrix_offset+4*4); 663 lut->e12 = read_s15Fixed16Number(src, matrix_offset+4*5); 664 lut->e20 = read_s15Fixed16Number(src, matrix_offset+4*6); 665 lut->e21 = read_s15Fixed16Number(src, matrix_offset+4*7); 666 lut->e22 = read_s15Fixed16Number(src, matrix_offset+4*8); 667 lut->e03 = read_s15Fixed16Number(src, matrix_offset+4*9); 668 lut->e13 = read_s15Fixed16Number(src, matrix_offset+4*10); 669 lut->e23 = read_s15Fixed16Number(src, matrix_offset+4*11); 670 } 671 672 if (a_curve_offset) { 673 read_nested_curveType(src, &lut->a_curves, num_in_channels, a_curve_offset); 674 } 675 if (m_curve_offset) { 676 read_nested_curveType(src, &lut->m_curves, num_out_channels, m_curve_offset); 677 } 678 if (b_curve_offset) { 679 read_nested_curveType(src, &lut->b_curves, num_out_channels, b_curve_offset); 680 } else { 681 invalid_source(src, "B curves required"); 682 } 683 684 if (clut_offset) { 685 clut_precision = read_u8(src, clut_offset + 16); 686 if (clut_precision == 1) { 687 for (i = 0; i < clut_size; i++) { 688 lut->clut_table[i] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + 20 + i*1)); 689 } 690 } else if (clut_precision == 2) { 691 for (i = 0; i < clut_size; i++) { 692 lut->clut_table[i] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + 20 + i*2)); 693 } 694 } else { 695 invalid_source(src, "Invalid clut precision"); 696 } 697 } 698 699 if (!src->valid) { 700 mAB_release(lut); 701 return NULL; 702 } 703 704 return lut; 705 } 706 707 static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index index, uint32_t tag_id) 708 { 709 struct tag *tag = find_tag(index, tag_id); 710 uint32_t offset = tag->offset; 711 uint32_t type = read_u32(src, offset); 712 uint16_t num_input_table_entries; 713 uint16_t num_output_table_entries; 714 uint8_t in_chan, grid_points, out_chan; 715 size_t clut_offset, output_offset; 716 uint32_t clut_size; 717 size_t entry_size; 718 struct lutType *lut; 719 int i; 720 721 /* I'm not sure why the spec specifies a fixed number of entries for LUT8 tables even though 722 * they have room for the num_entries fields */ 723 if (type == LUT8_TYPE) { 724 num_input_table_entries = 256; 725 num_output_table_entries = 256; 726 entry_size = 1; 727 } else if (type == LUT16_TYPE) { 728 num_input_table_entries = read_u16(src, offset + 48); 729 num_output_table_entries = read_u16(src, offset + 50); 730 entry_size = 2; 731 } else { 732 assert(0); // the caller checks that this doesn't happen 733 invalid_source(src, "Unexpected lut type"); 734 return NULL; 735 } 736 737 in_chan = read_u8(src, offset + 8); 738 out_chan = read_u8(src, offset + 9); 739 grid_points = read_u8(src, offset + 10); 740 741 clut_size = pow(grid_points, in_chan); 742 if (clut_size > MAX_CLUT_SIZE) { 743 return NULL; 744 } 745 746 if (in_chan != 3 || out_chan != 3) { 747 return NULL; 748 } 749 750 lut = malloc(sizeof(struct lutType) + (num_input_table_entries * in_chan + clut_size*out_chan + num_output_table_entries * out_chan)*sizeof(float)); 751 if (!lut) { 752 return NULL; 753 } 754 755 /* compute the offsets of tables */ 756 lut->input_table = &lut->table_data[0]; 757 lut->clut_table = &lut->table_data[in_chan*num_input_table_entries]; 758 lut->output_table = &lut->table_data[in_chan*num_input_table_entries + clut_size*out_chan]; 759 760 lut->num_input_table_entries = num_input_table_entries; 761 lut->num_output_table_entries = num_output_table_entries; 762 lut->num_input_channels = read_u8(src, offset + 8); 763 lut->num_output_channels = read_u8(src, offset + 9); 764 lut->num_clut_grid_points = read_u8(src, offset + 10); 765 lut->e00 = read_s15Fixed16Number(src, offset+12); 766 lut->e01 = read_s15Fixed16Number(src, offset+16); 767 lut->e02 = read_s15Fixed16Number(src, offset+20); 768 lut->e10 = read_s15Fixed16Number(src, offset+24); 769 lut->e11 = read_s15Fixed16Number(src, offset+28); 770 lut->e12 = read_s15Fixed16Number(src, offset+32); 771 lut->e20 = read_s15Fixed16Number(src, offset+36); 772 lut->e21 = read_s15Fixed16Number(src, offset+40); 773 lut->e22 = read_s15Fixed16Number(src, offset+44); 774 775 for (i = 0; i < lut->num_input_table_entries * in_chan; i++) { 776 if (type == LUT8_TYPE) { 777 lut->input_table[i] = uInt8Number_to_float(read_uInt8Number(src, offset + 52 + i * entry_size)); 778 } else { 779 lut->input_table[i] = uInt16Number_to_float(read_uInt16Number(src, offset + 52 + i * entry_size)); 780 } 781 } 782 783 clut_offset = offset + 52 + lut->num_input_table_entries * in_chan * entry_size; 784 for (i = 0; i < clut_size * out_chan; i+=3) { 785 if (type == LUT8_TYPE) { 786 lut->clut_table[i+0] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 0)); 787 lut->clut_table[i+1] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 1)); 788 lut->clut_table[i+2] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 2)); 789 } else { 790 lut->clut_table[i+0] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 0)); 791 lut->clut_table[i+1] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 2)); 792 lut->clut_table[i+2] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 4)); 793 } 794 } 795 796 output_offset = clut_offset + clut_size * out_chan * entry_size; 797 for (i = 0; i < lut->num_output_table_entries * out_chan; i++) { 798 if (type == LUT8_TYPE) { 799 lut->output_table[i] = uInt8Number_to_float(read_uInt8Number(src, output_offset + i*entry_size)); 800 } else { 801 lut->output_table[i] = uInt16Number_to_float(read_uInt16Number(src, output_offset + i*entry_size)); 802 } 803 } 804 805 return lut; 806 } 807 808 static void read_rendering_intent(qcms_profile *profile, struct mem_source *src) 809 { 810 profile->rendering_intent = read_u32(src, 64); 811 switch (profile->rendering_intent) { 812 case QCMS_INTENT_PERCEPTUAL: 813 case QCMS_INTENT_SATURATION: 814 case QCMS_INTENT_RELATIVE_COLORIMETRIC: 815 case QCMS_INTENT_ABSOLUTE_COLORIMETRIC: 816 break; 817 default: 818 invalid_source(src, "unknown rendering intent"); 819 } 820 } 821 822 qcms_profile *qcms_profile_create(void) 823 { 824 return calloc(sizeof(qcms_profile), 1); 825 } 826 827 828 829 /* build sRGB gamma table */ 830 /* based on cmsBuildParametricGamma() */ 831 static uint16_t *build_sRGB_gamma_table(int num_entries) 832 { 833 int i; 834 /* taken from lcms: Build_sRGBGamma() */ 835 double gamma = 2.4; 836 double a = 1./1.055; 837 double b = 0.055/1.055; 838 double c = 1./12.92; 839 double d = 0.04045; 840 841 uint16_t *table = malloc(sizeof(uint16_t) * num_entries); 842 if (!table) 843 return NULL; 844 845 for (i=0; i<num_entries; i++) { 846 double x = (double)i / (num_entries-1); 847 double y, output; 848 // IEC 61966-2.1 (sRGB) 849 // Y = (aX + b)^Gamma | X >= d 850 // Y = cX | X < d 851 if (x >= d) { 852 double e = (a*x + b); 853 if (e > 0) 854 y = pow(e, gamma); 855 else 856 y = 0; 857 } else { 858 y = c*x; 859 } 860 861 // Saturate -- this could likely move to a separate function 862 output = y * 65535. + .5; 863 if (output > 65535.) 864 output = 65535; 865 if (output < 0) 866 output = 0; 867 table[i] = (uint16_t)floor(output); 868 } 869 return table; 870 } 871 872 static struct curveType *curve_from_table(uint16_t *table, int num_entries) 873 { 874 struct curveType *curve; 875 int i; 876 curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*num_entries); 877 if (!curve) 878 return NULL; 879 curve->type = CURVE_TYPE; 880 curve->count = num_entries; 881 for (i = 0; i < num_entries; i++) { 882 curve->data[i] = table[i]; 883 } 884 return curve; 885 } 886 887 static uint16_t float_to_u8Fixed8Number(float a) 888 { 889 if (a > (255.f + 255.f/256)) 890 return 0xffff; 891 else if (a < 0.f) 892 return 0; 893 else 894 return floor(a*256.f + .5f); 895 } 896 897 static struct curveType *curve_from_gamma(float gamma) 898 { 899 struct curveType *curve; 900 int num_entries = 1; 901 curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*num_entries); 902 if (!curve) 903 return NULL; 904 curve->count = num_entries; 905 curve->data[0] = float_to_u8Fixed8Number(gamma); 906 return curve; 907 } 908 909 910 //XXX: it would be nice if we had a way of ensuring 911 // everything in a profile was initialized regardless of how it was created 912 913 //XXX: should this also be taking a black_point? 914 /* similar to CGColorSpaceCreateCalibratedRGB */ 915 qcms_profile* qcms_profile_create_rgb_with_gamma( 916 qcms_CIE_xyY white_point, 917 qcms_CIE_xyYTRIPLE primaries, 918 float gamma) 919 { 920 qcms_profile* profile = qcms_profile_create(); 921 if (!profile) 922 return NO_MEM_PROFILE; 923 924 //XXX: should store the whitepoint 925 if (!set_rgb_colorants(profile, white_point, primaries)) { 926 qcms_profile_release(profile); 927 return INVALID_PROFILE; 928 } 929 930 profile->redTRC = curve_from_gamma(gamma); 931 profile->blueTRC = curve_from_gamma(gamma); 932 profile->greenTRC = curve_from_gamma(gamma); 933 934 if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC) { 935 qcms_profile_release(profile); 936 return NO_MEM_PROFILE; 937 } 938 profile->class = DISPLAY_DEVICE_PROFILE; 939 profile->rendering_intent = QCMS_INTENT_PERCEPTUAL; 940 profile->color_space = RGB_SIGNATURE; 941 return profile; 942 } 943 944 qcms_profile* qcms_profile_create_rgb_with_table( 945 qcms_CIE_xyY white_point, 946 qcms_CIE_xyYTRIPLE primaries, 947 uint16_t *table, int num_entries) 948 { 949 qcms_profile* profile = qcms_profile_create(); 950 if (!profile) 951 return NO_MEM_PROFILE; 952 953 //XXX: should store the whitepoint 954 if (!set_rgb_colorants(profile, white_point, primaries)) { 955 qcms_profile_release(profile); 956 return INVALID_PROFILE; 957 } 958 959 profile->redTRC = curve_from_table(table, num_entries); 960 profile->blueTRC = curve_from_table(table, num_entries); 961 profile->greenTRC = curve_from_table(table, num_entries); 962 963 if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC) { 964 qcms_profile_release(profile); 965 return NO_MEM_PROFILE; 966 } 967 profile->class = DISPLAY_DEVICE_PROFILE; 968 profile->rendering_intent = QCMS_INTENT_PERCEPTUAL; 969 profile->color_space = RGB_SIGNATURE; 970 return profile; 971 } 972 973 /* from lcms: cmsWhitePointFromTemp */ 974 /* tempK must be >= 4000. and <= 25000. 975 * similar to argyll: icx_DTEMP2XYZ() */ 976 static qcms_CIE_xyY white_point_from_temp(int temp_K) 977 { 978 qcms_CIE_xyY white_point; 979 double x, y; 980 double T, T2, T3; 981 // double M1, M2; 982 983 // No optimization provided. 984 T = temp_K; 985 T2 = T*T; // Square 986 T3 = T2*T; // Cube 987 988 // For correlated color temperature (T) between 4000K and 7000K: 989 if (T >= 4000. && T <= 7000.) { 990 x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063; 991 } else { 992 // or for correlated color temperature (T) between 7000K and 25000K: 993 if (T > 7000.0 && T <= 25000.0) { 994 x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040; 995 } else { 996 assert(0 && "invalid temp"); 997 } 998 } 999 1000 // Obtain y(x) 1001 1002 y = -3.000*(x*x) + 2.870*x - 0.275; 1003 1004 // wave factors (not used, but here for futures extensions) 1005 1006 // M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y); 1007 // M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y); 1008 1009 // Fill white_point struct 1010 white_point.x = x; 1011 white_point.y = y; 1012 white_point.Y = 1.0; 1013 1014 return white_point; 1015 } 1016 1017 qcms_profile* qcms_profile_sRGB(void) 1018 { 1019 qcms_profile *profile; 1020 uint16_t *table; 1021 1022 qcms_CIE_xyYTRIPLE Rec709Primaries = { 1023 {0.6400, 0.3300, 1.0}, 1024 {0.3000, 0.6000, 1.0}, 1025 {0.1500, 0.0600, 1.0} 1026 }; 1027 qcms_CIE_xyY D65; 1028 1029 D65 = white_point_from_temp(6504); 1030 1031 table = build_sRGB_gamma_table(1024); 1032 1033 if (!table) 1034 return NO_MEM_PROFILE; 1035 1036 profile = qcms_profile_create_rgb_with_table(D65, Rec709Primaries, table, 1024); 1037 if (profile) 1038 strcpy(profile->description, "sRGB IEC61966-2.1"); 1039 1040 free(table); 1041 return profile; 1042 } 1043 1044 1045 /* qcms_profile_from_memory does not hold a reference to the memory passed in */ 1046 qcms_profile* qcms_profile_from_memory(const void *mem, size_t size) 1047 { 1048 uint32_t length; 1049 struct mem_source source; 1050 struct mem_source *src = &source; 1051 struct tag_index index; 1052 qcms_profile *profile; 1053 1054 source.buf = mem; 1055 source.size = size; 1056 source.valid = true; 1057 1058 if (size < 4) 1059 return INVALID_PROFILE; 1060 1061 length = read_u32(src, 0); 1062 if (length <= size) { 1063 // shrink the area that we can read if appropriate 1064 source.size = length; 1065 } else { 1066 return INVALID_PROFILE; 1067 } 1068 1069 /* ensure that the profile size is sane so it's easier to reason about */ 1070 if (source.size <= 64 || source.size >= MAX_PROFILE_SIZE) 1071 return INVALID_PROFILE; 1072 1073 profile = qcms_profile_create(); 1074 if (!profile) 1075 return NO_MEM_PROFILE; 1076 1077 check_CMM_type_signature(src); 1078 check_profile_version(src); 1079 read_class_signature(profile, src); 1080 read_rendering_intent(profile, src); 1081 read_color_space(profile, src); 1082 read_pcs(profile, src); 1083 //TODO read rest of profile stuff 1084 1085 if (!src->valid) 1086 goto invalid_profile; 1087 1088 index = read_tag_table(profile, src); 1089 if (!src->valid || !index.tags) 1090 goto invalid_tag_table; 1091 1092 if (!read_tag_descType(profile, src, index, TAG_desc)) 1093 goto invalid_tag_table; 1094 1095 if (find_tag(index, TAG_CHAD)) { 1096 profile->chromaticAdaption = read_tag_s15Fixed16ArrayType(src, index, TAG_CHAD); 1097 } else { 1098 profile->chromaticAdaption.invalid = true; //Signal the data is not present 1099 } 1100 1101 if (profile->class == DISPLAY_DEVICE_PROFILE || profile->class == INPUT_DEVICE_PROFILE || 1102 profile->class == OUTPUT_DEVICE_PROFILE || profile->class == COLOR_SPACE_PROFILE) { 1103 if (profile->color_space == RGB_SIGNATURE) { 1104 if (find_tag(index, TAG_A2B0)) { 1105 if (read_u32(src, find_tag(index, TAG_A2B0)->offset) == LUT8_TYPE || 1106 read_u32(src, find_tag(index, TAG_A2B0)->offset) == LUT16_TYPE) { 1107 profile->A2B0 = read_tag_lutType(src, index, TAG_A2B0); 1108 } else if (read_u32(src, find_tag(index, TAG_A2B0)->offset) == LUT_MAB_TYPE) { 1109 profile->mAB = read_tag_lutmABType(src, index, TAG_A2B0); 1110 } 1111 } 1112 if (find_tag(index, TAG_B2A0)) { 1113 if (read_u32(src, find_tag(index, TAG_B2A0)->offset) == LUT8_TYPE || 1114 read_u32(src, find_tag(index, TAG_B2A0)->offset) == LUT16_TYPE) { 1115 profile->B2A0 = read_tag_lutType(src, index, TAG_B2A0); 1116 } else if (read_u32(src, find_tag(index, TAG_B2A0)->offset) == LUT_MBA_TYPE) { 1117 profile->mBA = read_tag_lutmABType(src, index, TAG_B2A0); 1118 } 1119 } 1120 if (find_tag(index, TAG_rXYZ) || !qcms_supports_iccv4) { 1121 profile->redColorant = read_tag_XYZType(src, index, TAG_rXYZ); 1122 profile->greenColorant = read_tag_XYZType(src, index, TAG_gXYZ); 1123 profile->blueColorant = read_tag_XYZType(src, index, TAG_bXYZ); 1124 } 1125 1126 if (!src->valid) 1127 goto invalid_tag_table; 1128 1129 if (find_tag(index, TAG_rTRC) || !qcms_supports_iccv4) { 1130 profile->redTRC = read_tag_curveType(src, index, TAG_rTRC); 1131 profile->greenTRC = read_tag_curveType(src, index, TAG_gTRC); 1132 profile->blueTRC = read_tag_curveType(src, index, TAG_bTRC); 1133 1134 if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC) 1135 goto invalid_tag_table; 1136 } 1137 } else if (profile->color_space == GRAY_SIGNATURE) { 1138 1139 profile->grayTRC = read_tag_curveType(src, index, TAG_kTRC); 1140 if (!profile->grayTRC) 1141 goto invalid_tag_table; 1142 1143 } else { 1144 assert(0 && "read_color_space protects against entering here"); 1145 goto invalid_tag_table; 1146 } 1147 } else { 1148 goto invalid_tag_table; 1149 } 1150 1151 if (!src->valid) 1152 goto invalid_tag_table; 1153 1154 free(index.tags); 1155 1156 return profile; 1157 1158 invalid_tag_table: 1159 free(index.tags); 1160 invalid_profile: 1161 qcms_profile_release(profile); 1162 return INVALID_PROFILE; 1163 } 1164 1165 qcms_bool qcms_profile_match(qcms_profile *p1, qcms_profile *p2) 1166 { 1167 return memcmp(p1->description, p2->description, sizeof p1->description) == 0; 1168 } 1169 1170 qcms_intent qcms_profile_get_rendering_intent(qcms_profile *profile) 1171 { 1172 return profile->rendering_intent; 1173 } 1174 1175 icColorSpaceSignature 1176 qcms_profile_get_color_space(qcms_profile *profile) 1177 { 1178 return profile->color_space; 1179 } 1180 1181 static void lut_release(struct lutType *lut) 1182 { 1183 free(lut); 1184 } 1185 1186 void qcms_profile_release(qcms_profile *profile) 1187 { 1188 if (profile->output_table_r) 1189 precache_release(profile->output_table_r); 1190 if (profile->output_table_g) 1191 precache_release(profile->output_table_g); 1192 if (profile->output_table_b) 1193 precache_release(profile->output_table_b); 1194 1195 if (profile->A2B0) 1196 lut_release(profile->A2B0); 1197 if (profile->B2A0) 1198 lut_release(profile->B2A0); 1199 1200 if (profile->mAB) 1201 mAB_release(profile->mAB); 1202 if (profile->mBA) 1203 mAB_release(profile->mBA); 1204 1205 free(profile->redTRC); 1206 free(profile->blueTRC); 1207 free(profile->greenTRC); 1208 free(profile->grayTRC); 1209 free(profile); 1210 } 1211 1212 1213 #include <stdio.h> 1214 qcms_profile* qcms_profile_from_file(FILE *file) 1215 { 1216 uint32_t length, remaining_length; 1217 qcms_profile *profile; 1218 size_t read_length; 1219 be32 length_be; 1220 void *data; 1221 1222 if (fread(&length_be, 1, sizeof(length_be), file) != sizeof(length_be)) 1223 return BAD_VALUE_PROFILE; 1224 1225 length = be32_to_cpu(length_be); 1226 if (length > MAX_PROFILE_SIZE || length < sizeof(length_be)) 1227 return BAD_VALUE_PROFILE; 1228 1229 /* allocate room for the entire profile */ 1230 data = malloc(length); 1231 if (!data) 1232 return NO_MEM_PROFILE; 1233 1234 /* copy in length to the front so that the buffer will contain the entire profile */ 1235 *((be32*)data) = length_be; 1236 remaining_length = length - sizeof(length_be); 1237 1238 /* read the rest profile */ 1239 read_length = fread((unsigned char*)data + sizeof(length_be), 1, remaining_length, file); 1240 if (read_length != remaining_length) { 1241 free(data); 1242 return INVALID_PROFILE; 1243 } 1244 1245 profile = qcms_profile_from_memory(data, length); 1246 free(data); 1247 return profile; 1248 } 1249 1250 qcms_profile* qcms_profile_from_path(const char *path) 1251 { 1252 qcms_profile *profile = NULL; 1253 FILE *file = fopen(path, "rb"); 1254 if (file) { 1255 profile = qcms_profile_from_file(file); 1256 fclose(file); 1257 } 1258 return profile; 1259 } 1260 1261 #ifdef _WIN32 1262 /* Unicode path version */ 1263 qcms_profile* qcms_profile_from_unicode_path(const wchar_t *path) 1264 { 1265 qcms_profile *profile = NULL; 1266 FILE *file = _wfopen(path, L"rb"); 1267 if (file) { 1268 profile = qcms_profile_from_file(file); 1269 fclose(file); 1270 } 1271 return profile; 1272 } 1273 #endif 1274