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