From c07d0c2d2f6f7b0eb6e92cc6204bf05037957e82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 15:43:52 +0200 Subject: Imported Upstream version 1.6.3 --- icc/ClayRGB1998.icm | Bin 580 -> 584 bytes icc/EBU3213_PAL.icm | Bin 0 -> 9120 bytes icc/Jamfile | 18 +- icc/License.txt | 2 +- icc/ProPhoto.icm | Bin 0 -> 2892 bytes icc/ProPhotoLin.icm | Bin 0 -> 2900 bytes icc/Readme.txt | 8 +- icc/Rec2020.icm | Bin 0 -> 9064 bytes icc/Rec709.icm | Bin 0 -> 2920 bytes icc/SMPTE431_P3.icm | Bin 0 -> 872 bytes icc/SMPTE_RP145_NTSC.icm | Bin 0 -> 9100 bytes icc/afiles | 9 +- icc/icc.c | 1186 ++++++++++++++++++++++++++++++++++++++++------ icc/icc.h | 429 +++++++++++------ icc/iccdump.c | 3 + icc/icclu.c | 31 +- icc/icctest.c | 9 +- icc/lab2lab.icm | Bin 500 -> 500 bytes icc/log.txt | 8 +- icc/lutest.c | 139 +++--- icc/makezip.ksh | 6 - icc/mcheck.c | 2 +- icc/mkDispProf.c | 331 +++++++++++++ icc/sRGB.icm | Bin 3212 -> 3212 bytes 24 files changed, 1781 insertions(+), 400 deletions(-) create mode 100644 icc/EBU3213_PAL.icm create mode 100644 icc/ProPhoto.icm create mode 100644 icc/ProPhotoLin.icm create mode 100644 icc/Rec2020.icm create mode 100644 icc/Rec709.icm create mode 100644 icc/SMPTE431_P3.icm create mode 100644 icc/SMPTE_RP145_NTSC.icm delete mode 100644 icc/makezip.ksh create mode 100644 icc/mkDispProf.c (limited to 'icc') diff --git a/icc/ClayRGB1998.icm b/icc/ClayRGB1998.icm index 1eedf09..1c3a02e 100644 Binary files a/icc/ClayRGB1998.icm and b/icc/ClayRGB1998.icm differ diff --git a/icc/EBU3213_PAL.icm b/icc/EBU3213_PAL.icm new file mode 100644 index 0000000..54cfed7 Binary files /dev/null and b/icc/EBU3213_PAL.icm differ diff --git a/icc/Jamfile b/icc/Jamfile index abbeddb..9f27848 100644 --- a/icc/Jamfile +++ b/icc/Jamfile @@ -13,7 +13,9 @@ PREF_LINKFLAGS = $(LINKDEBUGFLAG) ; # Link debugging flags Libraries = libicc ; Executables = iccdump icclu ; Headers = icc.h ; -Samples = sRGB.icm ClayRGB1998.icm lab2lab.icm ; +Samples = sRGB.icm ClayRGB1998.icm EBU3213_PAL.icm SMPTE_RP145_NTSC.icm Rec709.icm + Rec2020.icm SMPTE431_P3.icm ProPhoto.icm ProPhotoLin.icm lab2lab.icm ; +# Remember to add samples to ref/afiles ! #Install InstallBin $(DESTDIR)$(PREFIX)/bin : $(Executables) ; @@ -36,19 +38,31 @@ LINKLIBS = libicc ; # All utils are made from a single source file MainsFromSources icctest.c lutest.c iccdump.c icclu.c iccrw.c ; +# This is an example program for making a matrix display profile +MainsFromSources mkDispProf.c ; + +#MainsFromSources t.c ; + if $(BUILD_JUNK) { # MainsFromSources tt.c ; MainsFromSources mksRGB.c ; +# MainsFromSources mkscRGB.c ; MainsFromSources mkAdobeRGB.c ; + MainsFromSources mkEBU3213.c ; + MainsFromSources mkSMPTERP145.c ; MainsFromSources mklab2lab.c ; + MainsFromSources mkRec709.c ; + MainsFromSources mkRec2020.c ; + MainsFromSources mkSMPTE431_P3.c ; + MainsFromSources mkProPhoto.c ; # MainsFromSources icm2ary.c ; # Check library is compatible with C++ Main cppcheck : cppcheck.cpp ; - # chech CIEDE2000 + # check CIEDE2000 MainsFromSources testDE2K.c ; #Monotonic behaviour checker diff --git a/icc/License.txt b/icc/License.txt index 9de79da..bd0c4f2 100644 --- a/icc/License.txt +++ b/icc/License.txt @@ -1,5 +1,5 @@ ************************************************************************* -Copyright (c) 1997-2009 Graeme W. Gill +Copyright (c) 1997-2013 Graeme W. Gill Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/icc/ProPhoto.icm b/icc/ProPhoto.icm new file mode 100644 index 0000000..3ed16cb Binary files /dev/null and b/icc/ProPhoto.icm differ diff --git a/icc/ProPhotoLin.icm b/icc/ProPhotoLin.icm new file mode 100644 index 0000000..579b903 Binary files /dev/null and b/icc/ProPhotoLin.icm differ diff --git a/icc/Readme.txt b/icc/Readme.txt index 0ec425e..d39e9da 100644 --- a/icc/Readme.txt +++ b/icc/Readme.txt @@ -5,7 +5,7 @@ This version is part of Argyll V 1.50 ------------------------------------------- -Date 22 September 2012, Version 2.14 +Date 22 September 2013, Version 2.15 This distribution contains source code which implements the reading and writing of color profile files that conform to the International Color @@ -70,12 +70,6 @@ In summary this library provides: system file and memory sub-systems. * Loads Tag Types on demand to conserve memory space. -Changes from V2.11 - -Many changes have been made to support ArgyllCMS. -A double memory free bug when iccdump'ing a profile -that has duplicate tags has been fixed. - Package contents: icclib.zip ZIP archive of the following files diff --git a/icc/Rec2020.icm b/icc/Rec2020.icm new file mode 100644 index 0000000..09d5ccf Binary files /dev/null and b/icc/Rec2020.icm differ diff --git a/icc/Rec709.icm b/icc/Rec709.icm new file mode 100644 index 0000000..46140bd Binary files /dev/null and b/icc/Rec709.icm differ diff --git a/icc/SMPTE431_P3.icm b/icc/SMPTE431_P3.icm new file mode 100644 index 0000000..f3861be Binary files /dev/null and b/icc/SMPTE431_P3.icm differ diff --git a/icc/SMPTE_RP145_NTSC.icm b/icc/SMPTE_RP145_NTSC.icm new file mode 100644 index 0000000..a6ce7f1 Binary files /dev/null and b/icc/SMPTE_RP145_NTSC.icm differ diff --git a/icc/afiles b/icc/afiles index 7df3ba5..acb730e 100644 --- a/icc/afiles +++ b/icc/afiles @@ -20,7 +20,14 @@ icctest.c lutest.c mcheck.c testDE2K.c -makezip.ksh +mkDispProf.c sRGB.icm ClayRGB1998.icm lab2lab.icm +EBU3213_PAL.icm +SMPTE_RP145_NTSC.icm +Rec709.icm +Rec2020.icm +SMPTE431_P3.icm +ProPhoto.icm +ProPhotoLin.icm diff --git a/icc/icc.c b/icc/icc.c index 68668d0..7dfe519 100644 --- a/icc/icc.c +++ b/icc/icc.c @@ -7,7 +7,7 @@ * Date: 2002/04/22 * Version: 2.15 * - * Copyright 1997 - 2012 Graeme W. Gill + * Copyright 1997 - 2013 Graeme W. Gill * * This material is licensed with an "MIT" free use license:- * see the License.txt file in this directory for licensing details. @@ -51,10 +51,10 @@ #define _ICC_C_ /* Turn on implimentation code */ -#undef DEBUG_SETLUT /* Show each value being set in setting lut contents */ -#undef DEBUG_SETLUT_CLIP /* Show clipped values when setting LUT */ -#undef DEBUG_LULUT /* Show each value being looked up from lut contents */ -#undef DEBUG_LLULUT /* Debug individual lookup steps (not fully implemented) */ +#undef DEBUG_SETLUT /* [Und] Show each value being set in setting lut contents */ +#undef DEBUG_SETLUT_CLIP /* [Und] Show clipped values when setting LUT */ +#undef DEBUG_LULUT /* [Und] Show each value being looked up from lut contents */ +#undef DEBUG_LLULUT /* [Und] Debug individual lookup steps (not fully implemented) */ #include #include @@ -127,6 +127,10 @@ #define DBLLL(xxx) #endif +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + /* =========================================================== */ /* Overflow protected unsigned int arithmatic functions. */ /* These functions saturate rather than wrapping around. */ @@ -706,6 +710,12 @@ static int write_S15Fixed16Number(double d, char *p) { return 0; } +static double round_S15Fixed16Number(double d) { + d = floor(d * 65536.0 + 0.5); /* Beware! (int)(d + 0.5) doesn't work! */ + d = d/65536.0; + return d; +} + /* Device coordinate as 8 bit value range 0.0 - 1.0 */ static double read_DCS8Number(char *p) { unsigned int rv; @@ -775,7 +785,7 @@ static void read_PCSNumber(icc *icp, icColorSpaceSignature csig, double pcs[3], if (csig == icmSigPCSData) csig = icp->header->pcs; if (csig == icSigLabData) { - if (icp->ver != 0) + if (icp->ver >= icmVersion4_1) csig = icmSigLabV4Data; else csig = icmSigLabV2Data; @@ -820,7 +830,7 @@ static int write_PCSNumber(icc *icp, icColorSpaceSignature csig, double pcs[3], if (csig == icmSigPCSData) csig = icp->header->pcs; if (csig == icSigLabData) { - if (icp->ver != 0) + if (icp->ver >= icmVersion4_1) csig = icmSigLabV4Data; else csig = icmSigLabV2Data; @@ -5373,6 +5383,257 @@ double *in /* Input array[outputChan] */ return rv; } +/* ----------------------------------------------- */ +/* Tune a single interpolated value. Based on lookup_clut functions (above) */ + +/* Helper function to fine tune a single value interpolation */ +/* Return 0 on success, 1 if input clipping occured, 2 if output clipping occured */ +int icmLut_tune_value_nl( +icmLut *p, /* Pointer to Lut object */ +double *out, /* Output array[inputChan] */ +double *in /* Input array[outputChan] */ +) { + icc *icp = p->icp; + int rv = 0; + double *gp; /* Pointer to grid cube base */ + double co[MAX_CHAN]; /* Coordinate offset with the grid cell */ + double *gw, GW[1 << 8]; /* weight for each grid cube corner */ + double cout[MAX_CHAN]; /* Current output value */ + + if (p->inputChan <= 8) { + gw = GW; /* Use stack allocation */ + } else { + if ((gw = (double *) icp->al->malloc(icp->al, sat_mul((1 << p->inputChan), sizeof(double)))) == NULL) { + sprintf(icp->err,"icmLut_lookup_clut: malloc() failed"); + return icp->errc = 2; + } + } + + /* We are using an multi-linear (ie. Trilinear for 3D input) interpolation. */ + /* The implementation here uses more multiplies that some other schemes, */ + /* (for instance, see "Tri-Linear Interpolation" by Steve Hill, */ + /* Graphics Gems IV, page 521), but has less involved bookeeping, */ + /* needs less local storage for intermediate output values, does fewer */ + /* output and intermediate value reads, and fp multiplies are fast on */ + /* todays processors! */ + + /* Compute base index into grid and coordinate offsets */ + { + unsigned int e; + double clutPoints_1 = (double)(p->clutPoints-1); + int clutPoints_2 = p->clutPoints-2; + gp = p->clutTable; /* Base of grid array */ + + for (e = 0; e < p->inputChan; e++) { + unsigned int x; + double val; + val = in[e] * clutPoints_1; + if (val < 0.0) { + val = 0.0; + rv |= 1; + } else if (val > clutPoints_1) { + val = clutPoints_1; + rv |= 1; + } + x = (unsigned int)floor(val); /* Grid coordinate */ + if (x > clutPoints_2) + x = clutPoints_2; + co[e] = val - (double)x; /* 1.0 - weight */ + gp += x * p->dinc[e]; /* Add index offset for base of cube */ + } + } + /* Compute corner weights needed for interpolation */ + { + unsigned int e; + int i, g = 1; + gw[0] = 1.0; + for (e = 0; e < p->inputChan; e++) { + for (i = 0; i < g; i++) { + gw[g+i] = gw[i] * co[e]; + gw[i] *= (1.0 - co[e]); + } + g *= 2; + } + } + /* Now compute the current output value, and distribute the correction */ + { + int i; + unsigned int f; + double w, *d, ww = 0.0; + for (f = 0; f < p->outputChan; f++) + cout[f] = 0.0; + for (i = 0; i < (1 << p->inputChan); i++) { /* For all other corners of cube */ + w = gw[i]; /* Strength reduce */ + ww += w * w; /* Sum of weights squared */ + d = gp + p->dcube[i]; + for (f = 0; f < p->outputChan; f++) + cout[f] += w * d[f]; + } + + /* We distribute the correction needed in proportion to the */ + /* interpolation weighting, so the biggest correction is to the */ + /* closest vertex. */ + + for (f = 0; f < p->outputChan; f++) + cout[f] = (out[f] - cout[f])/ww; /* Amount to distribute */ + + for (i = 0; i < (1 << p->inputChan); i++) { /* For all other corners of cube */ + w = gw[i]; /* Strength reduce */ + d = gp + p->dcube[i]; + for (f = 0; f < p->outputChan; f++) { + d[f] += w * cout[f]; /* Apply correction */ + if (d[f] < 0.0) { + d[f] = 0.0; + rv |= 2; + } else if (d[f] > 1.0) { + d[f] = 1.0; + rv |= 2; + } + } + } + } + + if (gw != GW) + icp->al->free(icp->al, (void *)gw); + return rv; +} + +/* Helper function to fine tune a single value interpolation */ +/* Return 0 on success, 1 if input clipping occured, 2 if output clipping occured */ +int icmLut_tune_value_sx( +icmLut *p, /* Pointer to Lut object */ +double *out, /* Output array[inputChan] */ +double *in /* Input array[outputChan] */ +) { + int rv = 0; + double *gp; /* Pointer to grid cube base */ + double co[MAX_CHAN]; /* Coordinate offset with the grid cell */ + int si[MAX_CHAN]; /* co[] Sort index, [0] = smalest */ + double cout[MAX_CHAN]; /* Current output value */ + + /* We are using a simplex (ie. tetrahedral for 3D input) interpolation. */ + /* This method is more appropriate for XYZ/RGB/CMYK input spaces, */ + + /* Compute base index into grid and coordinate offsets */ + { + unsigned int e; + double clutPoints_1 = (double)(p->clutPoints-1); + int clutPoints_2 = p->clutPoints-2; + gp = p->clutTable; /* Base of grid array */ + + for (e = 0; e < p->inputChan; e++) { + unsigned int x; + double val; + val = in[e] * clutPoints_1; + if (val < 0.0) { + val = 0.0; + rv |= 1; + } else if (val > clutPoints_1) { + val = clutPoints_1; + rv |= 1; + } + x = (unsigned int)floor(val); /* Grid coordinate */ + if (x > clutPoints_2) + x = clutPoints_2; + co[e] = val - (double)x; /* 1.0 - weight */ + gp += x * p->dinc[e]; /* Add index offset for base of cube */ + } + } + /* Do insertion sort on coordinates, smallest to largest. */ + { + int f, vf; + unsigned int e; + double v; + for (e = 0; e < p->inputChan; e++) + si[e] = e; /* Initial unsorted indexes */ + + for (e = 1; e < p->inputChan; e++) { + f = e; + v = co[si[f]]; + vf = f; + while (f > 0 && co[si[f-1]] > v) { + si[f] = si[f-1]; + f--; + } + si[f] = vf; + } + } + /* Now compute the current output value, and distribute the correction */ + { + unsigned int e, f; + double w, ww = 0.0; /* Current vertex weight, sum of weights squared */ + double *ogp = gp; /* Pointer to grid cube base */ + + w = 1.0 - co[si[p->inputChan-1]]; /* Vertex at base of cell */ + ww += w * w; /* Sum of weights squared */ + for (f = 0; f < p->outputChan; f++) + cout[f] = w * gp[f]; + + for (e = p->inputChan-1; e > 0; e--) { /* Middle verticies */ + w = co[si[e]] - co[si[e-1]]; + ww += w * w; /* Sum of weights squared */ + gp += p->dinc[si[e]]; /* Move to top of cell in next largest dimension */ + for (f = 0; f < p->outputChan; f++) + cout[f] += w * gp[f]; + } + + w = co[si[0]]; + ww += w * w; /* Sum of weights squared */ + gp += p->dinc[si[0]]; /* Far corner from base of cell */ + for (f = 0; f < p->outputChan; f++) + cout[f] += w * gp[f]; + + /* We distribute the correction needed in proportion to the */ + /* interpolation weighting, so the biggest correction is to the */ + /* closest vertex. */ + for (f = 0; f < p->outputChan; f++) + cout[f] = (out[f] - cout[f])/ww; /* Amount to distribute */ + + gp = ogp; + w = 1.0 - co[si[p->inputChan-1]]; /* Vertex at base of cell */ + for (f = 0; f < p->outputChan; f++) { + gp[f] += w * cout[f]; /* Apply correction */ + if (gp[f] < 0.0) { + gp[f] = 0.0; + rv |= 2; + } else if (gp[f] > 1.0) { + gp[f] = 1.0; + rv |= 2; + } + } + + for (e = p->inputChan-1; e > 0; e--) { /* Middle verticies */ + w = co[si[e]] - co[si[e-1]]; + gp += p->dinc[si[e]]; /* Move to top of cell in next largest dimension */ + for (f = 0; f < p->outputChan; f++) { + gp[f] += w * cout[f]; /* Apply correction */ + if (gp[f] < 0.0) { + gp[f] = 0.0; + rv |= 2; + } else if (gp[f] > 1.0) { + gp[f] = 1.0; + rv |= 2; + } + } + } + + w = co[si[0]]; + gp += p->dinc[si[0]]; /* Far corner from base of cell */ + for (f = 0; f < p->outputChan; f++) { + gp[f] += w * cout[f]; /* Apply correction */ + if (gp[f] < 0.0) { + gp[f] = 0.0; + rv |= 2; + } else if (gp[f] > 1.0) { + gp[f] = 1.0; + rv |= 2; + } + } + } + return rv; +} + + /* ----------------------------------------------- */ /* Pseudo - Hilbert count sequencer */ @@ -5550,35 +5811,44 @@ static int getNormFunc( #define CLIP_MARGIN 0.005 /* Margine to allow before reporting clipping = 0.5% */ +/* NOTE that ICM_CLUT_SET_FILTER turns out to be not very useful, */ +/* as it can result in reversals. Could #ifdef out the code ?? */ + /* Helper function to set multiple Lut tables simultaneously. */ /* Note that these tables all have to be compatible in */ /* having the same configuration and resolution. */ /* Set errc and return error number in underlying icc */ -/* Set warnc if there is clipping in the output values */ -/* 1 = input table, 2 = main clut, 3 = clut midpoin, 4 = midpoint interp, 5 = output table */ +/* Set warnc if there is clipping in the output values: */ +/* 1 = input table, 2 = main clut, 3 = clut midpoint, 4 = midpoint interp, 5 = output table */ +/* Note that clutfunc in[] value has "index under", ie: */ +/* at ((int *)in)[-chan-1], and for primary grid is simply the */ +/* grid index (ie. 5,3,8), and for the center of cells grid, is */ +/* the -index-1, ie. -6,-3,-8 */ int icmSetMultiLutTables( - int ntables, /* Number of tables to be set, 1..n */ - icmLut **pp, /* Pointer to array of Lut objects */ - int flags, /* Setting flags */ - void *cbctx, /* Opaque callback context pointer value */ - icColorSpaceSignature insig, /* Input color space */ - icColorSpaceSignature outsig, /* Output color space */ + int ntables, /* Number of tables to be set, 1..n */ + icmLut **pp, /* Pointer to array of Lut objects */ + int flags, /* Setting flags */ + void *cbctx, /* Opaque callback context pointer value */ + icColorSpaceSignature insig, /* Input color space */ + icColorSpaceSignature outsig, /* Output color space */ void (*infunc)(void *cbctx, double *out, double *in), /* Input transfer function, inspace->inspace' (NULL = default) */ /* Will be called ntables times for each input grid value */ - double *inmin, double *inmax, /* Maximum range of inspace' values */ - /* (NULL = default) */ + double *inmin, double *inmax, /* Maximum range of inspace' values */ + /* (NULL = default) */ void (*clutfunc)(void *cbntx, double *out, double *in), /* inspace' -> outspace[ntables]' transfer function */ /* will be called once for each input' grid value, and */ /* ntables output values should be written consecutively */ /* to out[]. */ - double *clutmin, double *clutmax, /* Maximum range of outspace' values */ - /* (NULL = default) */ - void (*outfunc)(void *cbntx, double *out, double *in)) + double *clutmin, double *clutmax, /* Maximum range of outspace' values */ + /* (NULL = default) */ + void (*outfunc)(void *cbntx, double *out, double *in), /* Output transfer function, outspace'->outspace (NULL = deflt) */ /* Will be called ntables times on each output value */ -{ + int *apxls_gmin, int *apxls_gmax /* If not NULL, the grid indexes not to be affected */ + /* by ICM_CLUT_SET_APXLS, defaulting to 0..>clutPoints-1 */ +) { icmLut *p, *pn; /* Pointer to 0'th nd tn'th Lut object */ icc *icp; /* Pointer to common icc */ int tn; @@ -5594,6 +5864,7 @@ int icmSetMultiLutTables( double *_iv, *iv, *ivn; /* Real index value/table value */ double imin[MAX_CHAN], imax[MAX_CHAN]; double omin[MAX_CHAN], omax[MAX_CHAN]; + int def_apxls_gmin[MAX_CHAN], def_apxls_gmax[MAX_CHAN]; void (*ifromindex)(double *out, double *in); /* Index to input color space function */ void (*itoentry)(double *out, double *in); /* Input color space to entry function */ void (*ifromentry)(double *out, double *in); /* Entry to input color space function */ @@ -5806,6 +6077,17 @@ int icmSetMultiLutTables( /* Allocate space for cell center value lookup */ if (flags & ICM_CLUT_SET_APXLS) { + if (apxls_gmin == NULL) { + apxls_gmin = def_apxls_gmin; + for (e = 0; e < p->inputChan; e++) + apxls_gmin[e] = 0; + } + if (apxls_gmax == NULL) { + apxls_gmax = def_apxls_gmax; + for (e = 0; e < p->inputChan; e++) + apxls_gmax[e] = p->clutPoints-1; + } + if ((clutTable2 = (double **) icp->al->calloc(icp->al,sizeof(double *), ntables)) == NULL) { sprintf(icp->err,"icmLut_set_tables malloc of cube center array failed"); icp->al->free(icp->al, _iv); @@ -5884,7 +6166,7 @@ int icmSetMultiLutTables( ti += ii[e] * p->dinc[e]; /* Clut index */ iv[e] = ii[e]/(p->clutPoints-1.0); /* Vertex coordinates */ iv[e] = iv[e] * (imax[e] - imin[e]) + imin[e]; /* Undo expansion to 0.0 - 1.0 */ - *((int *)&iv[-((int)e)-1]) = ii[e]; /* Trick to supply grid index in iv[] */ + *((int *)&iv[-((int)e)-1]) = ii[e]; /* Trick to supply grid index in iv[] */ } if (flags & ICM_CLUT_SET_FILTER) { @@ -5941,12 +6223,13 @@ int icmSetMultiLutTables( if (clutTable2 != NULL) { for (e = 0; e < p->inputChan; e++) { - if (ii[e] >= (p->clutPoints-1)) - break; /* Don't lookup beyond last */ + if (ii[e] < apxls_gmin[e] + || ii[e] >= apxls_gmax[e]) + break; /* Don't lookup outside least squares area */ iv[e] = (ii[e] + 0.5)/(p->clutPoints-1.0); /* Vertex coordinates + 0.5 */ iv[e] = iv[e] * (imax[e] - imin[e]) + imin[e]; /* Undo expansion to 0.0 - 1.0 */ - *((int *)&iv[-((int)e)-1]) = ii[e]; /* Trick to supply grid index in iv[] */ - /* (Not this is only the base for +0.5) */ + *((int *)&iv[-((int)e)-1]) = -ii[e]-1; /* Trick to supply -ve grid index in iv[] */ + /* (Not this is only the base for +0.5 center) */ } if (e >= p->inputChan) { /* We're not on the last row */ @@ -5995,7 +6278,7 @@ int icmSetMultiLutTables( #define APXLS_DIFF_THRHESH 0.2 /* Deal with cell center value, aproximate least squares adjustment. */ /* Subtract some of the mean of the surrounding center values from each grid value. */ - /* Skip the edges so that things like the white point are not changed. */ + /* Skip the range edges so that things like the white point or Video sync are not changed. */ /* Avoid modifying the value if the difference between the */ /* interpolated value and the current value is too great, */ /* and there is the possibility of different color aliases. */ @@ -6005,9 +6288,9 @@ int icmSetMultiLutTables( int ee; double cw = 1.0/(double)(1 << p->inputChan); /* Weight for each cube corner */ - /* For each cell center point except last row */ + /* For each cell center point except last row because we access ii[e]+1 */ for (e = 0; e < p->inputChan; e++) - ii[e] = 0; /* init coords */ + ii[e] = apxls_gmin[e]; /* init coords */ /* Compute linear interpolated value from center values */ for (ee = 0; ee < p->inputChan;) { @@ -6051,14 +6334,15 @@ int icmSetMultiLutTables( } pn->clutTable[ti + f] = vv; } + DBGSL(("nix %s apxls ov %s\n",icmPiv(p->inputChan, ii), icmPdv(pn->outputChan, ivn))); } } /* Increment coord */ for (ee = 0; ee < p->inputChan; ee++) { - if (++ii[ee] < (p->clutPoints-2)) /* Don't go through upper edge */ + if (++ii[ee] < (apxls_gmax[ee]-1)) /* Stop short of upper row of clutTable2 */ break; /* No carry */ - ii[ee] = 0; + ii[ee] = apxls_gmin[ee]; } } @@ -6069,6 +6353,7 @@ int icmSetMultiLutTables( } /* Apply any smoothing in the clipped region to the resulting clutTable */ + /* !!! should avoid smoothing outside apxls_gmin[e] & apxls_gmax[e] region !!! */ if (clutTable3 != NULL) { double *clutTable1; /* Copy of current unfilted values */ FCOUNT(cc, MAX_CHAN, p->inputChan); /* Surrounding counter */ @@ -6180,7 +6465,7 @@ int icmSetMultiLutTables( free(clutTable3); } - /* Create the output table entry values */ + /* Create the 1D output table entry values */ for (tn = 0; tn < ntables; tn++) { pn = pp[tn]; for (n = 0; n < pn->outputEnt; n++) { @@ -6239,19 +6524,21 @@ int icmSetMultiLutTables( /* Set errc and return error number */ /* Set warnc if there is clipping in the output values */ static int icmLut_set_tables ( -icmLut *p, /* Pointer to Lut object */ -int flags, /* Setting flags */ -void *cbctx, /* Opaque callback context pointer value */ -icColorSpaceSignature insig, /* Input color space */ -icColorSpaceSignature outsig, /* Output color space */ +icmLut *p, /* Pointer to Lut object */ +int flags, /* Setting flags */ +void *cbctx, /* Opaque callback context pointer value */ +icColorSpaceSignature insig, /* Input color space */ +icColorSpaceSignature outsig, /* Output color space */ void (*infunc)(void *cbcntx, double *out, double *in), /* Input transfer function, inspace->inspace' (NULL = default) */ -double *inmin, double *inmax, /* Maximum range of inspace' values (NULL = default) */ +double *inmin, double *inmax, /* Maximum range of inspace' values (NULL = default) */ void (*clutfunc)(void *cbctx, double *out, double *in), /* inspace' -> outspace' transfer function */ -double *clutmin, double *clutmax, /* Maximum range of outspace' values (NULL = default) */ -void (*outfunc)(void *cbctx, double *out, double *in) - /* Output transfer function, outspace'->outspace (NULL = deflt) */ +double *clutmin, double *clutmax, /* Maximum range of outspace' values (NULL = default) */ +void (*outfunc)(void *cbctx, double *out, double *in), + /* Output transfer function, outspace'->outspace (NULL = deflt) */ +int *apxls_gmin, int *apxls_gmax /* If not NULL, the grid indexes not to be affected */ + /* by ICM_CLUT_SET_APXLS, defaulting to 0..>clutPoints-1 */ ) { struct _icmLut *pp[3]; @@ -6263,7 +6550,8 @@ void (*outfunc)(void *cbctx, double *out, double *in) inmin, inmax, clutfunc, clutmin, clutmax, - outfunc); + outfunc, + apxls_gmin, apxls_gmax); } /* - - - - - - - - - - - - - - - - */ @@ -6347,16 +6635,8 @@ static int icmLut_read( p->outputChan = read_UInt8Number(bp+9); p->clutPoints = read_UInt8Number(bp+10); - /* Sanity check */ - if (p->inputChan > MAX_CHAN) { - sprintf(icp->err,"icmLut_read: Can't handle > %d input channels\n",MAX_CHAN); - return icp->errc = 1; - } - - if (p->outputChan > MAX_CHAN) { - sprintf(icp->err,"icmLut_read: Can't handle > %d output channels\n",MAX_CHAN); - return icp->errc = 1; - } + if (icp->allowclutPoints256 && p->clutPoints == 0) /* Special case */ + p->clutPoints = 256; /* Read 3x3 transform matrix */ for (j = 0; j < 3; j++) { /* Rows */ @@ -6375,7 +6655,7 @@ static int icmLut_read( bp = buf+52; } - /* Sanity check dimensions. This protects against */ + /* Sanity check tag size. This protects against */ /* subsequent integer overflows involving the dimensions. */ if ((size = icmLut_get_size((icmBase *)p)) == UINT_MAX || size > len) { @@ -6384,12 +6664,15 @@ static int icmLut_read( return icp->errc = 1; } - /* Read the input tables */ - size = (p->inputChan * p->inputEnt); + /* Sanity check the dimensions and resolution values agains limits, */ + /* allocate space for them and generate internal offset tables. */ if ((rv = p->allocate((icmBase *)p)) != 0) { icp->al->free(icp->al, buf); return rv; } + + /* Read the input tables */ + size = (p->inputChan * p->inputEnt); if (p->ttype == icSigLut8Type) { for (i = 0; i < size; i++, bp += 1) p->inputTable[i] = read_DCS8Number(bp); @@ -6400,10 +6683,6 @@ static int icmLut_read( /* Read the clut table */ size = (p->outputChan * sat_pow(p->clutPoints,p->inputChan)); - if ((rv = p->allocate((icmBase *)p)) != 0) { - icp->al->free(icp->al, buf); - return rv; - } if (p->ttype == icSigLut8Type) { for (i = 0; i < size; i++, bp += 1) p->clutTable[i] = read_DCS8Number(bp); @@ -6414,10 +6693,6 @@ static int icmLut_read( /* Read the output tables */ size = (p->outputChan * p->outputEnt); - if ((rv = p->allocate((icmBase *)p)) != 0) { - icp->al->free(icp->al, buf); - return rv; - } if (p->ttype == icSigLut8Type) { for (i = 0; i < size; i++, bp += 1) p->outputTable[i] = read_DCS8Number(bp); @@ -6426,20 +6701,6 @@ static int icmLut_read( p->outputTable[i] = read_DCS16Number(bp); } - /* Private: compute dimensional increment though clut */ - /* Note that first channel varies least rapidly. */ - i = p->inputChan-1; - p->dinc[i--] = p->outputChan; - for (; i < p->inputChan; i--) - p->dinc[i] = p->dinc[i+1] * p->clutPoints; - - /* Private: compute offsets from base of cube to other corners */ - for (p->dcube[0] = 0, g = 1, j = 0; j < p->inputChan; j++) { - for (i = 0; i < g; i++) - p->dcube[g+i] = p->dcube[i] + p->dinc[j]; - g *= 2; - } - icp->al->free(icp->al, buf); return 0; } @@ -6486,10 +6747,19 @@ static int icmLut_write( icp->al->free(icp->al, buf); return icp->errc = rv; } - if ((rv = write_UInt8Number(p->clutPoints, bp+10)) != 0) { - sprintf(icp->err,"icmLut_write: write_UInt8Number() failed"); - icp->al->free(icp->al, buf); - return icp->errc = rv; + + if (icp->allowclutPoints256 && p->clutPoints == 256) { + if ((rv = write_UInt8Number(0, bp+10)) != 0) { + sprintf(icp->err,"icmLut_write: write_UInt8Number() failed"); + icp->al->free(icp->al, buf); + return icp->errc = rv; + } + } else { + if ((rv = write_UInt8Number(p->clutPoints, bp+10)) != 0) { + sprintf(icp->err,"icmLut_write: write_UInt8Number() failed"); + icp->al->free(icp->al, buf); + return icp->errc = rv; + } } write_UInt8Number(0, bp+11); /* Set padding to 0 */ @@ -6678,7 +6948,9 @@ static void icmLut_dump( } } -/* Allocate variable sized data elements */ +/* Sanity check the input & output dimensions, and */ +/* allocate variable sized data elements, and */ +/* generate internal dimension offset tables */ static int icmLut_allocate( icmBase *pp ) { @@ -6686,7 +6958,7 @@ static int icmLut_allocate( icmLut *p = (icmLut *)pp; icc *icp = p->icp; - /* Sanity check */ + /* Sanity check, so that dinc[] comp. won't fail */ if (p->inputChan < 1) { sprintf(icp->err,"icmLut_alloc: Can't handle %d input channels\n",p->inputChan); return icp->errc = 1; @@ -6800,6 +7072,8 @@ static icmBase *new_icmLut( icmLut *p; if ((p = (icmLut *) icp->al->calloc(icp->al,1,sizeof(icmLut))) == NULL) return NULL; + p->icp = icp; + p->ttype = icSigLut16Type; p->refcount = 1; p->get_size = icmLut_get_size; @@ -6820,8 +7094,7 @@ static icmBase *new_icmLut( /* Set method */ p->set_tables = icmLut_set_tables; - - p->icp = icp; + p->tune_value = icmLut_tune_value_sx; /* Default to most likely simplex */ /* Set matrix to reasonable default */ for (i = 0; i < 3; i++) @@ -7941,7 +8214,7 @@ static int icmColorantTable_allocate( if (p->count != p->_count) { unsigned int i; if (ovr_mul(p->count, sizeof(icmColorantTableVal))) { - sprintf(icp->err,"icmColorantTable_alloc: count overflow (%d of %ld bytes)",p->count,sizeof(icmColorantTableVal)); + sprintf(icp->err,"icmColorantTable_alloc: count overflow (%d of %lu bytes)",p->count,sizeof(icmColorantTableVal)); return icp->errc = 1; } if (p->data != NULL) @@ -10784,10 +11057,18 @@ static int icmHeader_read( p->cmmId = read_SInt32Number(buf + 4); /* CMM for profile */ tt = read_UInt8Number(buf + 8); /* Raw major version number */ p->majv = (tt >> 4) * 10 + (tt & 0xf); /* Integer major version number */ - icp->ver = p->majv > 3 ? 1 : 0; /* Set major version flag in icc */ - tt = read_UInt8Number(buf + 9); /* Raw minor/bug fix version numbers */ + tt = read_UInt8Number(buf + 9); /* Raw minor & bug fix version numbers */ p->minv = (tt >> 4); /* Integer minor version number */ p->bfv = (tt & 0xf); /* Integer bug fix version number */ + if (p->majv < 3) { /* Set version class */ + if (p->minv >= 4) + icp->ver = icmVersion2_4; + else if (p->minv >= 3) + icp->ver = icmVersion2_3; + else + icp->ver = icmVersionDefault; + } else + icp->ver = icmVersion4_1; p->deviceClass = (icProfileClassSignature) read_SInt32Number(buf + 12); /* Type of profile */ p->colorSpace = (icColorSpaceSignature) @@ -10814,13 +11095,13 @@ static int icmHeader_read( } p->creator = read_SInt32Number(buf + 80); /* Profile creator */ - for (tt = 0; tt < 16; tt++) - p->id[tt] = icp->ver ? read_UInt8Number(buf + 84 + tt) : 0; /* Profile ID */ + for (tt = 0; tt < 16; tt++) /* Profile ID */ + p->id[tt] = icp->ver >= icmVersion4_1 ? read_UInt8Number(buf + 84 + tt) : 0; icp->al->free(icp->al, buf); #ifndef ENABLE_V4 - if (icp->ver) { + if (icp->ver >= icmVersion4_1) { sprintf(icp->err,"icmHeader_read: ICC V4 not supported!"); return icp->errc = 1; } @@ -10869,6 +11150,7 @@ static int icmHeader_write( icp->al->free(icp->al, buf); return icp->errc = 1; } + // ~~~ Hmm. We're not checking ->ver is >= corresponding header version number ~~ tt = ((p->majv/10) << 4) + (p->majv % 10); if ((rv = write_UInt8Number(tt, buf + 8)) != 0) { /* Raw major version number */ sprintf(icp->err,"icmHeader_write: Uint8Number major version"); @@ -10946,7 +11228,7 @@ static int icmHeader_write( icp->al->free(icp->al, buf); return icp->errc = rv; } - if (doid == 0 && icp->ver) { /* ID is V4.0+ feature */ + if (doid == 0 && icp->ver >= icmVersion4_1) { /* ID is V4.0+ feature */ for (tt = 0; tt < 16; tt++) { if ((rv = write_UInt8Number(p->id[tt], buf + 84 + tt)) != 0) { /* Profile ID */ sprintf(icp->err,"icmHeader_write: UInt8Number creator"); @@ -10992,7 +11274,7 @@ static void icmHeader_dump( op->gprintf(op," Rndrng Intnt = %s\n", string_RenderingIntent(p->renderingIntent)); op->gprintf(op," Illuminant = %s\n", string_XYZNumber_and_Lab(&p->illuminant)); op->gprintf(op," Creator = %s\n", tag2str(p->creator)); /* ~~~ */ - if (p->icp->ver) { /* V4.0+ feature */ + if (p->icp->ver >= icmVersion4_1) { /* V4.0+ feature */ for (i = 0; i < 16; i++) { /* Check if ID has been set */ if (p->id[i] != 0) break; @@ -11369,10 +11651,15 @@ static int check_icc_legal( /* Found entry, so now check that all the required tags are present. */ for (j = 0; tagchecktable[i].tags[j] != icMaxEnumType; j++) { if (p->find_tag(p, tagchecktable[i].tags[j]) != 0) { /* Not present! */ +#ifdef NEVER + printf("icc_check_legal: deviceClass %s is missing required tag %s", tag2str(sig), tag2str(tagchecktable[i].tags[j])); +#endif if (tagchecktable[i].chans == -200 || tagchecktable[i].chans == -dchans) { /* But can try next table */ break; } + /* ~~99 Hmm. Should report all possible missing tags from */ + /* previous failed tables ~~~999 */ sprintf(p->err,"icc_check_legal: deviceClass %s is missing required tag %s", tag2str(sig), tag2str(tagchecktable[i].tags[j])); return p->errc = 1; @@ -11776,7 +12063,7 @@ static int icc_write_x( /* If V4.0+, Compute the MD5 id for the profile. */ /* We do this by writing to a fake icmFile */ - if (p->ver) { + if (p->ver >= icmVersion4_1) { icmMD5 *md5 = NULL; icmFile *ofp, *dfp = NULL; @@ -11796,13 +12083,13 @@ static int icc_write_x( ofp = p->fp; p->fp = dfp; - /* Dumy write the header */ + /* Dummy write the header */ if ((rv = p->header->write(p->header, 0, 1)) != 0) { p->al->free(p->al, buf); return rv; } - /* Dumy write the tag table */ + /* Dummy write the tag table */ if ( p->fp->seek(p->fp, 128) != 0 || p->fp->write(p->fp, buf, 1, len) != len) { sprintf(p->err,"icc_write: seek() or write() failed"); @@ -11810,7 +12097,7 @@ static int icc_write_x( return p->errc = 1; } - /* Dumy write all the tag element data */ + /* Dummy write all the tag element data */ /* (We invert meaning of touched here) */ for (i = 0; i < p->count; i++) { /* For all the tag element data */ if (p->data[i].objp->touched == 0) @@ -12197,8 +12484,8 @@ static icmBase *icc_read_tag_ix( } /* Read the tag element data of the first matching, and return a pointer to the object */ -/* Returns NULL if error - icc->errc will contain: */ -/* 2 if not found */ +/* Returns NULL if error - icc->errc will contain: */ +/* 2 if not found */ /* Doesn't read uknown type tags */ static icmBase *icc_read_tag( icc *p, @@ -12222,7 +12509,7 @@ static icmBase *icc_read_tag( } /* Read the tag element data of the first matching, and return a pointer to the object */ -/* Returns NULL if error. +/* Returns NULL if error. */ /* Returns an icmSigUnknownType object if the tag type isn't handled by a specific object. */ /* NOTE: we don't handle tag duplication - you'll always get the first in the file. */ static icmBase *icc_read_tag_any( @@ -12650,6 +12937,25 @@ static void Lut_Luv2Lut(double *out, double *in) { out[2] = (in[2] + 128.0) * 256.0/65535.0; /* v */ } +/* - - - - - - - - - - - - - - - - */ +/* Convert YCbCr to Lut number */ +/* We are assuming full range here. foot/head scaling */ +/* should be done outside the icc profile. */ + +/* Convert Lut table index/value to YCbCr */ +static void Lut_Lut2YCbCr(double *out, double *in) { + out[0] = in[0]; /* Y */ + out[1] = in[1] - 0.5; /* Cb */ + out[2] = in[2] - 0.5; /* Cr */ +} + +/* Convert YCbCr to Lut table index/value */ +static void Lut_YCbCr2Lut(double *out, double *in) { + out[0] = in[0]; /* Y */ + out[1] = in[1] + 0.5; /* Cb */ + out[2] = in[2] + 0.5; /* Cr */ +} + /* - - - - - - - - - - - - - - - - */ /* Default N component conversions */ static void Lut_N(double *out, double *in, int nc) { @@ -12749,7 +13055,6 @@ static void Lut_15(double *out, double *in) { /* Function table - match conversions to color spaces. */ /* Anything not here, we don't know how to convert. */ -/* (ie. YCbCr) */ static struct { icColorSpaceSignature csig; void (*fromLut)(double *out, double *in); /* from Lut index/entry */ @@ -12764,6 +13069,7 @@ static struct { {icmSigLV2Data, Lut_Lut2LV2_16, Lut_L2LutV2_16 }, {icmSigLV4Data, Lut_Lut2LV4_16, Lut_L2LutV4_16 }, {icSigLuvData, Lut_Lut2Luv, Lut_Luv2Lut }, + {icSigYCbCrData, Lut_Lut2YCbCr, Lut_YCbCr2Lut }, {icSigYxyData, Lut_3, Lut_3 }, {icSigRgbData, Lut_3, Lut_3 }, {icSigGrayData, Lut_1, Lut_1 }, @@ -12837,7 +13143,7 @@ static int getNormFunc( if (tagType == icSigLut16Type) /* Lut16 retains legacy encoding */ csig = icmSigLabV2Data; else { /* Other tag types use version specific encoding */ - if (icp->ver) + if (icp->ver >= icmVersion4_1) csig = icmSigLabV4Data; else csig = icmSigLabV2Data; @@ -12870,7 +13176,6 @@ static int getNormFunc( /* Function table - match ranges to color spaces. */ /* Anything not here, we don't know how to convert. */ -/* (ie. YCbCr) */ /* Hmm. we're not handling Lab8 properly ?? ~~~8888 */ static struct { icColorSpaceSignature csig; @@ -12889,7 +13194,7 @@ static struct { {icmSigLV4Data, 1, { 0.0 }, { 100.0 } }, {icSigLuvData, 0, { 0.0, -128.0, -128.0 }, { 100.0, 127.0 + 255.0/256.0, 127.0 + 255.0/256.0 } }, - {icSigYCbCrData, 1, { 0.0 }, { 1.0 } }, /* ??? */ + {icSigYCbCrData, 0, { 0.0, -0.5, -0.5 }, { 1.0, 0.5, 0.5 } }, /* Full range */ {icSigYxyData, 1, { 0.0 }, { 1.0 } }, /* ??? */ {icSigRgbData, 1, { 0.0 }, { 1.0 } }, {icSigGrayData, 1, { 0.0 }, { 1.0 } }, @@ -12940,7 +13245,7 @@ static int getRange( if (tagType == icSigLut16Type) /* Lut16 retains legacy encoding */ csig = icmSigLabV2Data; else { /* Other tag types use version specific encoding */ - if (icp->ver) + if (icp->ver >= icmVersion4_1) csig = icmSigLabV4Data; else csig = icmSigLabV2Data; @@ -12974,8 +13279,8 @@ static int getRange( return 0; } -/* ============================================= */ -/* Misc. support functions. */ +/* =============================================================== */ +/* Misc. support functions. */ /* Clamp a 3 vector to be +ve */ void icmClamp3(double out[3], double in[3]) { @@ -12998,6 +13303,20 @@ void icmSub3(double out[3], double in1[3], double in2[3]) { out[2] = in1[2] - in2[2]; } +/* Divide two 3 vectors, out = in1/in2 */ +void icmDiv3(double out[3], double in1[3], double in2[3]) { + out[0] = in1[0]/in2[0]; + out[1] = in1[1]/in2[1]; + out[2] = in1[2]/in2[2]; +} + +/* Multiply two 3 vectors, out = in1 * in2 */ +void icmMul3(double out[3], double in1[3], double in2[3]) { + out[0] = in1[0] * in2[0]; + out[1] = in1[1] * in2[1]; + out[2] = in1[2] * in2[2]; +} + /* - - - - - - - - - - - - - - - - - - - - - - - - */ /* Set a 3x3 matrix to unity */ @@ -13307,6 +13626,18 @@ void icmBlend3(double out[3], double in0[3], double in1[3], double bf) { out[2] = (1.0 - bf) * in0[2] + bf * in1[2]; } +/* Clip a vector to the range 0.0 .. 1.0 */ +void icmClip3(double out[3], double in[3]) { + int j; + for (j = 0; j < 3; j++) { + out[j] = in[j]; + if (out[j] < 0.0) + out[j] = 0.0; + else if (out[j] > 1.0) + out[j] = 1.0; + } +} + /* Normalise a 3 vector to the given length. Return nz if not normalisable */ int icmNormalize3(double out[3], double in[3], double len) { double tt = sqrt(in[0] * in[0] + in[1] * in[1] + in[2] * in[2]); @@ -13680,6 +14011,74 @@ double icmPlaneDist3(double eq[4], double p[3]) { return rv; } +/* - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Given 2 2D points, compute a plane equation. */ +/* The normal will be right handed given the order of the points */ +/* The plane equation will be the 2 normal components and the constant. */ +/* Return nz if any points are cooincident or co-linear */ +int icmPlaneEqn2(double eq[3], double p0[2], double p1[2]) { + double ll, v1[3]; + + /* Compute vectors along edge */ + v1[0] = p1[0] - p0[0]; + v1[1] = p1[1] - p0[1]; + + /* Normal to vector */ + eq[0] = v1[1]; + eq[1] = -v1[0]; + + /* Normalise the equation */ + ll = sqrt(eq[0] * eq[0] + eq[1] * eq[1]); + if (ll < 1e-10) { + return 1; + } + eq[0] /= ll; + eq[1] /= ll; + + /* Compute the plane equation constant */ + eq[2] = - (eq[0] * p0[0]) + - (eq[1] * p0[1]); + + return 0; +} + +/* Given a 2D point and a plane equation, return the signed */ +/* distance from the plane. The distance will be +ve if the point */ +/* is to the right of the plane formed by two points in order */ +double icmPlaneDist2(double eq[3], double p[2]) { + double rv; + + rv = eq[0] * p[0] + + eq[1] * p[1] + + eq[2]; + + return rv; +} + +/* Given two infinite 2D lines define by 4 points, compute the intersection. */ +/* Return nz if there is no intersection (lines are parallel) */ +int icmLineIntersect2(double res[2], double p1[2], double p2[2], double p3[2], double p4[2]) { + /* Compute by determinants */ + double x1y2_y1x2 = p1[0] * p2[1] - p1[1] * p2[0]; + double x3y4_y3x4 = p3[0] * p4[1] - p3[1] * p4[0]; + double x1_x2 = p1[0] - p2[0]; + double y1_y2 = p1[1] - p2[1]; + double x3_x4 = p3[0] - p4[0]; + double y3_y4 = p3[1] - p4[1]; + double num; /* Numerator */ + + num = x1_x2 * y3_y4 - y1_y2 * x3_x4; + + if (fabs(num) < 1e-10) + return 1; + + res[0] = (x1y2_y1x2 * x3_x4 - x1_x2 * x3y4_y3x4)/num; + res[1] = (x1y2_y1x2 * y3_y4 - y1_y2 * x3y4_y3x4)/num; + + return 0; +} + + /* - - - - - - - - - - - - - - - - - - - - - - - - */ /* CIE Y (range 0 .. 1) to perceptual CIE 1976 L* (range 0 .. 100) */ double @@ -13778,7 +14177,7 @@ void icmLCh2Lab(double *out, double *in) { out[2] = C * sin(h); } -/* Lab to LCh */ +/* Lab to LCh (general to polar, works with Luv too) */ void icmLab2LCh(double *out, double *in) { double C, h; @@ -13883,9 +14282,6 @@ extern ICCLIB_API void icmLuv2XYZ(icmXYZNumber *w, double *out, double *in) { out[2] = Z; } -/* NOTE :- none of the following seven have been protected */ -/* against arithmmetic issues (ie. for black) */ - /* CIE XYZ to perceptual CIE 1976 UCS diagram Yu'v'*/ /* (Yu'v' is a better chromaticity space than Yxy) */ extern ICCLIB_API void icmXYZ21976UCS(double *out, double *in) { @@ -13893,8 +14289,15 @@ extern ICCLIB_API void icmXYZ21976UCS(double *out, double *in) { double den, u, v; den = (X + 15.0 * Y + 3.0 * Z); - u = (4.0 * X) / den; - v = (9.0 * Y) / den; + + if (den < 1e-9) { + Y = 0.0; + u = 4.0/19.0; + v = 9.0/19.0; + } else { + u = (4.0 * X) / den; + v = (9.0 * Y) / den; + } out[0] = Y; out[1] = u; @@ -13909,8 +14312,12 @@ extern ICCLIB_API void icm1976UCS2XYZ(double *out, double *in) { u = in[1]; v = in[2]; - X = ((9.0 * u * Y)/(4.0 * v)); - Z = -(((20.0 * v + 3.0 * u - 12.0) * Y)/(4.0 * v)); + if (v < 1e-9) { + X = Y = Z = 0.0; + } else { + X = ((9.0 * u * Y)/(4.0 * v)); + Z = -(((20.0 * v + 3.0 * u - 12.0) * Y)/(4.0 * v)); + } out[0] = X; out[1] = Y; @@ -13922,10 +14329,18 @@ extern ICCLIB_API void icm1976UCS2XYZ(double *out, double *in) { /* in computing color temperatures.) */ extern ICCLIB_API void icmXYZ21960UCS(double *out, double *in) { double X = in[0], Y = in[1], Z = in[2]; - double u, v; + double den, u, v; - u = (4.0 * X) / (X + 15.0 * Y + 3.0 * Z); - v = (6.0 * Y) / (X + 15.0 * Y + 3.0 * Z); + den = (X + 15.0 * Y + 3.0 * Z); + + if (den < 1e-9) { + Y = 0.0; + u = 4.0/19.0; + v = 6.0/19.0; + } else { + u = (4.0 * X) / den; + v = (6.0 * Y) / den; + } out[0] = Y; out[1] = u; @@ -13940,8 +14355,12 @@ extern ICCLIB_API void icm1960UCS2XYZ(double *out, double *in) { u = in[1]; v = in[2]; - X = ((3.0 * u * Y)/(2.0 * v)); - Z = -(((10.0 * v + u - 4.0) * Y)/(2.0 * v)); + if (v < 1e-9) { + X = Y = Z = 0.0; + } else { + X = ((3.0 * u * Y)/(2.0 * v)); + Z = -(((10.0 * v + u - 4.0) * Y)/(2.0 * v)); + } out[0] = X; out[1] = Y; @@ -13989,6 +14408,7 @@ extern ICCLIB_API void icm1964WUV2XYZ(icmXYZNumber *w, double *out, double *in) } /* CIE CIE1960 UCS to perceptual CIE 1964 WUV (U*V*W*) */ +/* (This is used in computing CRI) */ extern ICCLIB_API void icm1960UCS21964WUV(icmXYZNumber *w, double *out, double *in) { double W, U, V; double wucs[3]; @@ -14006,6 +14426,7 @@ extern ICCLIB_API void icm1960UCS21964WUV(icmXYZNumber *w, double *out, double * } /* - - - - - - - - - - - - - - - - - - - - - - - - */ +/* NOTE :- that these values are for the 1931 standard observer */ /* available D50 Illuminant */ icmXYZNumber icmD50 = { /* Profile illuminant - D50 */ @@ -14041,6 +14462,7 @@ double icmD65_100_ary3[3] = { /* Profile illuminant - D65, scaled to 100 */ 95.05, 100.00, 108.90 }; + /* Default black point */ icmXYZNumber icmBlack = { 0.0000, 0.0000, 0.0000 @@ -14126,7 +14548,7 @@ double icmCIE94sq(double Lab0[3], double Lab1[3]) { c12 = sqrt(c1 * c2); /* Symetric chromanance */ /* delta chromanance squared */ - dc = c2 - c1; + dc = c1 - c2; dcsq = dc * dc; } @@ -14137,8 +14559,8 @@ double icmCIE94sq(double Lab0[3], double Lab1[3]) { double sc, sh; /* Weighting factors for delta chromanance & delta hue */ - sc = 1.0 + 0.048 * c12; - sh = 1.0 + 0.014 * c12; + sc = 1.0 + 0.045 * c12; + sh = 1.0 + 0.015 * c12; return dlsq + dcsq/(sc * sc) + dhsq/(sh * sh); } } @@ -14286,7 +14708,7 @@ double icmCIE2K(double *Lab0, double *Lab1) { } /* Return the CIEDE2000 Delta E color difference measure for two XYZ values */ -extern ICCLIB_API double icmXYZCIE2K(icmXYZNumber *w, double *in0, double *in1) { +ICCLIB_API double icmXYZCIE2K(icmXYZNumber *w, double *in0, double *in1) { double lab0[3], lab1[3]; icmXYZ2Lab(w, lab0, in0); @@ -14295,12 +14717,15 @@ extern ICCLIB_API double icmXYZCIE2K(icmXYZNumber *w, double *in0, double *in1) } + /* - - - - - - - - - - - - - - - - - - - - - - - - */ /* Chromatic adaptation transform utility */ /* Return a 3x3 chromatic adaptation matrix */ /* Use icmMulBy3x3(dst, mat, src) */ +/* NOTE that to transform primaries they */ +/* must be mat[XYZ][RGB] format! */ void icmChromAdaptMatrix( - int flags, /* Use bradford, Transform given matrix flags */ + int flags, /* Use Bradford, Transform given matrix flags */ icmXYZNumber d_wp, /* Destination white point */ icmXYZNumber s_wp, /* Source white point */ double mat[3][3] /* Destination matrix */ @@ -14376,7 +14801,7 @@ int icmRGBprim2matrix( icmXYZNumber red, /* Red colorant */ icmXYZNumber green, /* Green colorant */ icmXYZNumber blue, /* Blue colorant */ - double mat[3][3] /* Destination matrix */ + double mat[3][3] /* Destination matrix[RGB][XYZ] */ ) { double tmat[3][3]; double t[3]; @@ -14409,18 +14834,72 @@ int icmRGBprim2matrix( /* Now formulate the transform matrix */ mat[0][0] = red.X * t[0]; - mat[0][1] = green.X * t[1]; - mat[0][2] = blue.X * t[2]; - mat[1][0] = red.Y * t[0]; + mat[1][0] = green.X * t[1]; + mat[2][0] = blue.X * t[2]; + mat[0][1] = red.Y * t[0]; mat[1][1] = green.Y * t[1]; - mat[1][2] = blue.Y * t[2]; - mat[2][0] = red.Z * t[0]; - mat[2][1] = green.Z * t[1]; + mat[2][1] = blue.Y * t[2]; + mat[0][2] = red.Z * t[0]; + mat[1][2] = green.Z * t[1]; mat[2][2] = blue.Z * t[2]; return 0; } +/* - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Pre-round RGB device primary values to ensure that */ +/* the sum of the quantized primaries is the same as */ +/* the quantized sum. */ +void quantizeRGBprimsS15Fixed16( + double mat[3][3] /* matrix[RGB][XYZ] */ +) { + int i, j; + double sum[3]; + +// printf("D50 = %f %f %f\n",icmD50.X, icmD50.Y, icmD50.Z); + + /* Compute target sum of primary XYZ */ + for (i = 0; i < 3; i++) { + sum[i] = 0.0; + for (j = 0; j < 3; j++) + sum[i] += mat[j][i]; + } +// printf("Sum = %f %f %f\n",sum[0], sum[1], sum[2]); + + /* Pre-quantize the primary XYZ's, and then ensure that the */ + /* sum of the quantized values is the quantized sum by assigning */ + /* the rounding error to the largest component. */ + for (i = 0; i < 3; i++) { + int bix = 0; + double bval = -1e9; + + /* locate the largest and quantize each component */ + for (j = 0; j < 3; j++) { + if (fabs(mat[j][i]) > bval) { /* Locate largest */ + bix = j; + bval = fabs(mat[j][i]); + } + mat[j][i] = round_S15Fixed16Number(mat[j][i]); + } + + /* Compute the value the largest has to be */ + /* to ensure that sum of the quantized values is */ + /* equal to the quantized sum */ + for (j = 0; j < 3; j++) { + if (j == bix) + continue; + sum[i] -= mat[j][i]; + } + mat[bix][i] = round_S15Fixed16Number(sum[i]); + + /* Check the sum of the quantized values */ +// sum[i] = 0.0; +// for (j = 0; j < 3; j++) +// sum[i] += mat[j][i]; + } +// printf("Q Sum = %f %f %f\n",sum[0], sum[1], sum[2]); +} + /* - - - - - - - - - - - - - - - - - - - - - - - - */ /* Some PCS utility functions */ @@ -14525,7 +15004,426 @@ int icmClipXYZ(double out[3], double in[3]) { return 1; } -/* - - - - - - - - - - - - - - - - - - - - - - - - */ +/* --------------------------------------------------------------- */ +/* Some video specific functions */ + +/* Convert Lut table index/value to YPbPr */ +/* (Same as Lut_Lut2YPbPr() ) */ +void icmLut2YPbPr(double *out, double *in) { + out[0] = in[0]; /* Y */ + out[1] = in[1] - 0.5; /* Cb */ + out[2] = in[2] - 0.5; /* Cr */ +} + +/* Convert YPbPr to Lut table index/value */ +/* (Same as Lut_YPbPr2Lut() ) */ +void icmYPbPr2Lut(double *out, double *in) { + out[0] = in[0]; /* Y */ + out[1] = in[1] + 0.5; /* Cb */ + out[2] = in[2] + 0.5; /* Cr */ +} + +/* Convert Rec601 RGB' into YPbPr, or "full range YCbCr" */ +/* where input 0..1, output 0..1, -0.5 .. 0.5, -0.5 .. 0.5 */ +/* [From the Rec601 spec. ] */ +void icmRec601_RGBd_2_YPbPr(double out[3], double in[3]) { + double tt[3]; + + tt[0] = 0.299 * in[0] + 0.587 * in[1] + 0.114 * in[2]; + + tt[1] = -0.299 /1.772 * in[0] + + -0.587 /1.772 * in[1] + + (1.0-0.114)/1.772 * in[2]; + + tt[2] = (1.0-0.299)/1.402 * in[0] + + -0.587 /1.402 * in[1] + + -0.114 /1.402 * in[2]; + + out[0] = tt[0]; + out[1] = tt[1]; + out[2] = tt[2]; +} + +/* Convert Rec601 YPbPr to RGB' (== "full range YCbCr") */ +/* where input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, output 0.0 .. 1 */ +/* [Inverse of above] */ +void icmRec601_YPbPr_2_RGBd(double out[3], double in[3]) { + double tt[3]; + + tt[0] = 1.000000000 * in[0] + 0.000000000 * in[1] + 1.402000000 * in[2]; + tt[1] = 1.000000000 * in[0] + -0.344136286 * in[1] + -0.714136286 * in[2]; + tt[2] = 1.000000000 * in[0] + 1.772000000 * in[1] + 0.000000000 * in[2]; + + out[0] = tt[0]; + out[1] = tt[1]; + out[2] = tt[2]; +} + + +/* Convert Rec709 1150/60/2:1 RGB' into YPbPr, or "full range YCbCr" */ +/* where input 0..1, output 0..1, -0.5 .. 0.5, -0.5 .. 0.5 */ +/* [From the Rec709 specification] */ +void icmRec709_RGBd_2_YPbPr(double out[3], double in[3]) { + double tt[3]; + + tt[0] = 0.2126 * in[0] + 0.7152 * in[1] + 0.0722 * in[2]; + + tt[1] = 0.5389 * -0.2126 * in[0] + + 0.5389 * -0.7152 * in[1] + + 0.5389 * (1.0-0.0722) * in[2]; + + tt[2] = 0.6350 * (1.0-0.2126) * in[0] + + 0.6350 * -0.7152 * in[1] + + 0.6350 * -0.0722 * in[2]; + + out[0] = tt[0]; + out[1] = tt[1]; + out[2] = tt[2]; +} + +/* Convert Rec709 1150/60/2:1 YPbPr to RGB' (== "full range YCbCr") */ +/* where input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, output 0.0 .. 1 */ +/* [Inverse of above] */ +void icmRec709_YPbPr_2_RGBd(double out[3], double in[3]) { + double tt[3]; + + tt[0] = 1.000000000 * in[0] + 0.000000000 * in[1] + 1.574803150 * in[2]; + tt[1] = 1.000000000 * in[0] + -0.187327487 * in[1] + -0.468125209 * in[2]; + tt[2] = 1.000000000 * in[0] + 1.855631843 * in[1] + 0.000000000 * in[2]; + + out[0] = tt[0]; + out[1] = tt[1]; + out[2] = tt[2]; +} + +/* Convert Rec709 1250/50/2:1 RGB' into YPbPr, or "full range YCbCr" */ +/* where input 0..1, output 0..1, -0.5 .. 0.5, -0.5 .. 0.5 */ +/* [From the Rec709 specification] */ +void icmRec709_50_RGBd_2_YPbPr(double out[3], double in[3]) { + double tt[3]; + + tt[0] = 0.299 * in[0] + 0.587 * in[1] + 0.114 * in[2]; + + tt[1] = 0.564 * -0.299 * in[0] + + 0.564 * -0.587 * in[1] + + 0.564 * (1.0-0.114) * in[2]; + + tt[2] = 0.713 * (1.0-0.299) * in[0] + + 0.713 * -0.587 * in[1] + + 0.713 * -0.114 * in[2]; + + out[0] = tt[0]; + out[1] = tt[1]; + out[2] = tt[2]; +} + +/* Convert Rec709 1250/50/2:1 YPbPr to RGB' (== "full range YCbCr") */ +/* where input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, output 0.0 .. 1 */ +/* [Inverse of above] */ +void icmRec709_50_YPbPr_2_RGBd(double out[3], double in[3]) { + double tt[3]; + + tt[0] = 1.000000000 * in[0] + 0.000000000 * in[1] + 1.402524544 * in[2]; + tt[1] = 1.000000000 * in[0] + -0.344340136 * in[1] + -0.714403473 * in[2]; + tt[2] = 1.000000000 * in[0] + 1.773049645 * in[1] + 0.000000000 * in[2]; + + out[0] = tt[0]; + out[1] = tt[1]; + out[2] = tt[2]; +} + + +/* Convert Rec2020 RGB' into Non-constant liminance YPbPr, or "full range YCbCr" */ +/* where input 0..1, output 0..1, -0.5 .. 0.5, -0.5 .. 0.5 */ +/* [From the Rec2020 specification] */ +void icmRec2020_NCL_RGBd_2_YPbPr(double out[3], double in[3]) { + double tt[3]; + + tt[0] = 0.2627 * in[0] + 0.6780 * in[1] + 0.0593 * in[2]; + + tt[1] = 1/1.8814 * -0.2627 * in[0] + + 1/1.8814 * -0.6780 * in[1] + + 1/1.8814 * (1.0-0.0593) * in[2]; + + tt[2] = 1/1.4746 * (1.0-0.2627) * in[0] + + 1/1.4746 * -0.6780 * in[1] + + 1/1.4746 * -0.0593 * in[2]; + + out[0] = tt[0]; + out[1] = tt[1]; + out[2] = tt[2]; +} + +/* Convert Rec2020 Non-constant liminance YPbPr into RGB' (== "full range YCbCr") */ +/* where input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, output 0.0 .. 1 */ +/* [Inverse of above] */ +void icmRec2020_NCL_YPbPr_2_RGBd(double out[3], double in[3]) { + double tt[3]; + + tt[0] = 1.000000000 * in[0] + 0.000000000 * in[1] + 1.474600000 * in[2]; + tt[1] = 1.000000000 * in[0] + -0.164553127 * in[1] + -0.571353127 * in[2]; + tt[2] = 1.000000000 * in[0] + 1.881400000 * in[1] + 0.000000000 * in[2]; + + out[0] = tt[0]; + out[1] = tt[1]; + out[2] = tt[2]; +} + +/* Convert Rec2020 RGB' into Constant liminance YPbPr, or "full range YCbCr" */ +/* where input 0..1, output 0..1, -0.5 .. 0.5, -0.5 .. 0.5 */ +/* [From the Rec2020 specification] */ +void icmRec2020_CL_RGBd_2_YPbPr(double out[3], double in[3]) { + int i; + double tt[3]; + + /* Convert RGB' to RGB */ + for (i = 0; i < 3; i++) { + if (in[i] < (4.5 * 0.0181)) + tt[i] = in[i]/4.5; + else + tt[i] = pow((in[i] + 0.0993)/1.0993, 1.0/0.45); + } + + /* Y value */ + tt[0] = 0.2627 * tt[0] + 0.6780 * tt[1] + 0.0593 * tt[2]; + + /* Y' value */ + if (tt[0] < 0.0181) + tt[0] = tt[0] * 4.5; + else + tt[0] = 1.0993 * pow(tt[0], 0.45) - 0.0993; + + tt[1] = in[2] - tt[0]; + if (tt[1] <= 0.0) + tt[1] /= 1.9404; + else + tt[1] /= 1.5816; + + tt[2] = in[0] - tt[0]; + if (tt[2] <= 0.0) + tt[2] /= 1.7184; + else + tt[2] /= 0.9936; + + out[0] = tt[0]; + out[1] = tt[1]; + out[2] = tt[2]; +} + +/* Convert Rec2020 Constant liminance YPbPr into RGB' (== "full range YCbCr") */ +/* where input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, output 0.0 .. 1 */ +/* [Inverse of above] */ +void icmRec2020_CL_YPbPr_2_RGBd(double out[3], double in[3]) { + int i; + double tin[3], tt[3]; + + /* Y' */ + tin[0] = in[0]; + + /* B' - Y' */ + if (in[1] <= 0.0) + tin[1] = 1.9404 * in[1]; + else + tin[1] = 1.5816 * in[1]; + + /* R' - Y' */ + if (in[2] <= 0.0) + tin[2] = 1.7184 * in[2]; + else + tin[2] = 0.9936 * in[2]; + + + /* R' */ + tt[0] = tin[2] + tin[0]; + + /* Y' */ + tt[1] = tin[0]; + + /* B' */ + tt[2] = tin[1] + tin[0]; + + /* Convert RYB' to RYB */ + for (i = 0; i < 3; i++) { + if (tt[i] < (4.5 * 0.0181)) + tin[i] = tt[i]/4.5; + else + tin[i] = pow((tt[i] + 0.0993)/1.0993, 1.0/0.45); + } + + /* G */ + tt[1] = (tin[1] - 0.2627 * tin[0] - 0.0593 * tin[2])/0.6780; + + /* G' */ + if (tt[1] < 0.0181) + tt[1] = tt[1] * 4.5; + else + tt[1] = 1.0993 * pow(tt[1], 0.45) - 0.0993; + + out[0] = tt[0]; + out[1] = tt[1]; + out[2] = tt[2]; +} + + +/* Convert Rec601/Rec709/Rec2020 YPbPr to YCbCr Video range. */ +/* input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, */ +/* output 16/255 .. 235/255, 16/255 .. 240/255, 16/255 .. 240/255 */ +void icmRecXXX_YPbPr_2_YCbCr(double out[3], double in[3]) { + out[0] = ((235.0 - 16.0) * in[0] + 16.0)/255.0; + out[1] = ((128.0 - 16.0) * 2.0 * in[1] + 128.0)/255.0; + out[2] = ((128.0 - 16.0) * 2.0 * in[2] + 128.0)/255.0; +} + +/* Convert Rec601/Rec709/Rec2020 Video YCbCr to YPbPr range. */ +/* input 16/255 .. 235/255, 16/255 .. 240/255, 16/255 .. 240/255 */ +/* output 0..1, -0.5 .. 0.5, -0.5 .. 0.5, */ +void icmRecXXX_YCbCr_2_YPbPr(double out[3], double in[3]) { + out[0] = (255.0 * in[0] - 16.0)/(235.0 - 16.0); + out[1] = (255.0 * in[1] - 128.0)/(2.0 * (128.0 - 16.0)); + out[2] = (255.0 * in[2] - 128.0)/(2.0 * (128.0 - 16.0)); +} + +/* Convert full range RGB to Video range 16..235 RGB */ +void icmRGB_2_VidRGB(double out[3], double in[3]) { + out[0] = ((235.0 - 16.0) * in[0] + 16.0)/255.0; + out[1] = ((235.0 - 16.0) * in[1] + 16.0)/255.0; + out[2] = ((235.0 - 16.0) * in[2] + 16.0)/255.0; +} + +/* Convert Video range 16..235 RGB to full range RGB */ +/* Return nz if outside RGB range */ +void icmVidRGB_2_RGB(double out[3], double in[3]) { + out[0] = (255.0 * in[0] - 16.0)/(235.0 - 16.0); + out[1] = (255.0 * in[1] - 16.0)/(235.0 - 16.0); + out[2] = (255.0 * in[2] - 16.0)/(235.0 - 16.0); +} + +/* =============================================================== */ +/* PS 3.14-2009, Digital Imaging and Communications in Medicine */ +/* (DICOM) Part 14: Grayscale Standard Display Function */ + +/* JND index value 1..1023 to L 0.05 .. 3993.404 cd/m^2 */ +static double icmDICOM_fwd_nl(double jnd) { + double a = -1.3011877; + double b = -2.5840191e-2; + double c = 8.0242636e-2; + double d = -1.0320229e-1; + double e = 1.3646699e-1; + double f = 2.8745620e-2; + double g = -2.5468404e-2; + double h = -3.1978977e-3; + double k = 1.2992634e-4; + double m = 1.3635334e-3; + double jj, num, den, rv; + + jj = jnd = log(jnd); + + num = a; + den = 1.0; + num += c * jj; + den += b * jj; + jj *= jnd; + num += e * jj; + den += d * jj; + jj *= jnd; + num += g * jj; + den += f * jj; + jj *= jnd; + num += m * jj; + den += h * jj; + jj *= jnd; + den += k * jj; + + rv = pow(10.0, num/den); + + return rv; +} + +/* JND index value 1..1023 to L 0.05 .. 3993.404 cd/m^2 */ +double icmDICOM_fwd(double jnd) { + + if (jnd < 0.5) + jnd = 0.5; + if (jnd > 1024.0) + jnd = 1024.0; + + return icmDICOM_fwd_nl(jnd); +} + +/* L 0.05 .. 3993.404 cd/m^2 to JND index value 1..1023 */ +/* This is not super accurate - typically to 0.03 .. 0.1 jne. */ +static double icmDICOM_bwd_apx(double L) { + double A = 71.498068; + double B = 94.593053; + double C = 41.912053; + double D = 9.8247004; + double E = 0.28175407; + double F = -1.1878455; + double G = -0.18014349; + double H = 0.14710899; + double I = -0.017046845; + double rv, LL; + + if (L < 0.049982) { /* == jnd 0.5 */ + return 0.5; + } + if (L > 4019.354716) /* == jnd 1024 */ + L = 4019.354716; + + LL = L = log10(L); + rv = A; + rv += B * LL; + LL *= L; + rv += C * LL; + LL *= L; + rv += D * LL; + LL *= L; + rv += E * LL; + LL *= L; + rv += F * LL; + LL *= L; + rv += G * LL; + LL *= L; + rv += H * LL; + LL *= L; + rv += I * LL; + + return rv; +} + +/* L 0.05 .. 3993.404 cd/m^2 to JND index value 1..1023 */ +/* Polish the aproximate solution twice using Newton's itteration */ +double icmDICOM_bwd(double L) { + double rv, Lc, prv, pLc, de; + int i; + + if (L < 0.045848) /* == jnd 0.5 */ + L = 0.045848; + if (L > 4019.354716) /* == jnd 1024 */ + L = 4019.354716; + + /* Approx solution */ + rv = icmDICOM_bwd_apx(L); + + /* Compute aprox derivative */ + Lc = icmDICOM_fwd_nl(rv); + + prv = rv + 0.01; + pLc = icmDICOM_fwd_nl(prv); + + do { + de = (rv - prv)/(Lc - pLc); + prv = rv; + rv -= (Lc - L) * de; + pLc = Lc; + Lc = icmDICOM_fwd_nl(rv); + } while (fabs(Lc - L) > 1e-8); + + return rv; +} + + +/* =============================================================== */ /* Object for computing RFC 1321 MD5 checksums. */ /* Derived from Colin Plumb's 1993 public domain code. */ @@ -16169,25 +17067,25 @@ double *in /* Vector of input values */ icmLut *lut = p->lut; double temp[MAX_CHAN]; - DBGLL(("icmLuLut_lookup: in = %s\n", icmPdv(p->inputChan, in))); + DBGLL(("icmLuLut_lookup: in = %s\n", icmPdv(p->lut->inputChan, in))); rv |= p->in_abs(p,temp,in); /* Possible absolute conversion */ - DBGLL(("icmLuLut_lookup: in_abs = %s\n", icmPdv(p->inputChan, temp))); + DBGLL(("icmLuLut_lookup: in_abs = %s\n", icmPdv(p->lut->inputChan, temp))); if (p->usematrix) { rv |= lut->lookup_matrix(lut,temp,temp);/* If XYZ, multiply by non-unity matrix */ - DBGLL(("icmLuLut_lookup: matrix = %s\n", icmPdv(p->inputChan, temp))); + DBGLL(("icmLuLut_lookup: matrix = %s\n", icmPdv(p->lut->inputChan, temp))); } p->in_normf(temp, temp); /* Normalize for input color space */ - DBGLL(("icmLuLut_lookup: norm = %s\n", icmPdv(p->inputChan, temp))); + DBGLL(("icmLuLut_lookup: norm = %s\n", icmPdv(p->lut->inputChan, temp))); rv |= lut->lookup_input(lut,temp,temp); /* Lookup though input tables */ - DBGLL(("icmLuLut_lookup: input = %s\n", icmPdv(p->inputChan, temp))); + DBGLL(("icmLuLut_lookup: input = %s\n", icmPdv(p->lut->inputChan, temp))); rv |= p->lookup_clut(lut,out,temp); /* Lookup though clut tables */ - DBGLL(("icmLuLut_lookup: clut = %s\n", icmPdv(p->outputChan, out))); + DBGLL(("icmLuLut_lookup: clut = %s\n", icmPdv(p->lut->outputChan, out))); rv |= lut->lookup_output(lut,out,out); /* Lookup though output tables */ - DBGLL(("icmLuLut_lookup: output = %s\n", icmPdv(p->outputChan, out))); + DBGLL(("icmLuLut_lookup: output = %s\n", icmPdv(p->lut->outputChan, out))); p->out_denormf(out,out); /* Normalize for output color space */ - DBGLL(("icmLuLut_lookup: denorm = %s\n", icmPdv(p->outputChan, out))); + DBGLL(("icmLuLut_lookup: denorm = %s\n", icmPdv(p->lut->outputChan, out))); rv |= p->out_abs(p,out,out); /* Possible absolute conversion */ - DBGLL(("icmLuLut_lookup: out_abse = %s\n", icmPdv(p->outputChan, out))); + DBGLL(("icmLuLut_lookup: out_abse = %s\n", icmPdv(p->lut->outputChan, out))); return rv; } @@ -16891,8 +17789,10 @@ icc_new_icmLuLut( if (use_sx) { p->lookup_clut = p->lut->lookup_clut_sx; + p->lut->tune_value = icmLut_tune_value_sx; } else { p->lookup_clut = p->lut->lookup_clut_nl; + p->lut->tune_value = icmLut_tune_value_nl; } } #else /* Development code */ @@ -17665,7 +18565,7 @@ icmAlloc *al /* Memory allocator */ if ((p = (icc *) al->calloc(al, 1,sizeof(icc))) == NULL) { return NULL; } - p->ver = 0; /* default is V2 profile */ + p->ver = icmVersionDefault; /* default is V2.2.0 profile */ p->al = al; /* Heap allocator */ diff --git a/icc/icc.h b/icc/icc.h index 7c3a65b..ba66581 100644 --- a/icc/icc.h +++ b/icc/icc.h @@ -8,7 +8,7 @@ * Date: 1999/11/29 * Version: 2.15 * - * Copyright 1997 - 2012 Graeme W. Gill + * Copyright 1997 - 2013 Graeme W. Gill * * This material is licensed with an "MIT" free use license:- * see the License.txt file in this directory for licensing details. @@ -30,8 +30,8 @@ /* Version of icclib release */ -#define ICCLIB_VERSION 0x020016 -#define ICCLIB_VERSION_STR "2.16" +#define ICCLIB_VERSION 0x020017 +#define ICCLIB_VERSION_STR "2.17" #undef ENABLE_V4 /* V4 is not fully implemented */ @@ -778,8 +778,20 @@ struct _icmLut { /* inspace' -> outspace' transfer function */ double *clutmin, double *clutmax, /* Maximum range of outspace' values */ /* (NULL = default) */ - void (*outfunc)(void *cbntx, double *out, double *in)); + void (*outfunc)(void *cbntx, double *out, double *in), /* Output transfer function, outspace'->outspace (NULL = deflt) */ + int *apxls_gmin, int *apxls_gmax /* If not NULL, the grid indexes not to be affected */ + /* by ICM_CLUT_SET_APXLS, defaulting to 0..>clutPoints-1 */ + ); + + /* Helper function to fine tune a single value interpolation */ + /* Return 0 on success, 1 if input clipping occured, 2 if output clipping occured */ + /* To guarantee the optimal function, an icmLuLut needs to be create. */ + /* The default will be to assume simplex interpolation will be used. */ + int (*tune_value) ( + struct _icmLut *p, /* Pointer to Lut object */ + double *out, /* Target value */ + double *in); /* Input value */ }; typedef struct _icmLut icmLut; @@ -792,27 +804,29 @@ struct _icmLut { /* If ICM_CLUT_SET_FILTER is set, the per grid node filtering radius */ /* is returned in clutfunc out[-1], out[-2] etc for each table */ int icmSetMultiLutTables( - int ntables, /* Number of tables to be set, 1..n */ - struct _icmLut **p, /* Pointer to Lut object */ - int flags, /* Setting flags */ - void *cbctx, /* Opaque callback context pointer value */ - icColorSpaceSignature insig, /* Input color space */ - icColorSpaceSignature outsig, /* Output color space */ + int ntables, /* Number of tables to be set, 1..n */ + struct _icmLut **p, /* Pointer to Lut object */ + int flags, /* Setting flags */ + void *cbctx, /* Opaque callback context pointer value */ + icColorSpaceSignature insig, /* Input color space */ + icColorSpaceSignature outsig, /* Output color space */ void (*infunc)(void *cbctx, double *out, double *in), /* Input transfer function, inspace->inspace' (NULL = default) */ /* Will be called ntables times each input grid value */ - double *inmin, double *inmax, /* Maximum range of inspace' values */ - /* (NULL = default) */ + double *inmin, double *inmax, /* Maximum range of inspace' values */ + /* (NULL = default) */ void (*clutfunc)(void *cbntx, double *out, double *in), /* inspace' -> outspace[ntables]' transfer function */ /* will be called once for each input' grid value, and */ /* ntables output values should be written consecutively */ /* to out[]. */ - double *clutmin, double *clutmax, /* Maximum range of outspace' values */ - /* (NULL = default) */ - void (*outfunc)(void *cbntx, double *out, double *in) + double *clutmin, double *clutmax, /* Maximum range of outspace' values */ + /* (NULL = default) */ + void (*outfunc)(void *cbntx, double *out, double *in), /* Output transfer function, outspace'->outspace (NULL = deflt) */ /* Will be called ntables times on each output value */ + int *apxls_gmin, int *apxls_gmax /* If not NULL, the grid indexes not to be affected */ + /* by ICM_CLUT_SET_APXLS, defaulting to 0..>clutPoints-1 */ ); /* - - - - - - - - - - - - - - - - - - - - - */ @@ -1487,6 +1501,8 @@ struct _icc { int errc; /* Error code */ int warnc; /* Warning code */ + int allowclutPoints256; /* Non standard - allow 256 res cLUT */ + /* Private: ? */ icmAlloc *al; /* Heap allocator */ int del_al; /* NZ if heap allocator should be deleted */ @@ -1652,135 +1668,9 @@ extern ICCLIB_API unsigned int icmCSSig2nchan(icColorSpaceSignature sig); /* 1 if it is a colorant based colorspace, and 2 if it is not a colorant based space */ extern ICCLIB_API unsigned int icmCSSig2chanNames( icColorSpaceSignature sig, char *cvals[]); - -/* Simple macro to transfer an array to an XYZ number */ -#define icmAry2XYZ(xyz, ary) ((xyz).X = (ary)[0], (xyz).Y = (ary)[1], (xyz).Z = (ary)[2]) - -/* And the reverse */ -#define icmXYZ2Ary(ary, xyz) ((ary)[0] = (xyz).X, (ary)[1] = (xyz).Y, (ary)[2] = (xyz).Z) - -/* Simple macro to transfer an XYZ number to an XYZ number */ -#define icmXYZ2XYZ(d_xyz, s_xyz) ((d_xyz).X = (s_xyz).X, (d_xyz).Y = (s_xyz).Y, \ - (d_xyz).Z = (s_xyz).Z) - -/* Simple macro to transfer an 3array to 3array */ -/* Hmm. Same as icmCpy3 */ -#define icmAry2Ary(d_ary, s_ary) ((d_ary)[0] = (s_ary)[0], (d_ary)[1] = (s_ary)[1], \ - (d_ary)[2] = (s_ary)[2]) - -/* CIE Y (range 0 .. 1) to perceptual CIE 1976 L* (range 0 .. 100) */ -double icmY2L(double val); - -/* Perceptual CIE 1976 L* (range 0 .. 100) to CIE Y (range 0 .. 1) */ -double icmL2Y(double val); - -/* CIE XYZ to perceptual Lab */ -extern ICCLIB_API void icmXYZ2Lab(icmXYZNumber *w, double *out, double *in); - -/* Perceptual Lab to CIE XYZ */ -extern ICCLIB_API void icmLab2XYZ(icmXYZNumber *w, double *out, double *in); - -/* LCh to Lab */ -extern ICCLIB_API void icmLCh2Lab(double *out, double *in); - -/* Lab to LCh */ -extern ICCLIB_API void icmLab2LCh(double *out, double *in); - -/* XYZ to Yxy */ -extern ICCLIB_API void icmXYZ2Yxy(double *out, double *in); - -/* Yxy to XYZ */ -extern ICCLIB_API void icmYxy2XYZ(double *out, double *in); - -/* CIE XYZ to perceptual Luv */ -extern ICCLIB_API void icmXYZ2Luv(icmXYZNumber *w, double *out, double *in); - -/* Perceptual Luv to CIE XYZ */ -extern ICCLIB_API void icmLuv2XYZ(icmXYZNumber *w, double *out, double *in); - - -/* NOTE :- none of the following seven have been protected */ -/* against arithmmetic issues (ie. for black) */ - -/* CIE XYZ to perceptual CIE 1976 UCS diagram Yu'v'*/ -/* (Yu'v' is a better chromaticity space than Yxy) */ -extern ICCLIB_API void icmXYZ21976UCS(double *out, double *in); - -/* Perceptual CIE 1976 UCS diagram Yu'v' to CIE XYZ */ -extern ICCLIB_API void icm1976UCS2XYZ(double *out, double *in); - -/* CIE XYZ to perceptual CIE 1960 UCS */ -/* (This was obsoleted by the 1976UCS, but is still used */ -/* in computing color temperatures.) */ -extern ICCLIB_API void icmXYZ21960UCS(double *out, double *in); - -/* Perceptual CIE 1960 UCS to CIE XYZ */ -extern ICCLIB_API void icm1960UCS2XYZ(double *out, double *in); - -/* CIE XYZ to perceptual CIE 1964 WUV (U*V*W*) */ -/* (This is obsolete but still used in computing CRI) */ -extern ICCLIB_API void icmXYZ21964WUV(icmXYZNumber *w, double *out, double *in); - -/* Perceptual CIE 1964 WUV (U*V*W*) to CIE XYZ */ -extern ICCLIB_API void icm1964WUV2XYZ(icmXYZNumber *w, double *out, double *in); - -/* CIE CIE1960 UCS to perceptual CIE 1964 WUV (U*V*W*) */ -extern ICCLIB_API void icm1960UCS21964WUV(icmXYZNumber *w, double *out, double *in); - - -/* The standard D50 illuminant value */ -extern icmXYZNumber icmD50; -extern icmXYZNumber icmD50_100; /* Scaled to 100 */ -double icmD50_ary3[3]; /* As an array */ -double icmD50_100_ary3[3]; /* Scaled to 100 as an array */ - -/* The standard D65 illuminant value */ -extern icmXYZNumber icmD65; -extern icmXYZNumber icmD65_100; /* Scaled to 100 */ -double icmD65_ary3[3]; /* As an array */ -double icmD65_100_ary3[3]; /* Scaled to 100 as an array */ - -/* The default black value */ -extern icmXYZNumber icmBlack; - - -/* Initialise a pseudo-hilbert grid counter, return total usable count. */ -extern ICCLIB_API unsigned psh_init(psh *p, int di, unsigned int res, int co[]); - -/* Reset the counter */ -extern ICCLIB_API void psh_reset(psh *p); - -/* Increment pseudo-hilbert coordinates */ -/* Return non-zero if count rolls over to 0 */ -extern ICCLIB_API int psh_inc(psh *p, int co[]); - - -/* RGB primaries to device to RGB->XYZ transform matrix */ -/* Return non-zero if matrix would be singular */ -int icmRGBprim2matrix( - icmXYZNumber white, /* White point */ - icmXYZNumber red, /* Red colorant */ - icmXYZNumber green, /* Green colorant */ - icmXYZNumber blue, /* Blue colorant */ - double mat[3][3] /* Destination matrix */ -); - -/* Chromatic Adaption transform utility */ -/* Return a 3x3 chromatic adaption matrix */ -/* Use icmMulBy3x3(dst, mat, src) */ - -#define ICM_CAM_BRADFORD 0x0001 /* Use Bradford sharpened response space */ -#define ICM_CAM_MULMATRIX 0x0002 /* Transform the given matrix */ - -void icmChromAdaptMatrix( - int flags, /* Flags as defined below */ - icmXYZNumber d_wp, /* Destination white point */ - icmXYZNumber s_wp, /* Source white point */ - double mat[3][3] /* Destination matrix */ -); - /* - - - - - - - - - - - - - - */ -/* Set a 3 vector */ + +/* Set a 3 vector to the same value */ #define icmSet3(d_ary, s_val) ((d_ary)[0] = (s_val), (d_ary)[1] = (s_val), \ (d_ary)[2] = (s_val)) @@ -1801,6 +1691,16 @@ void icmSub3(double out[3], double in1[3], double in2[3]); #define ICMSUB3(o, i, j) ((o)[0] = (i)[0] - (j)[0], (o)[1] = (i)[1] - (j)[1], (o)[2] = (i)[2] - (j)[2]) +/* Divide two 3 vectors, out = in1/in2 */ +void icmDiv3(double out[3], double in1[3], double in2[3]); + +#define ICMDIV3(o, i, j) ((o)[0] = (i)[0]/(j)[0], (o)[1] = (i)[1]/(j)[1], (o)[2] = (i)[2]/(j)[2]) + +/* Multiply two 3 vectors, out = in1 * in2 */ +void icmMul3(double out[3], double in1[3], double in2[3]); + +#define ICMMUL3(o, i, j) ((o)[0] = (i)[0] * (j)[0], (o)[1] = (i)[1] * (j)[1], (o)[2] = (i)[2] * (j)[2]) + /* Compute the dot product of two 3 vectors */ double icmDot3(double in1[3], double in2[3]); @@ -1822,10 +1722,13 @@ double icmNorm3(double in[3]); /* Scale a 3 vector by the given ratio */ void icmScale3(double out[3], double in[3], double rat); +#define ICMSCALE3(o, i, j) ((o)[0] = (i)[0] * (j), (o)[1] = (i)[1] * (j), (o)[2] = (i)[2] * (j)) + /* Compute a blend between in0 and in1 */ void icmBlend3(double out[3], double in0[3], double in1[3], double bf); -#define ICMSCALE3(o, i, j) ((o)[0] = (i)[0] * (j), (o)[1] = (i)[1] * (j), (o)[2] = (i)[2] * (j)) +/* Clip a vector to the range 0.0 .. 1.0 */ +void icmClip3(double out[3], double in[3]); /* Normalise a 3 vector to the given length. Return nz if not normalisable */ int icmNormalize3(double out[3], double in[3], double len); @@ -1922,9 +1825,128 @@ double icmPlaneDist3(double eq[4], double p[3]); /* - - - - - - - - - - - - - - - - - - - - - - - */ +/* Given 2 2D points, compute a plane equation. */ +/* The normal will be right handed given the order of the points */ +/* The plane equation will be the 2 normal components and the constant. */ +/* Return nz if any points are cooincident or co-linear */ +int icmPlaneEqn2(double eq[3], double p0[2], double p1[2]); + +/* Given a 2D point and a plane equation, return the signed */ +/* distance from the plane */ +double icmPlaneDist2(double eq[3], double p[2]); + +/* Given two infinite 2D lines define by two pairs of points, compute the intersection. */ +/* Return nz if there is no intersection (lines are parallel) */ +int icmLineIntersect2(double res[2], double p1[2], double p2[2], double p3[2], double p4[2]); + /* Multiply 2 array by 2x2 transform matrix */ void icmMulBy2x2(double out[2], double mat[2][2], double in[2]); +/* - - - - - - - - - - - - - - */ + +/* Simple macro to transfer an array to an XYZ number */ +#define icmAry2XYZ(xyz, ary) ((xyz).X = (ary)[0], (xyz).Y = (ary)[1], (xyz).Z = (ary)[2]) + +/* And the reverse */ +#define icmXYZ2Ary(ary, xyz) ((ary)[0] = (xyz).X, (ary)[1] = (xyz).Y, (ary)[2] = (xyz).Z) + +/* Simple macro to transfer an XYZ number to an XYZ number */ +#define icmXYZ2XYZ(d_xyz, s_xyz) ((d_xyz).X = (s_xyz).X, (d_xyz).Y = (s_xyz).Y, \ + (d_xyz).Z = (s_xyz).Z) + +/* Simple macro to transfer an 3array to 3array */ +/* Hmm. Same as icmCpy3 */ +#define icmAry2Ary(d_ary, s_ary) ((d_ary)[0] = (s_ary)[0], (d_ary)[1] = (s_ary)[1], \ + (d_ary)[2] = (s_ary)[2]) + +/* CIE Y (range 0 .. 1) to perceptual CIE 1976 L* (range 0 .. 100) */ +double icmY2L(double val); + +/* Perceptual CIE 1976 L* (range 0 .. 100) to CIE Y (range 0 .. 1) */ +double icmL2Y(double val); + +/* CIE XYZ to perceptual Lab */ +extern ICCLIB_API void icmXYZ2Lab(icmXYZNumber *w, double *out, double *in); + +/* Perceptual Lab to CIE XYZ */ +extern ICCLIB_API void icmLab2XYZ(icmXYZNumber *w, double *out, double *in); + +/* LCh to Lab */ +extern ICCLIB_API void icmLCh2Lab(double *out, double *in); + +/* Lab to LCh */ +extern ICCLIB_API void icmLab2LCh(double *out, double *in); + +/* XYZ to Yxy */ +extern ICCLIB_API void icmXYZ2Yxy(double *out, double *in); + +/* Yxy to XYZ */ +extern ICCLIB_API void icmYxy2XYZ(double *out, double *in); + +/* CIE XYZ to perceptual Luv */ +extern ICCLIB_API void icmXYZ2Luv(icmXYZNumber *w, double *out, double *in); + +/* Perceptual Luv to CIE XYZ */ +extern ICCLIB_API void icmLuv2XYZ(icmXYZNumber *w, double *out, double *in); + + +/* CIE XYZ to perceptual CIE 1976 UCS diagram Yu'v'*/ +/* (Yu'v' is a better chromaticity space than Yxy) */ +extern ICCLIB_API void icmXYZ21976UCS(double *out, double *in); + +/* Perceptual CIE 1976 UCS diagram Yu'v' to CIE XYZ */ +extern ICCLIB_API void icm1976UCS2XYZ(double *out, double *in); + + +/* CIE XYZ to perceptual CIE 1960 UCS */ +/* (This was obsoleted by the 1976UCS, but is still used */ +/* in computing color temperatures.) */ +extern ICCLIB_API void icmXYZ21960UCS(double *out, double *in); + +/* Perceptual CIE 1960 UCS to CIE XYZ */ +extern ICCLIB_API void icm1960UCS2XYZ(double *out, double *in); + + +/* CIE XYZ to perceptual CIE 1964 WUV (U*V*W*) */ +/* (This is obsolete but still used in computing CRI) */ +extern ICCLIB_API void icmXYZ21964WUV(icmXYZNumber *w, double *out, double *in); + +/* Perceptual CIE 1964 WUV (U*V*W*) to CIE XYZ */ +extern ICCLIB_API void icm1964WUV2XYZ(icmXYZNumber *w, double *out, double *in); + +/* CIE CIE1960 UCS to perceptual CIE 1964 WUV (U*V*W*) */ +extern ICCLIB_API void icm1960UCS21964WUV(icmXYZNumber *w, double *out, double *in); + + +/* NOTE :- that these values are for the 1931 standard observer */ + +/* The standard D50 illuminant value */ +extern icmXYZNumber icmD50; +extern icmXYZNumber icmD50_100; /* Scaled to 100 */ +extern double icmD50_ary3[3]; /* As an array */ +extern double icmD50_100_ary3[3]; /* Scaled to 100 as an array */ + +/* The standard D65 illuminant value */ +extern icmXYZNumber icmD65; +extern icmXYZNumber icmD65_100; /* Scaled to 100 */ +extern double icmD65_ary3[3]; /* As an array */ +extern double icmD65_100_ary3[3]; /* Scaled to 100 as an array */ + + +/* The default black value */ +extern icmXYZNumber icmBlack; + + +/* Initialise a pseudo-hilbert grid counter, return total usable count. */ +extern ICCLIB_API unsigned psh_init(psh *p, int di, unsigned int res, int co[]); + +/* Reset the counter */ +extern ICCLIB_API void psh_reset(psh *p); + +/* Increment pseudo-hilbert coordinates */ +/* Return non-zero if count rolls over to 0 */ +extern ICCLIB_API int psh_inc(psh *p, int co[]); + /* - - - - - - - - - - - - - - - - - - - - - - - */ /* Return the normal Delta E given two Lab values */ @@ -1956,6 +1978,7 @@ extern ICCLIB_API double icmCIE2Ksq(double *in0, double *in1); /* Return the CIEDE2000 Delta E color difference measure for two XYZ values */ extern ICCLIB_API double icmXYZCIE2K(icmXYZNumber *w, double *in0, double *in1); + /* - - - - - - - - - - - - - - - - - - - - - - - */ /* Clip Lab, while maintaining hue angle. */ /* Return nz if clipping occured */ @@ -1966,6 +1989,122 @@ int icmClipLab(double out[3], double in[3]); int icmClipXYZ(double out[3], double in[3]); /* - - - - - - - - - - - - - - - - - - - - - - - */ + +/* RGB primaries to device to RGB->XYZ transform matrix */ +/* Return non-zero if matrix would be singular */ +int icmRGBprim2matrix( + icmXYZNumber white, /* White point */ + icmXYZNumber red, /* Red colorant */ + icmXYZNumber green, /* Green colorant */ + icmXYZNumber blue, /* Blue colorant */ + double mat[3][3] /* Destination matrix[RGB[XYZ] */ +); + +/* Chromatic Adaption transform utility */ +/* Return a 3x3 chromatic adaption matrix */ +/* Use icmMulBy3x3(dst, mat, src) */ + +#define ICM_CAM_BRADFORD 0x0001 /* Use Bradford sharpened response space */ +#define ICM_CAM_MULMATRIX 0x0002 /* Transform the given matrix */ + /* NOTE that to transform primaries they */ + /* must be mat[XYZ][RGB] format! */ + +void icmChromAdaptMatrix( + int flags, /* Flags as defined below */ + icmXYZNumber d_wp, /* Destination white point */ + icmXYZNumber s_wp, /* Source white point */ + double mat[3][3] /* Destination matrix */ +); + +/* Pre-round RGB device primary values to ensure that */ +/* the sum of the quantized primaries is the same as */ +/* the quantized sum. */ +void quantizeRGBprimsS15Fixed16( + double mat[3][3] /* matrix[RGB][XYZ] */ +); + +/* - - - - - - - - - - - - - - */ +/* Video functions */ + +/* Convert Lut table index/value to YCbCr */ +void icmLut2YCbCr(double *out, double *in); + +/* Convert YCbCr to Lut table index/value */ +void icmYCbCr2Lut(double *out, double *in); + + +/* Convert Rec601 RGB' into YPbPr, (== "full range YCbCr") */ +/* where input 0..1, output 0..1, -0.5 .. 0.5, -0.5 .. 0.5 */ +void icmRec601_RGBd_2_YPbPr(double out[3], double in[3]); + +/* Convert Rec601 YPbPr to RGB' (== "full range YCbCr") */ +/* where input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, output 0.0 .. 1 */ +void icmRec601_YPbPr_2_RGBd(double out[3], double in[3]); + + +/* Convert Rec709 1150/60/2:1 RGB' into YPbPr, or "full range YCbCr" */ +/* where input 0..1, output 0..1, -0.5 .. 0.5, -0.5 .. 0.5 */ +void icmRec709_RGBd_2_YPbPr(double out[3], double in[3]); + +/* Convert Rec709 1150/60/2:1 YPbPr to RGB' (== "full range YCbCr") */ +/* where input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, output 0.0 .. 1 */ +void icmRec709_YPbPr_2_RGBd(double out[3], double in[3]); + +/* Convert Rec709 1250/50/2:1 RGB' into YPbPr, or "full range YCbCr" */ +/* where input 0..1, output 0..1, -0.5 .. 0.5, -0.5 .. 0.5 */ +void icmRec709_50_RGBd_2_YPbPr(double out[3], double in[3]); + +/* Convert Rec709 1250/50/2:1 YPbPr to RGB' (== "full range YCbCr") */ +/* where input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, output 0.0 .. 1 */ +void icmRec709_50_YPbPr_2_RGBd(double out[3], double in[3]); + + +/* Convert Rec2020 RGB' into Non-constant liminance YPbPr, or "full range YCbCr" */ +/* where input 0..1, output 0..1, -0.5 .. 0.5, -0.5 .. 0.5 */ +void icmRec2020_NCL_RGBd_2_YPbPr(double out[3], double in[3]); + +/* Convert Rec2020 Non-constant liminance YPbPr into RGB' (== "full range YCbCr") */ +/* where input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, output 0.0 .. 1 */ +void icmRec2020_NCL_YPbPr_2_RGBd(double out[3], double in[3]); + +/* Convert Rec2020 RGB' into Constant liminance YPbPr, or "full range YCbCr" */ +/* where input 0..1, output 0..1, -0.5 .. 0.5, -0.5 .. 0.5 */ +void icmRec2020_CL_RGBd_2_YPbPr(double out[3], double in[3]); + +/* Convert Rec2020 Constant liminance YPbPr into RGB' (== "full range YCbCr") */ +/* where input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, output 0.0 .. 1 */ +void icmRec2020_CL_YPbPr_2_RGBd(double out[3], double in[3]); + + +/* Convert Rec601/709/2020 YPbPr to YCbCr range. */ +/* input 0..1, -0.5 .. 0.5, -0.5 .. 0.5, */ +/* output 16/255 .. 235/255, 16/255 .. 240/255, 16/255 .. 240/255 */ +void icmRecXXX_YPbPr_2_YCbCr(double out[3], double in[3]); + +/* Convert Rec601/709/2020 YCbCr to YPbPr range. */ +/* input 16/255 .. 235/255, 16/255 .. 240/255, 16/255 .. 240/255 */ +/* output 0..1, -0.5 .. 0.5, -0.5 .. 0.5, */ +void icmRecXXX_YCbCr_2_YPbPr(double out[3], double in[3]); + + +/* Convert full range RGB to Video range 16..235 RGB */ +void icmRGB_2_VidRGB(double out[3], double in[3]); + +/* Convert Video range 16..235 RGB to full range RGB */ +void icmVidRGB_2_RGB(double out[3], double in[3]); + +/* ---------------------------------------------------------- */ +/* PS 3.14-2009, Digital Imaging and Communications in Medicine */ +/* (DICOM) Part 14: Grayscale Standard Display Function */ + +/* JND index value 1..1023 to L 0.05 .. 3993.404 cd/m^2 */ +double icmDICOM_fwd(double jnd); + +/* L 0.05 .. 3993.404 cd/m^2 to JND index value 1..1023 */ +double icmDICOM_bwd(double L); + + +/* ---------------------------------------------------------- */ /* Print an int vector to a string. */ /* Returned static buffer is re-used every 5 calls. */ char *icmPiv(int di, int *p); @@ -1981,8 +2120,8 @@ char *icmPfv(int di, float *p); /* Print an 0..1 range XYZ as a D50 Lab string */ /* Returned static buffer is re-used every 5 calls. */ char *icmPLab(double *p); +/* - - - - - - - - - - - - - - - - - - - - - - - */ -/* ---------------------------------------------------------- */ #ifdef __cplusplus } diff --git a/icc/iccdump.c b/icc/iccdump.c index aaf14f0..76959e7 100644 --- a/icc/iccdump.c +++ b/icc/iccdump.c @@ -182,6 +182,9 @@ main(int argc, char *argv[]) { if ((rv = icco->read(icco,fp,offset)) != 0) error ("%d, %s",rv,icco->err); + if (icco->header->cmmId == str2tag("argl")) + icco->allowclutPoints256 = 1; + if (ntag_names > 0) { int i; for (i = 0; i < ntag_names; i++) { diff --git a/icc/icclu.c b/icc/icclu.c index ea32da0..80126da 100644 --- a/icc/icclu.c +++ b/icc/icclu.c @@ -245,6 +245,9 @@ main(int argc, char *argv[]) { if ((rv = icco->read(icco,fp,0)) != 0) error ("%d, %s",rv,icco->err); + if (icco->header->cmmId == str2tag("argl")) + icco->allowclutPoints256 = 1; + if (verb > 1) { icmFile *op; if ((op = new_icmFileStd_fp(stdout)) == NULL) @@ -286,7 +289,7 @@ main(int argc, char *argv[]) { continue; } /* For each input number */ - for (bp = buf-1, nbp = buf, i = 0; i < MAX_CHAN; i++) { + for (nbp = buf, i = 0; i < MAX_CHAN; i++) { bp = nbp; in[i] = oin[i] = strtod(bp, &nbp); if (nbp == bp) @@ -310,19 +313,7 @@ main(int argc, char *argv[]) { } if (repYxy && ins == icSigYxyData) { - double Y = in[0]; - double x = in[1]; - double y = in[2]; - double z = 1.0 - x - y; - double sum; - if (y < 1e-6) { - in[0] = in[1] = in[2] = 0.0; - } else { - sum = Y/y; - in[0] = x * sum; - in[1] = Y; - in[2] = z * sum; - } + icmYxy2XYZ(in, in); } /* Do conversion */ @@ -342,17 +333,7 @@ main(int argc, char *argv[]) { } if (repYxy && outs == icSigYxyData) { - double X = out[0]; - double Y = out[1]; - double Z = out[2]; - double sum = X + Y + Z; - if (sum < 1e-6) { - out[0] = out[1] = out[2] = 0.0; - } else { - out[0] = Y; - out[1] = X/sum; - out[2] = Y/sum; - } + icmXYZ2Yxy(out, out); } /* If device data and scale */ diff --git a/icc/icctest.c b/icc/icctest.c index 97a3f62..e7534fb 100644 --- a/icc/icctest.c +++ b/icc/icctest.c @@ -111,6 +111,7 @@ main( if (md5_test() != 0) error ("MD5 checksum routine is faulty"); + /* Outer loop does a number of file write/reads, */ /* in order to exercise random tests, and to test file offsets. */ @@ -199,8 +200,6 @@ main( offset = rand_int(0,72789); } - printf("\nTest completed OK\n"); - return 0; } @@ -325,9 +324,9 @@ int doit( /* Values that are not normally set. Set them to non-defaults for testing */ wh->cmmId = str2tag("tst3"); - wh->majv = 3; /* Default version 2.1.0 */ - wh->minv = 2; - wh->bfv = 1; + wh->majv = 2; /* Default version 2.1.0 */ + wh->minv = 1; + wh->bfv = 0; wh->date.year = rand_int(1900,3000); /* Defaults to current date */ wh->date.month = rand_int(1,12); wh->date.day = rand_int(1,31); diff --git a/icc/lab2lab.icm b/icc/lab2lab.icm index fe518e6..0b8a4b3 100644 Binary files a/icc/lab2lab.icm and b/icc/lab2lab.icm differ diff --git a/icc/log.txt b/icc/log.txt index 97435b6..7c4bd83 100644 --- a/icc/log.txt +++ b/icc/log.txt @@ -1,7 +1,13 @@ Change History: (See ArgyllCMS log.txt too) - 2.15 + 2.16 + Clean up cLUT read code to make sanity checking + more explicit and remove redundant code. + + Added special mode that interprets cLUT res of 0 as 256 + for MadVR testing. + Change icc->read_tag() to only succeed if the tag type is known, since the standard expectation of a non NULL return type is that it is of a known type. Added new diff --git a/icc/lutest.c b/icc/lutest.c index e43fbc5..9bf127f 100644 --- a/icc/lutest.c +++ b/icc/lutest.c @@ -1995,13 +1995,15 @@ main( /* Use helper function to do the hard work. */ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL, - icSigRgbData, /* Input color space */ - icSigXYZData, /* Output color space */ - RGB_RGBp, /* Input transfer function, RGB->RGB' (NULL = default) */ - NULL, NULL, /* Use default Maximum range of RGB' values */ - RGBp_XYZp, /* RGB' -> XYZ' transfer function */ - xyzmin, xyzmax, /* Make XYZ' range 0.0 - 1.0 for better precision */ - XYZp_XYZ) != 0) /* Output transfer function, XYZ'->XYZ (NULL = deflt) */ + icSigRgbData, /* Input color space */ + icSigXYZData, /* Output color space */ + RGB_RGBp, /* Input transfer function, RGB->RGB' (NULL = default) */ + NULL, NULL, /* Use default Maximum range of RGB' values */ + RGBp_XYZp, /* RGB' -> XYZ' transfer function */ + xyzmin, xyzmax, /* Make XYZ' range 0.0 - 1.0 for better precision */ + XYZp_XYZ, /* Output transfer function, XYZ'->XYZ (NULL = deflt) */ + NULL, NULL + ) != 0) error("Setting 16 bit RGB->XYZ Lut failed: %d, %s",wr_icco->errc,wr_icco->err); } /* 16 bit dev -> pcs lut - link intent 0 to intent 1 */ @@ -2126,13 +2128,15 @@ main( #endif /* Use helper function to do the hard work. */ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL, - icSigXYZData, /* Input color space */ - icSigRgbData, /* Output color space */ - XYZ_XYZp, /* Input transfer function, XYZ->XYZ' (NULL = default) */ - xyzmin, xyzmax, /* Make XYZ' range 0.0 - 1.0 for better precision */ - XYZp_RGBp, /* XYZ' -> RGB' transfer function */ - rgbmin, rgbmax, /* Make RGB' range 0.0 - 1.333 for less clip rounding */ - RGBp_RGB) != 0) /* Output transfer function, RGB'->RGB (NULL = deflt) */ + icSigXYZData, /* Input color space */ + icSigRgbData, /* Output color space */ + XYZ_XYZp, /* Input transfer function, XYZ->XYZ' (NULL = default) */ + xyzmin, xyzmax, /* Make XYZ' range 0.0 - 1.0 for better precision */ + XYZp_RGBp, /* XYZ' -> RGB' transfer function */ + rgbmin, rgbmax, /* Make RGB' range 0.0 - 1.333 for less clip rounding */ + RGBp_RGB, /* Output transfer function, RGB'->RGB (NULL = deflt) */ + NULL, NULL + ) != 0) error("Setting 16 bit XYZ->RGB Lut failed: %d, %s",wr_icco->errc,wr_icco->err); } @@ -2181,13 +2185,14 @@ main( /* Use helper function to do the hard work. */ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL, - icSigXYZData, /* Input color space */ - icSigGrayData, /* Output color space */ - XYZ_XYZp, /* Input transfer function, XYZ->XYZ' (NULL = default) */ - xyzmin, xyzmax, /* Make XYZ' range 0.0 - 1.0 for better precision */ - XYZp_BDIST, /* XYZ' -> Boundary Distance transfer function */ - NULL, NULL, /* Default range from clut to output table */ - BDIST_GAMMUT /* Boundary Distance -> Out of gamut distance */ + icSigXYZData, /* Input color space */ + icSigGrayData, /* Output color space */ + XYZ_XYZp, /* Input transfer function, XYZ->XYZ' (NULL = default) */ + xyzmin, xyzmax, /* Make XYZ' range 0.0 - 1.0 for better precision */ + XYZp_BDIST, /* XYZ' -> Boundary Distance transfer function */ + NULL, NULL, /* Default range from clut to output table */ + BDIST_GAMMUT, /* Boundary Distance -> Out of gamut distance */ + NULL, NULL ) != 0) error("Setting 16 bit XYZ->Gammut Lut failed: %d, %s",wr_icco->errc,wr_icco->err); } @@ -2559,13 +2564,14 @@ main( /* Use helper function to do the hard work. */ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL, - icSigRgbData, /* Input color space */ - icSigLabData, /* Output color space */ - RGB_RGBp, /* Input transfer function, RGB->RGB' (NULL = default) */ - NULL, NULL, /* Use default Maximum range of RGB' values */ - RGBp_Labp, /* RGB' -> Lab' transfer function */ - NULL, NULL, /* Use default Maximum range of Lab' values */ - Labp_Lab /* Linear output transform Lab'->Lab */ + icSigRgbData, /* Input color space */ + icSigLabData, /* Output color space */ + RGB_RGBp, /* Input transfer function, RGB->RGB' (NULL = default) */ + NULL, NULL, /* Use default Maximum range of RGB' values */ + RGBp_Labp, /* RGB' -> Lab' transfer function */ + NULL, NULL, /* Use default Maximum range of Lab' values */ + Labp_Lab, /* Linear output transform Lab'->Lab */ + NULL, NULL ) != 0) error("Setting 16 bit RGB->Lab Lut failed: %d, %s",wr_icco->errc,wr_icco->err); } @@ -2643,13 +2649,15 @@ main( #endif /* Use helper function to do the hard work. */ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL, - icSigLabData, /* Input color space */ - icSigRgbData, /* Output color space */ - Lab_Labp, /* Linear input transform Lab->Lab' */ - NULL, NULL, /* Use default Lab' range */ - Labp_RGBp, /* Lab' -> RGB' transfer function */ - rgbmin, rgbmax, /* Make RGB' range 0.0 - 1.333 for less clip rounding */ - RGBp_RGB) != 0) /* Output transfer function, RGB'->RGB (NULL = deflt) */ + icSigLabData, /* Input color space */ + icSigRgbData, /* Output color space */ + Lab_Labp, /* Linear input transform Lab->Lab' */ + NULL, NULL, /* Use default Lab' range */ + Labp_RGBp, /* Lab' -> RGB' transfer function */ + rgbmin, rgbmax, /* Make RGB' range 0.0 - 1.333 for less clip rounding */ + RGBp_RGB, /* Output transfer function, RGB'->RGB (NULL = deflt) */ + NULL, NULL + ) != 0) error("Setting 16 bit Lab->RGB Lut failed: %d, %s",wr_icco->errc,wr_icco->err); } @@ -2690,13 +2698,14 @@ main( /* Use helper function to do the hard work. */ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL, - icSigLabData, /* Input color space */ - icSigGrayData, /* Output color space */ - Lab_Labp, /* Linear input transform Lab->Lab' */ - NULL, NULL , /* Default Lab' range */ - Labp_BDIST, /* Lab' -> Boundary Distance transfer function */ - NULL, NULL, /* Default range from clut to output table */ - BDIST_GAMMUT /* Boundary Distance -> Out of gamut distance */ + icSigLabData, /* Input color space */ + icSigGrayData, /* Output color space */ + Lab_Labp, /* Linear input transform Lab->Lab' */ + NULL, NULL, /* Default Lab' range */ + Labp_BDIST, /* Lab' -> Boundary Distance transfer function */ + NULL, NULL, /* Default range from clut to output table */ + BDIST_GAMMUT, /* Boundary Distance -> Out of gamut distance */ + NULL, NULL ) != 0) error("Setting 16 bit Lab->Gammut Lut failed: %d, %s",wr_icco->errc,wr_icco->err); } @@ -3065,13 +3074,14 @@ main( /* Use helper function to do the hard work. */ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL, - icSigRgbData, /* Input color space */ - icSigLabData, /* Output color space */ - RGB_RGBp, /* Input transfer function, RGB->RGB' (NULL = default) */ - NULL, NULL, /* Use default Maximum range of RGB' values */ - RGBp_Labp, /* RGB' -> Lab' transfer function */ - NULL, NULL, /* Use default Maximum range of Lab' values */ - Labp_Lab /* Linear output transform Lab'->Lab */ + icSigRgbData, /* Input color space */ + icSigLabData, /* Output color space */ + RGB_RGBp, /* Input transfer function, RGB->RGB' (NULL = default) */ + NULL, NULL, /* Use default Maximum range of RGB' values */ + RGBp_Labp, /* RGB' -> Lab' transfer function */ + NULL, NULL, /* Use default Maximum range of Lab' values */ + Labp_Lab, /* Linear output transform Lab'->Lab */ + NULL, NULL ) != 0) error("Setting 8 bit RGB->Lab Lut failed: %d, %s",wr_icco->errc,wr_icco->err); } @@ -3149,13 +3159,15 @@ main( #endif /* Use helper function to do the hard work. */ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL, - icSigLabData, /* Input color space */ - icSigRgbData, /* Output color space */ - Lab_Labp, /* Linear input transform Lab->Lab' */ - NULL, NULL, /* Use default Lab' range */ - Labp_RGBp, /* Lab' -> RGB' transfer function */ - rgbmin, rgbmax, /* Make RGB' range 0.0 - 1.333 for less clip rounding */ - RGBp_RGB) != 0) /* Output transfer function, RGB'->RGB (NULL = deflt) */ + icSigLabData, /* Input color space */ + icSigRgbData, /* Output color space */ + Lab_Labp, /* Linear input transform Lab->Lab' */ + NULL, NULL, /* Use default Lab' range */ + Labp_RGBp, /* Lab' -> RGB' transfer function */ + rgbmin, rgbmax, /* Make RGB' range 0.0 - 1.333 for less clip rounding */ + RGBp_RGB, /* Output transfer function, RGB'->RGB (NULL = deflt) */ + NULL, NULL + ) != 0) error("Setting 8 bit Lab->RGB Lut failed: %d, %s",wr_icco->errc,wr_icco->err); } @@ -3196,13 +3208,14 @@ main( /* Use helper function to do the hard work. */ if (wo->set_tables(wo, ICM_CLUT_SET_EXACT, NULL, - icSigLabData, /* Input color space */ - icSigGrayData, /* Output color space */ - Lab_Labp, /* Linear input transform Lab->Lab' */ - NULL, NULL , /* Default Lab' range */ - Labp_BDIST, /* Lab' -> Boundary Distance transfer function */ - NULL, NULL, /* Default range from clut to output table */ - BDIST_GAMMUT /* Boundary Distance -> Out of gamut distance */ + icSigLabData, /* Input color space */ + icSigGrayData, /* Output color space */ + Lab_Labp, /* Linear input transform Lab->Lab' */ + NULL, NULL , /* Default Lab' range */ + Labp_BDIST, /* Lab' -> Boundary Distance transfer function */ + NULL, NULL, /* Default range from clut to output table */ + BDIST_GAMMUT, /* Boundary Distance -> Out of gamut distance */ + NULL, NULL ) != 0) error("Setting 16 bit Lab->Gammut Lut failed: %d, %s",wr_icco->errc,wr_icco->err); } diff --git a/icc/makezip.ksh b/icc/makezip.ksh deleted file mode 100644 index f06efa4..0000000 --- a/icc/makezip.ksh +++ /dev/null @@ -1,6 +0,0 @@ -# Create icclib source distribution -#jam -#zip nt_iccdump.zip iccdump.exe -#zip nt_icclu.zip icclu.exe -rm icclib.zip -zip -9 -ll icclib.zip Readme.txt License.txt todo.txt log.txt Jamfile Makefile Makefile.WNT Makefile.IBMNT Makefile.UNIX Makefile.OSX icc.c iccstd.c icc.h iccV42.h iccdump.c icclu.c iccrw.c icctest.c lutest.c diff --git a/icc/mcheck.c b/icc/mcheck.c index 9000650..54811c3 100644 --- a/icc/mcheck.c +++ b/icc/mcheck.c @@ -155,7 +155,7 @@ main( error ("%d, %s",rd_icco->errc, rd_icco->err); } /* Get details of conversion */ - luo->spaces(luo, &ins, &inn, &outs, NULL, &alg, NULL, NULL, NULL); + luo->spaces(luo, &ins, &inn, &outs, NULL, &alg, NULL, NULL, NULL, NULL); if (alg != icmLutType) { error("Expecting Lut based profile"); diff --git a/icc/mkDispProf.c b/icc/mkDispProf.c new file mode 100644 index 0000000..bc70154 --- /dev/null +++ b/icc/mkDispProf.c @@ -0,0 +1,331 @@ + + +/* + * Create an ICC V2.4 compatible matrix display profile. + * + * Author: Graeme W. Gill + * Date: 8/2/2008 + * Version: 1.00 + * + * Copyright 2006 - 2014 Graeme W. Gill + * + * This material is licensed with an "MIT" free use license:- + * see the License.txt file in this directory for licensing details. + * + * Based on icc/lutest.c + */ + + +/* + * TTBD: + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "icc.h" + +void error(char *fmt, ...), warning(char *fmt, ...); + +void usage(void) { + fprintf(stderr,"Create a Matrix Display ICC profile\n"); + fprintf(stderr,"Author: Graeme W. Gill\n"); + fprintf(stderr,"usage: mkDispProf [-v level] outfile\n"); + fprintf(stderr," -v Verbose\n"); + exit(1); +} + +/* sRGB like device gamma encoded value to linear value 0.0 .. 1.0 */ +static double gdv2dv(double iv) { + double ov; + + if (iv < 0.04045) + ov = iv/12.92; + else + ov = pow((iv + 0.055)/1.055, 2.4); + return ov; +} + +int +main( +int argc, +char *argv[] +) { + int fa,nfa; + char out_name[1000]; + icmFile *wr_fp; + icc *wr_icco; + int rv = 0; + int verb = 0; + + if (argc < 2) + usage(); + + /* Process the arguments */ + for(fa = 1;fa < argc;fa++) { + nfa = fa; /* skip to nfa if next argument is used */ + if (argv[fa][0] == '-') { /* Look for any flags */ + char *na = NULL; /* next argument after flag, null if none */ + + if (argv[fa][2] != '\000') + na = &argv[fa][2]; /* next is directly after flag */ + else { + if ((fa+1) < argc) { + if (argv[fa+1][0] != '-') { + nfa = fa + 1; + na = argv[nfa]; /* next is seperate non-flag argument */ + } + } + } + + if (argv[fa][1] == '?') + usage(); + + /* Verbosity */ + else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') { + verb = 1; + } + + else + usage(); + } + else + break; + } + + if (fa >= argc || argv[fa][0] == '-') usage(); + strcpy(out_name,argv[fa]); + + /* ---------------------------------------- */ + /* Create a matrix/shaper based XYZ profile */ + /* ---------------------------------------- */ + + /* Open up the file for writing */ + if ((wr_fp = new_icmFileStd_name(out_name,"w")) == NULL) + error ("Write: Can't open file '%s'",out_name); + + if ((wr_icco = new_icc()) == NULL) + error ("Write: Creation of ICC object failed"); + + /* Add all the tags required */ + + /* The header: */ + { + icmHeader *wh = wr_icco->header; + + /* Values that must be set before writing */ + wh->deviceClass = icSigDisplayClass; + wh->colorSpace = icSigRgbData; /* It's RGB space */ + wh->pcs = icSigXYZData; + wh->renderingIntent = icPerceptual; + + /* Values that should be set before writing */ + wh->manufacturer = str2tag("????"); + wh->model = str2tag("????"); + } + /* Profile Description Tag: */ + { + icmTextDescription *wo; + char *dst; + + dst = "sRGB like Matrix Display profile"; + if ((wo = (icmTextDescription *)wr_icco->add_tag( + wr_icco, icSigProfileDescriptionTag, icSigTextDescriptionType)) == NULL) + error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err); + + wo->size = strlen(dst)+1; /* Allocated and used size of desc, inc null */ + wo->scCode = 0; + wo->scSize = strlen(dst)+1; + if (wo->scSize > 67) + error("Description scriptCode string longer than 67"); + wo->allocate((icmBase *)wo);/* Allocate space */ + strcpy(wo->desc, dst); /* Copy the string in */ + strcpy((char *)wo->scDesc, dst); /* Copy the string in */ + } + /* Copyright Tag: */ + { + icmText *wo; + char *crt = "Copyright tag goes here"; + if ((wo = (icmText *)wr_icco->add_tag( + wr_icco, icSigCopyrightTag, icSigTextType)) == NULL) + error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err); + + wo->size = strlen(crt)+1; /* Allocated and used size of text, inc null */ + wo->allocate((icmBase *)wo);/* Allocate space */ + strcpy(wo->data, crt); /* Copy the text in */ + } + + /* Could add other relevant tags here, such as: + + Device Manufacturers Description Tag + Device Model Description Tag + Device technology Tag + Viewing conditions Description Tag + Viewing conditions + Display Luminance Tag + Measurement information Tag + + etc. + */ + + /* Setup the primaries */ + { + /* Compute primaries as XYZ */ + icmXYZNumber wrgb[4] = { /* Primaries in Yxy from the standard */ + { 1.0, 0.3127, 0.3290 }, /* White */ + { 1.0, 0.6400, 0.3300 }, /* Red */ + { 1.0, 0.3000, 0.6000 }, /* Green */ + { 1.0, 0.1500, 0.0600 } /* Blue */ + }; + double mat[3][3]; + int i; + + /* Convert Yxy to XYZ */ + for (i = 0; i < 4; i++) { + double v[3]; + + icmXYZ2Ary(v, wrgb[i]); + icmYxy2XYZ(v, v); + icmAry2XYZ(wrgb[i], v); + } + + /* Convert XYZ to normalised 3x3 matrix */ + icmRGBprim2matrix(wrgb[0], wrgb[1], wrgb[2], wrgb[3], mat); + +#ifdef NEVER /* Dump XYZ of matrix */ + printf("sRGB: XYZ\n"); + printf("{ %f, %f, %f }, /* Red */\n" + "{ %f, %f, %f }, /* Green */\n" + "{ %f, %f, %f }, /* Blue */\n" + "{ %f, %f, %f } /* White */\n", + mat[0][0], mat[0][1], mat[0][2], + mat[1][0], mat[1][1], mat[1][2], + mat[2][0], mat[2][1], mat[2][2], + wrgb[0].X, wrgb[0].Y, wrgb[0].Z); +#endif + + /* White Point Tag: */ + { + icmXYZArray *wo; + + if ((wo = (icmXYZArray *)wr_icco->add_tag( + wr_icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL) + error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err); + + wo->size = 1; + wo->allocate((icmBase *)wo); /* Allocate space */ + wo->data[0].X = wrgb[0].X; + wo->data[0].Y = wrgb[0].Y; + wo->data[0].Z = wrgb[0].Z; + } + /* Black Point Tag: */ + { + icmXYZArray *wo; + + if ((wo = (icmXYZArray *)wr_icco->add_tag( + wr_icco, icSigMediaBlackPointTag, icSigXYZArrayType)) == NULL) + error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err); + + wo->size = 1; + wo->allocate((icmBase *)wo); /* Allocate space */ + wo->data[0].X = 0.00; + wo->data[0].Y = 0.00; + wo->data[0].Z = 0.00; + } + /* Red, Green and Blue Colorant Tags: */ + { + icmXYZArray *wor, *wog, *wob; + double fromAbs[3][3]; + double d50m[3][3]; + + /* Convert to D50 adapated */ + icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, wrgb[0], fromAbs); + icmMulBy3x3(d50m[0], fromAbs, mat[0]); + icmMulBy3x3(d50m[1], fromAbs, mat[1]); + icmMulBy3x3(d50m[2], fromAbs, mat[2]); + + /* Make sure rounding doesn't wreck white point */ + quantizeRGBprimsS15Fixed16(d50m); + + if ((wor = (icmXYZArray *)wr_icco->add_tag( + wr_icco, icSigRedColorantTag, icSigXYZArrayType)) == NULL) + error("add_tag failed: %d, %s",rv,wr_icco->err); + if ((wog = (icmXYZArray *)wr_icco->add_tag( + wr_icco, icSigGreenColorantTag, icSigXYZArrayType)) == NULL) + error("add_tag failed: %d, %s",rv,wr_icco->err); + if ((wob = (icmXYZArray *)wr_icco->add_tag( + wr_icco, icSigBlueColorantTag, icSigXYZArrayType)) == NULL) + error("add_tag failed: %d, %s",rv,wr_icco->err); + + wor->size = wog->size = wob->size = 1; + wor->allocate((icmBase *)wor); /* Allocate space */ + wog->allocate((icmBase *)wog); + wob->allocate((icmBase *)wob); + wor->data[0].X = d50m[0][0]; wor->data[0].Y = d50m[0][1]; wor->data[0].Z = d50m[0][2]; + wog->data[0].X = d50m[1][0]; wog->data[0].Y = d50m[1][1]; wog->data[0].Z = d50m[1][2]; + wob->data[0].X = d50m[2][0]; wob->data[0].Y = d50m[2][1]; wob->data[0].Z = d50m[2][2]; + } + } + /* Red, Green and Blue Gamma Curve Tags: */ + { + icmCurve *wor, *wog, *wob; + int i; + + if ((wor = (icmCurve *)wr_icco->add_tag( + wr_icco, icSigRedTRCTag, icSigCurveType)) == NULL) + error("add_tag failed: %d, %s",rv,wr_icco->err); + wor->flag = icmCurveSpec; + wor->size = 1024; + wor->allocate((icmBase *)wor); /* Allocate space */ + for (i = 0; i < wor->size; i++) + wor->data[i] = gdv2dv(i/(wor->size-1.0)); + + /* Link other channels to the red */ + if ((wog = (icmCurve *)wr_icco->link_tag( + wr_icco, icSigGreenTRCTag, icSigRedTRCTag)) == NULL) + error("link_tag failed: %d, %s",rv,wr_icco->err); + if ((wob = (icmCurve *)wr_icco->link_tag( + wr_icco, icSigBlueTRCTag, icSigRedTRCTag)) == NULL) + error("link_tag failed: %d, %s",rv,wr_icco->err); + } + + /* Write the file out */ + if ((rv = wr_icco->write(wr_icco,wr_fp,0)) != 0) + error ("Write file: %d, %s",rv,wr_icco->err); + + wr_icco->del(wr_icco); + wr_fp->del(wr_fp); + + return 0; +} + +/* ------------------------------------------------ */ +/* Basic printf type error() and warning() routines */ + +void +error(char *fmt, ...) { + va_list args; + + fprintf(stderr,"mkDispProf: Error - "); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); + exit (-1); +} + +void +warning(char *fmt, ...) { + va_list args; + + fprintf(stderr,"mkDispProf: Warning - "); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); +} diff --git a/icc/sRGB.icm b/icc/sRGB.icm index db0355f..5491330 100644 Binary files a/icc/sRGB.icm and b/icc/sRGB.icm differ -- cgit v1.2.3