LCOV - code coverage report
Current view: directory - gfx/qcms - iccread.c (source / functions) Found Hit Coverage
Test: app.info Lines: 569 0 0.0 %
Date: 2012-06-02 Functions: 42 0 0.0 %

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

Generated by: LCOV version 1.7