1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/qcms/iccread.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1384 @@ 1.4 +/* vim: set ts=8 sw=8 noexpandtab: */ 1.5 +// qcms 1.6 +// Copyright (C) 2009 Mozilla Foundation 1.7 +// Copyright (C) 1998-2007 Marti Maria 1.8 +// 1.9 +// Permission is hereby granted, free of charge, to any person obtaining 1.10 +// a copy of this software and associated documentation files (the "Software"), 1.11 +// to deal in the Software without restriction, including without limitation 1.12 +// the rights to use, copy, modify, merge, publish, distribute, sublicense, 1.13 +// and/or sell copies of the Software, and to permit persons to whom the Software 1.14 +// is furnished to do so, subject to the following conditions: 1.15 +// 1.16 +// The above copyright notice and this permission notice shall be included in 1.17 +// all copies or substantial portions of the Software. 1.18 +// 1.19 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 1.20 +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 1.21 +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 1.22 +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 1.23 +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 1.24 +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 1.25 +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 1.26 + 1.27 +#include <math.h> 1.28 +#include <assert.h> 1.29 +#include <stdlib.h> 1.30 +#include <string.h> //memset 1.31 +#include "qcmsint.h" 1.32 + 1.33 +/* It might be worth having a unified limit on content controlled 1.34 + * allocation per profile. This would remove the need for many 1.35 + * of the arbitrary limits that we used */ 1.36 + 1.37 +typedef uint32_t be32; 1.38 +typedef uint16_t be16; 1.39 + 1.40 +static be32 cpu_to_be32(uint32_t v) 1.41 +{ 1.42 +#ifdef IS_LITTLE_ENDIAN 1.43 + return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | ((v & 0xff000000) >> 24); 1.44 +#else 1.45 + return v; 1.46 +#endif 1.47 +} 1.48 + 1.49 +static be16 cpu_to_be16(uint16_t v) 1.50 +{ 1.51 +#ifdef IS_LITTLE_ENDIAN 1.52 + return ((v & 0xff) << 8) | ((v & 0xff00) >> 8); 1.53 +#else 1.54 + return v; 1.55 +#endif 1.56 +} 1.57 + 1.58 +static uint32_t be32_to_cpu(be32 v) 1.59 +{ 1.60 +#ifdef IS_LITTLE_ENDIAN 1.61 + return ((v & 0xff) << 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8) | ((v & 0xff000000) >> 24); 1.62 + //return __builtin_bswap32(v); 1.63 +#else 1.64 + return v; 1.65 +#endif 1.66 +} 1.67 + 1.68 +static uint16_t be16_to_cpu(be16 v) 1.69 +{ 1.70 +#ifdef IS_LITTLE_ENDIAN 1.71 + return ((v & 0xff) << 8) | ((v & 0xff00) >> 8); 1.72 +#else 1.73 + return v; 1.74 +#endif 1.75 +} 1.76 + 1.77 +/* a wrapper around the memory that we are going to parse 1.78 + * into a qcms_profile */ 1.79 +struct mem_source 1.80 +{ 1.81 + const unsigned char *buf; 1.82 + size_t size; 1.83 + qcms_bool valid; 1.84 + const char *invalid_reason; 1.85 +}; 1.86 + 1.87 +static void invalid_source(struct mem_source *mem, const char *reason) 1.88 +{ 1.89 + mem->valid = false; 1.90 + mem->invalid_reason = reason; 1.91 +} 1.92 + 1.93 +static uint32_t read_u32(struct mem_source *mem, size_t offset) 1.94 +{ 1.95 + /* Subtract from mem->size instead of the more intuitive adding to offset. 1.96 + * This avoids overflowing offset. The subtraction is safe because 1.97 + * mem->size is guaranteed to be > 4 */ 1.98 + if (offset > mem->size - 4) { 1.99 + invalid_source(mem, "Invalid offset"); 1.100 + return 0; 1.101 + } else { 1.102 + be32 k; 1.103 + memcpy(&k, mem->buf + offset, sizeof(k)); 1.104 + return be32_to_cpu(k); 1.105 + } 1.106 +} 1.107 + 1.108 +static uint16_t read_u16(struct mem_source *mem, size_t offset) 1.109 +{ 1.110 + if (offset > mem->size - 2) { 1.111 + invalid_source(mem, "Invalid offset"); 1.112 + return 0; 1.113 + } else { 1.114 + be16 k; 1.115 + memcpy(&k, mem->buf + offset, sizeof(k)); 1.116 + return be16_to_cpu(k); 1.117 + } 1.118 +} 1.119 + 1.120 +static uint8_t read_u8(struct mem_source *mem, size_t offset) 1.121 +{ 1.122 + if (offset > mem->size - 1) { 1.123 + invalid_source(mem, "Invalid offset"); 1.124 + return 0; 1.125 + } else { 1.126 + return *(uint8_t*)(mem->buf + offset); 1.127 + } 1.128 +} 1.129 + 1.130 +static s15Fixed16Number read_s15Fixed16Number(struct mem_source *mem, size_t offset) 1.131 +{ 1.132 + return read_u32(mem, offset); 1.133 +} 1.134 + 1.135 +static uInt8Number read_uInt8Number(struct mem_source *mem, size_t offset) 1.136 +{ 1.137 + return read_u8(mem, offset); 1.138 +} 1.139 + 1.140 +static uInt16Number read_uInt16Number(struct mem_source *mem, size_t offset) 1.141 +{ 1.142 + return read_u16(mem, offset); 1.143 +} 1.144 + 1.145 +static void write_u32(void *mem, size_t offset, uint32_t value) 1.146 +{ 1.147 + *((uint32_t *)((unsigned char*)mem + offset)) = cpu_to_be32(value); 1.148 +} 1.149 + 1.150 +static void write_u16(void *mem, size_t offset, uint16_t value) 1.151 +{ 1.152 + *((uint16_t *)((unsigned char*)mem + offset)) = cpu_to_be16(value); 1.153 +} 1.154 + 1.155 +#define BAD_VALUE_PROFILE NULL 1.156 +#define INVALID_PROFILE NULL 1.157 +#define NO_MEM_PROFILE NULL 1.158 + 1.159 +/* An arbitrary 4MB limit on profile size */ 1.160 +#define MAX_PROFILE_SIZE 1024*1024*4 1.161 +#define MAX_TAG_COUNT 1024 1.162 + 1.163 +static void check_CMM_type_signature(struct mem_source *src) 1.164 +{ 1.165 + //uint32_t CMM_type_signature = read_u32(src, 4); 1.166 + //TODO: do the check? 1.167 + 1.168 +} 1.169 + 1.170 +static void check_profile_version(struct mem_source *src) 1.171 +{ 1.172 + 1.173 + /* 1.174 + uint8_t major_revision = read_u8(src, 8 + 0); 1.175 + uint8_t minor_revision = read_u8(src, 8 + 1); 1.176 + */ 1.177 + uint8_t reserved1 = read_u8(src, 8 + 2); 1.178 + uint8_t reserved2 = read_u8(src, 8 + 3); 1.179 + /* Checking the version doesn't buy us anything 1.180 + if (major_revision != 0x4) { 1.181 + if (major_revision > 0x2) 1.182 + invalid_source(src, "Unsupported major revision"); 1.183 + if (minor_revision > 0x40) 1.184 + invalid_source(src, "Unsupported minor revision"); 1.185 + } 1.186 + */ 1.187 + if (reserved1 != 0 || reserved2 != 0) 1.188 + invalid_source(src, "Invalid reserved bytes"); 1.189 +} 1.190 + 1.191 +#define INPUT_DEVICE_PROFILE 0x73636e72 // 'scnr' 1.192 +#define DISPLAY_DEVICE_PROFILE 0x6d6e7472 // 'mntr' 1.193 +#define OUTPUT_DEVICE_PROFILE 0x70727472 // 'prtr' 1.194 +#define DEVICE_LINK_PROFILE 0x6c696e6b // 'link' 1.195 +#define COLOR_SPACE_PROFILE 0x73706163 // 'spac' 1.196 +#define ABSTRACT_PROFILE 0x61627374 // 'abst' 1.197 +#define NAMED_COLOR_PROFILE 0x6e6d636c // 'nmcl' 1.198 + 1.199 +static void read_class_signature(qcms_profile *profile, struct mem_source *mem) 1.200 +{ 1.201 + profile->class = read_u32(mem, 12); 1.202 + switch (profile->class) { 1.203 + case DISPLAY_DEVICE_PROFILE: 1.204 + case INPUT_DEVICE_PROFILE: 1.205 + case OUTPUT_DEVICE_PROFILE: 1.206 + case COLOR_SPACE_PROFILE: 1.207 + break; 1.208 + default: 1.209 + invalid_source(mem, "Invalid Profile/Device Class signature"); 1.210 + } 1.211 +} 1.212 + 1.213 +static void read_color_space(qcms_profile *profile, struct mem_source *mem) 1.214 +{ 1.215 + profile->color_space = read_u32(mem, 16); 1.216 + switch (profile->color_space) { 1.217 + case RGB_SIGNATURE: 1.218 + case GRAY_SIGNATURE: 1.219 + break; 1.220 + default: 1.221 + invalid_source(mem, "Unsupported colorspace"); 1.222 + } 1.223 +} 1.224 + 1.225 +static void read_pcs(qcms_profile *profile, struct mem_source *mem) 1.226 +{ 1.227 + profile->pcs = read_u32(mem, 20); 1.228 + switch (profile->pcs) { 1.229 + case XYZ_SIGNATURE: 1.230 + case LAB_SIGNATURE: 1.231 + break; 1.232 + default: 1.233 + invalid_source(mem, "Unsupported pcs"); 1.234 + } 1.235 +} 1.236 + 1.237 +struct tag 1.238 +{ 1.239 + uint32_t signature; 1.240 + uint32_t offset; 1.241 + uint32_t size; 1.242 +}; 1.243 + 1.244 +struct tag_index { 1.245 + uint32_t count; 1.246 + struct tag *tags; 1.247 +}; 1.248 + 1.249 +static struct tag_index read_tag_table(qcms_profile *profile, struct mem_source *mem) 1.250 +{ 1.251 + struct tag_index index = {0, NULL}; 1.252 + unsigned int i; 1.253 + 1.254 + index.count = read_u32(mem, 128); 1.255 + if (index.count > MAX_TAG_COUNT) { 1.256 + invalid_source(mem, "max number of tags exceeded"); 1.257 + return index; 1.258 + } 1.259 + 1.260 + index.tags = malloc(sizeof(struct tag)*index.count); 1.261 + if (index.tags) { 1.262 + for (i = 0; i < index.count; i++) { 1.263 + index.tags[i].signature = read_u32(mem, 128 + 4 + 4*i*3); 1.264 + index.tags[i].offset = read_u32(mem, 128 + 4 + 4*i*3 + 4); 1.265 + index.tags[i].size = read_u32(mem, 128 + 4 + 4*i*3 + 8); 1.266 + } 1.267 + } 1.268 + 1.269 + return index; 1.270 +} 1.271 + 1.272 +// Checks a profile for obvious inconsistencies and returns 1.273 +// true if the profile looks bogus and should probably be 1.274 +// ignored. 1.275 +qcms_bool qcms_profile_is_bogus(qcms_profile *profile) 1.276 +{ 1.277 + float sum[3], target[3], tolerance[3]; 1.278 + float rX, rY, rZ, gX, gY, gZ, bX, bY, bZ; 1.279 + bool negative; 1.280 + unsigned i; 1.281 + 1.282 + // We currently only check the bogosity of RGB profiles 1.283 + if (profile->color_space != RGB_SIGNATURE) 1.284 + return false; 1.285 + 1.286 + if (profile->A2B0 || profile->B2A0) 1.287 + return false; 1.288 + 1.289 + rX = s15Fixed16Number_to_float(profile->redColorant.X); 1.290 + rY = s15Fixed16Number_to_float(profile->redColorant.Y); 1.291 + rZ = s15Fixed16Number_to_float(profile->redColorant.Z); 1.292 + 1.293 + gX = s15Fixed16Number_to_float(profile->greenColorant.X); 1.294 + gY = s15Fixed16Number_to_float(profile->greenColorant.Y); 1.295 + gZ = s15Fixed16Number_to_float(profile->greenColorant.Z); 1.296 + 1.297 + bX = s15Fixed16Number_to_float(profile->blueColorant.X); 1.298 + bY = s15Fixed16Number_to_float(profile->blueColorant.Y); 1.299 + bZ = s15Fixed16Number_to_float(profile->blueColorant.Z); 1.300 + 1.301 + // Check if any of the XYZ values are negative (see mozilla bug 498245) 1.302 + // CIEXYZ tristimulus values cannot be negative according to the spec. 1.303 + negative = 1.304 + (rX < 0) || (rY < 0) || (rZ < 0) || 1.305 + (gX < 0) || (gY < 0) || (gZ < 0) || 1.306 + (bX < 0) || (bY < 0) || (bZ < 0); 1.307 + 1.308 + if (negative) 1.309 + return true; 1.310 + 1.311 + 1.312 + // Sum the values; they should add up to something close to white 1.313 + sum[0] = rX + gX + bX; 1.314 + sum[1] = rY + gY + bY; 1.315 + sum[2] = rZ + gZ + bZ; 1.316 + 1.317 + // Build our target vector (see mozilla bug 460629) 1.318 + target[0] = 0.96420; 1.319 + target[1] = 1.00000; 1.320 + target[2] = 0.82491; 1.321 + 1.322 + // Our tolerance vector - Recommended by Chris Murphy based on 1.323 + // conversion from the LAB space criterion of no more than 3 in any one 1.324 + // channel. This is similar to, but slightly more tolerant than Adobe's 1.325 + // criterion. 1.326 + tolerance[0] = 0.02; 1.327 + tolerance[1] = 0.02; 1.328 + tolerance[2] = 0.04; 1.329 + 1.330 + // Compare with our tolerance 1.331 + for (i = 0; i < 3; ++i) { 1.332 + if (!(((sum[i] - tolerance[i]) <= target[i]) && 1.333 + ((sum[i] + tolerance[i]) >= target[i]))) 1.334 + return true; 1.335 + } 1.336 + 1.337 + // All Good 1.338 + return false; 1.339 +} 1.340 + 1.341 +#define TAG_bXYZ 0x6258595a 1.342 +#define TAG_gXYZ 0x6758595a 1.343 +#define TAG_rXYZ 0x7258595a 1.344 +#define TAG_rTRC 0x72545243 1.345 +#define TAG_bTRC 0x62545243 1.346 +#define TAG_gTRC 0x67545243 1.347 +#define TAG_kTRC 0x6b545243 1.348 +#define TAG_A2B0 0x41324230 1.349 +#define TAG_B2A0 0x42324130 1.350 +#define TAG_CHAD 0x63686164 1.351 + 1.352 +static struct tag *find_tag(struct tag_index index, uint32_t tag_id) 1.353 +{ 1.354 + unsigned int i; 1.355 + struct tag *tag = NULL; 1.356 + for (i = 0; i < index.count; i++) { 1.357 + if (index.tags[i].signature == tag_id) { 1.358 + return &index.tags[i]; 1.359 + } 1.360 + } 1.361 + return tag; 1.362 +} 1.363 + 1.364 +#define XYZ_TYPE 0x58595a20 // 'XYZ ' 1.365 +#define CURVE_TYPE 0x63757276 // 'curv' 1.366 +#define PARAMETRIC_CURVE_TYPE 0x70617261 // 'para' 1.367 +#define LUT16_TYPE 0x6d667432 // 'mft2' 1.368 +#define LUT8_TYPE 0x6d667431 // 'mft1' 1.369 +#define LUT_MAB_TYPE 0x6d414220 // 'mAB ' 1.370 +#define LUT_MBA_TYPE 0x6d424120 // 'mBA ' 1.371 +#define CHROMATIC_TYPE 0x73663332 // 'sf32' 1.372 + 1.373 +static struct matrix read_tag_s15Fixed16ArrayType(struct mem_source *src, struct tag_index index, uint32_t tag_id) 1.374 +{ 1.375 + struct tag *tag = find_tag(index, tag_id); 1.376 + struct matrix matrix; 1.377 + if (tag) { 1.378 + uint8_t i; 1.379 + uint32_t offset = tag->offset; 1.380 + uint32_t type = read_u32(src, offset); 1.381 + 1.382 + // Check mandatory type signature for s16Fixed16ArrayType 1.383 + if (type != CHROMATIC_TYPE) { 1.384 + invalid_source(src, "unexpected type, expected 'sf32'"); 1.385 + } 1.386 + 1.387 + for (i = 0; i < 9; i++) { 1.388 + matrix.m[i/3][i%3] = s15Fixed16Number_to_float(read_s15Fixed16Number(src, offset+8+i*4)); 1.389 + } 1.390 + matrix.invalid = false; 1.391 + } else { 1.392 + matrix.invalid = true; 1.393 + invalid_source(src, "missing sf32tag"); 1.394 + } 1.395 + return matrix; 1.396 +} 1.397 + 1.398 +static struct XYZNumber read_tag_XYZType(struct mem_source *src, struct tag_index index, uint32_t tag_id) 1.399 +{ 1.400 + struct XYZNumber num = {0, 0, 0}; 1.401 + struct tag *tag = find_tag(index, tag_id); 1.402 + if (tag) { 1.403 + uint32_t offset = tag->offset; 1.404 + 1.405 + uint32_t type = read_u32(src, offset); 1.406 + if (type != XYZ_TYPE) 1.407 + invalid_source(src, "unexpected type, expected XYZ"); 1.408 + num.X = read_s15Fixed16Number(src, offset+8); 1.409 + num.Y = read_s15Fixed16Number(src, offset+12); 1.410 + num.Z = read_s15Fixed16Number(src, offset+16); 1.411 + } else { 1.412 + invalid_source(src, "missing xyztag"); 1.413 + } 1.414 + return num; 1.415 +} 1.416 + 1.417 +// Read the tag at a given offset rather then the tag_index. 1.418 +// This method is used when reading mAB tags where nested curveType are 1.419 +// present that are not part of the tag_index. 1.420 +static struct curveType *read_curveType(struct mem_source *src, uint32_t offset, uint32_t *len) 1.421 +{ 1.422 + static const uint32_t COUNT_TO_LENGTH[5] = {1, 3, 4, 5, 7}; 1.423 + struct curveType *curve = NULL; 1.424 + uint32_t type = read_u32(src, offset); 1.425 + uint32_t count; 1.426 + uint32_t i; 1.427 + 1.428 + if (type != CURVE_TYPE && type != PARAMETRIC_CURVE_TYPE) { 1.429 + invalid_source(src, "unexpected type, expected CURV or PARA"); 1.430 + return NULL; 1.431 + } 1.432 + 1.433 + if (type == CURVE_TYPE) { 1.434 + count = read_u32(src, offset+8); 1.435 + 1.436 +#define MAX_CURVE_ENTRIES 40000 //arbitrary 1.437 + if (count > MAX_CURVE_ENTRIES) { 1.438 + invalid_source(src, "curve size too large"); 1.439 + return NULL; 1.440 + } 1.441 + curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*count); 1.442 + if (!curve) 1.443 + return NULL; 1.444 + 1.445 + curve->count = count; 1.446 + curve->type = CURVE_TYPE; 1.447 + 1.448 + for (i=0; i<count; i++) { 1.449 + curve->data[i] = read_u16(src, offset + 12 + i*2); 1.450 + } 1.451 + *len = 12 + count * 2; 1.452 + } else { //PARAMETRIC_CURVE_TYPE 1.453 + count = read_u16(src, offset+8); 1.454 + 1.455 + if (count > 4) { 1.456 + invalid_source(src, "parametric function type not supported."); 1.457 + return NULL; 1.458 + } 1.459 + 1.460 + curve = malloc(sizeof(struct curveType)); 1.461 + if (!curve) 1.462 + return NULL; 1.463 + 1.464 + curve->count = count; 1.465 + curve->type = PARAMETRIC_CURVE_TYPE; 1.466 + 1.467 + for (i=0; i < COUNT_TO_LENGTH[count]; i++) { 1.468 + curve->parameter[i] = s15Fixed16Number_to_float(read_s15Fixed16Number(src, offset + 12 + i*4)); 1.469 + } 1.470 + *len = 12 + COUNT_TO_LENGTH[count] * 4; 1.471 + 1.472 + if ((count == 1 || count == 2)) { 1.473 + /* we have a type 1 or type 2 function that has a division by 'a' */ 1.474 + float a = curve->parameter[1]; 1.475 + if (a == 0.f) 1.476 + invalid_source(src, "parametricCurve definition causes division by zero."); 1.477 + } 1.478 + } 1.479 + 1.480 + return curve; 1.481 +} 1.482 + 1.483 +static struct curveType *read_tag_curveType(struct mem_source *src, struct tag_index index, uint32_t tag_id) 1.484 +{ 1.485 + struct tag *tag = find_tag(index, tag_id); 1.486 + struct curveType *curve = NULL; 1.487 + if (tag) { 1.488 + uint32_t len; 1.489 + return read_curveType(src, tag->offset, &len); 1.490 + } else { 1.491 + invalid_source(src, "missing curvetag"); 1.492 + } 1.493 + 1.494 + return curve; 1.495 +} 1.496 + 1.497 +#define MAX_CLUT_SIZE 500000 // arbitrary 1.498 +#define MAX_CHANNELS 10 // arbitrary 1.499 +static void read_nested_curveType(struct mem_source *src, struct curveType *(*curveArray)[MAX_CHANNELS], uint8_t num_channels, uint32_t curve_offset) 1.500 +{ 1.501 + uint32_t channel_offset = 0; 1.502 + int i; 1.503 + for (i = 0; i < num_channels; i++) { 1.504 + uint32_t tag_len; 1.505 + 1.506 + (*curveArray)[i] = read_curveType(src, curve_offset + channel_offset, &tag_len); 1.507 + if (!(*curveArray)[i]) { 1.508 + invalid_source(src, "invalid nested curveType curve"); 1.509 + } 1.510 + 1.511 + channel_offset += tag_len; 1.512 + // 4 byte aligned 1.513 + if ((tag_len % 4) != 0) 1.514 + channel_offset += 4 - (tag_len % 4); 1.515 + } 1.516 + 1.517 +} 1.518 + 1.519 +static void mAB_release(struct lutmABType *lut) 1.520 +{ 1.521 + uint8_t i; 1.522 + 1.523 + for (i = 0; i < lut->num_in_channels; i++){ 1.524 + free(lut->a_curves[i]); 1.525 + } 1.526 + for (i = 0; i < lut->num_out_channels; i++){ 1.527 + free(lut->b_curves[i]); 1.528 + free(lut->m_curves[i]); 1.529 + } 1.530 + free(lut); 1.531 +} 1.532 + 1.533 +/* See section 10.10 for specs */ 1.534 +static struct lutmABType *read_tag_lutmABType(struct mem_source *src, struct tag_index index, uint32_t tag_id) 1.535 +{ 1.536 + struct tag *tag = find_tag(index, tag_id); 1.537 + uint32_t offset = tag->offset; 1.538 + uint32_t a_curve_offset, b_curve_offset, m_curve_offset; 1.539 + uint32_t matrix_offset; 1.540 + uint32_t clut_offset; 1.541 + uint32_t clut_size = 1; 1.542 + uint8_t clut_precision; 1.543 + uint32_t type = read_u32(src, offset); 1.544 + uint8_t num_in_channels, num_out_channels; 1.545 + struct lutmABType *lut; 1.546 + uint32_t i; 1.547 + 1.548 + if (type != LUT_MAB_TYPE && type != LUT_MBA_TYPE) { 1.549 + return NULL; 1.550 + } 1.551 + 1.552 + num_in_channels = read_u8(src, offset + 8); 1.553 + num_out_channels = read_u8(src, offset + 8); 1.554 + if (num_in_channels > MAX_CHANNELS || num_out_channels > MAX_CHANNELS) 1.555 + return NULL; 1.556 + 1.557 + // We require 3in/out channels since we only support RGB->XYZ (or RGB->LAB) 1.558 + // XXX: If we remove this restriction make sure that the number of channels 1.559 + // is less or equal to the maximum number of mAB curves in qcmsint.h 1.560 + // also check for clut_size overflow. 1.561 + if (num_in_channels != 3 || num_out_channels != 3) 1.562 + return NULL; 1.563 + 1.564 + // some of this data is optional and is denoted by a zero offset 1.565 + // we also use this to track their existance 1.566 + a_curve_offset = read_u32(src, offset + 28); 1.567 + clut_offset = read_u32(src, offset + 24); 1.568 + m_curve_offset = read_u32(src, offset + 20); 1.569 + matrix_offset = read_u32(src, offset + 16); 1.570 + b_curve_offset = read_u32(src, offset + 12); 1.571 + 1.572 + // Convert offsets relative to the tag to relative to the profile 1.573 + // preserve zero for optional fields 1.574 + if (a_curve_offset) 1.575 + a_curve_offset += offset; 1.576 + if (clut_offset) 1.577 + clut_offset += offset; 1.578 + if (m_curve_offset) 1.579 + m_curve_offset += offset; 1.580 + if (matrix_offset) 1.581 + matrix_offset += offset; 1.582 + if (b_curve_offset) 1.583 + b_curve_offset += offset; 1.584 + 1.585 + if (clut_offset) { 1.586 + assert (num_in_channels == 3); 1.587 + // clut_size can not overflow since lg(256^num_in_channels) = 24 bits. 1.588 + for (i = 0; i < num_in_channels; i++) { 1.589 + clut_size *= read_u8(src, clut_offset + i); 1.590 + } 1.591 + } else { 1.592 + clut_size = 0; 1.593 + } 1.594 + 1.595 + // 24bits * 3 won't overflow either 1.596 + clut_size = clut_size * num_out_channels; 1.597 + 1.598 + if (clut_size > MAX_CLUT_SIZE) 1.599 + return NULL; 1.600 + 1.601 + lut = malloc(sizeof(struct lutmABType) + (clut_size) * sizeof(float)); 1.602 + if (!lut) 1.603 + return NULL; 1.604 + // we'll fill in the rest below 1.605 + memset(lut, 0, sizeof(struct lutmABType)); 1.606 + lut->clut_table = &lut->clut_table_data[0]; 1.607 + 1.608 + for (i = 0; i < num_in_channels; i++) { 1.609 + lut->num_grid_points[i] = read_u8(src, clut_offset + i); 1.610 + } 1.611 + 1.612 + // Reverse the processing of transformation elements for mBA type. 1.613 + lut->reversed = (type == LUT_MBA_TYPE); 1.614 + 1.615 + lut->num_in_channels = num_in_channels; 1.616 + lut->num_out_channels = num_out_channels; 1.617 + 1.618 + if (matrix_offset) { 1.619 + // read the matrix if we have it 1.620 + lut->e00 = read_s15Fixed16Number(src, matrix_offset+4*0); 1.621 + lut->e01 = read_s15Fixed16Number(src, matrix_offset+4*1); 1.622 + lut->e02 = read_s15Fixed16Number(src, matrix_offset+4*2); 1.623 + lut->e10 = read_s15Fixed16Number(src, matrix_offset+4*3); 1.624 + lut->e11 = read_s15Fixed16Number(src, matrix_offset+4*4); 1.625 + lut->e12 = read_s15Fixed16Number(src, matrix_offset+4*5); 1.626 + lut->e20 = read_s15Fixed16Number(src, matrix_offset+4*6); 1.627 + lut->e21 = read_s15Fixed16Number(src, matrix_offset+4*7); 1.628 + lut->e22 = read_s15Fixed16Number(src, matrix_offset+4*8); 1.629 + lut->e03 = read_s15Fixed16Number(src, matrix_offset+4*9); 1.630 + lut->e13 = read_s15Fixed16Number(src, matrix_offset+4*10); 1.631 + lut->e23 = read_s15Fixed16Number(src, matrix_offset+4*11); 1.632 + } 1.633 + 1.634 + if (a_curve_offset) { 1.635 + read_nested_curveType(src, &lut->a_curves, num_in_channels, a_curve_offset); 1.636 + } 1.637 + if (m_curve_offset) { 1.638 + read_nested_curveType(src, &lut->m_curves, num_out_channels, m_curve_offset); 1.639 + } 1.640 + if (b_curve_offset) { 1.641 + read_nested_curveType(src, &lut->b_curves, num_out_channels, b_curve_offset); 1.642 + } else { 1.643 + invalid_source(src, "B curves required"); 1.644 + } 1.645 + 1.646 + if (clut_offset) { 1.647 + clut_precision = read_u8(src, clut_offset + 16); 1.648 + if (clut_precision == 1) { 1.649 + for (i = 0; i < clut_size; i++) { 1.650 + lut->clut_table[i] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + 20 + i*1)); 1.651 + } 1.652 + } else if (clut_precision == 2) { 1.653 + for (i = 0; i < clut_size; i++) { 1.654 + lut->clut_table[i] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + 20 + i*2)); 1.655 + } 1.656 + } else { 1.657 + invalid_source(src, "Invalid clut precision"); 1.658 + } 1.659 + } 1.660 + 1.661 + if (!src->valid) { 1.662 + mAB_release(lut); 1.663 + return NULL; 1.664 + } 1.665 + 1.666 + return lut; 1.667 +} 1.668 + 1.669 +static struct lutType *read_tag_lutType(struct mem_source *src, struct tag_index index, uint32_t tag_id) 1.670 +{ 1.671 + struct tag *tag = find_tag(index, tag_id); 1.672 + uint32_t offset = tag->offset; 1.673 + uint32_t type = read_u32(src, offset); 1.674 + uint16_t num_input_table_entries; 1.675 + uint16_t num_output_table_entries; 1.676 + uint8_t in_chan, grid_points, out_chan; 1.677 + uint32_t clut_offset, output_offset; 1.678 + uint32_t clut_size; 1.679 + size_t entry_size; 1.680 + struct lutType *lut; 1.681 + uint32_t i; 1.682 + 1.683 + /* I'm not sure why the spec specifies a fixed number of entries for LUT8 tables even though 1.684 + * they have room for the num_entries fields */ 1.685 + if (type == LUT8_TYPE) { 1.686 + num_input_table_entries = 256; 1.687 + num_output_table_entries = 256; 1.688 + entry_size = 1; 1.689 + } else if (type == LUT16_TYPE) { 1.690 + num_input_table_entries = read_u16(src, offset + 48); 1.691 + num_output_table_entries = read_u16(src, offset + 50); 1.692 + entry_size = 2; 1.693 + } else { 1.694 + assert(0); // the caller checks that this doesn't happen 1.695 + invalid_source(src, "Unexpected lut type"); 1.696 + return NULL; 1.697 + } 1.698 + 1.699 + in_chan = read_u8(src, offset + 8); 1.700 + out_chan = read_u8(src, offset + 9); 1.701 + grid_points = read_u8(src, offset + 10); 1.702 + 1.703 + clut_size = pow(grid_points, in_chan); 1.704 + if (clut_size > MAX_CLUT_SIZE) { 1.705 + return NULL; 1.706 + } 1.707 + 1.708 + if (in_chan != 3 || out_chan != 3) { 1.709 + return NULL; 1.710 + } 1.711 + 1.712 + lut = malloc(sizeof(struct lutType) + (num_input_table_entries * in_chan + clut_size*out_chan + num_output_table_entries * out_chan)*sizeof(float)); 1.713 + if (!lut) { 1.714 + return NULL; 1.715 + } 1.716 + 1.717 + /* compute the offsets of tables */ 1.718 + lut->input_table = &lut->table_data[0]; 1.719 + lut->clut_table = &lut->table_data[in_chan*num_input_table_entries]; 1.720 + lut->output_table = &lut->table_data[in_chan*num_input_table_entries + clut_size*out_chan]; 1.721 + 1.722 + lut->num_input_table_entries = num_input_table_entries; 1.723 + lut->num_output_table_entries = num_output_table_entries; 1.724 + lut->num_input_channels = read_u8(src, offset + 8); 1.725 + lut->num_output_channels = read_u8(src, offset + 9); 1.726 + lut->num_clut_grid_points = read_u8(src, offset + 10); 1.727 + lut->e00 = read_s15Fixed16Number(src, offset+12); 1.728 + lut->e01 = read_s15Fixed16Number(src, offset+16); 1.729 + lut->e02 = read_s15Fixed16Number(src, offset+20); 1.730 + lut->e10 = read_s15Fixed16Number(src, offset+24); 1.731 + lut->e11 = read_s15Fixed16Number(src, offset+28); 1.732 + lut->e12 = read_s15Fixed16Number(src, offset+32); 1.733 + lut->e20 = read_s15Fixed16Number(src, offset+36); 1.734 + lut->e21 = read_s15Fixed16Number(src, offset+40); 1.735 + lut->e22 = read_s15Fixed16Number(src, offset+44); 1.736 + 1.737 + for (i = 0; i < lut->num_input_table_entries * in_chan; i++) { 1.738 + if (type == LUT8_TYPE) { 1.739 + lut->input_table[i] = uInt8Number_to_float(read_uInt8Number(src, offset + 52 + i * entry_size)); 1.740 + } else { 1.741 + lut->input_table[i] = uInt16Number_to_float(read_uInt16Number(src, offset + 52 + i * entry_size)); 1.742 + } 1.743 + } 1.744 + 1.745 + clut_offset = offset + 52 + lut->num_input_table_entries * in_chan * entry_size; 1.746 + for (i = 0; i < clut_size * out_chan; i+=3) { 1.747 + if (type == LUT8_TYPE) { 1.748 + lut->clut_table[i+0] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 0)); 1.749 + lut->clut_table[i+1] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 1)); 1.750 + lut->clut_table[i+2] = uInt8Number_to_float(read_uInt8Number(src, clut_offset + i*entry_size + 2)); 1.751 + } else { 1.752 + lut->clut_table[i+0] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 0)); 1.753 + lut->clut_table[i+1] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 2)); 1.754 + lut->clut_table[i+2] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 4)); 1.755 + } 1.756 + } 1.757 + 1.758 + output_offset = clut_offset + clut_size * out_chan * entry_size; 1.759 + for (i = 0; i < lut->num_output_table_entries * out_chan; i++) { 1.760 + if (type == LUT8_TYPE) { 1.761 + lut->output_table[i] = uInt8Number_to_float(read_uInt8Number(src, output_offset + i*entry_size)); 1.762 + } else { 1.763 + lut->output_table[i] = uInt16Number_to_float(read_uInt16Number(src, output_offset + i*entry_size)); 1.764 + } 1.765 + } 1.766 + 1.767 + return lut; 1.768 +} 1.769 + 1.770 +static void read_rendering_intent(qcms_profile *profile, struct mem_source *src) 1.771 +{ 1.772 + profile->rendering_intent = read_u32(src, 64); 1.773 + switch (profile->rendering_intent) { 1.774 + case QCMS_INTENT_PERCEPTUAL: 1.775 + case QCMS_INTENT_SATURATION: 1.776 + case QCMS_INTENT_RELATIVE_COLORIMETRIC: 1.777 + case QCMS_INTENT_ABSOLUTE_COLORIMETRIC: 1.778 + break; 1.779 + default: 1.780 + invalid_source(src, "unknown rendering intent"); 1.781 + } 1.782 +} 1.783 + 1.784 +qcms_profile *qcms_profile_create(void) 1.785 +{ 1.786 + return calloc(sizeof(qcms_profile), 1); 1.787 +} 1.788 + 1.789 + 1.790 + 1.791 +/* build sRGB gamma table */ 1.792 +/* based on cmsBuildParametricGamma() */ 1.793 +static uint16_t *build_sRGB_gamma_table(int num_entries) 1.794 +{ 1.795 + int i; 1.796 + /* taken from lcms: Build_sRGBGamma() */ 1.797 + double gamma = 2.4; 1.798 + double a = 1./1.055; 1.799 + double b = 0.055/1.055; 1.800 + double c = 1./12.92; 1.801 + double d = 0.04045; 1.802 + 1.803 + uint16_t *table = malloc(sizeof(uint16_t) * num_entries); 1.804 + if (!table) 1.805 + return NULL; 1.806 + 1.807 + for (i=0; i<num_entries; i++) { 1.808 + double x = (double)i / (num_entries-1); 1.809 + double y, output; 1.810 + // IEC 61966-2.1 (sRGB) 1.811 + // Y = (aX + b)^Gamma | X >= d 1.812 + // Y = cX | X < d 1.813 + if (x >= d) { 1.814 + double e = (a*x + b); 1.815 + if (e > 0) 1.816 + y = pow(e, gamma); 1.817 + else 1.818 + y = 0; 1.819 + } else { 1.820 + y = c*x; 1.821 + } 1.822 + 1.823 + // Saturate -- this could likely move to a separate function 1.824 + output = y * 65535. + .5; 1.825 + if (output > 65535.) 1.826 + output = 65535; 1.827 + if (output < 0) 1.828 + output = 0; 1.829 + table[i] = (uint16_t)floor(output); 1.830 + } 1.831 + return table; 1.832 +} 1.833 + 1.834 +static struct curveType *curve_from_table(uint16_t *table, int num_entries) 1.835 +{ 1.836 + struct curveType *curve; 1.837 + int i; 1.838 + curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*num_entries); 1.839 + if (!curve) 1.840 + return NULL; 1.841 + curve->type = CURVE_TYPE; 1.842 + curve->count = num_entries; 1.843 + for (i = 0; i < num_entries; i++) { 1.844 + curve->data[i] = table[i]; 1.845 + } 1.846 + return curve; 1.847 +} 1.848 + 1.849 +static uint16_t float_to_u8Fixed8Number(float a) 1.850 +{ 1.851 + if (a > (255.f + 255.f/256)) 1.852 + return 0xffff; 1.853 + else if (a < 0.f) 1.854 + return 0; 1.855 + else 1.856 + return floorf(a*256.f + .5f); 1.857 +} 1.858 + 1.859 +static struct curveType *curve_from_gamma(float gamma) 1.860 +{ 1.861 + struct curveType *curve; 1.862 + int num_entries = 1; 1.863 + curve = malloc(sizeof(struct curveType) + sizeof(uInt16Number)*num_entries); 1.864 + if (!curve) 1.865 + return NULL; 1.866 + curve->count = num_entries; 1.867 + curve->data[0] = float_to_u8Fixed8Number(gamma); 1.868 + curve->type = CURVE_TYPE; 1.869 + return curve; 1.870 +} 1.871 + 1.872 +//XXX: it would be nice if we had a way of ensuring 1.873 +// everything in a profile was initialized regardless of how it was created 1.874 + 1.875 +//XXX: should this also be taking a black_point? 1.876 +/* similar to CGColorSpaceCreateCalibratedRGB */ 1.877 +qcms_profile* qcms_profile_create_rgb_with_gamma( 1.878 + qcms_CIE_xyY white_point, 1.879 + qcms_CIE_xyYTRIPLE primaries, 1.880 + float gamma) 1.881 +{ 1.882 + qcms_profile* profile = qcms_profile_create(); 1.883 + if (!profile) 1.884 + return NO_MEM_PROFILE; 1.885 + 1.886 + //XXX: should store the whitepoint 1.887 + if (!set_rgb_colorants(profile, white_point, primaries)) { 1.888 + qcms_profile_release(profile); 1.889 + return INVALID_PROFILE; 1.890 + } 1.891 + 1.892 + profile->redTRC = curve_from_gamma(gamma); 1.893 + profile->blueTRC = curve_from_gamma(gamma); 1.894 + profile->greenTRC = curve_from_gamma(gamma); 1.895 + 1.896 + if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC) { 1.897 + qcms_profile_release(profile); 1.898 + return NO_MEM_PROFILE; 1.899 + } 1.900 + profile->class = DISPLAY_DEVICE_PROFILE; 1.901 + profile->rendering_intent = QCMS_INTENT_PERCEPTUAL; 1.902 + profile->color_space = RGB_SIGNATURE; 1.903 + return profile; 1.904 +} 1.905 + 1.906 +qcms_profile* qcms_profile_create_rgb_with_table( 1.907 + qcms_CIE_xyY white_point, 1.908 + qcms_CIE_xyYTRIPLE primaries, 1.909 + uint16_t *table, int num_entries) 1.910 +{ 1.911 + qcms_profile* profile = qcms_profile_create(); 1.912 + if (!profile) 1.913 + return NO_MEM_PROFILE; 1.914 + 1.915 + //XXX: should store the whitepoint 1.916 + if (!set_rgb_colorants(profile, white_point, primaries)) { 1.917 + qcms_profile_release(profile); 1.918 + return INVALID_PROFILE; 1.919 + } 1.920 + 1.921 + profile->redTRC = curve_from_table(table, num_entries); 1.922 + profile->blueTRC = curve_from_table(table, num_entries); 1.923 + profile->greenTRC = curve_from_table(table, num_entries); 1.924 + 1.925 + if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC) { 1.926 + qcms_profile_release(profile); 1.927 + return NO_MEM_PROFILE; 1.928 + } 1.929 + profile->class = DISPLAY_DEVICE_PROFILE; 1.930 + profile->rendering_intent = QCMS_INTENT_PERCEPTUAL; 1.931 + profile->color_space = RGB_SIGNATURE; 1.932 + return profile; 1.933 +} 1.934 + 1.935 +/* from lcms: cmsWhitePointFromTemp */ 1.936 +/* tempK must be >= 4000. and <= 25000. 1.937 + * Invalid values of tempK will return 1.938 + * (x,y,Y) = (-1.0, -1.0, -1.0) 1.939 + * similar to argyll: icx_DTEMP2XYZ() */ 1.940 +static qcms_CIE_xyY white_point_from_temp(int temp_K) 1.941 +{ 1.942 + qcms_CIE_xyY white_point; 1.943 + double x, y; 1.944 + double T, T2, T3; 1.945 + // double M1, M2; 1.946 + 1.947 + // No optimization provided. 1.948 + T = temp_K; 1.949 + T2 = T*T; // Square 1.950 + T3 = T2*T; // Cube 1.951 + 1.952 + // For correlated color temperature (T) between 4000K and 7000K: 1.953 + if (T >= 4000. && T <= 7000.) { 1.954 + x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063; 1.955 + } else { 1.956 + // or for correlated color temperature (T) between 7000K and 25000K: 1.957 + if (T > 7000.0 && T <= 25000.0) { 1.958 + x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040; 1.959 + } else { 1.960 + // Invalid tempK 1.961 + white_point.x = -1.0; 1.962 + white_point.y = -1.0; 1.963 + white_point.Y = -1.0; 1.964 + 1.965 + assert(0 && "invalid temp"); 1.966 + 1.967 + return white_point; 1.968 + } 1.969 + } 1.970 + 1.971 + // Obtain y(x) 1.972 + 1.973 + y = -3.000*(x*x) + 2.870*x - 0.275; 1.974 + 1.975 + // wave factors (not used, but here for futures extensions) 1.976 + 1.977 + // M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y); 1.978 + // M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y); 1.979 + 1.980 + // Fill white_point struct 1.981 + white_point.x = x; 1.982 + white_point.y = y; 1.983 + white_point.Y = 1.0; 1.984 + 1.985 + return white_point; 1.986 +} 1.987 + 1.988 +qcms_profile* qcms_profile_sRGB(void) 1.989 +{ 1.990 + qcms_profile *profile; 1.991 + uint16_t *table; 1.992 + 1.993 + qcms_CIE_xyYTRIPLE Rec709Primaries = { 1.994 + {0.6400, 0.3300, 1.0}, 1.995 + {0.3000, 0.6000, 1.0}, 1.996 + {0.1500, 0.0600, 1.0} 1.997 + }; 1.998 + qcms_CIE_xyY D65; 1.999 + 1.1000 + D65 = white_point_from_temp(6504); 1.1001 + 1.1002 + table = build_sRGB_gamma_table(1024); 1.1003 + 1.1004 + if (!table) 1.1005 + return NO_MEM_PROFILE; 1.1006 + 1.1007 + profile = qcms_profile_create_rgb_with_table(D65, Rec709Primaries, table, 1024); 1.1008 + free(table); 1.1009 + return profile; 1.1010 +} 1.1011 + 1.1012 + 1.1013 +/* qcms_profile_from_memory does not hold a reference to the memory passed in */ 1.1014 +qcms_profile* qcms_profile_from_memory(const void *mem, size_t size) 1.1015 +{ 1.1016 + uint32_t length; 1.1017 + struct mem_source source; 1.1018 + struct mem_source *src = &source; 1.1019 + struct tag_index index; 1.1020 + qcms_profile *profile; 1.1021 + 1.1022 + source.buf = mem; 1.1023 + source.size = size; 1.1024 + source.valid = true; 1.1025 + 1.1026 + if (size < 4) 1.1027 + return INVALID_PROFILE; 1.1028 + 1.1029 + length = read_u32(src, 0); 1.1030 + if (length <= size) { 1.1031 + // shrink the area that we can read if appropriate 1.1032 + source.size = length; 1.1033 + } else { 1.1034 + return INVALID_PROFILE; 1.1035 + } 1.1036 + 1.1037 + /* ensure that the profile size is sane so it's easier to reason about */ 1.1038 + if (source.size <= 64 || source.size >= MAX_PROFILE_SIZE) 1.1039 + return INVALID_PROFILE; 1.1040 + 1.1041 + profile = qcms_profile_create(); 1.1042 + if (!profile) 1.1043 + return NO_MEM_PROFILE; 1.1044 + 1.1045 + check_CMM_type_signature(src); 1.1046 + check_profile_version(src); 1.1047 + read_class_signature(profile, src); 1.1048 + read_rendering_intent(profile, src); 1.1049 + read_color_space(profile, src); 1.1050 + read_pcs(profile, src); 1.1051 + //TODO read rest of profile stuff 1.1052 + 1.1053 + if (!src->valid) 1.1054 + goto invalid_profile; 1.1055 + 1.1056 + index = read_tag_table(profile, src); 1.1057 + if (!src->valid || !index.tags) 1.1058 + goto invalid_tag_table; 1.1059 + 1.1060 + if (find_tag(index, TAG_CHAD)) { 1.1061 + profile->chromaticAdaption = read_tag_s15Fixed16ArrayType(src, index, TAG_CHAD); 1.1062 + } else { 1.1063 + profile->chromaticAdaption.invalid = true; //Signal the data is not present 1.1064 + } 1.1065 + 1.1066 + if (profile->class == DISPLAY_DEVICE_PROFILE || profile->class == INPUT_DEVICE_PROFILE || 1.1067 + profile->class == OUTPUT_DEVICE_PROFILE || profile->class == COLOR_SPACE_PROFILE) { 1.1068 + if (profile->color_space == RGB_SIGNATURE) { 1.1069 + if (find_tag(index, TAG_A2B0)) { 1.1070 + if (read_u32(src, find_tag(index, TAG_A2B0)->offset) == LUT8_TYPE || 1.1071 + read_u32(src, find_tag(index, TAG_A2B0)->offset) == LUT16_TYPE) { 1.1072 + profile->A2B0 = read_tag_lutType(src, index, TAG_A2B0); 1.1073 + } else if (read_u32(src, find_tag(index, TAG_A2B0)->offset) == LUT_MAB_TYPE) { 1.1074 + profile->mAB = read_tag_lutmABType(src, index, TAG_A2B0); 1.1075 + } 1.1076 + } 1.1077 + if (find_tag(index, TAG_B2A0)) { 1.1078 + if (read_u32(src, find_tag(index, TAG_B2A0)->offset) == LUT8_TYPE || 1.1079 + read_u32(src, find_tag(index, TAG_B2A0)->offset) == LUT16_TYPE) { 1.1080 + profile->B2A0 = read_tag_lutType(src, index, TAG_B2A0); 1.1081 + } else if (read_u32(src, find_tag(index, TAG_B2A0)->offset) == LUT_MBA_TYPE) { 1.1082 + profile->mBA = read_tag_lutmABType(src, index, TAG_B2A0); 1.1083 + } 1.1084 + } 1.1085 + if (find_tag(index, TAG_rXYZ) || !qcms_supports_iccv4) { 1.1086 + profile->redColorant = read_tag_XYZType(src, index, TAG_rXYZ); 1.1087 + profile->greenColorant = read_tag_XYZType(src, index, TAG_gXYZ); 1.1088 + profile->blueColorant = read_tag_XYZType(src, index, TAG_bXYZ); 1.1089 + } 1.1090 + 1.1091 + if (!src->valid) 1.1092 + goto invalid_tag_table; 1.1093 + 1.1094 + if (find_tag(index, TAG_rTRC) || !qcms_supports_iccv4) { 1.1095 + profile->redTRC = read_tag_curveType(src, index, TAG_rTRC); 1.1096 + profile->greenTRC = read_tag_curveType(src, index, TAG_gTRC); 1.1097 + profile->blueTRC = read_tag_curveType(src, index, TAG_bTRC); 1.1098 + 1.1099 + if (!profile->redTRC || !profile->blueTRC || !profile->greenTRC) 1.1100 + goto invalid_tag_table; 1.1101 + } 1.1102 + } else if (profile->color_space == GRAY_SIGNATURE) { 1.1103 + 1.1104 + profile->grayTRC = read_tag_curveType(src, index, TAG_kTRC); 1.1105 + if (!profile->grayTRC) 1.1106 + goto invalid_tag_table; 1.1107 + 1.1108 + } else { 1.1109 + assert(0 && "read_color_space protects against entering here"); 1.1110 + goto invalid_tag_table; 1.1111 + } 1.1112 + } else { 1.1113 + goto invalid_tag_table; 1.1114 + } 1.1115 + 1.1116 + if (!src->valid) 1.1117 + goto invalid_tag_table; 1.1118 + 1.1119 + free(index.tags); 1.1120 + 1.1121 + return profile; 1.1122 + 1.1123 +invalid_tag_table: 1.1124 + free(index.tags); 1.1125 +invalid_profile: 1.1126 + qcms_profile_release(profile); 1.1127 + return INVALID_PROFILE; 1.1128 +} 1.1129 + 1.1130 +qcms_intent qcms_profile_get_rendering_intent(qcms_profile *profile) 1.1131 +{ 1.1132 + return profile->rendering_intent; 1.1133 +} 1.1134 + 1.1135 +icColorSpaceSignature 1.1136 +qcms_profile_get_color_space(qcms_profile *profile) 1.1137 +{ 1.1138 + return profile->color_space; 1.1139 +} 1.1140 + 1.1141 +static void lut_release(struct lutType *lut) 1.1142 +{ 1.1143 + free(lut); 1.1144 +} 1.1145 + 1.1146 +void qcms_profile_release(qcms_profile *profile) 1.1147 +{ 1.1148 + if (profile->output_table_r) 1.1149 + precache_release(profile->output_table_r); 1.1150 + if (profile->output_table_g) 1.1151 + precache_release(profile->output_table_g); 1.1152 + if (profile->output_table_b) 1.1153 + precache_release(profile->output_table_b); 1.1154 + 1.1155 + if (profile->A2B0) 1.1156 + lut_release(profile->A2B0); 1.1157 + if (profile->B2A0) 1.1158 + lut_release(profile->B2A0); 1.1159 + 1.1160 + if (profile->mAB) 1.1161 + mAB_release(profile->mAB); 1.1162 + if (profile->mBA) 1.1163 + mAB_release(profile->mBA); 1.1164 + 1.1165 + free(profile->redTRC); 1.1166 + free(profile->blueTRC); 1.1167 + free(profile->greenTRC); 1.1168 + free(profile->grayTRC); 1.1169 + free(profile); 1.1170 +} 1.1171 + 1.1172 + 1.1173 +#include <stdio.h> 1.1174 +static void qcms_data_from_file(FILE *file, void **mem, size_t *size) 1.1175 +{ 1.1176 + uint32_t length, remaining_length; 1.1177 + size_t read_length; 1.1178 + be32 length_be; 1.1179 + void *data; 1.1180 + 1.1181 + *mem = NULL; 1.1182 + *size = 0; 1.1183 + 1.1184 + if (fread(&length_be, 1, sizeof(length_be), file) != sizeof(length_be)) 1.1185 + return; 1.1186 + 1.1187 + length = be32_to_cpu(length_be); 1.1188 + if (length > MAX_PROFILE_SIZE || length < sizeof(length_be)) 1.1189 + return; 1.1190 + 1.1191 + /* allocate room for the entire profile */ 1.1192 + data = malloc(length); 1.1193 + if (!data) 1.1194 + return; 1.1195 + 1.1196 + /* copy in length to the front so that the buffer will contain the entire profile */ 1.1197 + *((be32*)data) = length_be; 1.1198 + remaining_length = length - sizeof(length_be); 1.1199 + 1.1200 + /* read the rest profile */ 1.1201 + read_length = fread((unsigned char*)data + sizeof(length_be), 1, remaining_length, file); 1.1202 + if (read_length != remaining_length) { 1.1203 + free(data); 1.1204 + return; 1.1205 + } 1.1206 + 1.1207 + /* successfully get the profile.*/ 1.1208 + *mem = data; 1.1209 + *size = length; 1.1210 +} 1.1211 + 1.1212 +qcms_profile* qcms_profile_from_file(FILE *file) 1.1213 +{ 1.1214 + size_t length; 1.1215 + qcms_profile *profile; 1.1216 + void *data; 1.1217 + 1.1218 + qcms_data_from_file(file, &data, &length); 1.1219 + if ((data == NULL) || (length == 0)) 1.1220 + return INVALID_PROFILE; 1.1221 + 1.1222 + profile = qcms_profile_from_memory(data, length); 1.1223 + free(data); 1.1224 + return profile; 1.1225 +} 1.1226 + 1.1227 +qcms_profile* qcms_profile_from_path(const char *path) 1.1228 +{ 1.1229 + qcms_profile *profile = NULL; 1.1230 + FILE *file = fopen(path, "rb"); 1.1231 + if (file) { 1.1232 + profile = qcms_profile_from_file(file); 1.1233 + fclose(file); 1.1234 + } 1.1235 + return profile; 1.1236 +} 1.1237 + 1.1238 +void qcms_data_from_path(const char *path, void **mem, size_t *size) 1.1239 +{ 1.1240 + FILE *file = NULL; 1.1241 + *mem = NULL; 1.1242 + *size = 0; 1.1243 + 1.1244 + file = fopen(path, "rb"); 1.1245 + if (file) { 1.1246 + qcms_data_from_file(file, mem, size); 1.1247 + fclose(file); 1.1248 + } 1.1249 +} 1.1250 + 1.1251 +#ifdef _WIN32 1.1252 +/* Unicode path version */ 1.1253 +qcms_profile* qcms_profile_from_unicode_path(const wchar_t *path) 1.1254 +{ 1.1255 + qcms_profile *profile = NULL; 1.1256 + FILE *file = _wfopen(path, L"rb"); 1.1257 + if (file) { 1.1258 + profile = qcms_profile_from_file(file); 1.1259 + fclose(file); 1.1260 + } 1.1261 + return profile; 1.1262 +} 1.1263 + 1.1264 +void qcms_data_from_unicode_path(const wchar_t *path, void **mem, size_t *size) 1.1265 +{ 1.1266 + FILE *file = NULL; 1.1267 + *mem = NULL; 1.1268 + *size = 0; 1.1269 + 1.1270 + file = _wfopen(path, L"rb"); 1.1271 + if (file) { 1.1272 + qcms_data_from_file(file, mem, size); 1.1273 + fclose(file); 1.1274 + } 1.1275 +} 1.1276 +#endif 1.1277 + 1.1278 +/* 1.1279 +* This function constructs an ICC profile memory with given header and tag data, 1.1280 +* which can be read via qcms_profile_from_memory(). that means, we must satisfy 1.1281 +* the profiler header type check (which seems not complete till now) and proper 1.1282 +* information to read data from the tag table and tag data elements memory. 1.1283 +* 1.1284 +* To construct a valid ICC profile, its divided into three steps : 1.1285 +* (1) construct the r/g/bXYZ part 1.1286 +* (2) construct the r/g/bTRC part 1.1287 +* (3) construct the profile header 1.1288 +* this is a hardcode step just for "create_rgb_with_gamma", it is the only 1.1289 +* requirement till now, maybe we can make this method more general in future, 1.1290 +* 1.1291 +* NOTE : some of the parameters below are hardcode, please refer to the ICC documentation. 1.1292 +*/ 1.1293 +#define ICC_PROFILE_HEADER_LENGTH 128 1.1294 +void qcms_data_create_rgb_with_gamma(qcms_CIE_xyY white_point, qcms_CIE_xyYTRIPLE primaries, float gamma, void **mem, size_t *size) 1.1295 +{ 1.1296 + uint32_t length, offset, index, xyz_count, trc_count; 1.1297 + size_t tag_table_offset, tag_data_offset; 1.1298 + void *data; 1.1299 + struct matrix colorants; 1.1300 + 1.1301 + uint32_t TAG_XYZ[3] = {TAG_rXYZ, TAG_gXYZ, TAG_bXYZ}; 1.1302 + uint32_t TAG_TRC[3] = {TAG_rTRC, TAG_gTRC, TAG_bTRC}; 1.1303 + 1.1304 + if ((mem == NULL) || (size == NULL)) 1.1305 + return; 1.1306 + 1.1307 + *mem = NULL; 1.1308 + *size = 0; 1.1309 + 1.1310 + /* 1.1311 + * total length = icc profile header(128) + tag count(4) + 1.1312 + * (tag table item (12) * total tag (6 = 3 rTRC + 3 rXYZ)) + rTRC elements data (3 * 20) 1.1313 + * + rXYZ elements data (3*16), and all tag data elements must start at the 4-byte boundary. 1.1314 + */ 1.1315 + xyz_count = 3; // rXYZ, gXYZ, bXYZ 1.1316 + trc_count = 3; // rTRC, gTRC, bTRC 1.1317 + length = ICC_PROFILE_HEADER_LENGTH + 4 + (12 * (xyz_count + trc_count)) + (xyz_count * 20) + (trc_count * 16); 1.1318 + 1.1319 + // reserve the total memory. 1.1320 + data = malloc(length); 1.1321 + if (!data) 1.1322 + return; 1.1323 + memset(data, 0, length); 1.1324 + 1.1325 + // Part1 : write rXYZ, gXYZ and bXYZ 1.1326 + if (!get_rgb_colorants(&colorants, white_point, primaries)) { 1.1327 + free(data); 1.1328 + return; 1.1329 + } 1.1330 + 1.1331 + // the position of first tag's signature in tag table 1.1332 + tag_table_offset = ICC_PROFILE_HEADER_LENGTH + 4; 1.1333 + tag_data_offset = ICC_PROFILE_HEADER_LENGTH + 4 + 1.1334 + (12 * (xyz_count + trc_count)); // the start of tag data elements. 1.1335 + 1.1336 + for (index = 0; index < xyz_count; ++index) { 1.1337 + // tag table 1.1338 + write_u32(data, tag_table_offset, TAG_XYZ[index]); 1.1339 + write_u32(data, tag_table_offset+4, tag_data_offset); 1.1340 + write_u32(data, tag_table_offset+8, 20); // 20 bytes per TAG_(r/g/b)XYZ tag element 1.1341 + 1.1342 + // tag data element 1.1343 + write_u32(data, tag_data_offset, XYZ_TYPE); 1.1344 + // reserved 4 bytes. 1.1345 + write_u32(data, tag_data_offset+8, double_to_s15Fixed16Number(colorants.m[0][index])); 1.1346 + write_u32(data, tag_data_offset+12, double_to_s15Fixed16Number(colorants.m[1][index])); 1.1347 + write_u32(data, tag_data_offset+16, double_to_s15Fixed16Number(colorants.m[2][index])); 1.1348 + 1.1349 + tag_table_offset += 12; 1.1350 + tag_data_offset += 20; 1.1351 + } 1.1352 + 1.1353 + // Part2 : write rTRC, gTRC and bTRC 1.1354 + for (index = 0; index < trc_count; ++index) { 1.1355 + // tag table 1.1356 + write_u32(data, tag_table_offset, TAG_TRC[index]); 1.1357 + write_u32(data, tag_table_offset+4, tag_data_offset); 1.1358 + write_u32(data, tag_table_offset+8, 14); // 14 bytes per TAG_(r/g/b)TRC element 1.1359 + 1.1360 + // tag data element 1.1361 + write_u32(data, tag_data_offset, CURVE_TYPE); 1.1362 + // reserved 4 bytes. 1.1363 + write_u32(data, tag_data_offset+8, 1); // count 1.1364 + write_u16(data, tag_data_offset+12, float_to_u8Fixed8Number(gamma)); 1.1365 + 1.1366 + tag_table_offset += 12; 1.1367 + tag_data_offset += 16; 1.1368 + } 1.1369 + 1.1370 + /* Part3 : write profile header 1.1371 + * 1.1372 + * Important header fields are left empty. This generates a profile for internal use only. 1.1373 + * We should be generating: Profile version (04300000h), Profile signature (acsp), 1.1374 + * PCS illumiant field. Likewise mandatory profile tags are omitted. 1.1375 + */ 1.1376 + write_u32(data, 0, length); // the total length of this memory 1.1377 + write_u32(data, 12, DISPLAY_DEVICE_PROFILE); // profile->class 1.1378 + write_u32(data, 16, RGB_SIGNATURE); // profile->color_space 1.1379 + write_u32(data, 20, XYZ_SIGNATURE); // profile->pcs 1.1380 + write_u32(data, 64, QCMS_INTENT_PERCEPTUAL); // profile->rendering_intent 1.1381 + 1.1382 + write_u32(data, ICC_PROFILE_HEADER_LENGTH, 6); // total tag count 1.1383 + 1.1384 + // prepare the result 1.1385 + *mem = data; 1.1386 + *size = length; 1.1387 +}