LCOV - code coverage report
Current view: top level - gfx/qcms - iccread.c (source / functions) Hit Total Coverage
Test: output.info Lines: 94 660 14.2 %
Date: 2017-07-14 16:53:18 Functions: 9 49 18.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13