Coverage Report

Created: 2018-09-25 14:53

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