From 094535c010320967639e8e86f974d878e80baa72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Fri, 1 May 2015 16:13:57 +0200 Subject: Imported Upstream version 1.7.0 --- icc/ClayRGB1998.icm | Bin 584 -> 640 bytes icc/EBU3213_PAL.icm | Bin 9120 -> 9176 bytes icc/Jamfile | 9 +- icc/Makefile | 9 +- icc/ProPhoto.icm | Bin 2892 -> 2948 bytes icc/ProPhotoLin.icm | Bin 2900 -> 2956 bytes icc/Rec2020.icm | Bin 9064 -> 9120 bytes icc/Rec709.icm | Bin 2920 -> 2976 bytes icc/SMPTE431_P3.icm | Bin 872 -> 928 bytes icc/SMPTE_RP145_NTSC.icm | Bin 9100 -> 9156 bytes icc/icc.c | 987 ++++++++++++++++++++++++++++++----------------- icc/icc.h | 153 ++++++-- icc/iccstd.c | 7 +- icc/icctest.c | 2 +- icc/lab2lab.icm | Bin 500 -> 556 bytes icc/log.txt | 12 + icc/mcheck.c | 250 +----------- icc/mkDispProf.c | 65 +++- icc/sRGB.icm | Bin 3212 -> 3268 bytes 19 files changed, 841 insertions(+), 653 deletions(-) (limited to 'icc') diff --git a/icc/ClayRGB1998.icm b/icc/ClayRGB1998.icm index 1c3a02e..4c8ecb3 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 index 54cfed7..b0e67bc 100644 Binary files a/icc/EBU3213_PAL.icm and b/icc/EBU3213_PAL.icm differ diff --git a/icc/Jamfile b/icc/Jamfile index 9f27848..4bedbba 100644 --- a/icc/Jamfile +++ b/icc/Jamfile @@ -45,6 +45,12 @@ MainsFromSources mkDispProf.c ; if $(BUILD_JUNK) { + HDRS += ../plot ; + LINKLIBS += ../plot/libvrml ; + + #Monotonic behaviour checker + MainsFromSources mcheck.c ; + # MainsFromSources tt.c ; MainsFromSources mksRGB.c ; @@ -64,9 +70,6 @@ if $(BUILD_JUNK) { # check CIEDE2000 MainsFromSources testDE2K.c ; - - #Monotonic behaviour checker - MainsFromSources mcheck.c ; } diff --git a/icc/Makefile b/icc/Makefile index dbb9992..85f19be 100644 --- a/icc/Makefile +++ b/icc/Makefile @@ -28,7 +28,7 @@ CCFLAGS = $(CCFLAGSDEF) $(CCOPTFLAG) $(CCDEFINES) STDHDRS = $(STDHDRSDEF) LINKFLAGS = $(LINKFLAGSDEF) $(LINKDEBUGFLAG) -all:: libicc$(SUFLIB) icctest$(SUFEXE) lutest$(SUFEXE) icclu$(SUFEXE) iccdump$(SUFEXE) iccrw$(SUFEXE) +all:: libicc$(SUFLIB) icctest$(SUFEXE) lutest$(SUFEXE) icclu$(SUFEXE) iccdump$(SUFEXE) iccrw$(SUFEXE) mkDispProf$(SUFEXE) icc$(SUFOBJ): icc.c icc.h @@ -79,4 +79,11 @@ iccrw$(SUFEXE): iccrw$(SUFOBJ) iccstd$(SUFOBJ) libicc$(SUFLIB) $(LINK) $(LINKOF)iccrw$(SUFEXE) iccrw$(SUFOBJ) iccstd$(SUFOBJ) libicc$(SUFLIB) +mkDispProf$(SUFOBJ): mkDispProf.c icc.h + $(CC) $(CCOF)mkDispProf$(SUFOBJ) mkDispProf.c + +mkDispProf$(SUFEXE): mkDispProf$(SUFOBJ) iccstd$(SUFOBJ) libicc$(SUFLIB) + $(LINK) $(LINKOF)mkDispProf$(SUFEXE) mkDispProf$(SUFOBJ) iccstd$(SUFOBJ) libicc$(SUFLIB) + + diff --git a/icc/ProPhoto.icm b/icc/ProPhoto.icm index 3ed16cb..cb6ee56 100644 Binary files a/icc/ProPhoto.icm and b/icc/ProPhoto.icm differ diff --git a/icc/ProPhotoLin.icm b/icc/ProPhotoLin.icm index 579b903..28369aa 100644 Binary files a/icc/ProPhotoLin.icm and b/icc/ProPhotoLin.icm differ diff --git a/icc/Rec2020.icm b/icc/Rec2020.icm index 09d5ccf..0decaf6 100644 Binary files a/icc/Rec2020.icm and b/icc/Rec2020.icm differ diff --git a/icc/Rec709.icm b/icc/Rec709.icm index 46140bd..abf0a63 100644 Binary files a/icc/Rec709.icm and b/icc/Rec709.icm differ diff --git a/icc/SMPTE431_P3.icm b/icc/SMPTE431_P3.icm index f3861be..8055849 100644 Binary files a/icc/SMPTE431_P3.icm and b/icc/SMPTE431_P3.icm differ diff --git a/icc/SMPTE_RP145_NTSC.icm b/icc/SMPTE_RP145_NTSC.icm index a6ce7f1..7fc78fb 100644 Binary files a/icc/SMPTE_RP145_NTSC.icm and b/icc/SMPTE_RP145_NTSC.icm differ diff --git a/icc/icc.c b/icc/icc.c index 7dfe519..87ce500 100644 --- a/icc/icc.c +++ b/icc/icc.c @@ -373,13 +373,13 @@ const char *format, int rv; va_list args; icmFileMem *p = (icmFileMem *)pp; - int len; + int alen, len; va_start(args, format); rv = 1; - len = 100; /* Initial allocation for printf */ - icmFileMem_filemem_resize(p, p->cur + len); + alen = 100; /* Initial allocation for printf */ + icmFileMem_filemem_resize(p, p->cur + alen); /* We have to use the available printf functions to resize the buffer if needed. */ for (;rv != 0;) { @@ -391,15 +391,15 @@ const char *format, break; if (len > -1) /* vsnprintf returned needed size-1 */ - len = len+2; /* (In case vsnprintf returned 1 less than it needs) */ + alen = len+2; /* (In case vsnprintf returned 1 less than it needs) */ else - len *= 2; /* We just have to guess */ + alen *= 2; /* We just have to guess */ /* Attempt to resize */ - icmFileMem_filemem_resize(p, p->cur + len); + icmFileMem_filemem_resize(p, p->cur + alen); /* If resize failed */ - if ((p->aend - p->cur) < len) { + if ((p->aend - p->cur) < alen) { rv = 0; break; } @@ -710,12 +710,17 @@ static int write_S15Fixed16Number(double d, char *p) { return 0; } +/* Round a number to the same quantization as a S15Fixed16 */ static double round_S15Fixed16Number(double d) { - d = floor(d * 65536.0 + 0.5); /* Beware! (int)(d + 0.5) doesn't work! */ + d = floor(d * 65536.0 + 0.5); /* Beware! (int)(d + 0.5) doesn't work for -ve nummbets ! */ d = d/65536.0; return d; } +/* Macro version */ +#define RND_S15FIXED16(xxx) ((xxx) > 0.0 ? (int)((xxx) * 65536.0 + 0.5)/65536.0 \ + : (int)((xxx) * 65536.0 - 0.5)/65536.0) + /* Device coordinate as 8 bit value range 0.0 - 1.0 */ static double read_DCS8Number(char *p) { unsigned int rv; @@ -774,7 +779,6 @@ static void Lut_L2LutV2_16(double *out, double *in); static void Lut_Lut2LV4_16(double *out, double *in); static void Lut_L2LutV4_16(double *out, double *in); -//~~~~888888 /* read a PCS number. PCS can be profile PCS, profile version Lab, */ /* or a specific type of Lab, depending on the value of csig: */ /* icmSigPCSData, icSigXYZData, icmSigLab8Data, icSigLabData, */ @@ -1397,6 +1401,8 @@ static const char *string_TagSignature(icTagSignature sig) { return "BToA2 Multidimentional Transform"; case icSigCalibrationDateTimeTag: return "Calibration Date & Time"; + case icSigChromaticAdaptationTag: + return "Chromatic Adaptation"; case icSigCharTargetTag: return "Characterization Target"; case icSigCopyrightTag: @@ -1467,6 +1473,11 @@ static const char *string_TagSignature(icTagSignature sig) { return "Viewing Condition Description"; case icSigViewingConditionsTag: return "Viewing Condition Paramaters"; + + /* ArgyllCMS private tag: */ + case icmSigAbsToRelTransSpace: + return "Absolute to Media Relative Transformation Space matrix"; + default: sprintf(buf,"Unrecognized - %s",tag2str(sig)); return buf; @@ -3071,7 +3082,7 @@ static void icmU16Fixed16Array_dump( if (verb >= 2) { unsigned int i; for (i = 0; i < p->size; i++) - op->gprintf(op," %lu: %f\n",i,p->data[i]); + op->gprintf(op," %lu: %.8f\n",i,p->data[i]); } } @@ -3265,7 +3276,7 @@ static void icmS15Fixed16Array_dump( if (verb >= 2) { unsigned int i; for (i = 0; i < p->size; i++) - op->gprintf(op," %lu: %f\n",i,p->data[i]); + op->gprintf(op," %lu: %.8f\n",i,p->data[i]); } } @@ -3350,7 +3361,7 @@ static int read_XYZNumber(icmXYZNumber *p, char *d) { static char *string_XYZNumber(icmXYZNumber *p) { static char buf[40]; - sprintf(buf,"%f, %f, %f", p->X, p->Y, p->Z); + sprintf(buf,"%.8f, %.8f, %.8f", p->X, p->Y, p->Z); return buf; } @@ -3363,7 +3374,7 @@ static char *string_XYZNumber_and_Lab(icmXYZNumber *p) { lab[1] = p->Y; lab[2] = p->Z; icmXYZ2Lab(&icmD50, lab, lab); - snprintf(buf,sizeof(buf),"%f, %f, %f [Lab %f, %f, %f]", p->X, p->Y, p->Z, lab[0], lab[1], lab[2]); + snprintf(buf,sizeof(buf),"%.8f, %.8f, %.8f [Lab %f, %f, %f]", p->X, p->Y, p->Z, lab[0], lab[1], lab[2]); return buf; } @@ -3772,6 +3783,8 @@ static int icmTable_lookup_bwd( /* Do a reverse lookup through the curve */ /* Return 0 on success, 1 if clipping occured, 2 on other error */ +/* (Note that clipping means mathematical clipping, and is not */ +/* set just because a device value is out of gamut. */ static int icmCurve_lookup_bwd( icmCurve *p, double *out, @@ -3950,7 +3963,7 @@ static int icmCurve_write( return icp->errc = 1; } if ((rv = write_U8Fixed8Number(p->data[0],bp)) != 0) { - sprintf(icp->err,"icmCurve_write: write_U8Fixed8umber(%f) failed",p->data[0]); + sprintf(icp->err,"icmCurve_write: write_U8Fixed8umber(%.8f) failed",p->data[0]); icp->al->free(icp->al, buf); return icp->errc = rv; } @@ -3962,7 +3975,7 @@ static int icmCurve_write( } for (i = 0; i < p->size; i++, bp += 2) { if ((rv = write_DCS16Number(p->data[i],bp)) != 0) { - sprintf(icp->err,"icmCurve_write: write_UInt16umber(%f) failed",p->data[i]); + sprintf(icp->err,"icmCurve_write: write_UInt16umber(%.8f) failed",p->data[i]); icp->al->free(icp->al, buf); return icp->errc = rv; } @@ -3995,13 +4008,13 @@ static void icmCurve_dump( if (p->flag == icmCurveLin) { op->gprintf(op," Curve is linear\n"); } else if (p->flag == icmCurveGamma) { - op->gprintf(op," Curve is gamma of %f\n",p->data[0]); + op->gprintf(op," Curve is gamma of %.8f\n",p->data[0]); } else { op->gprintf(op," No. elements = %lu\n",p->size); if (verb >= 2) { unsigned int i; for (i = 0; i < p->size; i++) - op->gprintf(op," %3lu: %f\n",i,p->data[i]); + op->gprintf(op," %3lu: %.8f\n",i,p->data[i]); } } } @@ -4979,7 +4992,7 @@ double *in /* Input array[inputChan] */ return 0; } -/* Convert normalized numbers though this Luts input tables. */ +/* Convert normalized numbers though this Luts per channel input tables. */ /* Return 0 on success, 1 if clipping occured, 2 on other error */ static int icmLut_lookup_input( icmLut *p, /* Pointer to Lut object */ @@ -5212,140 +5225,7 @@ double *in /* Input array[outputChan] */ return rv; } -#ifdef NEVER // ~~~99 development code - -/* Convert normalized numbers though this Luts multi-dimensional table */ -/* using optimised simplex interpolation. */ -/* This version optimses the simplex split axis depending on the input */ -/* colorspace. */ -static int icmLut_lookup_clut_osx( -/* Return 0 on success, 1 if clipping occured, 2 on other error */ -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 */ - char xflip[MAX_CHAN]; /* Optimised simplex axis flip flags */ - - /* 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; -// ~~~999 -#ifdef NEVER - xflip[e] = p->finfo[e].bthff; - if (in[e] >= p->finfo[e].fth) - xflip[e] = p->finfo[e].athff; -#else - - xflip[e] = 0; - if (e == 0) - xflip[e] = 1; -#endif - 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 */ - if (xflip[e]) { /* Reverse sense of direction for this axis */ - co[e] = 1.0 - co[e]; - gp += p->dinc[e]; - } - } - } -//printf("*");fflush(stdout); -#ifdef NEVER - /* Do selection sort on coordinates, smallest to largest. */ - { - int e, f; - for (e = 0; e < p->inputChan; e++) - si[e] = e; /* Initial unsorted indexes */ - for (e = 0; e < (p->inputChan-1); e++) { - double cosn; - cosn = co[si[e]]; /* Current smallest value */ - for (f = e+1; f < p->inputChan; f++) { /* Check against rest */ - int tt; - tt = si[f]; - if (cosn > co[tt]) { - si[f] = si[e]; /* Exchange */ - si[e] = tt; - cosn = co[tt]; - } - } - } - } -#else - /* 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; - } - } -#endif - /* Now compute the weightings, simplex vertices and output values */ - { - unsigned int e, f; - double w; /* Current vertex weight */ - - w = 1.0 - co[si[p->inputChan-1]]; /* Vertex at base of cell */ - for (f = 0; f < p->outputChan; f++) - out[f] = w * gp[f]; - - for (e = p->inputChan-1; e > 0; e--) { /* Middle verticies */ - w = co[si[e]] - co[si[e-1]]; - if (xflip[e]) - gp -= p->dinc[si[e]]; /* Move to top of cell in next largest dimension */ - else - gp += p->dinc[si[e]]; /* Move to top of cell in next largest dimension */ - for (f = 0; f < p->outputChan; f++) - out[f] += w * gp[f]; - } - - w = co[si[0]]; - if (xflip[0]) - gp -= p->dinc[si[0]]; /* Far corner from base of cell */ - else - gp += p->dinc[si[0]]; /* Far corner from base of cell */ - for (f = 0; f < p->outputChan; f++) - out[f] += w * gp[f]; - } - return rv; -} - -#endif /* NEVER */ // ~~~99 development code - - -/* Convert normalized numbers though this Luts output tables. */ +/* Convert normalized numbers though this Luts per channel output tables. */ /* Return 0 on success, 1 if clipping occured, 2 on other error */ static int icmLut_lookup_output( icmLut *p, /* Pointer to Lut object */ @@ -5509,7 +5389,6 @@ double *in /* Input array[outputChan] */ 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, */ @@ -5562,6 +5441,7 @@ double *in /* Input array[outputChan] */ { unsigned int e, f; double w, ww = 0.0; /* Current vertex weight, sum of weights squared */ + double cout[MAX_CHAN]; /* Current output value */ double *ogp = gp; /* Pointer to grid cube base */ w = 1.0 - co[si[p->inputChan-1]]; /* Vertex at base of cell */ @@ -6461,8 +6341,8 @@ int icmSetMultiLutTables( } /* Next pass */ } /* Next table */ - free(clutTable1); - free(clutTable3); + icp->al->free(icp->al, clutTable1); + icp->al->free(icp->al, clutTable3); } /* Create the 1D output table entry values */ @@ -6815,7 +6695,7 @@ static int icmLut_write( } else { for (i = 0; i < size; i++, bp += 2) { if ((rv = write_DCS16Number(p->inputTable[i], bp)) != 0) { - sprintf(icp->err,"icmLut_write: inputTable write_DCS16Number(%f) failed",p->inputTable[i]); + sprintf(icp->err,"icmLut_write: inputTable write_DCS16Number(%.8f) failed",p->inputTable[i]); icp->al->free(icp->al, buf); return icp->errc = rv; } @@ -6835,7 +6715,7 @@ static int icmLut_write( } else { for (i = 0; i < size; i++, bp += 2) { if ((rv = write_DCS16Number(p->clutTable[i], bp)) != 0) { - sprintf(icp->err,"icmLut_write: clutTable write_DCS16Number(%f) failed",p->clutTable[i]); + sprintf(icp->err,"icmLut_write: clutTable write_DCS16Number(%.8f) failed",p->clutTable[i]); icp->al->free(icp->al, buf); return icp->errc = rv; } @@ -6855,7 +6735,7 @@ static int icmLut_write( } else { for (i = 0; i < size; i++, bp += 2) { if ((rv = write_DCS16Number(p->outputTable[i], bp)) != 0) { - sprintf(icp->err,"icmLut_write: outputTable write_DCS16Number(%f) failed",p->outputTable[i]); + sprintf(icp->err,"icmLut_write: outputTable write_DCS16Number(%.8f) failed",p->outputTable[i]); icp->al->free(icp->al, buf); return icp->errc = rv; } @@ -6893,9 +6773,9 @@ static void icmLut_dump( op->gprintf(op," CLUT resolution = %u\n",p->clutPoints); op->gprintf(op," Input Table entries = %u\n",p->inputEnt); op->gprintf(op," Output Table entries = %u\n",p->outputEnt); - op->gprintf(op," XYZ matrix = %f, %f, %f\n",p->e[0][0],p->e[0][1],p->e[0][2]); - op->gprintf(op," %f, %f, %f\n",p->e[1][0],p->e[1][1],p->e[1][2]); - op->gprintf(op," %f, %f, %f\n",p->e[2][0],p->e[2][1],p->e[2][2]); + op->gprintf(op," XYZ matrix = %.8f, %.8f, %.8f\n",p->e[0][0],p->e[0][1],p->e[0][2]); + op->gprintf(op," %.8f, %.8f, %.8f\n",p->e[1][0],p->e[1][1],p->e[1][2]); + op->gprintf(op," %.8f, %.8f, %.8f\n",p->e[2][0],p->e[2][1],p->e[2][2]); if (verb >= 2) { unsigned int i, j, size; @@ -7819,7 +7699,7 @@ static void icmNamedColor_dump( if (p->ttype == icSigNamedColor2Type) { switch(icp->header->pcs) { case icSigXYZData: - op->gprintf(op," XYZ = %f, %f, %f\n", + op->gprintf(op," XYZ = %.8f, %.8f, %.8f\n", vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]); break; case icSigLabData: @@ -7836,7 +7716,7 @@ static void icmNamedColor_dump( for (n = 0; n < p->nDeviceCoords; n++) { if (n > 0) op->gprintf(op,", "); - op->gprintf(op,"%f",vp->deviceCoords[n]); + op->gprintf(op,"%.8f",vp->deviceCoords[n]); } op->gprintf(op,"\n"); } @@ -8188,7 +8068,7 @@ static void icmColorantTable_dump( switch(pcs) { case icSigXYZData: - op->gprintf(op," XYZ = %f, %f, %f\n", + op->gprintf(op," XYZ = %.8f, %.8f, %.8f\n", vp->pcsCoords[0],vp->pcsCoords[1],vp->pcsCoords[2]); break; case icSigLabData: @@ -8214,7 +8094,8 @@ 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 %lu bytes)",p->count,sizeof(icmColorantTableVal)); + sprintf(icp->err,"icmColorantTable_alloc: count overflow (%d of %lu bytes)", + p->count,(unsigned long)sizeof(icmColorantTableVal)); return icp->errc = 1; } if (p->data != NULL) @@ -9788,7 +9669,7 @@ static int icmUcrBg_write( } } else { if ((rv = write_DCS16Number(p->UCRcurve[i],bp)) != 0) { - sprintf(icp->err,"icmUcrBg_write: write_DCS16umber(%f) failed",p->UCRcurve[i]); + sprintf(icp->err,"icmUcrBg_write: write_DCS16umber(%.8f) failed",p->UCRcurve[i]); icp->al->free(icp->al, buf); return icp->errc = rv; } @@ -9812,7 +9693,7 @@ static int icmUcrBg_write( } } else { if ((rv = write_DCS16Number(p->BGcurve[i],bp)) != 0) { - sprintf(icp->err,"icmUcrBg_write: write_DCS16umber(%f) failed",p->BGcurve[i]); + sprintf(icp->err,"icmUcrBg_write: write_DCS16umber(%.8f) failed",p->BGcurve[i]); icp->al->free(icp->al, buf); return icp->errc = rv; } @@ -10302,15 +10183,15 @@ static void icmVideoCardGamma_dump( } } else if (p->tagType == icmVideoCardGammaFormulaType) { op->gprintf(op,"VideoCardGammaFormula:\n"); - op->gprintf(op," red gamma = %f\n", p->u.formula.redGamma); - op->gprintf(op," red min = %f\n", p->u.formula.redMin); - op->gprintf(op," red max = %f\n", p->u.formula.redMax); - op->gprintf(op," green gamma = %f\n", p->u.formula.greenGamma); - op->gprintf(op," green min = %f\n", p->u.formula.greenMin); - op->gprintf(op," green max = %f\n", p->u.formula.greenMax); - op->gprintf(op," blue gamma = %f\n", p->u.formula.blueGamma); - op->gprintf(op," blue min = %f\n", p->u.formula.blueMin); - op->gprintf(op," blue max = %f\n", p->u.formula.blueMax); + op->gprintf(op," red gamma = %.8f\n", p->u.formula.redGamma); + op->gprintf(op," red min = %.8f\n", p->u.formula.redMin); + op->gprintf(op," red max = %.8f\n", p->u.formula.redMax); + op->gprintf(op," green gamma = %.8f\n", p->u.formula.greenGamma); + op->gprintf(op," green min = %.8f\n", p->u.formula.greenMin); + op->gprintf(op," green max = %.8f\n", p->u.formula.greenMax); + op->gprintf(op," blue gamma = %.8f\n", p->u.formula.blueGamma); + op->gprintf(op," blue min = %.8f\n", p->u.formula.blueMin); + op->gprintf(op," blue max = %.8f\n", p->u.formula.blueMax); } else { op->gprintf(op," Unknown tag format\n"); } @@ -11364,6 +11245,7 @@ static struct { {icSigBToA1Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}}, {icSigBToA2Tag, {icSigLut8Type,icSigLut16Type,icMaxEnumType}}, {icSigCalibrationDateTimeTag, {icSigDateTimeType,icMaxEnumType}}, + {icSigChromaticAdaptationTag, {icSigS15Fixed16ArrayType,icMaxEnumType}}, {icSigCharTargetTag, {icSigTextType,icMaxEnumType}}, {icSigColorantTableTag, {icSigColorantTableType,icmSigAltColorantTableType, icMaxEnumType}}, @@ -11403,6 +11285,9 @@ static struct { {icSigVideoCardGammaTag, {icSigVideoCardGammaType,icMaxEnumType}}, {icSigViewingCondDescTag, {icSigTextDescriptionType,icMaxEnumType}}, {icSigViewingConditionsTag, {icSigViewingConditionsType,icMaxEnumType}}, + + {icmSigAbsToRelTransSpace, {icSigS15Fixed16ArrayType,icMaxEnumType}}, + {icMaxEnumTag, {icMaxEnumType}} }; @@ -11550,13 +11435,13 @@ static struct { {icSigNamedColorClass, -200, icMaxEnumData, icMaxEnumData, {icSigProfileDescriptionTag, - icSigNamedColor2Tag, + icSigNamedColorTag, /* Not strictly V3.4 */ icSigMediaWhitePointTag, icSigCopyrightTag, icMaxEnumTag}}, {icSigNamedColorClass, -100, icMaxEnumData, icMaxEnumData, {icSigProfileDescriptionTag, - icSigNamedColorTag, /* Not strictly V3.4 */ + icSigNamedColor2Tag, icSigMediaWhitePointTag, icSigCopyrightTag, icMaxEnumTag}}, @@ -11652,7 +11537,7 @@ static int check_icc_legal( 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])); + printf("icc_check_legal: deviceClass %s is missing required tag %s\n", tag2str(sig), tag2str(tagchecktable[i].tags[j])); #endif if (tagchecktable[i].chans == -200 || tagchecktable[i].chans == -dchans) { /* But can try next table */ @@ -11778,7 +11663,7 @@ static int icc_read_x( || p->data[i].size > (maxoff - minoff) || (p->data[i].offset + p->data[i].size) < p->data[i].offset /* Overflow */ || (p->data[i].offset + p->data[i].size) > p->header->size) { - sprintf(p->err,"icc_read: tag %d is out of range of the nominated file size",i); + sprintf(p->err,"icc_read: tag %d sig %s offset %d size %d is out of range of the nominated file size %d",i,tag2str(p->data[i].sig),p->data[i].offset,p->data[i].size,maxoff); p->al->free(p->al, p->data); p->data = NULL; return p->errc = 1; @@ -11799,6 +11684,82 @@ static int icc_read_x( } } /* p->count > 0 */ + /* Check if there is an ArgyllCMS 'arts' tag, and setup the wpchtmx[][] matrix from it. */ + { + icmS15Fixed16Array *artsTag; + + if ((artsTag = (icmS15Fixed16Array *)p->read_tag(p, icmSigAbsToRelTransSpace)) != NULL + && artsTag->ttype == icSigS15Fixed16ArrayType + && artsTag->size >= 9) { + + p->wpchtmx[0][0] = artsTag->data[0]; + p->wpchtmx[0][1] = artsTag->data[1]; + p->wpchtmx[0][2] = artsTag->data[2]; + p->wpchtmx[1][0] = artsTag->data[3]; + p->wpchtmx[1][1] = artsTag->data[4]; + p->wpchtmx[1][2] = artsTag->data[5]; + p->wpchtmx[2][0] = artsTag->data[6]; + p->wpchtmx[2][1] = artsTag->data[7]; + p->wpchtmx[2][2] = artsTag->data[8]; + + icmInverse3x3(p->iwpchtmx, p->wpchtmx); + + p->useArts = 1; /* Save it if it was in profile */ + + } else { + /* If an ArgyllCMS created profile, or if it's a Display profile, */ + /* use Bradford. This makes sRGB and AdobeRGB etc. work correctly */ + /* for absolute colorimetic. Note that for display profiles that set */ + /* the WP to D50 and store their chromatic transform in the 'chad' tag, */ + /* (i.e. some V2 profiles and all V4 profiles) this will have no effect */ + /* on the Media Relative WP Transformation since D50 -> D50, and */ + /* the 'chad' tag will be used to set the internal MediaWhite value */ + /* and transform matrix. */ + if (p->header->creator == str2tag("argl") + || p->header->deviceClass == icSigDisplayClass) { + + icmCpy3x3(p->wpchtmx, icmBradford); + icmInverse3x3(p->iwpchtmx, p->wpchtmx); + + /* Default to ICC standard Wrong Von Kries */ + /* for non-ArgyllCMS, non-Display profiles. */ + } else { + icmCpy3x3(p->wpchtmx, icmWrongVonKries); + icmCpy3x3(p->iwpchtmx, icmWrongVonKries); + } + + p->useArts = 0; /* Don't save it if it wasn't in profile */ + } + + p->autoWpchtmx = 0; /* It's been set on reading - don't set automatically */ + } + + /* If this is a Display profile, check if there is a 'chad' tag, and read it */ + /* in if it exists. We will use this latter. */ + { + icmS15Fixed16Array *chadTag; + + if (p->header->deviceClass == icSigDisplayClass + && (chadTag = (icmS15Fixed16Array *)p->read_tag(p, icSigChromaticAdaptationTag)) != NULL + && chadTag->ttype == icSigS15Fixed16ArrayType + && chadTag->size == 9) { + + p->chadmx[0][0] = chadTag->data[0]; + p->chadmx[0][1] = chadTag->data[1]; + p->chadmx[0][2] = chadTag->data[2]; + p->chadmx[1][0] = chadTag->data[3]; + p->chadmx[1][1] = chadTag->data[4]; + p->chadmx[1][2] = chadTag->data[5]; + p->chadmx[2][0] = chadTag->data[6]; + p->chadmx[2][1] = chadTag->data[7]; + p->chadmx[2][2] = chadTag->data[8]; + + p->chadValid = 1; + + p->useChad = 1; /* Use it when writing */ + } + } + return er; } @@ -11946,6 +11907,8 @@ static unsigned int icc_get_size( return size; /* Total size needed, or UINT_MAX if overflow */ } +void quantize3x3S15Fixed16(double targ[3], double mat[3][3], double in[3]); + /* Write the contents of the object. Return 0 on sucess, error code on failure */ /* NOTE: fp ownership is taken even if the function fails. */ static int icc_write_x( @@ -11965,19 +11928,121 @@ static int icc_write_x( p->del_fp = 1; p->of = of; /* Offset of ICC profile */ - for (i = 0; i < ALIGN_SIZE; i++) - pbuf[i] = 0; + /* Compute the total size and tag element data offsets */ + if (p->header == NULL) { + sprintf(p->err,"icc_write: No header defined"); + return p->errc = 1; + } + + /* If we're using the ArgyllCMS 'arts' tag to record the chromatic */ + /* adapation cone matrix used for the Media Relative WP Transformation, */ + /* create it and set it from the wpchtmx[][] matrix. */ + /* Don't write it if there is to 'wtpt' tag (i.e. it's a device link) */ + if (p->useArts + && p->find_tag(p, icSigMediaWhitePointTag) == 0) { + icmS15Fixed16Array *artsTag; + + /* Make sure no 'arts' tag currently exists */ + if (p->delete_tag(p, icmSigAbsToRelTransSpace) != 0 + && p->errc != 2) { + sprintf(p->err,"icc_write: Deleting existing 'arts' tag failed"); + return p->errc = 1; + } + + /* Add one */ + if ((artsTag = (icmS15Fixed16Array *)p->add_tag(p, icmSigAbsToRelTransSpace, + icSigS15Fixed16ArrayType)) == NULL) { + sprintf(p->err,"icc_write: Adding 'arts' tag failed"); + return p->errc = 1; + } + artsTag->size = 9; + if ((rv = artsTag->allocate((icmBase *)artsTag) ) != 0) { + sprintf(p->err,"icc_write: Allocating 'arts' tag failed"); + return p->errc = 1; + } + + /* The cone matrix is assumed to be arranged conventionaly for matrix */ + /* times vector multiplication. */ + /* Consistent with ICC usage, the dimension corresponding to the matrix */ + /* rows varies least rapidly while the one corresponding to the matrix */ + /* columns varies most rapidly. */ + artsTag->data[0] = p->wpchtmx[0][0]; + artsTag->data[1] = p->wpchtmx[0][1]; + artsTag->data[2] = p->wpchtmx[0][2]; + artsTag->data[3] = p->wpchtmx[1][0]; + artsTag->data[4] = p->wpchtmx[1][1]; + artsTag->data[5] = p->wpchtmx[1][2]; + artsTag->data[6] = p->wpchtmx[2][0]; + artsTag->data[7] = p->wpchtmx[2][1]; + artsTag->data[8] = p->wpchtmx[2][2]; + } + + /* If this is a Display profile, and we have been told to save it in */ + /* ICCV4 style, then set the media white point tag to D50 and save */ + /* the chromatic adapation matrix to the 'chad' tag. */ + { + icmXYZArray *whitePointTag; + icmS15Fixed16Array *chadTag; + + if (p->header->deviceClass == icSigDisplayClass + && p->useChad + && (whitePointTag = (icmXYZArray *)p->read_tag(p, icSigMediaWhitePointTag)) != NULL + && whitePointTag->ttype == icSigXYZType + && whitePointTag->size >= 1) { + + /* If we've set this profile, not just read it, */ + /* compute the fromAbs/chad matrix from media white point and cone matrix */ + if (!p->chadValid) { + double wp[3]; + p->chromAdaptMatrix(p, ICM_CAM_NONE, icmD50, whitePointTag->data[0], p->chadmx); + + /* Optimally quantize chad matrix to preserver white point */ + icmXYZ2Ary(wp, whitePointTag->data[0]); + quantize3x3S15Fixed16(icmD50_ary3, p->chadmx, wp); + } + + /* Make sure no 'chad' tag currently exists */ + if (p->delete_tag(p, icSigChromaticAdaptationTag) != 0 + && p->errc != 2) { + sprintf(p->err,"icc_write: Deleting existing 'chad' tag failed"); + return p->errc = 1; + } + + /* Add one */ + if ((chadTag = (icmS15Fixed16Array *)p->add_tag(p, icSigChromaticAdaptationTag, + icSigS15Fixed16ArrayType)) == NULL) { + sprintf(p->err,"icc_write: Adding 'chad' tag failed"); + return p->errc = 1; + } + chadTag->size = 9; + if ((rv = chadTag->allocate((icmBase *)chadTag) ) != 0) { + sprintf(p->err,"icc_write: Allocating 'chad' tag failed"); + return p->errc = 1; + } + + /* Save in ICC matrix order */ + chadTag->data[0] = p->chadmx[0][0]; + chadTag->data[1] = p->chadmx[0][1]; + chadTag->data[2] = p->chadmx[0][2]; + chadTag->data[3] = p->chadmx[1][0]; + chadTag->data[4] = p->chadmx[1][1]; + chadTag->data[5] = p->chadmx[1][2]; + chadTag->data[6] = p->chadmx[2][0]; + chadTag->data[7] = p->chadmx[2][1]; + chadTag->data[8] = p->chadmx[2][2]; + + /* Set the media white point to D50 */ + whitePointTag->data[0] = icmD50; + } + } /* Check that the right tags etc. are present for a legal ICC profile */ if ((rv = check_icc_legal(p)) != 0) { return rv; } - /* Compute the total size and tag element data offsets */ - if (p->header == NULL) { - sprintf(p->err,"icc_write: No header defined"); - return p->errc = 1; - } + for (i = 0; i < ALIGN_SIZE; i++) + pbuf[i] = 0; size = sat_add(size, p->header->get_size(p->header)); /* Assume header is aligned */ @@ -12201,6 +12266,7 @@ static int icc_write( /* Returns NULL if error - icc->errc will contain */ /* 2 on system error, */ /* 3 if unknown tag */ +/* 4 if duplicate tag */ /* NOTE: that we prevent tag duplication */ /* NOTE: to create a tag type icmSigUnknownType, set ttype to icmSigUnknownType, */ /* and set the actual tag type in icmSigUnknownType->uttype */ @@ -12250,7 +12316,7 @@ static icmBase *icc_add_tag( for (j = 0; j < p->count; j++) { if (p->data[j].sig == sig) { sprintf(p->err,"icc_add_tag: Already have tag '%s' in profile",tag2str(p->data[j].sig)); - p->errc = 1; + p->errc = 4; return NULL; } } @@ -12458,6 +12524,7 @@ static icmBase *icc_read_tag_ix( if (typetable[j].ttype == icMaxEnumType) { if (!alow_unk) { + sprintf(p->err,"icc_read_tag_ix: found unknown tag"); p->errc = 2; return NULL; } @@ -12631,7 +12698,7 @@ static int icc_unread_tag( /* Delete the tag, and free the underlying tag type, */ /* if this was the last reference to it. */ -/* Note this finds the first tag with a matching signature */ +/* Note this finds the first tag with a matching signature. */ /* Returns non-zero on error: */ /* tag not found - icc->errc will contain 2 */ static int icc_delete_tag_ix( @@ -13108,11 +13175,11 @@ static struct { ICC V2 Lab encoding should be used in all PCS encodings in a icSigLut16Type or icSigNamedColor2Type tag, and can be used - for Lab encoding in device spaces for these tags. + for device space Lab encoding for these tags. ICC V4 Lab encoding should be used in all PCS encodings in - all other situations, and can be used for Lab encoding in - device spaces for all other situtaions. + all other situations, and can be used for device space Lab encoding + for all other situtaions. [ Since the ICC spec. doesn't cover device spaces labeled as Lab, these are ripe for mis-matches between different implementations.] @@ -14426,40 +14493,59 @@ extern ICCLIB_API void icm1960UCS21964WUV(icmXYZNumber *w, double *out, double * } /* - - - - - - - - - - - - - - - - - - - - - - - - */ -/* NOTE :- that these values are for the 1931 standard observer */ +/* NOTE :- that these values are for the 1931 standard observer. */ +/* Since they are an arbitrary 4 decimal place accuracy, we round */ +/* them to be exactly the same as ICC header encoded values, */ +/* to avoid any slight discrepancy with PCS white from profiles. */ /* available D50 Illuminant */ icmXYZNumber icmD50 = { /* Profile illuminant - D50 */ - 0.9642, 1.0000, 0.8249 + RND_S15FIXED16(0.9642), + RND_S15FIXED16(1.0000), + RND_S15FIXED16(0.8249) }; icmXYZNumber icmD50_100 = { /* Profile illuminant - D50, scaled to 100 */ - 96.42, 100.00, 82.49 + RND_S15FIXED16(0.9642) * 100.0, + RND_S15FIXED16(1.0000) * 100.0, + RND_S15FIXED16(0.8249) * 100.0 }; double icmD50_ary3[3] = { /* Profile illuminant - D50 */ - 0.9642, 1.0000, 0.8249 + RND_S15FIXED16(0.9642), + RND_S15FIXED16(1.0000), + RND_S15FIXED16(0.8249) }; double icmD50_100_ary3[3] = { /* Profile illuminant - D50, scaled to 100 */ - 96.42, 100.00, 82.49 + RND_S15FIXED16(0.9642) * 100.0, + RND_S15FIXED16(1.0000) * 100.0, + RND_S15FIXED16(0.8249) * 100.0 }; /* available D65 Illuminant */ icmXYZNumber icmD65 = { /* Profile illuminant - D65 */ - 0.9505, 1.0000, 1.0890 + RND_S15FIXED16(0.9505), + RND_S15FIXED16(1.0000), + RND_S15FIXED16(1.0890) }; icmXYZNumber icmD65_100 = { /* Profile illuminant - D65, scaled to 100 */ - 95.05, 100.00, 108.90 + RND_S15FIXED16(0.9505) * 100.0, + RND_S15FIXED16(1.0000) * 100.0, + RND_S15FIXED16(1.0890) * 100.0 }; double icmD65_ary3[3] = { /* Profile illuminant - D65 */ - 0.9505, 1.0000, 1.0890 + RND_S15FIXED16(0.9505), + RND_S15FIXED16(1.0000), + RND_S15FIXED16(1.0890) }; double icmD65_100_ary3[3] = { /* Profile illuminant - D65, scaled to 100 */ - 95.05, 100.00, 108.90 + RND_S15FIXED16(0.9505) * 100.0, + RND_S15FIXED16(1.0000) * 100.0, + RND_S15FIXED16(1.0890) * 100.0 }; @@ -14468,6 +14554,20 @@ icmXYZNumber icmBlack = { 0.0000, 0.0000, 0.0000 }; +/* The Standard ("wrong Von-Kries") chromatic transform matrix */ +double icmWrongVonKries[3][3] = { + { 1.0000, 0.0000, 0.0000 }, + { 0.0000, 1.0000, 0.0000 }, + { 0.0000, 0.0000, 1.0000 } +}; + +/* The Bradford chromatic transform matrix */ +double icmBradford[3][3] = { + { RND_S15FIXED16( 0.8951), RND_S15FIXED16( 0.2664), RND_S15FIXED16(-0.1614) }, + { RND_S15FIXED16(-0.7502), RND_S15FIXED16( 1.7135), RND_S15FIXED16( 0.0367) }, + { RND_S15FIXED16( 0.0389), RND_S15FIXED16(-0.0685), RND_S15FIXED16( 1.0296) } +}; + /* Return the normal Delta E given two Lab values */ double icmLabDE(double *Lab0, double *Lab1) { double rv = 0.0, tt; @@ -14659,6 +14759,7 @@ double icmCIE2Ksq(double *Lab0, double *Lab1) { L = 0.5 * (Lab0[0] + Lab1[0]); C = 0.5 * (C1 + C2); + if (C1 < 1e-9 || C2 < 1e-9) { h = h1 + h2; } else { @@ -14671,22 +14772,25 @@ double icmCIE2Ksq(double *Lab0, double *Lab1) { } h *= 0.5; } + T = 1.0 - 0.17 * cos(DEG2RAD(h-30.0)) + 0.24 * cos(DEG2RAD(2.0 * h)) + 0.32 * cos(DEG2RAD(3.0 * h + 6.0)) - 0.2 * cos(DEG2RAD(4.0 * h - 63.0)); - hh = (h - 275.0)/25.0; - ddeg = 30.0 * exp(-hh * hh); - C7 = pow(C,7.0); - RC = 2.0 * sqrt(C7/(C7 + 6103515625.0)); L50sq = (L - 50.0) * (L - 50.0); + SL = 1.0 + (0.015 * L50sq)/sqrt(20.0 + L50sq); SC = 1.0 + 0.045 * C; SH = 1.0 + 0.015 * C * T; - RT = -sin(DEG2RAD(2 * ddeg)) * RC; dLsq = dL/SL; dCsq = dC/SC; dHsq = dH/SH; + hh = (h - 275.0)/25.0; + ddeg = 30.0 * exp(-hh * hh); + C7 = pow(C, 7.0); + RC = 2.0 * sqrt(C7/(C7 + 6103515625.0)); + RT = -sin(DEG2RAD(2 * ddeg)) * RC; + RCH = RT * dCsq * dHsq; dLsq *= dLsq; @@ -14719,35 +14823,23 @@ ICCLIB_API double icmXYZCIE2K(icmXYZNumber *w, double *in0, double *in1) { /* - - - - - - - - - - - - - - - - - - - - - - - - */ -/* Chromatic adaptation transform utility */ +/* Independent 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 flag, Transform given matrix flag */ icmXYZNumber d_wp, /* Destination white point */ icmXYZNumber s_wp, /* Source white point */ double mat[3][3] /* Destination matrix */ ) { double dst[3], src[3]; /* Source & destination white points */ double vkmat[3][3]; /* Von Kries matrix */ -#ifdef NEVER - static double bradford[3][3] = { /* Linear cone space matrix */ - { 1.0, 0.0, 0.0 }, - { 0.0, 1.0, 0.0 }, - { 0.0, 0.0, 1.0 } - }; -#endif /* NEVER */ - static double bradford[3][3] = { /* Bradford cone space matrix */ - { 0.8951, 0.2664, -0.1614 }, - { -0.7502, 1.7135, 0.0367 }, - { 0.0389, -0.0685, 1.0296 } - }; static int inited = 0; /* Compute inverse bradford once */ static double ibradford[3][3]; /* Inverse Bradford */ - /* Set initial matrix to unity */ + /* Set initial matrix to unity if creating from scratch */ if (!(flags & ICM_CAM_MULMATRIX)) { icmSetUnity3x3(mat); } @@ -14756,8 +14848,8 @@ void icmChromAdaptMatrix( icmXYZ2Ary(dst, d_wp); if (flags & ICM_CAM_BRADFORD) { - icmMulBy3x3(src, bradford, src); - icmMulBy3x3(dst, bradford, dst); + icmMulBy3x3(src, icmBradford, src); + icmMulBy3x3(dst, icmBradford, dst); } /* Setup the Von Kries white point adaptation matrix */ @@ -14770,7 +14862,7 @@ void icmChromAdaptMatrix( /* Transform to Bradford space if requested */ if (flags & ICM_CAM_BRADFORD) { - icmMul3x3(mat, bradford); + icmMul3x3(mat, icmBradford); } /* Apply chromatic adaptation */ @@ -14779,7 +14871,7 @@ void icmChromAdaptMatrix( /* Transform from Bradford space */ if (flags & ICM_CAM_BRADFORD) { if (inited == 0) { - icmInverse3x3(ibradford, bradford); + icmInverse3x3(ibradford, icmBradford); inited = 1; } icmMul3x3(mat, ibradford); @@ -14788,68 +14880,237 @@ void icmChromAdaptMatrix( /* We're done */ } +/* Setup the wpchtmx appropriately for creating profile */ +static void icc_setup_wpchtmx(icc *p) { + int useBradford = 1; /* Default use Bradford */ + + if (!p->autoWpchtmx) + return; /* Reading profile has set wpchtmx[][] */ + + /* If we should use ICC standard Wrong Von Kries for white point chromatic adapation */ + if (p->header->deviceClass == icSigOutputClass + && p->useLinWpchtmx) { + useBradford = 0; + } + + if (useBradford) { + icmCpy3x3(p->wpchtmx, icmBradford); + icmInverse3x3(p->iwpchtmx, p->wpchtmx); + } else { + icmCpy3x3(p->wpchtmx, icmWrongVonKries); + icmCpy3x3(p->iwpchtmx, icmWrongVonKries); + } + + /* This is set for this profile class now */ + p->wpchtmx_class = p->header->deviceClass; +} + +/* icc Chromatic adaptation transform utility using */ +/* the current Absolute to Media Relative Transformation Space wpchtmx. */ +/* Return a 3x3 chromatic adaptation matrix */ +/* Use icmMulBy3x3(dst, mat, src) */ +/* NOTE that to transform primaries they */ +/* must be mat[XYZ][RGB] format! */ +/* NOTE that this resets the chadValid flag (i.e. we assume that if */ +/* this method gets called, that we are discarding any 'chad' tag */ +/* and creating our own chromatic adapation) */ +static void icc_chromAdaptMatrix( + icc *p, + int flags, /* Transform given matrix flag */ + icmXYZNumber d_wp, /* Destination white point */ + icmXYZNumber s_wp, /* Source white point */ + double mat[3][3] /* Destination matrix */ +) { + double dst[3], src[3]; /* Source & destination white points */ + double vkmat[3][3]; /* Von Kries matrix */ + + if (p->header->deviceClass == icMaxEnumClass) { + fprintf(stderr,"icc_chromAdaptMatrix called with no deviceClass!\n"); + } + + /* See if the profile type has changed, re-evaluate wpchtmx */ + if (p->wpchtmx_class != p->header->deviceClass) { + icc_setup_wpchtmx(p); + } + + /* Set initial matrix to unity if creating from scratch */ + if (!(flags & ICM_CAM_MULMATRIX)) { + icmSetUnity3x3(mat); + } + + /* Take a copy of src/dst */ + icmXYZ2Ary(src, s_wp); + icmXYZ2Ary(dst, d_wp); + + /* Transform src/dst to cone space */ + icmMulBy3x3(src, p->wpchtmx, src); + icmMulBy3x3(dst, p->wpchtmx, dst); + + /* Transform incoming matrix cone space */ + icmMul3x3(mat, p->wpchtmx); + + /* Setup the Von Kries white point adaptation matrix */ + vkmat[0][0] = dst[0]/src[0]; + vkmat[1][1] = dst[1]/src[1]; + vkmat[2][2] = dst[2]/src[2]; + vkmat[0][1] = vkmat[0][2] = 0.0; + vkmat[1][0] = vkmat[1][2] = 0.0; + vkmat[2][0] = vkmat[2][1] = 0.0; + + /* Apply chromatic adaptation */ + icmMul3x3(mat, vkmat); + + /* Transform from con space */ + icmMul3x3(mat, p->iwpchtmx); + + p->chadValid = 0; /* Don't use this now */ + + /* We're done */ +} + /* - - - - - - - - - - - - - - - - - - - - - - - - */ -/* RGB primaries device to RGB->XYZ transform matrix. */ +/* RGB XYZ primaries device to RGB->XYZ transform matrix. */ /* We assume that the device is perfectly additive, but that */ /* there may be a scale factor applied to the channels to */ /* match the white point at RGB = 1. */ - +/* Use icmMulBy3x3(dst, mat, src) */ /* 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] */ +int icmRGBXYZprim2matrix( + double red[3], /* Red colorant */ + double green[3], /* Green colorant */ + double blue[3], /* Blue colorant */ + double white[3], /* White point */ + double mat[3][3] /* Destination matrix[RGB][XYZ] */ ) { double tmat[3][3]; double t[3]; /* Assemble the colorants into a matrix */ - tmat[0][0] = red.X; - tmat[0][1] = green.X; - tmat[0][2] = blue.X; - tmat[1][0] = red.Y; - tmat[1][1] = green.Y; - tmat[1][2] = blue.Y; - tmat[2][0] = red.Z; - tmat[2][1] = green.Z; - tmat[2][2] = blue.Z; + tmat[0][0] = red[0]; + tmat[0][1] = green[0]; + tmat[0][2] = blue[0]; + tmat[1][0] = red[1]; + tmat[1][1] = green[1]; + tmat[1][2] = blue[1]; + tmat[2][0] = red[2]; + tmat[2][1] = green[2]; + tmat[2][2] = blue[2]; /* Compute the inverse */ if (icmInverse3x3(mat, tmat)) return 1; /* Compute scale vector that maps colorants to white point */ - t[0] = mat[0][0] * white.X - + mat[0][1] * white.Y - + mat[0][2] * white.Z; - t[1] = mat[1][0] * white.X - + mat[1][1] * white.Y - + mat[1][2] * white.Z; - t[2] = mat[2][0] * white.X - + mat[2][1] * white.Y - + mat[2][2] * white.Z; + t[0] = mat[0][0] * white[0] + + mat[0][1] * white[1] + + mat[0][2] * white[2]; + t[1] = mat[1][0] * white[0] + + mat[1][1] * white[1] + + mat[1][2] * white[2]; + t[2] = mat[2][0] * white[0] + + mat[2][1] * white[1] + + mat[2][2] * white[2]; /* Now formulate the transform matrix */ - mat[0][0] = red.X * 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[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]; + mat[0][0] = red[0] * t[0]; + mat[0][1] = green[0] * t[1]; + mat[0][2] = blue[0] * t[2]; + mat[1][0] = red[1] * t[0]; + mat[1][1] = green[1] * t[1]; + mat[1][2] = blue[1] * t[2]; + mat[2][0] = red[2] * t[0]; + mat[2][1] = green[2] * t[1]; + mat[2][2] = blue[2] * t[2]; return 0; } +/* RGB Yxy primaries to device to RGB->XYZ transform matrix */ +/* Return non-zero if matrix would be singular */ +/* Use icmMulBy3x3(dst, mat, src) */ +int icmRGBYxyprim2matrix( + double red[3], /* Red colorant */ + double green[3], /* Green colorant */ + double blue[3], /* Blue colorant */ + double white[3], /* White point */ + double mat[3][3], /* Return matrix[RGB][XYZ] */ + double wXYZ[3] /* Return white XYZ */ +) { + double r[3], g[3], b[3]; + + icmYxy2XYZ(r, red); + icmYxy2XYZ(g, green); + icmYxy2XYZ(b, blue); + icmYxy2XYZ(wXYZ, white); + + return icmRGBXYZprim2matrix(r, g, b, wXYZ, mat); +} + /* - - - - - - - - - - - - - - - - - - - - - - - - */ +/* Pre-round a 3x3 matrix to ensure that the product of */ +/* the matrix and the input value is the same as */ +/* the quantized matrix product. This is used to improve accuracy */ +/* of 'chad' tag in computing absolute white point. */ +void quantize3x3S15Fixed16( + double targ[3], /* Target of product */ + double mat[3][3], /* matrix[][] to be quantized */ + double in[3] /* Input of product - must not be 0.0! */ +) { + int i, j; + double sum[3]; /* == target */ + double tmp[3]; /* Uncorrected sum */ + + printf("In = %.8f %.8f %.8f\n",in[0], in[1], in[2]); + printf("Target = %.8f %.8f %.8f\n",targ[0], targ[1], targ[2]); + + for (j = 0; j < 3; j++) + sum[j] = targ[j]; + + /* Pre-quantize the matrix, and then ensure that the */ + /* sum of the product 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 matrix component */ + for (j = 0; j < 3; j++) { + if (fabs(mat[i][j]) > bval) { /* Locate largest */ + bix = j; + bval = fabs(mat[i][j]); + } + mat[i][j] = round_S15Fixed16Number(mat[i][j]); + } + + /* Check the product of the uncorrected quantized values */ + tmp[i] = 0.0; + for (j = 0; j < 3; j++) + tmp[i] += mat[i][j] * in[j]; + + /* Compute the value the largest has to be */ + /* to ensure that sum of the quantized mat[][] times in[] is */ + /* equal to the quantized sum. */ + for (j = 0; j < 3; j++) { + if (j == bix) + continue; + sum[i] -= mat[i][j] * in[j]; + } + mat[i][bix] = round_S15Fixed16Number(sum[i]/in[i]); + + /* Check the product of the corrected quantized values */ + sum[i] = 0.0; + for (j = 0; j < 3; j++) + sum[i] += mat[i][j] * in[j]; + } + printf("Q Sum = %.8f %.8f %.8f\n",tmp[0], tmp[1], tmp[2]); + printf("Q cor Sum = %.8f %.8f %.8f\n",sum[0], sum[1], sum[2]); +} + /* Pre-round RGB device primary values to ensure that */ /* the sum of the quantized primaries is the same as */ /* the quantized sum. */ +/* [Note matrix is transposed compared to quantize3x3S15Fixed16() ] */ void quantizeRGBprimsS15Fixed16( double mat[3][3] /* matrix[RGB][XYZ] */ ) { @@ -14897,7 +15158,7 @@ void quantizeRGBprimsS15Fixed16( // for (j = 0; j < 3; j++) // sum[i] += mat[j][i]; } -// printf("Q Sum = %f %f %f\n",sum[0], sum[1], sum[2]); +// printf("Q cor Sum = %f %f %f\n",sum[0], sum[1], sum[2]); } /* - - - - - - - - - - - - - - - - - - - - - - - - */ @@ -15062,19 +15323,20 @@ 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 */ +/* (This is for digital Rec709 and is very close to analog Rec709 60Hz.) */ /* [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[1] = 1.0/1.8556 * -0.2126 * in[0] + + 1.0/1.8556 * -0.7152 * in[1] + + 1.0/1.8556 * (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]; + tt[2] = 1.0/1.5748 * (1.0-0.2126) * in[0] + + 1.0/1.5748 * -0.7152 * in[1] + + 1.0/1.5748 * -0.0722 * in[2]; out[0] = tt[0]; out[1] = tt[1]; @@ -15083,13 +15345,14 @@ 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 */ +/* (This is for digital Rec709 and is very close to analog Rec709 60Hz.) */ /* [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]; + tt[0] = 1.000000000 * in[0] + 0.000000000 * in[1] + 1.574800000 * in[2]; + tt[1] = 1.000000000 * in[0] + -0.187324273 * in[1] + -0.468124273 * in[2]; + tt[2] = 1.000000000 * in[0] + 1.855600000 * in[1] + 0.000000000 * in[2]; out[0] = tt[0]; out[1] = tt[1]; @@ -15098,19 +15361,20 @@ 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 */ +/* (This is for analog Rec709 50Hz) */ /* [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] + 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]; + + 0.713 * -0.587 * in[1] + + 0.713 * -0.114 * in[2]; out[0] = tt[0]; out[1] = tt[1]; @@ -15119,6 +15383,7 @@ 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 */ +/* (This is for analog Rec709 50Hz) */ /* [Inverse of above] */ void icmRec709_50_YPbPr_2_RGBd(double out[3], double in[3]) { double tt[3]; @@ -15141,13 +15406,13 @@ void icmRec2020_NCL_RGBd_2_YPbPr(double out[3], double in[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] + 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]; + + 1/1.4746 * -0.6780 * in[1] + + 1/1.4746 * -0.0593 * in[2]; out[0] = tt[0]; out[1] = tt[1]; @@ -15913,12 +16178,44 @@ struct _icmLuBase *lup lup->blackisassumed = 0; /* The black is from the tag */ } - /* Create absolute <-> relative conversion matricies */ - icmChromAdaptMatrix(ICM_CAM_BRADFORD, lup->whitePoint, icmD50, lup->toAbs); - icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, lup->whitePoint, lup->fromAbs); - DBLLL(("toAbs and fromAbs created from wp %f %f %f and D50 %f %f %f\n", lup->whitePoint.X, lup->whitePoint.Y, lup->whitePoint.Z, icmD50.X, icmD50.Y, icmD50.Z)); - DBLLL(("toAbs = %f %f %f\n %f %f %f\n %f %f %f\n", lup->toAbs[0][0], lup->toAbs[0][1], lup->toAbs[0][2], lup->toAbs[1][0], lup->toAbs[1][1], lup->toAbs[1][2], lup->toAbs[2][0], lup->toAbs[2][1], lup->toAbs[2][2])); - DBLLL(("fromAbs = %f %f %f\n %f %f %f\n %f %f %f\n", lup->fromAbs[0][0], lup->fromAbs[0][1], lup->fromAbs[0][2], lup->fromAbs[1][0], lup->fromAbs[1][1], lup->fromAbs[1][2], lup->fromAbs[2][0], lup->fromAbs[2][1], lup->fromAbs[2][2])); + /* If this is a Display profile, check if there is a 'chad' tag, and setup the */ + /* white point and toAbs/fromAbs matricies from that, so as to implement an */ + /* effective Absolute Colorimetric intent for such profiles. */ + if (p->header->deviceClass == icSigDisplayClass + && p->chadValid) { + double wp[3]; + + icmCpy3x3(lup->fromAbs, p->chadmx); + icmInverse3x3(lup->toAbs, lup->fromAbs); + + /* Compute absolute white point. We deliberately ignore what's in the white point tag */ + /* and assume D50, since dealing with a non-D50 white point tag is contrary to ICCV4 */ + /* and full of ambiguity (i.e. is it a separate "media" white different to the */ + /* display white and not D50, or has the profile creator mistakenly put the display */ + /* white in the white point tag ?) */ + icmMulBy3x3(wp, lup->toAbs, icmD50_ary3); + icmAry2XYZ(lup->whitePoint, wp); + + DBLLL(("toAbs and fromAbs created from 'chad' tag\n")); + DBLLL(("computed wp %.8f %.8f %.8f\n", lup->whitePoint.X, + lup->whitePoint.Y, lup->whitePoint.Z)); + + } else { + /* Create absolute <-> relative conversion matricies */ + p->chromAdaptMatrix(p, ICM_CAM_NONE, lup->whitePoint, icmD50, lup->toAbs); + p->chromAdaptMatrix(p, ICM_CAM_NONE, icmD50, lup->whitePoint, lup->fromAbs); + DBLLL(("toAbs and fromAbs created from wp %f %f %f and D50 %f %f %f\n", lup->whitePoint.X, + lup->whitePoint.Y, lup->whitePoint.Z, icmD50.X, icmD50.Y, icmD50.Z)); + } + + DBLLL(("toAbs = %f %f %f\n %f %f %f\n %f %f %f\n", + lup->toAbs[0][0], lup->toAbs[0][1], lup->toAbs[0][2], + lup->toAbs[1][0], lup->toAbs[1][1], lup->toAbs[1][2], + lup->toAbs[2][0], lup->toAbs[2][1], lup->toAbs[2][2])); + DBLLL(("fromAbs = %f %f %f\n %f %f %f\n %f %f %f\n", + lup->fromAbs[0][0], lup->fromAbs[0][1], lup->fromAbs[0][2], + lup->fromAbs[1][0], lup->fromAbs[1][1], lup->fromAbs[1][2], + lup->fromAbs[2][0], lup->fromAbs[2][1], lup->fromAbs[2][2])); return 0; } @@ -16925,6 +17222,8 @@ static int icmLuLut_in_abs(icmLuLut *p, double *out, double *in) { /* If Bwd Lut, take care of Absolute color space and effective input space */ if ((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview) + && (p->e_inSpace == icSigLabData + || p->e_inSpace == icSigXYZData) && (p->intent == icAbsoluteColorimetric || p->intent == icmAbsolutePerceptual || p->intent == icmAbsoluteSaturation)) { @@ -17022,6 +17321,8 @@ static int icmLuLut_out_abs(icmLuLut *p, double *out, double *in) { /* If Fwd Lut, take care of Absolute color space */ /* and convert from native to effective out PCS */ if ((p->function == icmFwd || p->function == icmPreview) + && (p->outSpace == icSigLabData + || p->outSpace == icSigXYZData) && (p->intent == icAbsoluteColorimetric || p->intent == icmAbsolutePerceptual || p->intent == icmAbsoluteSaturation)) { @@ -17253,6 +17554,8 @@ static int icmLuLut_inv_out_abs(icmLuLut *p, double *out, double *in) { /* and convert from effective to native inverse output PCS */ /* OutSpace must be PCS: XYZ or Lab */ if ((p->function == icmFwd || p->function == icmPreview) + && (p->e_outSpace == icSigLabData + || p->e_outSpace == icSigXYZData) && (p->intent == icAbsoluteColorimetric || p->intent == icmAbsolutePerceptual || p->intent == icmAbsoluteSaturation)) { @@ -17385,6 +17688,8 @@ static int icmLuLut_inv_in_abs(icmLuLut *p, double *out, double *in) { /* If Bwd Lut, take care of Absolute color space, and */ /* convert from native to effective input space */ if ((p->function == icmBwd || p->function == icmGamut || p->function == icmPreview) + && (p->inSpace == icSigLabData + || p->inSpace == icSigXYZData) && (p->intent == icAbsoluteColorimetric || p->intent == icmAbsolutePerceptual || p->intent == icmAbsoluteSaturation)) { @@ -17667,9 +17972,6 @@ icc_new_icmLuLut( return NULL; } -// ~~~999 -#ifndef NEVER - /* Standard code */ /* Determine appropriate clut lookup algorithm */ { int use_sx; /* -1 = undecided, 0 = N-linear, 1 = Simplex lookup */ @@ -17795,64 +18097,6 @@ icc_new_icmLuLut( p->lut->tune_value = icmLut_tune_value_nl; } } -#else /* Development code */ - /* Determine appropriate clut lookup algorithm, */ - /* and set optimised simplex tables */ - { - int lualg = 0; /* 0 = simplex, 1 = multi-linear, 2 = optimised simlpex */ - icColorSpaceSignature ins, outs; /* In and out Lut color spaces */ - int inn, outn; /* in and out number of Lut components */ - - p->lutspaces((icmLuBase *)p, &ins, &inn, &outs, &outn, NULL); - - /* Determine which type of Lut lookup algorithm is likely to give */ - /* minimal interpolation errors. */ - /* To use the optimised simplex, we should ideally determine */ - /* the simplex cell orientation that makes the simplex split diagonal */ - /* always point towards the white or black points. */ - switch(ins) { - - case icSigXYZData: /* This seems to be appropriate ? */ - case icSigRgbData: - case icSigGrayData: - case icSigCmykData: - case icSigCmyData: - case icSigMch6Data: - lualg = 0; /* Simplex interpolation is appropriate */ - break; - - case icSigLabData: -// ~~~~9999 this isn't right! need to lookup Lab 50,0,0 through input curves then */ -/* quantize to clut nodes to figure threshold for axis flips */ - p->lut->finfo[0].fth = 0.5; p->lut->finfo[0].bthff = 0; p->lut->finfo[0].athff = 1; - p->lut->finfo[1].fth = 0.5; p->lut->finfo[1].bthff = 1; p->lut->finfo[1].athff = 0; - p->lut->finfo[2].fth = 0.5; p->lut->finfo[2].bthff = 1; p->lut->finfo[2].athff = 0; - lualg = 2; - break; - - /* !!! Should switch to optimised simplex for these too !!! */ - case icSigLuvData: - case icSigYCbCrData: - case icSigYxyData: - case icSigHlsData: - case icSigHsvData: - lualg = 1; /* Multi-linear is best as a default for these ? */ - break; - - default: - lualg = 0; /* Simplex is best if we known nothing. */ - break; - } - - if (lualg == 2) { - p->lookup_clut = icmLut_lookup_clut_osx; - } else if (lualg == 1) { - p->lookup_clut = p->lut->lookup_clut_nl; - } else { - p->lookup_clut = p->lut->lookup_clut_sx; - } - } -#endif return (icmLuBase *)p; } @@ -18591,6 +18835,7 @@ icmAlloc *al /* Memory allocator */ p->get_tac = icm_get_tac; p->get_luobj = icc_get_luobj; p->new_clutluobj = icc_new_icmLuLut; + p->chromAdaptMatrix = icc_chromAdaptMatrix; #if defined(__IBMC__) && defined(_M_IX86) _control87(EM_UNDERFLOW, EM_UNDERFLOW); @@ -18609,10 +18854,10 @@ icmAlloc *al /* Memory allocator */ p->header->renderingIntent = icMaxEnumIntent; /* Rendering intent - must be set ! */ /* Values that should be set before writing */ - p->header->manufacturer = -1; /* Dev manufacturer - should be set ! */ - p->header->model = -1; /* Dev model number - should be set ! */ - p->header->attributes.l = 0; /* ICC Device attributes - should set ! */ - p->header->flags = 0; /* Embedding flags - should be set ! */ + p->header->manufacturer = icmSigUnknownType;/* Dev manufacturer - should be set ! */ + p->header->model = icmSigUnknownType; /* Dev model number - should be set ! */ + p->header->attributes.l = 0; /* ICC Device attributes - should set ! */ + p->header->flags = 0; /* Embedding flags - should be set ! */ /* Values that may be set before writing */ p->header->attributes.h = 0; /* Dev Device attributes - may be set ! */ @@ -18624,13 +18869,53 @@ icmAlloc *al /* Memory allocator */ p->header->minv = 2; p->header->bfv = 0; setcur_DateTimeNumber(&p->header->date);/* Creation Date */ +#ifdef NT p->header->platform = icSigMicrosoft; /* Primary Platform */ +#endif +#ifdef __APPLE__ + p->header->platform = icSigMacintosh; +#endif +#if defined(UNIX) && !defined(__APPLE__) + p->header->platform = icmSig_nix; +#endif p->header->illuminant = icmD50; /* Profile illuminant - D50 */ /* Values that will be created automatically */ for (i = 0; i < 16; i++) p->header->id[i] = 0; + p->autoWpchtmx = 1; /* Auto on create */ + + /* Should we use ICC standard Wrong Von Kries for */ + /* white point chromatic adapation for output class ? */ + if (getenv("ARGYLL_CREATE_WRONG_VON_KRIES_OUTPUT_CLASS_REL_WP") != NULL) + p->useLinWpchtmx = 1; /* Use Wrong Von Kries */ + else + p->useLinWpchtmx = 0; /* Use Bradford by default */ + p->wpchtmx_class = icMaxEnumClass; /* Not set yet */ + + /* Default to saving ArgyllCMS private 'arts' tag (if appropriate type of */ + /* profile) to make white point chromatic adapation explicit. */ + p->useArts = 1; + + /* Should we create a V4 style Display profile with D50 media white point */ + /* tag and 'chad' tag ? */ + if (getenv("ARGYLL_CREATE_DISPLAY_PROFILE_WITH_CHAD") != NULL) + p->useChad = 1; /* Mark Media WP as D50 and put absolute to relative */ + /* transform matrix in 'chad' tag. */ + else + p->useChad = 0; /* No by default - use Bradford and store real Media WP */ + + /* Set a default wpchtmx in case the profile being read or written */ + /* doesn't use a white point (i.e., it's a device link) */ + if (!p->useLinWpchtmx) { + icmCpy3x3(p->wpchtmx, icmBradford); + icmInverse3x3(p->iwpchtmx, p->wpchtmx); + } else { + icmCpy3x3(p->wpchtmx, icmWrongVonKries); + icmCpy3x3(p->iwpchtmx, icmWrongVonKries); + } + return p; } @@ -18677,7 +18962,7 @@ char *icmPdv(int di, double *p) { for (e = 0; e < di; e++) { if (e > 0) *bp++ = ' '; - sprintf(bp, "%f", p[e]); bp += strlen(bp); + sprintf(bp, "%.8f", p[e]); bp += strlen(bp); } return buf[ix]; } @@ -18700,7 +18985,7 @@ char *icmPfv(int di, float *p) { for (e = 0; e < di; e++) { if (e > 0) *bp++ = ' '; - sprintf(bp, "%f", p[e]); bp += strlen(bp); + sprintf(bp, "%.8f", p[e]); bp += strlen(bp); } return buf[ix]; } diff --git a/icc/icc.h b/icc/icc.h index ba66581..8f90d26 100644 --- a/icc/icc.h +++ b/icc/icc.h @@ -30,8 +30,8 @@ /* Version of icclib release */ -#define ICCLIB_VERSION 0x020017 -#define ICCLIB_VERSION_STR "2.17" +#define ICCLIB_VERSION 0x020020 +#define ICCLIB_VERSION_STR "2.20" #undef ENABLE_V4 /* V4 is not fully implemented */ @@ -61,19 +61,22 @@ # define ICCLIB_API /* empty */ #endif +#if UINT_MAX < 0xffffffff +# error "icclib: integer size is too small, must be at least 32 bit" +#endif + /* =========================================================== */ /* Platform specific primitive defines. */ /* This really needs checking for each different platform. */ /* Using C99 and MSC covers a lot of cases, */ /* and the fallback default is pretty reliable with modern compilers and machines. */ +/* Note that MSWin is LLP64 == 32 bit long, while OS X/Linux is LP64 == 64 bit long. */ +/* so long shouldn't really be used in any code.... */ +/* (duplicated in icc.h) */ -/* Note that the code assume that int is at least 32 bits, */ -/* and avoid using long as it is uncertain whether this is */ -/* the same size as an int or larger, opening the scope */ -/* for oveflow errors. */ - -#if UINT_MAX < 0xffffffff -# error "icclib: integer size is too small, must be at least 32 bit" +/* Use __LP64__ as cross platform 64 bit pointer #define */ +#if !defined(__LP64__) && defined(_WIN64) +# define __LP64__ 1 #endif #ifndef ORD32 /* If not defined elsewhere */ @@ -93,13 +96,15 @@ #define PNTR intptr_t -/* printf format precision specifier */ -#define PF64PREC "ll" +#define PF64PREC "ll" /* printf format precision specifier */ +#define CF64PREC "LL" /* Constant precision specifier */ -/* Constant precision specifier */ -#define CF64PREC "LL" +#ifndef ATTRIBUTE_NORETURN +# define ATTRIBUTE_NORETURN __declspec(noreturn) +#endif #else /* !__STDC_VERSION__ */ + #ifdef _MSC_VER #define INR8 __int8 /* 8 bit signed */ @@ -115,16 +120,17 @@ #define vsnprintf _vsnprintf -/* printf format precision specifier */ -#define PF64PREC "I64" +#define PF64PREC "I64" /* printf format precision specifier */ +#define CF64PREC "LL" /* Constant precision specifier */ -/* Constant precision specifier */ -#define CF64PREC "LL" +#ifndef ATTRIBUTE_NORETURN +# define ATTRIBUTE_NORETURN __declspec(noreturn) +#endif #else /* !_MSC_VER */ /* The following works on a lot of modern systems, including */ -/* LP64 model 64 bit modes */ +/* LLP64 and LP64 models, but won't work with ILP64 which needs int32 */ #define INR8 signed char /* 8 bit signed */ #define INR16 signed short /* 16 bit signed */ @@ -134,19 +140,28 @@ #define ORD32 unsigned int /* 32 bit unsigned */ #ifdef __GNUC__ -#define INR64 long long /* 64 bit signed - not used in icclib */ -#define ORD64 unsigned long long /* 64 bit unsigned - not used in icclib */ +# ifdef __LP64__ /* long long could be 128 bit */ +# define INR64 long /* 64 bit signed - not used in icclib */ +# define ORD64 unsigned long /* 64 bit unsigned - not used in icclib */ +# define PF64PREC "l" /* printf format precision specifier */ +# define CF64PREC "L" /* Constant precision specifier */ +# else +# define INR64 long long /* 64 bit signed - not used in icclib */ +# define ORD64 unsigned long long /* 64 bit unsigned - not used in icclib */ +# define PF64PREC "ll" /* printf format precision specifier */ +# define CF64PREC "LL" /* Constant precision specifier */ +# endif /* !__LP64__ */ +#endif /* __GNUC__ */ -/* printf format precision specifier */ -#define PF64PREC "ll" +#define PNTR unsigned long +#ifndef ATTRIBUTE_NORETURN +# define ATTRIBUTE_NORETURN __attribute__((noreturn)) #endif -#define PNTR unsigned long - #endif /* !_MSC_VER */ #endif /* !__STDC_VERSION__ */ -#endif /* !defined(ORD32) */ +#endif /* !ORD32 */ /* =========================================================== */ #include "iccV42.h" /* ICC Version 4.2 definitions. */ @@ -321,6 +336,15 @@ icmFile *new_icmFileMem_d(void *base, size_t length); typedef int icmSig; /* Otherwise un-enumerated 4 byte signature */ +/* ArgyllCMS Private tagSignature: */ +/* Absolute to Media Relative Transformation Space matrix. */ +/* Uses the icSigS15Fixed16ArrayType */ +/* (Unit matrix for default ICC, Bradford matrix for Argll default) */ +#define icmSigAbsToRelTransSpace ((icTagSignature) icmMakeTag('a','r','t','s')) + +/* Non-standard Platform Signature for Linux and similiar */ +#define icmSig_nix ((icPlatformSignature) icmMakeTag('*','n','i','x')) + /* Non-standard Color Space Signatures - will be incompatible outside icclib! */ /* A monochrome CIE L* space */ @@ -353,10 +377,6 @@ typedef int icmSig; /* Otherwise un-enumerated 4 byte signature */ /* Pseudo PCS colospace to signal V4 16 bit L */ #define icmSigLV4Data ((icColorSpaceSignature) icmMakeTag('L',' ',' ','4')) -/* Non-standard Platform Signature */ -#define icmSig_nix ((icPlatformSignature) icmMakeTag('*','n','i','x')) - - /* Alias for icSigColorantTableType found in LOGO profiles (Byte swapped clrt !) */ #define icmSigAltColorantTableType ((icTagTypeSignature)icmMakeTag('t','r','l','c')) @@ -1095,6 +1115,7 @@ struct _icmVideoCardGamma { /* ------------------------------------------------- */ /* The Profile header */ +/* NOTE that the deviceClass should be set up before calling chromAdaptMatrix()! */ struct _icmHeader { /* Private: */ @@ -1471,7 +1492,14 @@ struct _icc { /* Returns 0 if deleted OK */ int (*check_id)(struct _icc *p, ORD8 *id); /* Returns 0 if ID is OK, 1 if not present etc. */ double (*get_tac)(struct _icc *p, double *chmax, /* Returns total ink limit and channel maximums */ - void (*calfunc)(void *cntx, double *out, double *in), void *cntx); /* optional cal. lookup */ + void (*calfunc)(void *cntx, double *out, double *in), void *cntx); + /* optional cal. lookup */ + void (*chromAdaptMatrix)(struct _icc *p, int flags, icmXYZNumber d_wp, + icmXYZNumber s_wp, double mat[3][3]); + /* Chromatic transform function that uses icc */ + /* Absolute to Media Relative Transformation Space matrix */ + /* Set header->deviceClass before calling this! */ + /* Get a particular color conversion function */ icmLuBase * (*get_luobj) (struct _icc *p, @@ -1503,6 +1531,26 @@ struct _icc { int allowclutPoints256; /* Non standard - allow 256 res cLUT */ + int autoWpchtmx; /* Whether to automatically set wpchtmx[][] based on */ + /* the header and the state of the env override */ + /* ARGYLL_CREATE_WRONG_VON_KRIES_OUTPUT_CLASS_REL_WP. */ + /* Default true, set false on reading a profile. */ + int useLinWpchtmx; /* Force Wrong Von Kries for output class (default false) */ + icProfileClassSignature wpchtmx_class; /* Class of profile wpchtmx was set for */ + double wpchtmx[3][3]; /* Absolute to Media Relative Transformation Space matrix */ + double iwpchtmx[3][3]; /* Inverse of wpchtmx[][] */ + /* (Default is Bradford matrix) */ + int useArts; /* Save ArgyllCMS private 'arts' tag (default yes) */ + /* (This creates 'arts' tag on writing) */ + + int chadValid; /* nz if 'chad' tag has been read and chadmx is valid */ + double chadmx[3][3]; /* 'chad' tag matrix if read */ + int useChad; /* Create 'chad' tag for Display profile (default no) */ + /* Override with ARGYLL_CREATE_DISPLAY_PROFILE_WITH_CHAD */ + /* (This set media white point tag to D50 and */ + /* creates 'chad' tag on writing) */ + + /* Private: ? */ icmAlloc *al; /* Heap allocator */ int del_al; /* NZ if heap allocator should be deleted */ @@ -1923,19 +1971,24 @@ extern ICCLIB_API void icm1960UCS21964WUV(icmXYZNumber *w, double *out, double * /* 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 */ +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 */ +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; +/* The Standard ("wrong Von-Kries") chromatic transform matrix */ +extern double icmWrongVonKries[3][3]; + +/* The Bradford chromatic transform matrix */ +extern double icmBradford[3][3]; /* 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[]); @@ -1990,22 +2043,38 @@ int icmClipXYZ(double out[3], double in[3]); /* - - - - - - - - - - - - - - - - - - - - - - - */ -/* RGB primaries to device to RGB->XYZ transform matrix */ +/* RGB XYZ primaries to device to RGB->XYZ transform matrix */ +/* Return non-zero if matrix would be singular */ +/* Use icmMulBy3x3(dst, mat, src) */ +int icmRGBXYZprim2matrix( + double red[3], /* Red colorant */ + double green[3], /* Green colorant */ + double blue[3], /* Blue colorant */ + double white[3], /* White point */ + double mat[3][3] /* Destination matrix[RGB][XYZ] */ +); + +/* RGB Yxy 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] */ +/* Use icmMulBy3x3(dst, mat, src) */ +int icmRGBYxyprim2matrix( + double red[3], /* Red colorant */ + double green[3], /* Green colorant */ + double blue[3], /* Blue colorant */ + double white[3], /* White point */ + double mat[3][3], /* Return matrix[RGB][XYZ] */ + double wXYZ[3] /* Return white XYZ */ ); /* Chromatic Adaption transform utility */ /* Return a 3x3 chromatic adaption matrix */ /* Use icmMulBy3x3(dst, mat, src) */ +/* [ use icc->chromAdaptMatrix() to use the profiles cone space matrix] */ +#define ICM_CAM_NONE 0x0000 /* No flags */ #define ICM_CAM_BRADFORD 0x0001 /* Use Bradford sharpened response space */ -#define ICM_CAM_MULMATRIX 0x0002 /* Transform the given matrix */ +#define ICM_CAM_MULMATRIX 0x0002 /* Transform the given matrix rather than */ + /* create a transform from scratch. */ /* NOTE that to transform primaries they */ /* must be mat[XYZ][RGB] format! */ diff --git a/icc/iccstd.c b/icc/iccstd.c index 742be4d..3f7966e 100644 --- a/icc/iccstd.c +++ b/icc/iccstd.c @@ -310,7 +310,6 @@ icmAlloc *al /* heap allocator, NULL for default */ ) { icmFileStd *p; int del_al = 0; - struct stat sbuf; if (al == NULL) { /* None provided, create default */ if ((al = new_icmAllocStd()) == NULL) @@ -334,8 +333,10 @@ icmAlloc *al /* heap allocator, NULL for default */ p->get_buf = icmFileStd_get_buf; p->del = icmFileStd_delete; - if (fstat(fileno(fp), &sbuf) == 0) { - p->size = sbuf.st_size; + // fstat() is not so robust on MSWin. + if (fseek(fp, 0L, SEEK_END) == 0) { + p->size = ftell(fp); + fseek(fp, 0L, SEEK_SET); } else { p->size = 0; } diff --git a/icc/icctest.c b/icc/icctest.c index e7534fb..f0b1280 100644 --- a/icc/icctest.c +++ b/icc/icctest.c @@ -160,7 +160,7 @@ main( } /* - Would normally call icco->free(wr_icco); + Would normally call wr_icco->del(wr_icco); but leave it, so that we can verify the read. */ diff --git a/icc/lab2lab.icm b/icc/lab2lab.icm index 0b8a4b3..39a5911 100644 Binary files a/icc/lab2lab.icm and b/icc/lab2lab.icm differ diff --git a/icc/log.txt b/icc/log.txt index 7c4bd83..2e185dc 100644 --- a/icc/log.txt +++ b/icc/log.txt @@ -1,6 +1,18 @@ Change History: (See ArgyllCMS log.txt too) + 2.20 + Add better cross compatibility with non-Argyll ICC profiles: + + Use "wrong Von Kries" media white point adapation for non-Argyll non-display profile + + Optionally create "wrong Von Kries" media white point adapation profiles + using the "ARGYLL_CREATE_WRONG_VON_KRIES_OUTPUT_CLASS_REL_WP" env. variable, + while maintaining ArgyllCMS compatibility using the 'arts' tag. + + Implement proper absolute colorimetric intent for ICCV2 Display profiles + that use the ICCV4 style of have a media white od D50 and storing the + media chromatic trasnform in the 'chad' tag. + + Optionally create such ICCV4 style V2 Display profiles by using the + "ARGYLL_CREATE_DISPLAY_PROFILE_WITH_CHAD" env. variable. + 2.16 Clean up cLUT read code to make sanity checking more explicit and remove redundant code. diff --git a/icc/mcheck.c b/icc/mcheck.c index 54811c3..a392f31 100644 --- a/icc/mcheck.c +++ b/icc/mcheck.c @@ -24,6 +24,7 @@ #include #include #include +#include "vrml.h" #include "icc.h" void error(char *fmt, ...), warning(char *fmt, ...); @@ -31,24 +32,18 @@ void error(char *fmt, ...), warning(char *fmt, ...); void usage(void) { fprintf(stderr,"Check device to PCS monotonicity of a CMYK ICC file, V%s\n",ICCLIB_VERSION_STR); fprintf(stderr,"Author: Graeme W. Gill\n"); - fprintf(stderr,"usage: kcheck [-v] [-w] infile\n"); + fprintf(stderr,"usage: mcheck [-v] [-w] infile\n"); fprintf(stderr," -v verbose\n"); fprintf(stderr," -c Check just Cyan monotonicity\n"); fprintf(stderr," -m Check just Magenta monotonicity\n"); fprintf(stderr," -y Check just Yellow monotonicity\n"); fprintf(stderr," -k Check just Black monotonicity\n"); - fprintf(stderr," -w create VRML visualisation\n"); + fprintf(stderr," -w create %s visualisation\n",vrml_format()); + fprintf(stderr," -x use %s axes\n",vrml_format()); exit(1); } -#define MGR 50 /* Maximum grid resolution handled */ - - -FILE *start_vrml(char *name, int doaxes); -void start_line_set(FILE *wrl); -void add_vertex(FILE *wrl, double pp[3]); -void make_lines(FILE *wrl, int ppset); -void end_vrml(FILE *wrl); +#define MGR 50 /* Maximum grid resolution handled */ int main( @@ -73,7 +68,7 @@ main( icColorSpaceSignature ins, outs; /* Type of input and output spaces */ int inn; /* Number of input chanels */ icmLuAlgType alg; - FILE *wrl; + vrml *wrl; int dx[4]; /* Device index mapping */ int chan, cs, ce; @@ -101,7 +96,7 @@ main( if (argv[fa][1] == 'v' || argv[fa][1] == 'V') { verb = 1; } - /* VRML */ + /* VRML/X3D */ else if (argv[fa][1] == 'w' || argv[fa][1] == 'W') { dovrml = 1; } @@ -121,6 +116,9 @@ main( else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') { cchan = 3; } + else if (argv[fa][1] == 'x') { + doaxes = 1; + } else if (argv[fa][1] == '?') usage(); else @@ -136,7 +134,7 @@ main( strcpy(out_name, in_name); if ((xl = strrchr(out_name, '.')) == NULL) /* Figure where extention is */ xl = out_name + strlen(out_name); - strcpy(xl,".wrl"); + xl[0] = '\000'; /* Remove extension */ /* Open up the file for reading */ if ((rd_fp = new_icmFileStd_name(in_name,"r")) == NULL) @@ -177,8 +175,10 @@ main( } if (dovrml) { - wrl = start_vrml(out_name, doaxes); - start_line_set(wrl); + wrl = new_vrml(out_name, doaxes, vrml_lab); + if (wrl == NULL) + error("new_vrml for '%s%s' failed",out_name,vrml_ext()); + wrl->start_line_set(wrl, 0); } /* For all the device chanels chosen */ @@ -237,7 +237,7 @@ main( error ("%d, %s",rd_icco->errc,rd_icco->err); // if (dovrml) -// add_vertex(wrl, pcs[ck]); +// wrl->add_vertex(wrl, 0, pcs[ck]); } /* Compute average vector direction */ @@ -276,7 +276,7 @@ main( /* Display just the non mono threads */ if (nm && dovrml) { for (j = 0; j < gres; j++) - add_vertex(wrl, pcs[j]); + wrl->add_vertex(wrl, 0, pcs[j]); } if (verb) { printf("."); fflush(stdout); @@ -287,8 +287,8 @@ main( } if (dovrml) { - make_lines(wrl, gres); - end_vrml(wrl); + wrl->make_lines(wrl, 0, gres); + wrl->del(wrl); } /* Done with lookup object */ @@ -300,218 +300,6 @@ main( return 0; } -/* ------------------------------------------------ */ -/* Some simple functions to do basix VRML work */ - -#define GAMUT_LCENT 50.0 -static int npoints = 0; -static int paloc = 0; -static struct { double pp[3]; } *pary; - -static void Lab2RGB(double *out, double *in); - -FILE *start_vrml(char *name, int doaxes) { - FILE *wrl; - struct { - double x, y, z; - double wx, wy, wz; - double r, g, b; - } axes[5] = { - { 0, 0, 50-GAMUT_LCENT, 2, 2, 100, .7, .7, .7 }, /* L axis */ - { 50, 0, 0-GAMUT_LCENT, 100, 2, 2, 1, 0, 0 }, /* +a (red) axis */ - { 0, -50, 0-GAMUT_LCENT, 2, 100, 2, 0, 0, 1 }, /* -b (blue) axis */ - { -50, 0, 0-GAMUT_LCENT, 100, 2, 2, 0, 1, 0 }, /* -a (green) axis */ - { 0, 50, 0-GAMUT_LCENT, 2, 100, 2, 1, 1, 0 }, /* +b (yellow) axis */ - }; - int i; - - if ((wrl = fopen(name,"w")) == NULL) - error("Error opening VRML file '%s'\n",name); - - npoints = 0; - - fprintf(wrl,"#VRML V2.0 utf8\n"); - fprintf(wrl,"\n"); - fprintf(wrl,"# Created by the Argyll CMS\n"); - fprintf(wrl,"Transform {\n"); - fprintf(wrl,"children [\n"); - fprintf(wrl," NavigationInfo {\n"); - fprintf(wrl," type \"EXAMINE\" # It's an object we examine\n"); - fprintf(wrl," } # We'll add our own light\n"); - fprintf(wrl,"\n"); - fprintf(wrl," DirectionalLight {\n"); - fprintf(wrl," direction 0 0 -1 # Light illuminating the scene\n"); - fprintf(wrl," direction 0 -1 0 # Light illuminating the scene\n"); - fprintf(wrl," }\n"); - fprintf(wrl,"\n"); - fprintf(wrl," Viewpoint {\n"); - fprintf(wrl," position 0 0 340 # Position we view from\n"); - fprintf(wrl," }\n"); - fprintf(wrl,"\n"); - if (doaxes != 0) { - fprintf(wrl,"# Lab axes as boxes:\n"); - for (i = 0; i < 5; i++) { - fprintf(wrl,"Transform { translation %f %f %f\n", axes[i].x, axes[i].y, axes[i].z); - fprintf(wrl,"\tchildren [\n"); - fprintf(wrl,"\t\tShape{\n"); - fprintf(wrl,"\t\t\tgeometry Box { size %f %f %f }\n", - axes[i].wx, axes[i].wy, axes[i].wz); - fprintf(wrl,"\t\t\tappearance Appearance { material Material "); - fprintf(wrl,"{ diffuseColor %f %f %f} }\n", axes[i].r, axes[i].g, axes[i].b); - fprintf(wrl,"\t\t}\n"); - fprintf(wrl,"\t]\n"); - fprintf(wrl,"}\n"); - } - fprintf(wrl,"\n"); - } - - return wrl; -} - -void -start_line_set(FILE *wrl) { - - fprintf(wrl,"\n"); - fprintf(wrl,"Shape {\n"); - fprintf(wrl," geometry IndexedLineSet { \n"); - fprintf(wrl," coord Coordinate { \n"); - fprintf(wrl," point [\n"); -} - -void add_vertex(FILE *wrl, double pp[3]) { - - fprintf(wrl,"%f %f %f,\n",pp[1], pp[2], pp[0]-GAMUT_LCENT); - - if (paloc < (npoints+1)) { - paloc = (paloc + 10) * 2; - if (pary == NULL) - pary = malloc(paloc * 3 * sizeof(double)); - else - pary = realloc(pary, paloc * 3 * sizeof(double)); - - if (pary == NULL) - error ("Malloc failed"); - } - pary[npoints].pp[0] = pp[0]; - pary[npoints].pp[1] = pp[1]; - pary[npoints].pp[2] = pp[2]; - npoints++; -} - - -void make_lines(FILE *wrl, int ppset) { - int i, j; - - fprintf(wrl," ]\n"); - fprintf(wrl," }\n"); - fprintf(wrl," coordIndex [\n"); - - for (i = 0; i < npoints;) { - for (j = 0; j < ppset; j++, i++) { - fprintf(wrl,"%d, ", i); - } - fprintf(wrl,"-1,\n"); - } - fprintf(wrl," ]\n"); - - /* Color */ - fprintf(wrl," colorPerVertex TRUE\n"); - fprintf(wrl," color Color {\n"); - fprintf(wrl," color [ # RGB colors of each vertex\n"); - - for (i = 0; i < npoints; i++) { - double rgb[3], Lab[3]; - Lab[0] = pary[i].pp[0]; - Lab[1] = pary[i].pp[1]; - Lab[2] = pary[i].pp[2]; - Lab2RGB(rgb, Lab); - fprintf(wrl," %f %f %f,\n", rgb[0], rgb[1], rgb[2]); - } - fprintf(wrl," ] \n"); - fprintf(wrl," }\n"); - /* End color */ - - fprintf(wrl," }\n"); - fprintf(wrl,"} # end shape\n"); - -} - -void end_vrml(FILE *wrl) { - - fprintf(wrl,"\n"); - fprintf(wrl," ] # end of children for world\n"); - fprintf(wrl,"}\n"); - - if (fclose(wrl) != 0) - error("Error closing VRML file\n"); -} - - -/* Convert a gamut Lab value to an RGB value for display purposes */ -static void -Lab2RGB(double *out, double *in) { - double L = in[0], a = in[1], b = in[2]; - double x,y,z,fx,fy,fz; - double R, G, B; - - /* Scale so that black is visible */ - L = L * (100 - 40.0)/100.0 + 40.0; - - /* First convert to XYZ using D50 white point */ - if (L > 8.0) { - fy = (L + 16.0)/116.0; - y = pow(fy,3.0); - } else { - y = L/903.2963058; - fy = 7.787036979 * y + 16.0/116.0; - } - - fx = a/500.0 + fy; - if (fx > 24.0/116.0) - x = pow(fx,3.0); - else - x = (fx - 16.0/116.0)/7.787036979; - - fz = fy - b/200.0; - if (fz > 24.0/116.0) - z = pow(fz,3.0); - else - z = (fz - 16.0/116.0)/7.787036979; - - x *= 0.9642; /* Multiply by white point, D50 */ - y *= 1.0; - z *= 0.8249; - - /* Now convert to sRGB values */ - R = x * 3.2410 + y * -1.5374 + z * -0.4986; - G = x * -0.9692 + y * 1.8760 + z * 0.0416; - B = x * 0.0556 + y * -0.2040 + z * 1.0570; - - if (R < 0.0) - R = 0.0; - else if (R > 1.0) - R = 1.0; - - if (G < 0.0) - G = 0.0; - else if (G > 1.0) - G = 1.0; - - if (B < 0.0) - B = 0.0; - else if (B > 1.0) - B = 1.0; - - R = pow(R, 1.0/2.2); - G = pow(G, 1.0/2.2); - B = pow(B, 1.0/2.2); - - out[0] = R; - out[1] = G; - out[2] = B; -} - - /* ------------------------------------------------ */ /* Basic printf type error() and warning() routines */ diff --git a/icc/mkDispProf.c b/icc/mkDispProf.c index bc70154..6878aad 100644 --- a/icc/mkDispProf.c +++ b/icc/mkDispProf.c @@ -176,7 +176,7 @@ char *argv[] /* Setup the primaries */ { /* Compute primaries as XYZ */ - icmXYZNumber wrgb[4] = { /* Primaries in Yxy from the standard */ + double wrgb[4][3] = { /* 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 */ @@ -185,28 +185,49 @@ char *argv[] double mat[3][3]; int i; - /* Convert Yxy to XYZ */ - for (i = 0; i < 4; i++) { - double v[3]; + /* Convert yxy to normalised 3x3 matrix */ + icmRGBYxyprim2matrix(wrgb[1], wrgb[2], wrgb[3], wrgb[0], mat, wrgb[0]); + icmTranspose3x3(mat, mat); - 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 */ +#ifdef NEVER /* Dump XYZ */ printf("sRGB: XYZ\n"); - printf("{ %f, %f, %f }, /* Red */\n" - "{ %f, %f, %f }, /* Green */\n" - "{ %f, %f, %f }, /* Blue */\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); + wrgb[0][0], wrgb[0][1], wrgb[0][2]); +#endif + +#ifdef NEVER /* Dump xyz */ +{ + double mat2[4][3]; + double tt; + + for (i = 0; i < 3; i++) { + + tt = mat[i][0] + mat[i][1] + mat[i][2]; + mat2[i][0] = mat[i][0] / tt; + mat2[i][1] = mat[i][1] / tt; + mat2[i][2] = mat[i][2] / tt; + } + tt = wrgb[0][0] + wrgb[0][1] + wrgb[0][2]; + mat2[i][0] = wrgb[0][0] / tt; + mat2[i][1] = wrgb[0][1] / tt; + mat2[i][2] = wrgb[0][2] / tt; + + 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", + mat2[0][0], mat2[0][1], mat2[0][2], + mat2[1][0], mat2[1][1], mat2[1][2], + mat2[2][0], mat2[2][1], mat2[2][2], + mat2[3][0], mat2[3][1], mat2[3][2]); +} #endif /* White Point Tag: */ @@ -219,9 +240,9 @@ char *argv[] 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; + wo->data[0].X = wrgb[0][0]; + wo->data[0].Y = wrgb[0][1]; + wo->data[0].Z = wrgb[0][2]; } /* Black Point Tag: */ { @@ -239,12 +260,14 @@ char *argv[] } /* Red, Green and Blue Colorant Tags: */ { + icmXYZNumber white; icmXYZArray *wor, *wog, *wob; double fromAbs[3][3]; double d50m[3][3]; /* Convert to D50 adapated */ - icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, wrgb[0], fromAbs); + icmAry2XYZ(white, wrgb[0]); + wr_icco->chromAdaptMatrix(wr_icco, ICM_CAM_NONE, icmD50, white, fromAbs); icmMulBy3x3(d50m[0], fromAbs, mat[0]); icmMulBy3x3(d50m[1], fromAbs, mat[1]); icmMulBy3x3(d50m[2], fromAbs, mat[2]); diff --git a/icc/sRGB.icm b/icc/sRGB.icm index 5491330..9d33a4b 100644 Binary files a/icc/sRGB.icm and b/icc/sRGB.icm differ -- cgit v1.2.3