summaryrefslogtreecommitdiff
path: root/xicc
diff options
context:
space:
mode:
Diffstat (limited to 'xicc')
-rw-r--r--xicc/cam02.c94
-rw-r--r--xicc/cam02.h41
-rw-r--r--xicc/cam02plot.c6
-rw-r--r--xicc/cam02ref.h60
-rw-r--r--xicc/cam02test.c24
-rw-r--r--xicc/ccss.c14
-rw-r--r--xicc/iccgamut.c52
-rw-r--r--xicc/monctest.c5
-rw-r--r--xicc/moncurve.c13
-rw-r--r--xicc/mpp.h2
-rw-r--r--xicc/mpplu.c4
-rw-r--r--xicc/revfix.c18
-rw-r--r--xicc/specplot.c6
-rw-r--r--xicc/tiffgamut.c65
-rw-r--r--xicc/tiffgmts.c41
-rw-r--r--xicc/transplot.c68
-rw-r--r--xicc/xcal.c98
-rw-r--r--xicc/xcal.h7
-rw-r--r--xicc/xcam.c13
-rw-r--r--xicc/xcam.h3
-rw-r--r--xicc/xcolorantslu.c2
-rw-r--r--xicc/xfit.c261
-rw-r--r--xicc/xfit.h5
-rw-r--r--xicc/xicc.c466
-rw-r--r--xicc/xicc.h52
-rw-r--r--xicc/xicclu.c774
-rw-r--r--xicc/xlut.c73
-rw-r--r--xicc/xlutfix.c9
-rw-r--r--xicc/xmatrix.c205
-rw-r--r--xicc/xmono.c2
-rw-r--r--xicc/xspect.c2452
-rw-r--r--xicc/xspect.h78
32 files changed, 4023 insertions, 990 deletions
diff --git a/xicc/cam02.c b/xicc/cam02.c
index 1ab6abc..3684a78 100644
--- a/xicc/cam02.c
+++ b/xicc/cam02.c
@@ -39,6 +39,21 @@
/* Note that all whites are assumed to be normalised (ie. Y = 1.0) */
/*
+ TTBD: Should convert to using Timo Kunkel and Erik Reinhard's simplified
+ and improved version of CIECAM02 (ie. "CIECAM02-KR").
+
+ The rgbp compression has it's problems in terms of perceptual
+ uniformity. A color with one component near zero might shift
+ all the components to -ve values on inverse conversion - ie.
+ a 1 DE shift in Jab becomes a masive DE in XYZ/Lab/perceptual,
+ with (say) a darl red becomong black because the blue
+ value is small. One way around this is to re-introduce
+ a flag to turn off perfect symetry by disabling
+ expansion on the reverse conversion.
+
+ */
+
+/*
Various additions and changes have been made to allow the CAM
conversions to and from an unbounded range of XYZ and Jab values,
in a (somewhat) geometrically consistent maner. This is because
@@ -106,6 +121,7 @@
#include "numlib.h"
#define ENABLE_COMPR /* [Def] Enable XYZ compression */
+#undef ENABLE_DECOMPR /* [Undef] Enable XYZ de-compression */
#define ENABLE_BLUE_ANGLE_FIX /* [Def] Limit maximum blue angle */
#define ENABLE_DDL /* [Def] Enable k1,k2,k3 overall ss limit values (seems to be the best scheme) */
#undef ENABLE_SS /* [Undef] Disable overall ss limit values (not the scheme used) */
@@ -123,10 +139,10 @@
#undef DISABLE_HHKR /* Debug - disable Helmholtz-Kohlraush */
#ifdef ENABLE_COMPR
-# define BC_WHMINY 0.3 /* [0.3] Compression direction minimum Y value */
-# define BC_RANGE_R 0.01 /* [0.01] Set compression range as prop. of distance to neutral - red */
-# define BC_RANGE_G 0.05 /* [0.05] Set compression range as prop. of distance to neutral - green*/
-# define BC_RANGE_B 0.10 /* [0.10] Set compression range as prop. of distance to neutral - blue */
+# define BC_WHMINY 0.2 /* [0.2] Compression direction minimum Y value */
+# define BC_RANGE_R 0.01 /* [0.02] Set comp. range as prop. of distance to neutral - red */
+# define BC_RANGE_G 0.01 /* [0.02] Set comp. range as prop. of distance to neutral - green*/
+# define BC_RANGE_B 0.01 /* [0.02] Set comp. range as prop. of distance to neutral - blue */
# define BC_MAXRANGE 0.13 /* [0.13] Maximum compression range */
# define BC_LIMIT 0.7 /* [0.7] Correction limit (abs. rgbp distance shift) */
#endif /* ENABLE_COMPR */
@@ -174,7 +190,7 @@ double minj = 1e38, maxj = -1e38;
static void cam_free(cam02 *s);
static int set_view(struct _cam02 *s, ViewingCondition Ev, double Wxyz[3],
- double La, double Yb, double Lv, double Yf, double Fxyz[3],
+ double La, double Yb, double Lv, double Yf, double Yg, double Gxyz[3],
int hk);
static int XYZ_to_cam(struct _cam02 *s, double *Jab, double *xyz);
static int cam_to_XYZ(struct _cam02 *s, double *xyz, double *Jab);
@@ -263,7 +279,9 @@ double Yb, /* Relative Luminance of Background to reference white (range 0.0 ..
double Lv, /* Luminance of white in the Viewing/Scene/Image field (cd/m^2) */
/* Ignored if Ev is set to other than vc_none */
double Yf, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
-double Fxyz[3], /* The Flare white coordinates (typically the Ambient color) */
+double Yg, /* Flare as a fraction of the ambient (Y range 0.0 .. 1.0) */
+double Gxyz[3], /* The Glare white coordinates (typically the Ambient color) */
+ /* If <= 0 will Wxyz will be used. */
int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
) {
double tt, t1, t2;
@@ -271,7 +289,8 @@ int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
int i;
if (Ev == vc_none) {
- /* Compute the internal parameters by interpolation */
+ /* Compute the internal parameters from the */
+ /* ratio of La to Lv by interpolation */
int i;
double r, bf;
/* Dark, dim, average, above average */
@@ -302,26 +321,32 @@ int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
s->F = t_F[i] * (1.0 - bf) + t_F[i+1] * bf;
} else {
/* Compute the internal parameters by category */
+ /* Fake up Lv according to category */
switch(Ev) {
case vc_dark:
s->C = 0.525;
s->Nc = 0.8;
s->F = 0.8;
+ Lv = La/0.033;
break;
case vc_dim:
s->C = 0.59;
s->Nc = 0.95;
s->F = 0.9;
+ Lv = La/0.1;
+ break;
+ case vc_average:
+ default:
+ s->C = 0.69;
+ s->Nc = 1.0;
+ s->F = 1.0;
+ Lv = La/0.2;
break;
case vc_cut_sheet:
s->C = 0.41;
s->Nc = 0.8;
s->F = 0.8;
- break;
- default: /* average */
- s->C = 0.69;
- s->Nc = 1.0;
- s->F = 1.0;
+ Lv = La/0.02; // ???
break;
}
}
@@ -331,12 +356,21 @@ int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
s->Wxyz[0] = Wxyz[0];
s->Wxyz[1] = Wxyz[1];
s->Wxyz[2] = Wxyz[2];
- s->Yb = Yb > 0.005 ? Yb : 0.005; /* Set minimum to avoid divide by 0.0 */
s->La = La;
+ s->Yb = Yb > 0.005 ? Yb : 0.005; /* Set minimum to avoid divide by 0.0 */
+ s->Lv = Lv;
s->Yf = Yf;
- s->Fxyz[0] = Fxyz[0];
- s->Fxyz[1] = Fxyz[1];
- s->Fxyz[2] = Fxyz[2];
+ s->Yg = Yg;
+ if (Gxyz[0] > 0.0 && Gxyz[1] > 0.0 && Gxyz[2] > 0.0) {
+ tt = Wxyz[1]/Gxyz[1]; /* Scale to white ref white */
+ s->Gxyz[0] = tt * Gxyz[0];
+ s->Gxyz[1] = tt * Gxyz[1];
+ s->Gxyz[2] = tt * Gxyz[2];
+ } else {
+ s->Gxyz[0] = Wxyz[0];
+ s->Gxyz[1] = Wxyz[1];
+ s->Gxyz[2] = Wxyz[2];
+ }
s->hk = hk;
/* The rgba vectors */
@@ -365,10 +399,15 @@ int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
/* Compute values that only change with viewing parameters */
/* Figure out the Flare contribution to the flareless XYZ input */
- tt = s->Yf * s->Wxyz[1]/s->Fxyz[1];
- s->Fsxyz[0] = tt * s->Fxyz[0];
- s->Fsxyz[1] = tt * s->Fxyz[1];
- s->Fsxyz[2] = tt * s->Fxyz[2];
+ s->Fsxyz[0] = s->Yf * s->Wxyz[0];
+ s->Fsxyz[1] = s->Yf * s->Wxyz[1];
+ s->Fsxyz[2] = s->Yf * s->Wxyz[2];
+
+ /* Add in the Glare contribution from the ambient */
+ tt = s->Yg * s->La/s->Lv;
+ s->Fsxyz[0] += tt * s->Gxyz[0];
+ s->Fsxyz[1] += tt * s->Gxyz[1];
+ s->Fsxyz[2] += tt * s->Gxyz[2];
/* Rescale so that the sum of the flare and the input doesn't exceed white */
s->Fsc = s->Wxyz[1]/(s->Fsxyz[1] + s->Wxyz[1]);
@@ -522,7 +561,8 @@ int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
printf("Relative liminance of background Yb = %f\n", s->Yb);
printf("Adapting liminance La = %f\n", s->La);
printf("Flare Yf = %f\n", s->Yf);
- printf("Flare color Fxyz = %f %f %f\n", s->Fxyz[0], s->Fxyz[1], s->Fxyz[2]);
+ printf("Glare Yg = %f\n", s->Yg);
+ printf("Glare color Gxyz = %f %f %f\n", s->Gxyz[0], s->Gxyz[1], s->Gxyz[2]);
printf("Internal parameters:\n");
printf("Surround Impact C = %f\n", s->C);
@@ -569,7 +609,7 @@ double XYZ[3]
XYZi[2] = XYZ[2];
#endif
- TRACE(("\nForward conversion:\n"))
+ TRACE(("\nCIECAM02 Forward conversion:\n"))
TRACE(("XYZ = %f %f %f\n",XYZ[0], XYZ[1], XYZ[2]))
#ifdef DISABLE_MATRIX
@@ -622,7 +662,7 @@ double XYZ[3]
/* but compressing towards white seems to be the best.) */
icmSub3(cvec, wrgb, rgbp); /* Direction of white target */
- TRACE(("rgbp %f %f %f\n", rgbp[0], rgbp[1], rgbp[2]))
+ TRACE(("ch %d, rgbp %f %f %f\n", i, rgbp[0], rgbp[1], rgbp[2]))
TRACE(("cvec %f %f %f\n", cvec[0], cvec[1], cvec[2]))
if (cvec[i] < 1e-9) { /* compression direction can't correct this coord */
@@ -632,7 +672,7 @@ double XYZ[3]
/* Scale compression vector to make it move a unit in normal direction */
icmScale3(cvec, cvec, 1.0/cvec[i]); /* Normalized vector to white */
- TRACE(("cvec %f %f %f\n", cvec[0], cvec[1], cvec[2]))
+ TRACE(("ncvec %f %f %f\n", cvec[0], cvec[1], cvec[2]))
/* Compute intersection of correction direction with this limit plane */
/* (This corresponds with finding displacement of rgbp by cvec */
@@ -971,7 +1011,7 @@ double Jab[3]
#endif
- TRACE(("\nReverse conversion:\n"))
+ TRACE(("\nCIECAM02 Reverse conversion:\n"))
TRACE(("Jab %f %f %f\n",Jab[0], Jab[1], Jab[2]))
JJ = Jab[0] * 0.01; /* J/100 */
@@ -1153,7 +1193,7 @@ double Jab[3]
#endif
-#ifdef ENABLE_COMPR
+#ifdef ENABLE_DECOMPR
/* Undo soft limiting */
{
double tt; /* Temporary */
@@ -1181,7 +1221,7 @@ double Jab[3]
/* but compressing towards white seems to be the best.) */
icmSub3(cvec, wrgb, rgbp); /* Direction of white target */
- TRACE(("rgbp %f %f %f\n", rgbp[0], rgbp[1], rgbp[2]))
+ TRACE(("ch %d, rgbp %f %f %f\n", i, rgbp[0], rgbp[1], rgbp[2]))
TRACE(("cvec %f %f %f\n", cvec[0], cvec[1], cvec[2]))
if (cvec[i] < 1e-9) { /* compression direction can't correct this coord */
diff --git a/xicc/cam02.h b/xicc/cam02.h
index 9491572..a42e71f 100644
--- a/xicc/cam02.h
+++ b/xicc/cam02.h
@@ -6,10 +6,8 @@
* CIECAM02, "The CIECAM02 Color Appearance Model"
* by Nathan Moroney, Mark D. Fairchild, Robert W.G. Hunt, Changjun Li,
* M. Ronnier Luo and Todd Newman, IS&T/SID Tenth Color Imaging
- * Conference, with the addition of the Viewing Flare
- * model described on page 487 of "Digital Color Management",
- * by Edward Giorgianni and Thomas Madden, and the
- * Helmholtz-Kohlraush effect, using the equation
+ * Conference, with the addition of a Viewing Flare+Glare
+ * model, and the Helmholtz-Kohlraush effect, using the equation
* the Bradford-Hunt 96C model as detailed in Mark Fairchilds
* book "Color Appearance Models".
*
@@ -19,7 +17,7 @@
*
* This file is based on cam97s3.h by Graeme Gill.
*
- * Copyright 2004 - 2011 Graeme W. Gill
+ * Copyright 2004 - 2013 Graeme W. Gill
* Please refer to COPYRIGHT file for details.
* This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
* see the License.txt file for licencing details.
@@ -88,13 +86,18 @@
/* The Background relative luminance Yb is typically assumed to */
/* be 0.18 .. 0.2, and is assumed to be grey. */
-/* The source of flare light depends on the type of display system. */
-/* For a CRT, it will be the Ambient light reflecting off the glass surface. */
-/* (This implies Yf = Lamb * reflectance/Lv) */
-/* For a reflection print, it will be the Illuminant or Ambient reflecting from the media */
-/* surface. (Yf = Li * reflectance) */
-/* For a projected image, it will be stray projector light, scattered by the */
-/* surround, screen and air particles. (Yf = Li * reflectance_and_scattering) */
+/* Flare is assumed to be stray light from light parts of the */
+/* image illuminating dark parts of the image, and is display technology dependent. */
+/* In theory reflective systems have no Flare ? */
+
+/* Glare is assumed to be stray ambient light reflecting from the display */
+/* surface, dust, or entering the observers eye directly, and as a result */
+/* fogging the dark parts of the image. */
+/* This is typically the major source of veiling light ? */
+
+/* By separatedly specifiying these two, the effect can be automatically */
+/* scalled according to the ambient light level, modelling the effect of */
+/* reduced glare in a darkened viewing environment. */
/*
@@ -135,7 +138,9 @@ struct _cam02 {
double Lv, /* Luminance of white in the Viewing/Scene/Image field (cd/m^2) */
/* Ignored if Ev is set */
double Yf, /* Flare as a fraction of the reference white (range 0.0 .. 1.0) */
- double Fxyz[3], /* The Flare white coordinates (typically the Ambient color) */
+ double Yg, /* Glare as a fraction of the ambient (range 0.0 .. 1.0) */
+ double Gxyz[3], /* The Glare white coordinates (ie. the Ambient color) */
+ /* If <= 0 will Wxyz will be used. */
int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
);
@@ -146,11 +151,13 @@ struct _cam02 {
/* Private: */
/* Scene parameters */
ViewingCondition Ev; /* Enumerated Viewing Condition */
+ double Lv; /* Luminance of white in the Viewing/Image cd/m^2 */
double La; /* Adapting/Surround Luminance cd/m^2 */
double Wxyz[3]; /* Reference/Adapted White XYZ (Y range 0.0 .. 1.0) */
double Yb; /* Relative Luminance of Background to reference white (Y range 0.0 .. 1.0) */
double Yf; /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
- double Fxyz[3]; /* The Flare white coordinates (typically the Ambient color) */
+ double Yg; /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */
+ double Gxyz[3]; /* The Glare white coordinates (typically the Ambient color) */
/* Internal parameters */
double C; /* Surround Impact */
@@ -163,9 +170,9 @@ struct _cam02 {
double crange[3]; /* ENABLE_COMPR compression range */
double Va[3], Vb[3], VttA[3], Vttd[3]; /* rgba vectors */
double dcomp[3]; /* Vttd in terms of VttA, Va, Vb */
- double Fsc; /* Flare scale */
- double Fisc; /* Inverse flare scale */
- double Fsxyz[3]; /* Scaled Flare color coordinates */
+ double Fsc; /* Flare+Glare scale */
+ double Fisc; /* Inverse Flare+Glare scale */
+ double Fsxyz[3]; /* Scaled Flare+Glare color coordinates */
double rgbW[3]; /* Sharpened cone response white values */
double D; /* Degree of chromatic adaption */
double Drgb[3]; /* Chromatic transformation value */
diff --git a/xicc/cam02plot.c b/xicc/cam02plot.c
index ca68b52..f397e09 100644
--- a/xicc/cam02plot.c
+++ b/xicc/cam02plot.c
@@ -575,7 +575,8 @@ main(int argc, char *argv[]) {
0.20, /* Relative Luminance of Background to reference white */
0.0, /* Luminance of white in image - not used */
0.00, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
- white[4], /* The Flare color coordinates (typically the Ambient color) */
+ 0.00, /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */
+ white[4], /* The Glare color coordinates (typically the Ambient color) */
use_hk /* use Helmholtz-Kohlraush flag */
);
@@ -592,7 +593,8 @@ main(int argc, char *argv[]) {
0.20, /* Relative Luminance of Background to reference white */
0.0, /* Luminance of white in image - not used */
0.00, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
- white[4], /* The Flare color coordinates (typically the Ambient color) */
+ 0.00, /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */
+ white[4], /* The Glare color coordinates (typically the Ambient color) */
use_hk /* use Helmholtz-Kohlraush flag */
);
diff --git a/xicc/cam02ref.h b/xicc/cam02ref.h
index de75f6b..8965b7a 100644
--- a/xicc/cam02ref.h
+++ b/xicc/cam02ref.h
@@ -41,7 +41,8 @@ struct _cam02ref {
double Lv, /* Luminance of white in the Viewing/Scene/Image field (cd/m^2) */
/* Ignored if Ev is set */
double Yf, /* Flare as a fraction of the reference white (range 0.0 .. 1.0) */
- double Fxyz[3], /* The Flare white coordinates (typically the Ambient color) */
+ double Yg, /* Glare as a fraction of the ambient (range 0.0 .. 1.0) */
+ double Gxyz[3], /* The Glare white coordinates (typically the Ambient color) */
int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
);
@@ -51,9 +52,10 @@ struct _cam02ref {
/* Private: */
/* Scene parameters */
ViewingCondition Ev; /* Enumerated Viewing Condition */
+ double Lv; /* Luminance of white in the Viewing/Image cd/m^2 */
+ double La; /* Adapting/Surround Luminance cd/m^2 */
double Wxyz[3]; /* Reference/Adapted White XYZ (Y range 0.0 .. 1.0) */
double Yb; /* Relative Luminance of Background to reference white (Y range 0.0 .. 1.0) */
- double La; /* Adapting/Surround Luminance cd/m^2 */
double Yf; /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
double Fxyz[3]; /* The Flare white coordinates (typically the Ambient color) */
@@ -120,7 +122,7 @@ double Lv /* Luminence of white in the Viewing/Scene/Image field (cd/m^2) */
static void cam02ref_free(cam02ref *s);
static int cam02ref_set_view(cam02ref *s, ViewingCondition Ev, double Wxyz[3],
- double Yb, double La, double Lv, double Yf, double Fxyz[3], int hk);
+ double Yb, double La, double Lv, double Yf, double Yg, double Gxyz[3], int hk);
static int cam02ref_XYZ_to_cam(cam02ref *s, double *Jab, double *xyz);
static int cam02ref_cam_to_XYZ(cam02ref *s, double XYZ[3], double Jab[3]);
@@ -156,7 +158,8 @@ double Yb, /* Relative Luminence of Background to reference white */
double Lv, /* Luminence of white in the Viewing/Scene/Image field (cd/m^2) */
/* Ignored if Ev is set to other than vc_none */
double Yf, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
-double Fxyz[3], /* The Flare white coordinates (typically the Ambient color) */
+double Yg, /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */
+double Gxyz[3], /* The Glare white coordinates (typically the Ambient color) */
int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
) {
double tt;
@@ -171,9 +174,16 @@ int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
s->Yb = Yb > 0.005 ? Yb : 0.005; /* Set minimum to avoid divide by 0.0 */
s->La = La;
s->Yf = Yf;
- s->Fxyz[0] = Fxyz[0];
- s->Fxyz[1] = Fxyz[1];
- s->Fxyz[2] = Fxyz[2];
+ if (Gxyz[0] > 0.0 && Gxyz[1] > 0.0 && Gxyz[2] > 0.0) {
+ tt = Wxyz[1]/Gxyz[1]; /* Scale to white ref white */
+ s->Gxyz[0] = tt * Gxyz[0];
+ s->Gxyz[1] = tt * Gxyz[1];
+ s->Gxyz[2] = tt * Gxyz[2];
+ } else {
+ s->Gxyz[0] = Wxyz[0];
+ s->Gxyz[1] = Wxyz[1];
+ s->Gxyz[2] = Wxyz[2];
+ }
s->hk = hk;
/* Compute the internal parameters by category */
@@ -182,38 +192,42 @@ int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
s->C = 0.525;
s->Nc = 0.8;
s->F = 0.8;
+ Lv = La/0.033;
break;
case vc_dim:
s->C = 0.59;
s->Nc = 0.95;
s->F = 0.9;
+ Lv = La/0.1;
break;
- case vc_cut_sheet:
- s->C = 0.41;
- s->Nc = 0.8;
- s->F = 0.8;
- break;
+ case vc_average:
default: /* average */
s->C = 0.69;
s->Nc = 1.0;
s->F = 1.0;
+ Lv = La/0.2;
+ break;
+ case vc_cut_sheet:
+ s->C = 0.41;
+ s->Nc = 0.8;
+ s->F = 0.8;
+ Lv = La/0.02; // ???
break;
}
+ s->Lv = Lv;
/* Compute values that only change with viewing parameters */
/* Figure out the Flare contribution to the flareless XYZ input */
- tt = s->Yf * s->Wxyz[1]/s->Fxyz[1];
- s->Fsxyz[0] = tt * s->Fxyz[0];
- s->Fsxyz[1] = tt * s->Fxyz[1];
- s->Fsxyz[2] = tt * s->Fxyz[2];
-
- /* Rescale so that the sum of the flare and the input doesn't exceed white */
- s->Fsc = s->Wxyz[1]/(s->Fsxyz[1] + s->Wxyz[1]);
- s->Fsxyz[0] *= s->Fsc;
- s->Fsxyz[1] *= s->Fsc;
- s->Fsxyz[2] *= s->Fsc;
- s->Fisc = 1.0/s->Fsc;
+ s->Fsxyz[0] = s->Yf * s->Wxyz[0];
+ s->Fsxyz[1] = s->Yf * s->Wxyz[1];
+ s->Fsxyz[2] = s->Yf * s->Wxyz[2];
+
+ /* Add in the Glare contribution from the ambient */
+ tt = s->Yg * s->La/s->Lv;
+ s->Fsxyz[0] += tt * s->Gxyz[0];
+ s->Fsxyz[1] += tt * s->Gxyz[1];
+ s->Fsxyz[2] += tt * s->Gxyz[2];
/* Sharpened cone response white values */
s->rgbW[0] = 0.7328 * s->Wxyz[0] + 0.4296 * s->Wxyz[1] - 0.1624 * s->Wxyz[2];
diff --git a/xicc/cam02test.c b/xicc/cam02test.c
index 3dafc9c..eba27d6 100644
--- a/xicc/cam02test.c
+++ b/xicc/cam02test.c
@@ -405,6 +405,7 @@ main(void) {
0.20, /* Relative Luminance of Background to reference white */
0.0, /* Luminance of white in image - not used */
tFlair[c], /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
+ 0.00, /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */
twhite[c], /* The Flare color coordinates (typically the Ambient color) */
USE_HK /* use Helmholtz-Kohlraush flag */
);
@@ -416,6 +417,7 @@ main(void) {
0.20, /* Relative Luminance of Background to reference white */
0.0, /* Luminance of white in image - not used */
tFlair[c], /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
+ 0.00, /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */
twhite[c], /* The Flare color coordinates (typically the Ambient color) */
USE_HK /* use Helmholtz-Kohlraush flag */
);
@@ -558,7 +560,8 @@ main(void) {
0.20, /* Relative Luminance of Background to reference white */
0.0, /* Luminance of white in image - not used */
0.00, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
- sp_white[c],/* The Flare color coordinates (typically the Ambient color) */
+ 0.00, /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */
+ sp_white[c],/* The Glare color coordinates (typically the Ambient color) */
USE_HK /* use Helmholtz-Kohlraush flag */
);
@@ -570,7 +573,8 @@ main(void) {
0.20, /* Relative Luminance of Background to reference white */
0.0, /* Luminance of white in image - not used */
0.00, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
- sp_white[c],/* The Flare color coordinates (typically the Ambient color) */
+ 0.00, /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */
+ sp_white[c],/* The Glare color coordinates (typically the Ambient color) */
USE_HK /* use Helmholtz-Kohlraush flag */
);
@@ -710,7 +714,8 @@ main(void) {
0.20, /* Relative Luminance of Background to reference white */
0.0, /* Luminance of white in image - not used */
0.00, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
- white[c], /* The Flare color coordinates (typically the Ambient color) */
+ 0.00, /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */
+ white[c], /* The Glare color coordinates (typically the Ambient color) */
USE_HK /* use Helmholtz-Kohlraush flag */
);
@@ -722,7 +727,8 @@ main(void) {
0.20, /* Relative Luminance of Background to reference white */
0.0, /* Luminance of white in image - not used */
0.00, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
- white[c], /* The Flare color coordinates (typically the Ambient color) */
+ 0.00, /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */
+ white[c], /* The Glare color coordinates (typically the Ambient color) */
USE_HK /* use Helmholtz-Kohlraush flag */
);
@@ -941,8 +947,9 @@ main(void) {
34.0, /* Adapting/Surround Luminance cd/m^2 */
0.20, /* Relative Luminance of Background to reference white */
0.0, /* Luminance of white in image - not used */
- 0.01, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
- white[c], /* The Flare color coordinates (typically the Ambient color) */
+ 0.0, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
+ 0.0, /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */
+ white[c], /* The Glare color coordinates (typically the Ambient color) */
USE_HK /* use Helmholtz-Kohlraush flag */
);
@@ -1122,8 +1129,9 @@ main(void) {
34.0, /* Adapting/Surround Luminance cd/m^2 */
0.20, /* Relative Luminance of Background to reference white */
0.0, /* Luminance of white in image - not used */
- 0.01, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
- white[c], /* The Flare color coordinates (typically the Ambient color) */
+ 0.0, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
+ 0.0, /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */
+ white[c], /* The Glare color coordinates (typically the Ambient color) */
USE_HK /* use Helmholtz-Kohlraush flag */
);
diff --git a/xicc/ccss.c b/xicc/ccss.c
index cd9b4e4..6b5ebeb 100644
--- a/xicc/ccss.c
+++ b/xicc/ccss.c
@@ -4,7 +4,7 @@
* Colorimeter Correction Matrix
*
* Author: Graeme W. Gill
- * Date: 1r9/8/2010
+ * Date: 9/8/2010
*
* Copyright 2010 Graeme W. Gill
* All rights reserved.
@@ -112,7 +112,11 @@ cgats **pocg /* return CGATS structure */
ocg->add_kword(ocg, 0, "SPECTRAL_NORM",buf, NULL);
/* Fields we want */
- ocg->add_field(ocg, 0, "SAMPLE_ID", nqcs_t);
+ if (ocg->add_field(ocg, 0, "SAMPLE_ID", nqcs_t) < 0) {
+ sprintf(p->err, "cgats add_field SAMPLE_ID failed with '%s'!",ocg->err);
+ ocg->del(ocg); /* Clean up */
+ return 2;
+ }
nsetel += 1; /* For id */
/* Generate fields for spectral values */
@@ -124,7 +128,11 @@ cgats **pocg /* return CGATS structure */
* (p->samples[0].spec_wl_long - p->samples[0].spec_wl_short) + 0.5);
sprintf(buf,"SPEC_%03d",nm);
- ocg->add_field(ocg, 0, buf, r_t);
+ if (ocg->add_field(ocg, 0, buf, r_t) < 0) {
+ sprintf(p->err, "cgats add_field %s failed with '%s'",buf,ocg->err);
+ ocg->del(ocg); /* Clean up */
+ return 2;
+ }
}
nsetel += p->samples[0].spec_n; /* Spectral values */
diff --git a/xicc/iccgamut.c b/xicc/iccgamut.c
index 7bd3b4c..bb97f26 100644
--- a/xicc/iccgamut.c
+++ b/xicc/iccgamut.c
@@ -81,10 +81,11 @@ void usage(char *diag) {
fprintf(stderr," w:x:y Adapted white point as x, y\n");
fprintf(stderr," a:adaptation Adaptation luminance in cd.m^2 (default 50.0)\n");
fprintf(stderr," b:background Background %% of image luminance (default 20)\n");
- fprintf(stderr," l:scenewhite Scene white in cd.m^2 if surround = auto (default 250)\n");
- fprintf(stderr," f:flare Flare light %% of image luminance (default 1)\n");
- fprintf(stderr," f:X:Y:Z Flare color as XYZ (default media white)\n");
- fprintf(stderr," f:x:y Flare color as x, y\n");
+ fprintf(stderr," l:imagewhite Image white in cd.m^2 if surround = auto (default 250)\n");
+ fprintf(stderr," f:flare Flare light %% of image luminance (default 0)\n");
+ fprintf(stderr," g:glare Flare light %% of ambient (default 1)\n");
+ fprintf(stderr," g:X:Y:Z Flare color as XYZ (default media white, Abs: D50)\n");
+ fprintf(stderr," g:x:y Flare color as x, y\n");
fprintf(stderr," -s Create special cube surface topology plot\n");
fprintf(stderr,"\n");
exit(1);
@@ -93,8 +94,8 @@ void usage(char *diag) {
int
main(int argc, char *argv[]) {
int fa,nfa; /* argument we're looking at */
- char prof_name[100];
- char *xl, out_name[100];
+ char prof_name[MAXNAMEL+1];
+ char *xl, out_name[MAXNAMEL+4+1];
icmFile *fp;
icc *icco;
xicc *xicco;
@@ -119,8 +120,9 @@ main(int argc, char *argv[]) {
double vc_b = -1.0; /* Background % overide */
double vc_l = -1.0; /* Scene luminance override */
double vc_f = -1.0; /* Flare % overide */
- double vc_fXYZ[3] = {-1.0, -1.0, -1.0}; /* Flare color override in XYZ */
- double vc_fxy[2] = {-1.0, -1.0}; /* Flare color override in x,y */
+ double vc_g = -1.0; /* Glare % overide */
+ double vc_gXYZ[3] = {-1.0, -1.0, -1.0}; /* Glare color override in XYZ */
+ double vc_gxy[2] = {-1.0, -1.0}; /* Glare color override in x,y */
icxLuBase *luo;
@@ -328,18 +330,22 @@ main(int argc, char *argv[]) {
vc_b = atof(na+2);
} else if (na[0] == 'l' || na[0] == 'L') {
if (na[1] != ':')
- usage("Viewing conditions (-[cd]l) missing ':'");
+ usage("Viewing conditions (-cl) missing ':'");
vc_l = atof(na+2);
} else if (na[0] == 'f' || na[0] == 'F') {
+ if (na[1] != ':')
+ usage("Viewing conditions (-cf) missing ':'");
+ vc_f = atof(na+2);
+ } else if (na[0] == 'g' || na[0] == 'G') {
double x, y, z;
if (sscanf(na+1,":%lf:%lf:%lf",&x,&y,&z) == 3) {
- vc_fXYZ[0] = x; vc_fXYZ[1] = y; vc_fXYZ[2] = z;
+ vc_gXYZ[0] = x; vc_gXYZ[1] = y; vc_gXYZ[2] = z;
} else if (sscanf(na+1,":%lf:%lf",&x,&y) == 2) {
- vc_fxy[0] = x; vc_fxy[1] = y;
+ vc_gxy[0] = x; vc_gxy[1] = y;
} else if (sscanf(na+1,":%lf",&x) == 1) {
- vc_f = x;
+ vc_g = x;
} else
- usage("Unrecognised parameters after -cf");
+ usage("Unrecognised parameters after -cg");
} else
usage("Unrecognised parameters after -c");
}
@@ -357,7 +363,7 @@ main(int argc, char *argv[]) {
}
if (fa >= argc || argv[fa][0] == '-') usage("Expected profile name");
- strcpy(prof_name,argv[fa]);
+ strncpy(prof_name, argv[fa],MAXNAMEL); prof_name[MAXNAMEL] = '\000';
/* Open up the profile for reading */
if ((fp = new_icmFileStd_name(prof_name,"r")) == NULL)
@@ -431,17 +437,19 @@ main(int argc, char *argv[]) {
vc.Lv = vc_l;
if (vc_f >= 0.0)
vc.Yf = vc_f/100.0;
- if (vc_fXYZ[1] > 0.0) {
+ if (vc_g >= 0.0)
+ vc.Yg = vc_g/100.0;
+ if (vc_gXYZ[1] > 0.0) {
/* Normalise it to current media white */
- vc.Fxyz[0] = vc_fXYZ[0]/vc_fXYZ[1] * vc.Fxyz[1];
- vc.Fxyz[2] = vc_fXYZ[2]/vc_fXYZ[1] * vc.Fxyz[1];
+ vc.Gxyz[0] = vc_gXYZ[0]/vc_gXYZ[1] * vc.Gxyz[1];
+ vc.Gxyz[2] = vc_gXYZ[2]/vc_gXYZ[1] * vc.Gxyz[1];
}
- if (vc_fxy[0] >= 0.0) {
- double x = vc_fxy[0];
- double y = vc_fxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
+ if (vc_gxy[0] >= 0.0) {
+ double x = vc_gxy[0];
+ double y = vc_gxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
double z = 1.0 - x - y;
- vc.Fxyz[0] = x/y * vc.Fxyz[1];
- vc.Fxyz[2] = z/y * vc.Fxyz[1];
+ vc.Gxyz[0] = x/y * vc.Gxyz[1];
+ vc.Gxyz[2] = z/y * vc.Gxyz[1];
}
fl |= ICX_CLIP_NEAREST; /* Don't setup rev uncessarily */
diff --git a/xicc/monctest.c b/xicc/monctest.c
index 29c9298..8630099 100644
--- a/xicc/monctest.c
+++ b/xicc/monctest.c
@@ -37,7 +37,8 @@ void usage(void);
#undef NORMONLY /* Defined to use 0.0 - 1.0 limited curve */
#undef ORDER_STEP /* Step orders from 2 to SHAPE_ORDERS */
-#define SHAPE_ORDS 30 /* Number of order to use */
+//#define SHAPE_ORDS 30 /* Number of order to use */
+#define SHAPE_ORDS 12 /* Number of order to use */
#define ABS_MAX_PNTS 100
@@ -169,7 +170,7 @@ int main() {
xa[i] = (xa[i]/xa[pnts-1]);
/* Create y values */
- ya[0] = xa[0] + d_rand(-0.1, 0.5);
+ ya[0] = xa[0] + d_rand(-0.2, 0.7);
for (i = 1; i < pnts; i++)
ya[i] = ya[i-1] + d_rand(0.1,1.0) + d_rand(-0.1,0.4) + d_rand(-0.4,0.5);
diff --git a/xicc/moncurve.c b/xicc/moncurve.c
index b904420..1769ce4 100644
--- a/xicc/moncurve.c
+++ b/xicc/moncurve.c
@@ -39,11 +39,10 @@
#undef TEST_PDE /* Ckeck partial derivative calcs */
-/* Normalization factors for an average data point error squared, scale 100 */
-#define HW01 0.002 /* 0 & 1 harmonic weights */
-#define HBREAK 4 /* Harmonic that has HWBR */
-#define HWBR 0.8 /* Base weight of harmonics HBREAK up */
-#define HWINC 0.5 /* Increase in weight for each harmonic above HWBR */
+#define HW01 0.01 /* 0 & 1 harmonic weights */
+#define HBREAK 3 /* Harmonic that has HWBR */
+#define HWBR 0.5 /* Base weight of harmonics HBREAK up */
+#define HWINC 0.7 /* Increase in weight for each harmonic above HWBR */
static void mcv_del(mcv *p);
static void mcv_fit(mcv *p, int verb, int order, mcvco *d, int ndp, double smooth);
@@ -550,7 +549,7 @@ double smooth) {
w = HW01;
} else if (cx <= HBREAK) {
double bl = (cx - 1.0)/(HBREAK - 1.0);
- w = (1.0 - bl) * HW01 + bl * HWBR;
+ w = (1.0 - bl) * HW01 + bl * HWBR * smooth;
} else {
w = HWBR + (cx-HBREAK) * HWINC * smooth;
}
@@ -653,7 +652,7 @@ double smooth) {
w = HW01;
} else if (cx <= HBREAK) { /* First or second curves */
double bl = (cx - 1.0)/(HBREAK - 1.0);
- w = (1.0 - bl) * HW01 + bl * HWBR;
+ w = (1.0 - bl) * HW01 + bl * HWBR * smooth;
} else {
w = HWBR + (cx-HBREAK) * HWINC * smooth;
}
diff --git a/xicc/mpp.h b/xicc/mpp.h
index 0becaad..826c4de 100644
--- a/xicc/mpp.h
+++ b/xicc/mpp.h
@@ -27,7 +27,7 @@
/* ------------------------------------------------------------------------------ */
#define MPP_MXINKS 8 /* Would like to be ICX_MXINKS but need more dynamic allocation */
-#define MPP_MXTCORD 10 /* Maxkimum shaper harmonic orders to use */
+#define MPP_MXTCORD 10 /* Maximum shaper harmonic orders to use */
#define MPP_MXCCOMB (1 << MPP_MXINKS) /* Maximum number of primary combinations */
#define MPP_MXPARMS (MPP_MXINKS * MPP_MXTCORD + (MPP_MXINKS * MPP_MXCCOMB/2) + MPP_MXCCOMB)
/* Maximum total parameters for a band */
diff --git a/xicc/mpplu.c b/xicc/mpplu.c
index dcbd1c3..a2bb9ad 100644
--- a/xicc/mpplu.c
+++ b/xicc/mpplu.c
@@ -348,7 +348,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] = strtod(bp, &nbp);
if (nbp == bp)
@@ -468,7 +468,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] = strtod(bp, &nbp);
if (nbp == bp)
diff --git a/xicc/revfix.c b/xicc/revfix.c
index 59fdd0e..ee9662a 100644
--- a/xicc/revfix.c
+++ b/xicc/revfix.c
@@ -747,14 +747,16 @@ main(int argc, char *argv[]) {
/* Use helper function to do the hard work. */
if (wo->set_tables(wo,
ICM_CLUT_SET_APXLS,
- &cb, /* Context */
- icSigLabData, /* Input color space */
- icSigCmykData, /* Output color space */
- Lab_Labp, /* Linear input transform Lab->Lab' */
- NULL, NULL, /* Use default Lab' range */
- Labp_CMYKp, /* Lab' -> CMYK' transfer function */
- NULL, NULL, /* Use default CMYK' range */
- CMYKp_CMYK) != 0) /* Output transfer function, CMYK'->CMYK (NULL = deflt) */
+ &cb, /* Context */
+ icSigLabData, /* Input color space */
+ icSigCmykData, /* Output color space */
+ Lab_Labp, /* Linear input transform Lab->Lab' */
+ NULL, NULL, /* Use default Lab' range */
+ Labp_CMYKp, /* Lab' -> CMYK' transfer function */
+ NULL, NULL, /* Use default CMYK' range */
+ CMYKp_CMYK, /* Output transfer function, CMYK'->CMYK */
+ NULL, NULL /* default APXLS range */
+ ) != 0)
error("Setting 16 bit Lab->CMYK Lut failed: %d, %s",icco->errc,icco->err);
if (verb)
diff --git a/xicc/specplot.c b/xicc/specplot.c
index b2baf05..8e52726 100644
--- a/xicc/specplot.c
+++ b/xicc/specplot.c
@@ -107,7 +107,7 @@ static int do_spec(
/* Compute XYZ of illuminant */
if (icx_ill_sp2XYZ(xyz, icxOT_CIE_1931_2, NULL, icxIT_custom, 0, &tsp) != 0)
- error ("icx_sp_temp2XYZ returned error");
+ warning("icx_sp_temp2XYZ returned error");
icmXYZ2Yxy(Yxy, xyz);
icmXYZ2Lab(&icmD50, Lab, xyz);
@@ -129,11 +129,11 @@ static int do_spec(
/* Compute CCT */
if ((cct = icx_XYZ2ill_ct(cct_xyz, BBTYPE, icxOT_CIE_1931_2, NULL, xyz, NULL, 0)) < 0)
- error ("Got bad cct\n");
+ warning("Got bad cct\n");
/* Compute VCT */
if ((vct = icx_XYZ2ill_ct(vct_xyz, BBTYPE, icxOT_CIE_1931_2, NULL, xyz, NULL, 1)) < 0)
- error ("Got bad vct\n");
+ warning("Got bad vct\n");
#ifdef PLANKIAN
printf("CCT = %f, VCT = %f\n",cct, vct);
diff --git a/xicc/tiffgamut.c b/xicc/tiffgamut.c
index e3e34e8..9423eae 100644
--- a/xicc/tiffgamut.c
+++ b/xicc/tiffgamut.c
@@ -88,10 +88,11 @@ void usage(void) {
fprintf(stderr," w:x:y Adapted white point as x, y\n");
fprintf(stderr," a:adaptation Adaptation luminance in cd.m^2 (default 50.0)\n");
fprintf(stderr," b:background Background %% of image luminance (default 20)\n");
- fprintf(stderr," l:scenewhite Scene white in cd.m^2 if surround = auto (default 250)\n");
- fprintf(stderr," f:flare Flare light %% of image luminance (default 1)\n");
- fprintf(stderr," f:X:Y:Z Flare color as XYZ (default media white)\n");
- fprintf(stderr," f:x:y Flare color as x, y\n");
+ fprintf(stderr," l:imagewhite Image white in cd.m^2 if surround = auto (default 250)\n");
+ fprintf(stderr," f:flare Flare light %% of image luminance (default 0)\n");
+ fprintf(stderr," g:glare Flare light %% of ambient (default 1)\n");
+ fprintf(stderr," g:X:Y:Z Flare color as XYZ (default media white, Abs: D50)\n");
+ fprintf(stderr," g:x:y Flare color as x, y\n");
fprintf(stderr," -O outputfile Override the default output filename.\n");
exit(1);
}
@@ -344,9 +345,10 @@ main(int argc, char *argv[]) {
double vc_a = -1.0; /* Adapted luminance */
double vc_b = -1.0; /* Background % overid */
double vc_l = -1.0; /* Scene luminance override */
- double vc_f = -1.0; /* Flare % overid */
- double vc_fXYZ[3] = {-1.0, -1.0, -1.0}; /* Flare color override in XYZ */
- double vc_fxy[2] = {-1.0, -1.0}; /* Flare color override in x,y */
+ double vc_f = -1.0; /* Flare % overide */
+ double vc_g = -1.0; /* Glare % overide */
+ double vc_gXYZ[3] = {-1.0, -1.0, -1.0}; /* Glare color override in XYZ */
+ double vc_gxy[2] = {-1.0, -1.0}; /* Glare color override in x,y */
icxLuBase *luo = NULL; /* Generic lookup object */
icColorSpaceSignature ins = icSigLabData, outs; /* Type of input and output spaces */
int inn, outn; /* Number of components */
@@ -528,13 +530,17 @@ main(int argc, char *argv[]) {
usage();
vc_l = atof(na+2);
} else if (na[0] == 'f' || na[0] == 'F') {
+ if (na[1] != ':')
+ usage();
+ vc_f = atof(na+2);
+ } else if (na[0] == 'g' || na[0] == 'G') {
double x, y, z;
if (sscanf(na+1,":%lf:%lf:%lf",&x,&y,&z) == 3) {
- vc_fXYZ[0] = x; vc_fXYZ[1] = y; vc_fXYZ[2] = z;
+ vc_gXYZ[0] = x; vc_gXYZ[1] = y; vc_gXYZ[2] = z;
} else if (sscanf(na+1,":%lf:%lf",&x,&y) == 2) {
- vc_fxy[0] = x; vc_fxy[1] = y;
+ vc_gxy[0] = x; vc_gxy[1] = y;
} else if (sscanf(na+1,":%lf",&x) == 1) {
- vc_f = x;
+ vc_g = x;
} else
usage();
} else
@@ -666,19 +672,22 @@ main(int argc, char *argv[]) {
vc.Yb = vc_b/100.0;
if (vc_l >= 0.0)
vc.Lv = vc_l;
+
if (vc_f >= 0.0)
vc.Yf = vc_f/100.0;
- if (vc_fXYZ[1] > 0.0) {
+ if (vc_g >= 0.0)
+ vc.Yg = vc_g/100.0;
+ if (vc_gXYZ[1] > 0.0) {
/* Normalise it to current media white */
- vc.Fxyz[0] = vc_fXYZ[0]/vc_fXYZ[1] * vc.Fxyz[1];
- vc.Fxyz[2] = vc_fXYZ[2]/vc_fXYZ[1] * vc.Fxyz[1];
+ vc.Gxyz[0] = vc_gXYZ[0]/vc_gXYZ[1] * vc.Gxyz[1];
+ vc.Gxyz[2] = vc_gXYZ[2]/vc_gXYZ[1] * vc.Gxyz[1];
}
- if (vc_fxy[0] >= 0.0) {
- double x = vc_fxy[0];
- double y = vc_fxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
+ if (vc_gxy[0] >= 0.0) {
+ double x = vc_gxy[0];
+ double y = vc_gxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
double z = 1.0 - x - y;
- vc.Fxyz[0] = x/y * vc.Fxyz[1];
- vc.Fxyz[2] = z/y * vc.Fxyz[1];
+ vc.Gxyz[0] = x/y * vc.Gxyz[1];
+ vc.Gxyz[2] = z/y * vc.Gxyz[1];
}
/* Get a expanded color conversion object */
@@ -726,23 +735,25 @@ main(int argc, char *argv[]) {
vc.Yb = vc_b/100.0;
if (vc_f >= 0.0)
vc.Yf = vc_f/100.0;
- if (vc_fXYZ[1] > 0.0) {
+ if (vc_g >= 0.0)
+ vc.Yg = vc_g/100.0;
+ if (vc_gXYZ[1] > 0.0) {
/* Normalise it to current media white */
- vc.Fxyz[0] = vc_fXYZ[0]/vc_fXYZ[1] * vc.Fxyz[1];
- vc.Fxyz[2] = vc_fXYZ[2]/vc_fXYZ[1] * vc.Fxyz[1];
+ vc.Gxyz[0] = vc_gXYZ[0]/vc_gXYZ[1] * vc.Gxyz[1];
+ vc.Gxyz[2] = vc_gXYZ[2]/vc_gXYZ[1] * vc.Gxyz[1];
}
- if (vc_fxy[0] >= 0.0) {
- double x = vc_fxy[0];
- double y = vc_fxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
+ if (vc_gxy[0] >= 0.0) {
+ double x = vc_gxy[0];
+ double y = vc_gxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
double z = 1.0 - x - y;
- vc.Fxyz[0] = x/y * vc.Fxyz[1];
- vc.Fxyz[2] = z/y * vc.Fxyz[1];
+ vc.Gxyz[0] = x/y * vc.Gxyz[1];
+ vc.Gxyz[2] = z/y * vc.Gxyz[1];
}
if ((cam = new_icxcam(cam_default)) == NULL)
error("new_icxcam failed");
- cam->set_view(cam, vc.Ev, vc.Wxyz, vc.La, vc.Yb, vc.Lv, vc.Yf, vc.Fxyz,
+ cam->set_view(cam, vc.Ev, vc.Wxyz, vc.La, vc.Yb, vc.Lv, vc.Yf, vc.Yg, vc.Gxyz,
XICC_USE_HK);
}
diff --git a/xicc/tiffgmts.c b/xicc/tiffgmts.c
index 9d21d36..03f6363 100644
--- a/xicc/tiffgmts.c
+++ b/xicc/tiffgmts.c
@@ -75,9 +75,10 @@ void usage(void) {
fprintf(stderr," w:x:y Adapted white point as x, y\n");
fprintf(stderr," a:adaptation Adaptation luminance in cd.m^2 (default 50.0)\n");
fprintf(stderr," b:background Background %% of image luminance (default 20)\n");
- fprintf(stderr," f:flare Flare light %% of image luminance (default 1)\n");
- fprintf(stderr," f:X:Y:Z Flare color as XYZ (default media white)\n");
- fprintf(stderr," f:x:y Flare color as x, y\n");
+ fprintf(stderr," f:flare Flare light %% of image luminance (default 0)\n");
+ fprintf(stderr," g:glare Flare light %% of ambient (default 1)\n");
+ fprintf(stderr," g:X:Y:Z Flare color as XYZ (default media white, Abs: D50)\n");
+ fprintf(stderr," g:x:y Flare color as x, y\n");
fprintf(stderr," -V L,a,b Overide normal vector direction for span\n");
fprintf(stderr," -O outputfile Override the default output filename (locus.ts)\n");
fprintf(stderr," infile.tif File to create test value from\n");
@@ -294,9 +295,10 @@ main(int argc, char *argv[]) {
double vc_wxy[2] = {-1.0, -1.0}; /* Adapted white override in x,y */
double vc_a = -1.0; /* Adapted luminance */
double vc_b = -1.0; /* Background % overid */
- double vc_f = -1.0; /* Flare % overid */
- double vc_fXYZ[3] = {-1.0, -1.0, -1.0}; /* Flare color override in XYZ */
- double vc_fxy[2] = {-1.0, -1.0}; /* Flare color override in x,y */
+ double vc_f = -1.0; /* Flare % overide */
+ double vc_g = -1.0; /* Glare % overide */
+ double vc_gXYZ[3] = {-1.0, -1.0, -1.0}; /* Glare color override in XYZ */
+ double vc_gxy[2] = {-1.0, -1.0}; /* Glare color override in x,y */
icxLuBase *luo = NULL; /* Generic lookup object */
icColorSpaceSignature ins = icSigLabData, outs; /* Type of input and output spaces */
int inn, outn; /* Number of components */
@@ -466,13 +468,16 @@ main(int argc, char *argv[]) {
usage();
vc_b = atof(na+2);
} else if (na[0] == 'f' || na[0] == 'F') {
+ vc_f = atof(na+2);
+ usage();
+ } else if (na[0] == 'g' || na[0] == 'G') {
double x, y, z;
if (sscanf(na+1,":%lf:%lf:%lf",&x,&y,&z) == 3) {
- vc_fXYZ[0] = x; vc_fXYZ[1] = y; vc_fXYZ[2] = z;
+ vc_gXYZ[0] = x; vc_gXYZ[1] = y; vc_gXYZ[2] = z;
} else if (sscanf(na+1,":%lf:%lf",&x,&y) == 2) {
- vc_fxy[0] = x; vc_fxy[1] = y;
+ vc_gxy[0] = x; vc_gxy[1] = y;
} else if (sscanf(na+1,":%lf",&x) == 1) {
- vc_f = x;
+ vc_g = x;
} else
usage();
} else
@@ -585,17 +590,19 @@ main(int argc, char *argv[]) {
vc.Yb = vc_b/100.0;
if (vc_f >= 0.0)
vc.Yf = vc_f/100.0;
- if (vc_fXYZ[1] > 0.0) {
+ if (vc_g >= 0.0)
+ vc.Yg = vc_g/100.0;
+ if (vc_gXYZ[1] > 0.0) {
/* Normalise it to current media white */
- vc.Fxyz[0] = vc_fXYZ[0]/vc_fXYZ[1] * vc.Fxyz[1];
- vc.Fxyz[2] = vc_fXYZ[2]/vc_fXYZ[1] * vc.Fxyz[1];
+ vc.Gxyz[0] = vc_gXYZ[0]/vc_gXYZ[1] * vc.Gxyz[1];
+ vc.Gxyz[2] = vc_gXYZ[2]/vc_gXYZ[1] * vc.Gxyz[1];
}
- if (vc_fxy[0] >= 0.0) {
- double x = vc_fxy[0];
- double y = vc_fxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
+ if (vc_gxy[0] >= 0.0) {
+ double x = vc_gxy[0];
+ double y = vc_gxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
double z = 1.0 - x - y;
- vc.Fxyz[0] = x/y * vc.Fxyz[1];
- vc.Fxyz[2] = z/y * vc.Fxyz[1];
+ vc.Gxyz[0] = x/y * vc.Gxyz[1];
+ vc.Gxyz[2] = z/y * vc.Gxyz[1];
}
/* Get a expanded color conversion object */
diff --git a/xicc/transplot.c b/xicc/transplot.c
index e196544..d4600d9 100644
--- a/xicc/transplot.c
+++ b/xicc/transplot.c
@@ -1,7 +1,7 @@
/*
* International Color Consortium Format Library (icclib)
- * Check various aspects of CMYK device link,
+ * Check various aspects of RGB or CMYK device link,
* and RGB/CMYK profile transfer characteristics.
*
* Author: Graeme W. Gill
@@ -35,6 +35,10 @@ void usage(void) {
fprintf(stderr,"Author: Graeme W. Gill\n");
fprintf(stderr,"usage: transplot [-v] infile\n");
fprintf(stderr," -v verbose\n");
+ fprintf(stderr," -i intent p = perceptual, r = relative colorimetric,\n");
+ fprintf(stderr," s = saturation, a = absolute\n");
+ fprintf(stderr," -o order n = normal (priority: lut > matrix > monochrome)\n");
+ fprintf(stderr," r = reverse (priority: monochrome > matrix > lut)\n");
fprintf(stderr," -c -m -y -k Check Cyan and/or Magenta and/or Yellow and/or Black input\n");
fprintf(stderr," -r -g -b Check Red and/or Green and/or Blue input\n");
fprintf(stderr," -L -A -B Check L and/or a* and/or b* input\n");
@@ -57,6 +61,10 @@ main(
icc *rd_icco; /* Keep object separate */
int rv = 0;
+ /* Lookup parameters */
+ icRenderingIntent intent = icmDefaultIntent; /* Default */
+ icmLookupOrder order = icmLuOrdNorm; /* Default */
+
/* Check variables */
icmLuBase *luo;
icmLuLut *luluto; /* Lookup xLut type object */
@@ -94,6 +102,54 @@ main(
if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
verb = 1;
}
+
+ /* Intent */
+ else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') {
+ fa = nfa;
+ if (na == NULL) usage();
+ switch (na[0]) {
+ case 'p':
+ intent = icPerceptual;
+ break;
+ case 'r':
+ intent = icRelativeColorimetric;
+ break;
+ case 's':
+ intent = icSaturation;
+ break;
+ case 'a':
+ intent = icAbsoluteColorimetric;
+ break;
+ /* Special function icclib intents */
+ case 'P':
+ intent = icmAbsolutePerceptual;
+ break;
+ case 'S':
+ intent = icmAbsoluteSaturation;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ /* Search order */
+ else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') {
+ fa = nfa;
+ if (na == NULL) usage();
+ switch (na[0]) {
+ case 'n':
+ case 'N':
+ order = icmLuOrdNorm;
+ break;
+ case 'r':
+ case 'R':
+ order = icmLuOrdRev;
+ break;
+ default:
+ usage();
+ }
+ }
+
/* Cyan, Red */
else if (argv[fa][1] == 'c' || argv[fa][1] == 'C'
|| argv[fa][1] == 'r' || argv[fa][1] == 'R') {
@@ -153,12 +209,12 @@ main(
if (labin) {
/* Get a Device to PCS conversion object */
- if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, icmDefaultIntent, icSigLabData, icmLuOrdNorm)) == NULL)
+ if ((luo = rd_icco->get_luobj(rd_icco, icmBwd, intent, icSigLabData, order)) == NULL)
error ("%d, %s",rd_icco->errc, rd_icco->err);
} else {
/* Get a PCS to Device conversion object */
- if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icmDefaultIntent, icSigLabData, icmLuOrdNorm)) == NULL) {
- if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icmDefaultIntent, icmSigDefaultData, icmLuOrdNorm)) == NULL) {
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, intent, icSigLabData, order)) == NULL) {
+ if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, intent, icmSigDefaultData, order)) == NULL) {
error ("%d, %s",rd_icco->errc, rd_icco->err);
}
}
@@ -188,8 +244,8 @@ main(
chans[3] = 0;
}
- if (outs != icSigCmykData && outs != icSigLabData) {
- error("Expecting Lab or CMYK output space");
+ if (outs != icSigCmykData && outs != icSigLabData && outs != icSigRgbData) {
+ error("Expecting Lab or CMYK or RGB output space");
}
if (outs == icSigLabData)
diff --git a/xicc/xcal.c b/xicc/xcal.c
index 97a3086..06d343c 100644
--- a/xicc/xcal.c
+++ b/xicc/xcal.c
@@ -110,6 +110,12 @@ static int xcal_read_cgats(xcal *p, cgats *tcg, int table, char *filename) {
p->noramdac = 1;
}
+ if ((ti = tcg->find_kword(tcg, table, "TV_OUTPUT_ENCODING")) >= 0) {
+ if (strcmp(tcg->t[0].kdata[ti], "YES") == 0
+ || strcmp(tcg->t[0].kdata[ti], "yes") == 0)
+ p->tvenc = 1;
+ }
+
p->colspace = icx_colorant_comb_to_icc(p->devmask); /* 0 if none */
p->devchan = icx_noofinks(p->devmask);
ident = icx_inkmask2char(p->devmask, 1);
@@ -190,6 +196,90 @@ static int xcal_read_cgats(xcal *p, cgats *tcg, int table, char *filename) {
return 0;
}
+/* Read a calibration file from an ICC vcgt tag */
+/* Return nz if this fails */
+int xcal_read_icc(xcal *p, icc *c) {
+ icmVideoCardGamma *vg;
+ icmTextDescription *td;
+ int res, i, j;
+
+ /* See if there is a vcgt tag */
+ if ((vg = (icmVideoCardGamma *)c->read_tag(c, icSigVideoCardGammaTag)) == NULL) {
+ sprintf(p->err, "ICC profile has no vcgt");
+ return p->errc = 1;
+ }
+
+ /* What sort of device the profile is for */
+ p->devclass = c->header->deviceClass;
+ p->colspace = c->header->colorSpace;
+
+ if ((p->devmask = icx_icc_to_colorant_comb(p->colspace, p->devclass)) == 0) {
+ sprintf(p->err, "Unable to determine inkmask from ICC profile");
+ return p->errc = 1;
+ }
+ p->devchan = icx_noofinks(p->devmask);
+
+ /* Grab any descriptive information */
+ if ((td = (icmTextDescription *)c->read_tag(c, icSigDeviceMfgDescTag)) != NULL) {
+ p->xpi.deviceMfgDesc = strdup(td->desc);
+ }
+ if ((td = (icmTextDescription *)c->read_tag(c, icSigDeviceModelDescTag)) != NULL) {
+ p->xpi.modelDesc = strdup(td->desc);
+ }
+ if ((td = (icmTextDescription *)c->read_tag(c, icSigProfileDescriptionTag)) != NULL) {
+ p->xpi.profDesc = strdup(td->desc);
+ }
+ if ((td = (icmTextDescription *)c->read_tag(c, icSigCopyrightTag)) != NULL) {
+ p->xpi.copyright = strdup(td->desc);
+ }
+
+ /* Decide the lut resolution */
+ if (vg->tagType == icmVideoCardGammaFormulaType)
+ res = 2048;
+ else
+ res = vg->u.table.entryCount;
+
+ /* Read in each channels values and put them in a rspl */
+ for (j = 0; j < p->devchan; j++) {
+ datai low,high;
+ int gres[MXDI];
+ double smooth = 1.0;
+ co *dpoints;
+
+ low[0] = 0.0;
+ high[0] = 1.0;
+ gres[0] = res;
+
+ if ((p->cals[j] = new_rspl(RSPL_NOFLAGS,1, 1)) == NULL) {
+ sprintf(p->err,"new_rspl() failed");
+ return p->errc = 2;
+ }
+
+ if ((dpoints = malloc(sizeof(co) * gres[0])) == NULL) {
+ sprintf(p->err,"malloc dpoints[%d] failed",gres[0]);
+ return p->errc = 2;
+ }
+
+ /* Copy the points to our array */
+ for (i = 0; i < gres[0]; i++) {
+ dpoints[i].p[0] = i/(double)(gres[0]-1);
+ dpoints[i].v[0] = vg->lookup(vg, j, dpoints[i].p[0]);
+ }
+
+ /* Set the rspl */
+ p->cals[j]->set_rspl(p->cals[j],
+ 0,
+ (void *)dpoints, /* Read points */
+ xcal_rsplset, /* Setting function */
+ low, high, gres, /* Low, high, resolution of grid */
+ NULL, NULL /* Default data scale */
+ );
+ free(dpoints);
+ }
+
+ return 0;
+}
+
/* Read a calibration file */
/* Return nz if this fails */
static int xcal_read(xcal *p, char *filename) {
@@ -258,6 +348,13 @@ static int xcal_write_cgats(xcal *p, cgats *tcg) {
bident = icx_inkmask2char(p->devmask, 0);
tcg->add_kword(tcg, table, "COLOR_REP", ident, NULL);
+ /* Other tags */
+ if (p->noramdac)
+ tcg->add_kword(tcg, table, "VIDEO_LUT_CALIBRATION_POSSIBLE", "NO", NULL);
+
+ if (p->tvenc)
+ tcg->add_kword(tcg, table, "TV_OUTPUT_ENCODING", "YES", NULL);
+
/* Grab any descriptive information */
if (p->xpi.deviceMfgDesc != NULL)
tcg->add_kword(tcg, table, "MANUFACTURER",p->xpi.deviceMfgDesc, NULL);
@@ -483,6 +580,7 @@ xcal *new_xcal(void) {
/* Init method pointers */
p->del = xcal_del;
p->read_cgats = xcal_read_cgats;
+ p->read_icc = xcal_read_icc;
p->read = xcal_read;
p->write_cgats = xcal_write_cgats;
p->write = xcal_write;
diff --git a/xicc/xcal.h b/xicc/xcal.h
index 99f1c9c..f7c3fa9 100644
--- a/xicc/xcal.h
+++ b/xicc/xcal.h
@@ -29,6 +29,10 @@ struct _xcal {
/* Return nz if this fails (filename is for error messages) */
int (*read_cgats) (struct _xcal *p, cgats *cg, int table, char *filename);
+ /* Read a calibration file from an ICC vcgt tag */
+ /* Return nz if this fails */
+ int (*read_icc) (struct _xcal *p, icc *c);
+
/* Read a calibration file */
/* Return nz if this fails */
int (*read) (struct _xcal *p, char *filename);
@@ -41,7 +45,7 @@ struct _xcal {
/* Return nz if this fails */
int (*write)(struct _xcal *p, char *filename);
- /* Translate values through the curves curves. */
+ /* Translate values through the curves. */
void (*interp) (struct _xcal *p, double *out, double *in);
/* Translate a value backwards through the curves. */
@@ -56,6 +60,7 @@ struct _xcal {
double (*inv_interp_ch) (struct _xcal *p, int ch, double in);
int noramdac; /* Set to nz if there was no VideoLUT access */
+ int tvenc; /* nz if this cal was created using (16-235)/255 Video encoding */
/* Private: */
icProfileClassSignature devclass; /* Type of device */
diff --git a/xicc/xcam.c b/xicc/xcam.c
index 99f3c67..8e1a0cd 100644
--- a/xicc/xcam.c
+++ b/xicc/xcam.c
@@ -23,7 +23,7 @@
static void icx_cam_free(icxcam *s);
static int icx_set_view(icxcam *s, ViewingCondition Ev, double Wxyz[3],
- double La, double Yb, double Lv, double Yf, double Fxyz[3],
+ double La, double Yb, double Lv, double Yf, double Yg, double Gxyz[3],
int hk);
static int icx_XYZ_to_cam(struct _icxcam *s, double Jab[3], double XYZ[3]);
static int icx_cam_to_XYZ(struct _icxcam *s, double XYZ[3], double Jab[3]);
@@ -61,8 +61,8 @@ icxcam *new_icxcam(icxCAM which) {
}
/* Initialise methods */
- s->del = icx_cam_free;
- s->set_view = icx_set_view;
+ s->del = icx_cam_free;
+ s->set_view = icx_set_view;
s->XYZ_to_cam = icx_XYZ_to_cam;
s->cam_to_XYZ = icx_cam_to_XYZ;
s->settrace = settrace;
@@ -126,7 +126,8 @@ double Yb, /* Relative Luminance of Background to reference white */
double Lv, /* Luminance of white in the Viewing/Scene/Image field (cd/m^2) */
/* Ignored if Ev is set to other than vc_none */
double Yf, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
-double Fxyz[3], /* The Flare white coordinates (typically the Ambient color) */
+double Yg, /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */
+double Gxyz[3], /* The Glare white coordinates (typically the Ambient color) */
int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
) {
s->Wxyz[0] = Wxyz[0];
@@ -136,11 +137,11 @@ int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
switch(s->tag) {
case cam_CIECAM97s3: {
cam97s3 *pp = (cam97s3 *)s->p;
- return pp->set_view(pp, Ev, Wxyz, La, Yb, Lv, Yf, Fxyz, hk);
+ return pp->set_view(pp, Ev, Wxyz, La, Yb, Lv, 0.2 * Yg, Gxyz, hk);
}
case cam_CIECAM02: {
cam02 *pp = (cam02 *)s->p;
- return pp->set_view(pp, Ev, Wxyz, La, Yb, Lv, Yf, Fxyz, hk);
+ return pp->set_view(pp, Ev, Wxyz, La, Yb, Lv, Yf, Yg, Gxyz, hk);
}
default:
break;
diff --git a/xicc/xcam.h b/xicc/xcam.h
index d4f4857..7ec6949 100644
--- a/xicc/xcam.h
+++ b/xicc/xcam.h
@@ -49,7 +49,8 @@ struct _icxcam {
double Lv, /* Luminance of white in the Viewing/Scene/Image field (cd/m^2) */
/* Ignored if Ev is set */
double Yf, /* Flare as a fraction of the reference white (range 0.0 .. 1.0) */
- double Fxyz[3], /* The Flare white coordinates (typically the Ambient color) */
+ double Yg, /* Glare as a fraction of the ambient (range 0.0 .. 1.0) */
+ double Gxyz[3], /* The Glare white coordinates (typically the Ambient color) */
int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
);
diff --git a/xicc/xcolorantslu.c b/xicc/xcolorantslu.c
index 6c537dc..601c4bd 100644
--- a/xicc/xcolorantslu.c
+++ b/xicc/xcolorantslu.c
@@ -153,7 +153,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] = strtod(bp, &nbp);
if (nbp == bp)
diff --git a/xicc/xfit.c b/xicc/xfit.c
index 50916b3..d0912f9 100644
--- a/xicc/xfit.c
+++ b/xicc/xfit.c
@@ -21,8 +21,9 @@
* Need to use this for B2A tables rather than inverting
* A2B curves. Need to add grid sizing to cover just gamut range
* (including level axis gamut, but watch out for devices that
- * have values below the black point), 3x3 matrix optimization,
- * and white point to grid node mapping for B2A.
+ * have values below the black point or above the white point),
+ * 3x3 matrix optimization, and white point to grid node mapping for B2A.
+ * Currently code assumes output is always PCS ?? - would need to fix for opposite.
*
* Currently the Lab A2B output tables are adjusted for ab symetry
* to make the B2A white point land on a grid point, given that
@@ -78,6 +79,9 @@
#include "xfit.h"
#include "sort.h"
+#undef USE_XYZ_Y2LCURVE /* [Und] Use underlying L* curve for XYZ encoding */
+ /* This seems to work badly, even with high smoothness. Why ? */
+ /* It does speed up 1D lut creation though. */
#undef DEBUG /* Verbose debug information */
#undef DEBUG_PLOT /* Plot in & out curves */
@@ -110,6 +114,104 @@
#define PSHAPE_MINE 0.02 /* Minum background residual error level */
#define PSHAPE_DIST 1.0 /* Agressivness of grid distribution */
+
+/* - - - - - - - - - - - - - - - - - */
+
+/* Extra non-linearity used as base for XYZ output curves. */
+/* This makes the XYZ grid values more perceptual, and asks less */
+/* of the automatically created output curve shape. */
+/* (We assume XYZ is in 0..1 scale */
+
+/* Transfer function with offset and scale + Y2L curve */
+static double icxSTransFuncY2L(
+double *v, /* Pointer to first parameter */
+int luord, /* Number of parameters */
+double vv, /* Source of value */
+double min, /* Scale values */
+double max
+) {
+ max -= min;
+
+ vv = (vv - min)/max;
+
+#ifdef USE_XYZ_Y2LCURVE
+ if (vv > 0.008856451586)
+ vv = 1.16 * pow(vv,1.0/3.0) - 0.16;
+ else
+ vv = 9.032962896 * vv;
+#endif
+
+ vv = icxTransFunc(v, luord, vv);
+
+ vv = (vv * max) + min;
+ return vv;
+}
+
+/* Inverse Transfer function with offset and scale + Y2L */
+static double icxInvSTransFuncY2L(
+double *v, /* Pointer to first parameter */
+int luord, /* Number of parameters */
+double vv, /* Source of value */
+double min, /* Scale values */
+double max
+) {
+ max -= min;
+
+ vv = (vv - min)/max;
+ vv = icxInvTransFunc(v, luord, vv);
+
+#ifdef USE_XYZ_Y2LCURVE
+ if (vv > 0.08)
+ vv = pow((vv + 0.16)/1.16, 3.0);
+ else
+ vv = vv/9.032962896;
+#endif
+
+ vv = (vv * max) + min;
+
+ return vv;
+}
+
+/* Transfer function with offset and scale, and */
+/* partial derivative with respect to the */
+/* parameters and the input value. */
+static double icxdpdiSTransFuncY2L(
+double *v, /* Pointer to first parameter */
+double *dv, /* Return derivative wrt each parameter */
+double *pdin, /* Return derivative wrt source value */
+int luord, /* Number of parameters */
+double vv, /* Source of value */
+double min, /* Scale values */
+double max
+) {
+ int i;
+ double idv = 1.0;
+ max -= min;
+
+#ifdef USE_XYZ_Y2LCURVE
+ if (vv > 0.008856451586) {
+ vv = 1.16 * pow(vv,1.0/3.0) - 0.16;
+ idv = 1.16 / (3.0 * pow(vv, 2.0/3.0));
+ } else {
+ vv = 9.032962896 * vv;
+ idv = 9.032962896;
+ }
+#endif
+
+ vv = (vv - min)/max;
+
+ vv = icxdpdiTransFunc(v, dv, pdin, luord, vv);
+
+ *pdin *= idv; /* Account for input multiplier */
+
+ vv = (vv * max) + min;
+
+ for (i = 0; i < luord; i++) {
+ dv[i] *= max;
+ }
+ return vv;
+}
+
/* - - - - - - - - - - - - - - - - - */
#ifdef DEBUG
@@ -255,9 +357,15 @@ static void xfit_shmatsh(xfit *p, double *out, double *in) {
icxCubeInterp(p->v + p->mat_off, p->fdi, p->di, out, tin);
- for (f = 0; f < p->fdi; f++)
- out[f] = icxSTransFunc(p->v + p->out_offs[f], p->oluord[f], out[f],
- p->out_min[f], p->out_max[f]);
+ if (p->flags & XFIT_OUT_LAB) {
+ for (f = 0; f < p->fdi; f++)
+ out[f] = icxSTransFunc(p->v + p->out_offs[f], p->oluord[f], out[f],
+ p->out_min[f], p->out_max[f]);
+ } else {
+ for (f = 0; f < p->fdi; f++)
+ out[f] = icxSTransFuncY2L(p->v + p->out_offs[f], p->oluord[f], out[f],
+ p->out_min[f], p->out_max[f]);
+ }
}
/* - - - - - - - - - - - - - - - - - - - - */
@@ -453,10 +561,14 @@ static void xfit_invinpscurves(xfit *p, double *out, double *in) {
/* Lookup a value though an output curve */
static double xfit_outcurve(xfit *p, double in, int chan) {
double rv;
- if (p->tcomb & oc_o)
- rv = icxSTransFunc(p->v + p->out_offs[chan], p->oluord[chan], in,
- p->out_min[chan], p->out_max[chan]);
- else
+ if (p->tcomb & oc_o) {
+ if (p->flags & XFIT_OUT_LAB)
+ rv = icxSTransFunc(p->v + p->out_offs[chan], p->oluord[chan], in,
+ p->out_min[chan], p->out_max[chan]);
+ else
+ rv = icxSTransFuncY2L(p->v + p->out_offs[chan], p->oluord[chan], in,
+ p->out_min[chan], p->out_max[chan]);
+ } else
rv = in;
return rv;
}
@@ -465,22 +577,36 @@ static double xfit_outcurve(xfit *p, double in, int chan) {
static void xfit_outcurves(xfit *p, double *out, double *in) {
int f;
- for (f = 0; f < p->fdi; f++) {
- double val = in[f];
- if (p->tcomb & oc_o)
- val = icxSTransFunc(p->v + p->out_offs[f], p->oluord[f], val,
- p->out_min[f], p->out_max[f]);
- out[f] = val;
+ if (p->flags & XFIT_OUT_LAB) {
+ for (f = 0; f < p->fdi; f++) {
+ double val = in[f];
+ if (p->tcomb & oc_o)
+ val = icxSTransFunc(p->v + p->out_offs[f], p->oluord[f], val,
+ p->out_min[f], p->out_max[f]);
+ out[f] = val;
+ }
+ } else {
+ for (f = 0; f < p->fdi; f++) {
+ double val = in[f];
+ if (p->tcomb & oc_o)
+ val = icxSTransFuncY2L(p->v + p->out_offs[f], p->oluord[f], val,
+ p->out_min[f], p->out_max[f]);
+ out[f] = val;
+ }
}
}
/* Inverse Lookup a value though an output curve */
static double xfit_invoutcurve(xfit *p, double in, int chan) {
double rv;
- if (p->tcomb & oc_o)
- rv = icxInvSTransFunc(p->v + p->out_offs[chan], p->oluord[chan], in,
- p->out_min[chan], p->out_max[chan]);
- else
+ if (p->tcomb & oc_o) {
+ if (p->flags & XFIT_OUT_LAB)
+ rv = icxInvSTransFunc(p->v + p->out_offs[chan], p->oluord[chan], in,
+ p->out_min[chan], p->out_max[chan]);
+ else
+ rv = icxInvSTransFuncY2L(p->v + p->out_offs[chan], p->oluord[chan], in,
+ p->out_min[chan], p->out_max[chan]);
+ } else
rv = in;
return rv;
}
@@ -489,12 +615,22 @@ static double xfit_invoutcurve(xfit *p, double in, int chan) {
static void xfit_invoutcurves(xfit *p, double *out, double *in) {
int f;
- for (f = 0; f < p->fdi; f++) {
- double val = in[f];
- if (p->tcomb & oc_o)
- val = icxInvSTransFunc(p->v + p->out_offs[f], p->oluord[f], val,
- p->out_min[f], p->out_max[f]);
- out[f] = val;
+ if (p->flags & XFIT_OUT_LAB) {
+ for (f = 0; f < p->fdi; f++) {
+ double val = in[f];
+ if (p->tcomb & oc_o)
+ val = icxInvSTransFunc(p->v + p->out_offs[f], p->oluord[f], val,
+ p->out_min[f], p->out_max[f]);
+ out[f] = val;
+ }
+ } else {
+ for (f = 0; f < p->fdi; f++) {
+ double val = in[f];
+ if (p->tcomb & oc_o)
+ val = icxInvSTransFuncY2L(p->v + p->out_offs[f], p->oluord[f], val,
+ p->out_min[f], p->out_max[f]);
+ out[f] = val;
+ }
}
}
@@ -742,9 +878,15 @@ static double xfitfunc(void *edata, double *v) {
icxCubeInterp(p->v + p->mat_off, fdi, di, out, tin);
/* Apply output channel curves */
- for (f = 0; f < fdi; f++)
- out[f] = icxSTransFunc(p->v + p->out_offs[f], p->oluord[f], out[f],
- p->out_min[f], p->out_max[f]);
+ for (f = 0; f < fdi; f++) {
+ if (p->flags & XFIT_OUT_LAB) {
+ out[f] = icxSTransFunc(p->v + p->out_offs[f], p->oluord[f], out[f],
+ p->out_min[f], p->out_max[f]);
+ } else {
+ out[f] = icxSTransFuncY2L(p->v + p->out_offs[f], p->oluord[f], out[f],
+ p->out_min[f], p->out_max[f]);
+ }
+ }
/* Evaluate the error squared */
if (p->flags & XFIT_FM_INPUT) {
@@ -849,11 +991,16 @@ static double dxfitfunc(void *edata, double *dv, double *v) {
icxdpdiCubeInterp(p->v + p->mat_off, dmato_mv, dmato_tin, fdi, di, out, tin);
/* Apply output channel curves */
- for (f = 0; f < fdi; f++)
- out[f] = icxdpdiSTransFunc(p->v + p->out_offs[f],
- &dout_ov[p->out_offs[f] - p->out_off], &dout_mato[f],
- p->oluord[f], out[f], p->out_min[f], p->out_max[f]);
-
+ for (f = 0; f < fdi; f++) {
+ if (p->flags & XFIT_OUT_LAB)
+ out[f] = icxdpdiSTransFunc(p->v + p->out_offs[f],
+ &dout_ov[p->out_offs[f] - p->out_off], &dout_mato[f],
+ p->oluord[f], out[f], p->out_min[f], p->out_max[f]);
+ else
+ out[f] = icxdpdiSTransFuncY2L(p->v + p->out_offs[f],
+ &dout_ov[p->out_offs[f] - p->out_off], &dout_mato[f],
+ p->oluord[f], out[f], p->out_min[f], p->out_max[f]);
+ }
/* Convert to Delta E and compute pde's into dout_de squared */
if (p->flags & XFIT_FM_INPUT) {
@@ -1095,7 +1242,11 @@ static double symoptfunc(void *edata, double *v) {
/* Copy the parameter being tested back into xfit */
p->v[p->out_offs[ch]] = v[0];
- *out = icxSTransFunc(p->v + p->out_offs[ch], p->oluord[ch], *in,
+ if (p->flags & XFIT_OUT_LAB)
+ *out = icxSTransFunc(p->v + p->out_offs[ch], p->oluord[ch], *in,
+ p->out_min[ch], p->out_max[ch]);
+ else
+ *out = icxSTransFuncY2L(p->v + p->out_offs[ch], p->oluord[ch], *in,
p->out_min[ch], p->out_max[ch]);
rv = out[0] * out[0];
@@ -1253,10 +1404,14 @@ static void setup_piv(xfit *p) {
icxCubeInterp(p->v + p->mat_off, fdi, di, vv, vv);
/* Apply output channel curves */
- for (f = 0; f < fdi; f++)
- vv[f] = icxSTransFunc(p->v + p->out_offs[f], p->oluord[f], vv[f],
- p->out_min[f], p->out_max[f]);
-
+ for (f = 0; f < fdi; f++) {
+ if (p->flags & XFIT_OUT_LAB)
+ vv[f] = icxSTransFunc(p->v + p->out_offs[f], p->oluord[f], vv[f],
+ p->out_min[f], p->out_max[f]);
+ else
+ vv[f] = icxSTransFuncY2L(p->v + p->out_offs[f], p->oluord[f], vv[f],
+ p->out_min[f], p->out_max[f]);
+ }
for (e = 0; e < di; e++) {
double tt[MXDIDO];
@@ -1272,10 +1427,14 @@ static void setup_piv(xfit *p) {
icxCubeInterp(p->v + p->mat_off, fdi, di, tt, tt);
/* Apply output channel curves */
- for (f = 0; f < fdi; f++)
- tt[f] = icxSTransFunc(p->v + p->out_offs[f], p->oluord[f], tt[f],
- p->out_min[f], p->out_max[f]);
-
+ for (f = 0; f < fdi; f++) {
+ if (p->flags & XFIT_OUT_LAB)
+ tt[f] = icxSTransFunc(p->v + p->out_offs[f], p->oluord[f], tt[f],
+ p->out_min[f], p->out_max[f]);
+ else
+ tt[f] = icxSTransFuncY2L(p->v + p->out_offs[f], p->oluord[f], tt[f],
+ p->out_min[f], p->out_max[f]);
+ }
for (f = 0; f < p->fdi; f++)
pd[f][e] = (tt[f] - vv[f])/1e-4;
@@ -1502,6 +1661,7 @@ int xfit_fit(
double out_max[MXDO], /* Output value scaling/range maximum */
double smooth, /* clut rspl smoothing factor */
double oavgdev[MXDO], /* Average output value deviation */
+ double demph, /* dark emphasis factor for cLUT grid res. */
int iord[], /* Order of input pos/shaper curve for each dimension */
int sord[], /* Order of input sub-grid shaper curve (not used) */
int oord[], /* Order of output shaper curve for each dimension */
@@ -2110,6 +2270,7 @@ dump_xfit(p);
pgp[i].v = vv;
else
pgp[i].v = pgp[i-1].v + vv;
+
}
resid->del(resid);
@@ -2120,7 +2281,10 @@ dump_xfit(p);
for (i = 0; i < NPGP; i++) {
pgp[i].v = (pgp[i].v - vo)/vs;
-//printf("~1 guide point %d: %f -> %f\n",i,pgp[i].p,pgp[i].v);
+ /* Apply any dark emphasis */
+ if (demph > 1.0) {
+ pgp[i].v = icx_powlike(pgp[i].v, 1.0/demph);
+ }
}
/* Fit the non-monotonic parameters to the guide points */
if ((posc = new_mcv_noos()) == NULL)
@@ -2237,19 +2401,13 @@ dump_xfit(p);
p->skm->lookup(p->skm, skval, p->ipoints[i].p);
xfit_abs_to_rel(p, skval, skval);
xfit_invoutcurves(p, skval, skval);
-
-//printf("~1 point %d at %f %f %f, targ %f %f %f skm %f %f %f\n",
-//i,p->ipoints[i].p[0],p->ipoints[i].p[1],p->ipoints[i].p[2],
-//p->rpoints[i].v[0],p->rpoints[i].v[1],p->rpoints[i].v[2],
-//skval[0], skval[1], skval[2]);
+//printf("~1 point %d at %f %f %f, targ %f %f %f skm %f %f %f\n", i,p->ipoints[i].p[0],p->ipoints[i].p[1],p->ipoints[i].p[2], p->rpoints[i].v[0],p->rpoints[i].v[1],p->rpoints[i].v[2], skval[0], skval[1], skval[2]);
/* Subtract it from value at this point, */
/* so rspl will fit difference to skeleton model */
for (f = 0; f < fdi; f++)
p->rpoints[i].v[f] -= skval[f];
}
-//printf("~1 point %d, w %f, %f %f %f %f -> %f %f %f\n",
-//i,p->rpoints[i].w,p->rpoints[i].p[0], p->rpoints[i].p[1], p->rpoints[i].p[2], p->rpoints[i].p[3],
-//p->rpoints[i].v[0], p->rpoints[i].v[1], p->rpoints[i].v[2]);
+//printf("~1 point %d, w %f, %f %f %f %f -> %f %f %f\n",i,p->rpoints[i].w,p->rpoints[i].p[0], p->rpoints[i].p[1], p->rpoints[i].p[2], p->rpoints[i].p[3],p->rpoints[i].v[0], p->rpoints[i].v[1], p->rpoints[i].v[2]);
}
/* Create ipos[] arrays, that hold the shaper space */
@@ -2342,6 +2500,7 @@ printf("~1 ipos[%d][%d] = %f\n",e,i,cv);
// p->clut->fit_rspl_w_df(p->clut, rsplflags, p->rpoints, p->nodp, in_min, in_max, gres,
// out_min, out_max, smooth, oavgdev, ipos, 1.0, (void *)p, skm_weak);
// } else
+ /* Normal multi-d scattered point fitting */
p->clut->fit_rspl_w(p->clut, rsplflags, p->rpoints, p->nodp, in_min, in_max, gres,
out_min, out_max, smooth, oavgdev, ipos);
#endif
diff --git a/xicc/xfit.h b/xicc/xfit.h
index f9f3233..519f071 100644
--- a/xicc/xfit.h
+++ b/xicc/xfit.h
@@ -55,8 +55,8 @@ typedef enum {
#define XFIT_OUT_WP_REL 0x0010 /* Extract the white point and make output relative */
#define XFIT_OUT_WP_REL_US 0x0030 /* Same as above but scale to avoid clipping above WP */
#define XFIT_OUT_WP_REL_C 0x0050 /* Same as above but clip any cLUT values over D50 */
-#define XFIT_CLIP_WP 0x0080 /* Clip white point to have Y <= 1.0 (conflict with above) */
-#define XFIT_OUT_LAB 0x0100 /* Output space is LAB else XYZ for reading WP */
+#define XFIT_CLIP_WP 0x0080 /* Clip white point to have Y <= 1.0 (conflict with above) */
+#define XFIT_OUT_LAB 0x0100 /* Output space is LAB else XYZ for reading WP */
#define XFIT_OUT_ZERO 0x0200 /* Adjust output curves 1 & 2 for zero */
@@ -172,6 +172,7 @@ struct _xfit {
double out_max[MXDO], /* Output value scaling/range maximum */
double smooth, /* clut rspl smoothing factor */
double oavgdev[MXDO], /* Average output value deviation */
+ double demph, /* dark emphasis factor for cLUT grid res. */
int iord[], /* Order of input positioning/shaper curve for each dimension */
int sord[], /* Order of input sub-grid shaper curve (not used) */
int oord[], /* Order of output shaper curve for each dimension */
diff --git a/xicc/xicc.c b/xicc/xicc.c
index 9b6d867..a1c4531 100644
--- a/xicc/xicc.c
+++ b/xicc/xicc.c
@@ -61,7 +61,7 @@ static icxLuBase *xicc_set_luobj(xicc *p, icmLookupFunc func, icRenderingIntent
icmLookupOrder order, int flags, int no, int nobw, cow *points,
icxMatrixModel *skm,
double dispLuminance, double wpscale, double smooth, double avgdev,
- icxViewCond *vc, icxInk *ink, xcal *cal, int quality);
+ double demph, icxViewCond *vc, icxInk *ink, xcal *cal, int quality);
static void icxLutSpaces(icxLuBase *p, icColorSpaceSignature *ins, int *inn,
icColorSpaceSignature *outs, int *outn,
icColorSpaceSignature *pcs);
@@ -929,7 +929,7 @@ icxInk *ink /* inking details (NULL for default) */
icRenderingIntent n_intent = intent; /* Native Intent to request */
icColorSpaceSignature n_pcs = icmSigDefaultData; /* Native PCS to request */
-//printf("~1 xicc_get_luobj got intent %s and pcsor %s\n",icx2str(icmRenderingIntent,intent),icx2str(icmColorSpaceSignature,pcsor));
+//printf("~1 xicc_get_luobj got intent '%s' and pcsor '%s'\n",icx2str(icmRenderingIntent,intent),icx2str(icmColorSpaceSignature,pcsor));
/* Ensure that appropriate PCS is slected for an appearance intent */
if (intent == icxAppearance
@@ -939,6 +939,7 @@ icxInk *ink /* inking details (NULL for default) */
|| intent == icxSaturationAppearance
|| intent == icxAbsSaturationAppearance) {
pcsor = icxSigJabData;
+//printf("~1 pcsor = %s\n",tag2str(pcsor));
/* Translate non-Jab intents to the equivalent appearance "intent" if pcsor == Jab. */
/* This is how we get these when the UI's don't list all the apperances intents, */
@@ -960,6 +961,7 @@ icxInk *ink /* inking details (NULL for default) */
else
intent = icxAppearance;
}
+//printf("~1 intent = %s\n",tag2str(intent));
/* Translate intent asked for into intent needed in icclib */
if (intent == icxAppearance
@@ -971,6 +973,7 @@ icxInk *ink /* inking details (NULL for default) */
else if (intent == icxSaturationAppearance
|| intent == icxAbsSaturationAppearance)
n_intent = icmAbsoluteSaturation;
+//printf("~1 n_intent = %s\n",tag2str(n_intent));
if (pcsor != icmSigDefaultData)
n_pcs = pcsor; /* There is an icclib override */
@@ -995,15 +998,16 @@ icxInk *ink /* inking details (NULL for default) */
&& (intent == icxAbsAppearance
|| intent == icxAbsPerceptualAppearance
|| intent == icxAbsSaturationAppearance)) { /* make sure its "Abs CAM" */
+//printf("~1 xicc_get_luobj using absolute apperance space with white = D50\n");
/* Set white point and flare color to D50 */
/* (Hmm. This doesn't match what happens within collink with absolute intent!!) */
vc->Wxyz[0] = icmD50.X/icmD50.Y;
vc->Wxyz[1] = icmD50.Y/icmD50.Y; // Normalise white reference to Y = 1 ?
vc->Wxyz[2] = icmD50.Z/icmD50.Y;
- vc->Fxyz[0] = icmD50.X;
- vc->Fxyz[1] = icmD50.Y;
- vc->Fxyz[2] = icmD50.Z;
+ vc->Gxyz[0] = icmD50.X;
+ vc->Gxyz[1] = icmD50.Y;
+ vc->Gxyz[2] = icmD50.Z;
}
/* Call xiccLu wrapper creation */
@@ -1054,6 +1058,7 @@ double dispLuminance, /* > 0.0 if display luminance value and is known */
double wpscale, /* > 0.0 if input white point is to be scaled */
double smooth, /* RSPL smoothing factor, -ve if raw */
double avgdev, /* reading Average Deviation as a proportion of the input range */
+double demph, /* dark emphasis factor for cLUT grid res. */
icxViewCond *vc, /* Viewing Condition (NULL if not using CAM) */
icxInk *ink, /* inking details (NULL for default) */
xcal *cal, /* Optional cal, will override any existing (not deleted with xicc)*/
@@ -1104,7 +1109,7 @@ int quality /* Quality metric, 0..3 */
case icmLutType:
/* ~~~ Should add check that it is a fwd profile ~~~ */
- xplu = set_icxLuLut(p, plu, func, intent, flags, no, nobw, points, skm, dispLuminance, wpscale, smooth, avgdev, vc, ink, quality);
+ xplu = set_icxLuLut(p, plu, func, intent, flags, no, nobw, points, skm, dispLuminance, wpscale, smooth, avgdev, demph, vc, ink, quality);
break;
default:
@@ -1217,7 +1222,8 @@ icxViewCond *vc /* Viewing parameters to return */
double Lvr = -1.0; /* Reflective device image luminance */
double Lv = -1.0; /* device image luminance */
double Yf = -1.0; /* Flare relative luminance to Lv */
- double Fxyz[3] = {-1.0, -1.0, -1.0}; /* Flare color */
+ double Yg = -1.0; /* Glare relative luminance to La */
+ double Gxyz[3] = {-1.0, -1.0, -1.0}; /* Glare color */
icTechnologySignature tsig = icMaxEnumTechnology; /* Technology Signature */
icProfileClassSignature devc = icMaxEnumClass;
int trans = -1; /* Set to 0 if not transparency, 1 if it is */
@@ -1241,7 +1247,8 @@ icxViewCond *vc /* Viewing parameters to return */
if ((ro = (icmMeasurement *)pp->read_tag(pp, icSigMeasurementTag)) != NULL
&& ro->ttype == icSigMeasurementType) {
- Yf = ro->flare;
+ Yf = 0.0 * ro->flare; // ?????
+ Yg = 1.0 * ro->flare; // ?????
/* ro->illuminant ie D50, D65, D93, A etc. */
}
}
@@ -1345,7 +1352,8 @@ icxViewCond *vc /* Viewing parameters to return */
printf("Reflective Image White Lvr = %f\n",Lvr);
printf("Device Image White Lv = %f\n",Lv);
printf("Relative Flare Yf = %f\n",Yf);
- printf("Flare color %f %f %f\n",Fxyz[0], Fxyz[1], Fxyz[2]);
+ printf("Relative Glare Yg = %f\n",Yg);
+ printf("Glare color %f %f %f\n",Gxyz[0], Gxyz[1], Gxyz[2]);
printf("Technology = %s\n",tag2str(tsig));
printf("deviceClass = %s\n",tag2str(devc));
printf("Transparency = %d\n",trans);
@@ -1357,7 +1365,8 @@ icxViewCond *vc /* Viewing parameters to return */
&& Yb >= 0.0
&& Lv >= 0.0
&& Yf >= 0.0
- && Fxyz[0] >= 0.0 && Fxyz[1] >= 0.0 && Fxyz[2] >= 0.0) {
+ && Yg >= 0.0
+ && Gxyz[0] >= 0.0 && Gxyz[1] >= 0.0 && Gxyz[2] >= 0.0) {
vc->Ev = vc_none;
vc->Wxyz[0] = Wxyz[0];
@@ -1367,9 +1376,10 @@ icxViewCond *vc /* Viewing parameters to return */
vc->Yb = Yb;
vc->Lv = Lv;
vc->Yf = Yf;
- vc->Fxyz[0] = Fxyz[0];
- vc->Fxyz[1] = Fxyz[1];
- vc->Fxyz[2] = Fxyz[2];
+ vc->Yg = Yg;
+ vc->Gxyz[0] = Gxyz[0];
+ vc->Gxyz[1] = Gxyz[1];
+ vc->Gxyz[2] = Gxyz[2];
return 0;
}
@@ -1400,9 +1410,11 @@ icxViewCond *vc /* Viewing parameters to return */
if (Lv < 0.0) /* No device image luminance */
Ev = vc_average; /* Assume average viewing conditions */
if (Yf < 0.0) /* No flare figure */
- Yf = 0.01; /* Assume 1% flare */
- if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */
- Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2];
+ Yf = 0.0; /* Assume 0% flare */
+ if (Yg < 0.0) /* No glare figure */
+ Yg = 0.01; /* Assume 1% glare */
+ if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */
+ Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2];
break;
}
@@ -1419,9 +1431,11 @@ icxViewCond *vc /* Viewing parameters to return */
if (Lv < 0.0) /* No device image luminance */
Ev = vc_average; /* Assume average viewing conditions */
if (Yf < 0.0) /* No flare figure */
- Yf = 0.005; /* Assume 0.5% flare */
- if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */
- Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2];
+ Yf = 0.0; /* Assume 0% flare */
+ if (Yg < 0.0) /* No glare figure */
+ Yg = 0.01; /* Assume 1% glare */
+ if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */
+ Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2];
break;
}
@@ -1437,8 +1451,12 @@ icxViewCond *vc /* Viewing parameters to return */
Ev = vc_average; /* Assume average viewing conditions */
if (Yf < 0.0) /* No flare figure */
Yf = 0.0; /* Assume 0% flare */
- if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */
- Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2];
+ if (Yg < 0.0) /* No glare figure */
+ Yg = 0.01; /* Assume 1% glare */
+ if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */
+ Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2];
+ if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */
+ Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2];
break;
}
@@ -1453,9 +1471,11 @@ icxViewCond *vc /* Viewing parameters to return */
if (Lv < 0.0) /* No device image luminance */
Ev = vc_dim; /* Assume dim viewing conditions */
if (Yf < 0.0) /* No flare figure */
- Yf = 0.01; /* Assume 1% flare */
- if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */
- Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2];
+ Yf = 0.0; /* Assume 0% flare */
+ if (Yg < 0.0) /* No glare figure */
+ Yg = 0.01; /* Assume 1% glare */
+ if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */
+ Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2];
break;
}
@@ -1472,9 +1492,11 @@ icxViewCond *vc /* Viewing parameters to return */
if (Lv < 0.0) /* No device image luminance */
Ev = vc_average; /* Assume average viewing conditions */
if (Yf < 0.0) /* No flare figure */
- Yf = 0.02; /* Assume 2% flare */
- if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */
- Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2];
+ Yf = 0.0; /* Assume 0% flare */
+ if (Yg < 0.0) /* No glare figure */
+ Yg = 0.01; /* Assume 1% glare */
+ if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */
+ Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2];
break;
}
@@ -1489,9 +1511,11 @@ icxViewCond *vc /* Viewing parameters to return */
if (Lv < 0.0) /* No device image luminance */
Ev = vc_average; /* Assume average viewing conditions */
if (Yf < 0.0) /* No flare figure */
- Yf = 0.00; /* Assume 0% flare */
- if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */
- Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2];
+ Yf = 0.0; /* Assume 0% flare */
+ if (Yg < 0.0) /* No glare figure */
+ Yg = 0.0; /* Assume 0% glare */
+ if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */
+ Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2];
break;
}
@@ -1507,9 +1531,11 @@ icxViewCond *vc /* Viewing parameters to return */
if (Lv < 0.0) /* No device image luminance */
Ev = vc_dim; /* Dim environment */
if (Yf < 0.0) /* No flare figure */
- Yf = 0.01; /* Assume 1% flare */
- if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */
- Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2];
+ Yf = 0.0; /* Assume 0% flare */
+ if (Yg < 0.0) /* No glare figure */
+ Yg = 0.01; /* Assume 1% glare */
+ if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */
+ Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2];
break;
}
/* Assume very darkened room, no background */
@@ -1522,9 +1548,11 @@ icxViewCond *vc /* Viewing parameters to return */
if (Lv < 0.0) /* No device image luminance */
Ev = vc_dark; /* Dark environment */
if (Yf < 0.0) /* No flare figure */
- Yf = 0.01; /* Assume 1% flare */
- if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */
- Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2];
+ Yf = 0.0; /* Assume 0% flare */
+ if (Yg < 0.0) /* No glare figure */
+ Yg = 0.01; /* Assume 1% glare */
+ if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */
+ Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2];
break;
}
@@ -1549,9 +1577,11 @@ icxViewCond *vc /* Viewing parameters to return */
if (Lv < 0.0) /* No device image luminance */
Ev = vc_average; /* Assume average viewing conditions */
if (Yf < 0.0) /* No flare figure */
- Yf = 0.01; /* Assume 1% flare */
- if (Fxyz[0] < 0.0 || Fxyz[1] < 0.0 || Fxyz[2] < 0.0) /* No flare color */
- Fxyz[0] = Wxyz[0], Fxyz[1] = Wxyz[1], Fxyz[2] = Wxyz[2];
+ Yf = 0.0; /* Assume 0% flare */
+ if (Yg < 0.0) /* No glare figure */
+ Yg = 0.01; /* Assume 1% glare */
+ if (Gxyz[0] < 0.0 || Gxyz[1] < 0.0 || Gxyz[2] < 0.0) /* No flare color */
+ Gxyz[0] = Wxyz[0], Gxyz[1] = Wxyz[1], Gxyz[2] = Wxyz[2];
break;
}
@@ -1623,19 +1653,20 @@ double *wp /* Provide white point if xicc is NULL */
}
}
- /* Set a default flare color */
- vc->Fxyz[0] = vc->Wxyz[0];
- vc->Fxyz[1] = vc->Wxyz[1];
- vc->Fxyz[2] = vc->Wxyz[2];
+ /* Set a default Glare color */
+ vc->Gxyz[0] = vc->Wxyz[0];
+ vc->Gxyz[1] = vc->Wxyz[1];
+ vc->Gxyz[2] = vc->Wxyz[2];
}
/*
Typical adapting field luminances and white luminance in reflective setup:
+ (Note that displays Lv is typically brighter under the same conditions)
E = illuminance in Lux
- Lv = White luminance assuming 100% reflectance
La = Adapting field luminance in cd/m^2, assuming 20% reflectance from surround
+ Lv = White luminance assuming 100% reflectance
E La Lv Condition
11 0.7 4 Twilight
@@ -1651,6 +1682,22 @@ double *wp /* Provide white point if xicc is NULL */
10000 637 3183 Typical outdoors, full daylight
50000 3185 15915 Bright summers day
+ Display numbers:
+
+ SMPTE video standard white 100
+ SMPTE cinema standard white 55
+
+ Flare is image content dependent, and is typically 1% from factors
+ including display self illumination and observer/camera internal
+ stray light. Because image content is not static, using a 1% of white point
+ flare results quite erronious appearance modelling for predominantly
+ dark images. As a result, it is best to default to a Yf of 0%,
+ and only introduce a higher number depending on the known image content.
+
+ Glare is assumed to be from the ambient light reflecting from the display
+ and also striking the observer directly, and is (typically) defaulted
+ to 1% of ambient here.
+
*/
if (no == -1
@@ -1663,7 +1710,22 @@ double *wp /* Provide white point if xicc is NULL */
vc->La = 50.0; /* Practical to Good lighting */
vc->Lv = 250.0; /* Average viewing conditions ratio */
vc->Yb = 0.2; /* Grey world */
- vc->Yf = 0.01; /* 1% flare */
+ vc->Yf = 0.0; /* 0% flare */
+ vc->Yg = 0.01; /* 1% glare */
+ }
+ }
+ else if (no == 2
+ || (as != NULL && stricmp(as,"pc") == 0)) {
+
+ no = 2;
+ if (vc != NULL) {
+ vc->desc = " pc - Critical print evaluation environment (ISO-3664 P1)";
+ vc->Ev = vc_average; /* Average viewing conditions */
+ vc->La = 127.0; /* 0.2 * Lv ? */
+ vc->Lv = 2000.0/3.1415; /* White of the image field */
+ vc->Yb = 0.2; /* Grey world */
+ vc->Yf = 0.0; /* 0% flare */
+ vc->Yg = 0.01; /* 1% glare */
}
}
else if (no == 0
@@ -1672,10 +1734,12 @@ double *wp /* Provide white point if xicc is NULL */
no = 0;
if (vc != NULL) {
vc->desc = " pp - Practical Reflection Print (ISO-3664 P2)";
- vc->Ev = vc_average; /* Average viewing conditions */
- vc->La = 32.0; /* Use a practical print evaluation number */
+ vc->Ev = vc_none; /* Use explicit La/Lv */
+ vc->La = 32.0; /* 0.2 * Lv ? */
+ vc->Lv = 500.0/3.1415; /* White of the image field */
vc->Yb = 0.2; /* Grey world */
- vc->Yf = 0.01; /* 1% flare */
+ vc->Yf = 0.0; /* 0% flare */
+ vc->Yg = 0.01; /* 1% glare */
}
}
else if (no == 1
@@ -1684,22 +1748,26 @@ double *wp /* Provide white point if xicc is NULL */
no = 1;
if (vc != NULL) {
vc->desc = " pe - Print evaluation environment (CIE 116-1995)";
- vc->Ev = vc_average; /* Average viewing conditions */
- vc->La = 64.0; /* Good */
+ vc->Ev = vc_none; /* Use explicit La/Lv */
+ vc->La = 30.0; /* 0.2 * Lv ? */
+ vc->Lv = 150.0; /* White of the image field */
vc->Yb = 0.2; /* Grey world */
- vc->Yf = 0.01; /* 1% flare */
+ vc->Yf = 0.0; /* 0% flare */
+ vc->Yg = 0.01; /* 1% glare */
}
}
- else if (no == 2
- || (as != NULL && stricmp(as,"pc") == 0)) {
+ else if (no == 4
+ || (as != NULL && stricmp(as,"mb") == 0)) {
- no = 2;
+ no = 4;
if (vc != NULL) {
- vc->desc = " pc - Critical print evaluation environment (ISO-3664 P1)";
- vc->Ev = vc_average; /* Average viewing conditions */
- vc->La = 127.0; /* Critical */
+ vc->desc = " mb - Bright monitor in bright work environment";
+ vc->Ev = vc_none; /* Use explicit La/Lv */
+ vc->La = 42.0; /* Bright work environment */
+ vc->Lv = 150.0; /* White of the image field */
vc->Yb = 0.2; /* Grey world */
- vc->Yf = 0.01; /* 1% flare */
+ vc->Yf = 0.0; /* 0% flare */
+ vc->Yg = 0.01; /* 1% glare */
}
}
else if (no == 3
@@ -1708,22 +1776,12 @@ double *wp /* Provide white point if xicc is NULL */
no = 3;
if (vc != NULL) {
vc->desc = " mt - Monitor in typical work environment";
- vc->Ev = vc_average; /* Average viewing conditions */
+ vc->Ev = vc_none; /* Use explicit La/Lv */
vc->La = 22.0; /* Typical work environment */
+ vc->Lv = 120.0; /* White of the image field */
vc->Yb = 0.2; /* Grey world */
- vc->Yf = 0.02; /* 2% flare */
- }
- }
- else if (no == 4
- || (as != NULL && stricmp(as,"mb") == 0)) {
-
- no = 4;
- if (vc != NULL) {
- vc->desc = " mb - Bright monitor in bright work environment";
- vc->Ev = vc_average; /* Average viewing conditions */
- vc->La = 42.0; /* Bright work environment */
- vc->Yb = 0.2; /* Grey world */
- vc->Yf = 0.02; /* 2% flare */
+ vc->Yf = 0.0; /* 0% flare */
+ vc->Yg = 0.01; /* 1% glare */
}
}
else if (no == 5
@@ -1732,10 +1790,12 @@ double *wp /* Provide white point if xicc is NULL */
no = 5;
if (vc != NULL) {
vc->desc = " md - Monitor in darkened work environment";
- vc->Ev = vc_dim; /* Dim viewing conditions */
- vc->La = 4.0; /* Darkened work environment */
+ vc->Ev = vc_none; /* Use explicit La/Lv */
+ vc->La = 10.0; /* Darkened work environment */
+ vc->Lv = 100.0; /* White of the image field */
vc->Yb = 0.2; /* Grey world */
- vc->Yf = 0.01; /* 1% flare */
+ vc->Yf = 0.0; /* 0% flare */
+ vc->Yg = 0.01; /* 1% glare */
}
}
else if (no == 6
@@ -1744,10 +1804,12 @@ double *wp /* Provide white point if xicc is NULL */
no = 6;
if (vc != NULL) {
vc->desc = " jm - Projector in dim environment";
- vc->Ev = vc_dim; /* Dim viewing conditions */
+ vc->Ev = vc_none; /* Use explicit La/Lv */
vc->La = 10.0; /* Adaptation is from display */
+ vc->Lv = 80.0; /* White of the image field */
vc->Yb = 0.2; /* Grey world */
- vc->Yf = 0.01; /* 1% flare */
+ vc->Yf = 0.0; /* 0% flare */
+ vc->Yg = 0.01; /* 1% glare */
}
}
else if (no == 7
@@ -1756,46 +1818,65 @@ double *wp /* Provide white point if xicc is NULL */
no = 7;
if (vc != NULL) {
vc->desc = " jd - Projector in dark environment";
- vc->Ev = vc_dark; /* Dark viewing conditions */
- vc->La = 10.0; /* Adaptation is from display */
+ vc->Ev = vc_none; /* Use explicit La/Lv */
+ vc->La = 8.0; /* Adaptation is from display */
+ vc->Lv = 80.0; /* White of the image field */
vc->Yb = 0.2; /* Grey world */
- vc->Yf = 0.01; /* 1% flare ? */
+ vc->Yf = 0.0; /* 0% flare */
+ vc->Yg = 0.01; /* 1% glare */
}
}
else if (no == 8
- || (as != NULL && stricmp(as,"pcd") == 0)) {
+ || (as != NULL && stricmp(as,"tv") == 0)) {
no = 8;
if (vc != NULL) {
+ vc->desc = " tv - Television/Film Studio";
+ vc->Ev = vc_none; /* Compute from La/Lv */
+ vc->La = 0.2 * 1000.0/3.1415; /* Adative/Surround */
+ vc->Yb = 0.2; /* Grey world */
+ vc->Lv = 1000.0/3.1415; /* White of the image field */
+ vc->Yf = 0.0; /* 0% flare */
+ vc->Yg = 0.01; /* 1% glare */
+ }
+ }
+ else if (no == 9
+ || (as != NULL && stricmp(as,"pcd") == 0)) {
+
+ no = 9;
+ if (vc != NULL) {
vc->desc = "pcd - Photo CD - original scene outdoors";
vc->Ev = vc_average; /* Average viewing conditions */
vc->La = 320.0; /* Typical outdoors, 1600 cd/m^2 */
vc->Yb = 0.2; /* Grey world */
- vc->Yf = 0.00; /* 0% flare */
+ vc->Yf = 0.0; /* 0% flare */
+ vc->Yg = 0.0; /* 0% glare - assumed to be compensated ? */
}
}
- else if (no == 9
+ else if (no == 10
|| (as != NULL && stricmp(as,"ob") == 0)) {
- no = 9;
+ no = 10;
if (vc != NULL) {
vc->desc = " ob - Original scene - Bright Outdoors";
vc->Ev = vc_average; /* Average viewing conditions */
vc->La = 2000.0; /* Bright Outdoors */
vc->Yb = 0.2; /* Grey world */
- vc->Yf = 0.00; /* 0% flare */
+ vc->Yf = 0.0; /* 0% flare */
+ vc->Yg = 0.0; /* 0% glare - assumed to be compensated ? */
}
}
- else if (no == 10
+ else if (no == 11
|| (as != NULL && stricmp(as,"cx") == 0)) {
- no = 10;
+ no = 11;
if (vc != NULL) {
vc->desc = " cx - Cut Sheet Transparencies on a viewing box";
vc->Ev = vc_cut_sheet; /* Cut sheet viewing conditions */
vc->La = 53.0; /* Dim, adapted to slide ? */
vc->Yb = 0.2; /* Grey world */
- vc->Yf = 0.01; /* 1% flare ? */
+ vc->Yf = 0.0; /* 0% flare */
+ vc->Yg = 0.01; /* 1% glare */
}
}
else {
@@ -1828,7 +1909,8 @@ icxViewCond *vc
if (vc->Ev == vc_none)
printf(" Image luminance = %f cd/m^2\n",vc->Lv);
printf(" Flare to image ratio = %f\n",vc->Yf);
- printf(" Flare color = %f %f %f\n",vc->Fxyz[0], vc->Fxyz[1], vc->Fxyz[2]);
+ printf(" Glare to ambient ratio = %f\n",vc->Yg);
+ printf(" Flare color = %f %f %f\n",vc->Gxyz[0], vc->Gxyz[1], vc->Gxyz[2]);
}
@@ -1899,11 +1981,11 @@ int no, /* Enumeration selected, icxNoGMIntent for none */
char *as /* Alias string selector, NULL for none */
) {
#ifdef USE_CAM
- int colccas = 0x2; /* Use cas clipping for colorimetric style intents */
- int perccas = 0x1; /* Use cas for perceptual style intents */
+ int colccas = 0x3; /* Use abs. CAS for abs colorimetric intents */
+ int perccas = 0x2; /* Use CAS for other intents */
#else
- int colccas = 0x0; /* Use Lab for colorimetric style intents */
- int perccas = 0x0; /* Use Lab for perceptual style intents */
+ int colccas = 0x1; /* Use abs. Lab for abs colorimetric intents */
+ int perccas = 0x0; /* Use Lab for other intents */
fprintf(stderr,"!!!!!! Warning, USE_CAM is off in xicc.c !!!!!!\n");
#endif
@@ -1927,6 +2009,7 @@ char *as /* Alias string selector, NULL for none */
gmi->glumbcpf = 0.0;
gmi->glumbexf = 0.0;
gmi->glumknf = 0.0;
+ gmi->bph = gmm_noBPadpt; /* No BP adapation */
gmi->gamcpf = 0.0;
gmi->gamexf = 0.0;
gmi->gamcknf = 0.0;
@@ -1958,6 +2041,7 @@ char *as /* Alias string selector, NULL for none */
gmi->glumbcpf = 0.0;
gmi->glumbexf = 0.0;
gmi->glumknf = 0.0;
+ gmi->bph = gmm_noBPadpt; /* No BP adapation */
gmi->gamcpf = 0.0;
gmi->gamexf = 0.0;
gmi->gamcknf = 0.0;
@@ -1982,6 +2066,7 @@ char *as /* Alias string selector, NULL for none */
gmi->glumbcpf = 0.0;
gmi->glumbexf = 0.0;
gmi->glumknf = 0.0;
+ gmi->bph = gmm_noBPadpt; /* No BP adapation */
gmi->gamcpf = 0.0;
gmi->gamexf = 0.0;
gmi->gamcknf = 0.0;
@@ -2008,6 +2093,7 @@ char *as /* Alias string selector, NULL for none */
gmi->glumbcpf = 0.0; /* No compression at black end */
gmi->glumbexf = 0.0; /* No expansion at black end */
gmi->glumknf = 0.0;
+ gmi->bph = gmm_noBPadpt; /* No BP adapation */
gmi->gamcpf = 0.0;
gmi->gamexf = 0.0;
gmi->gamcknf = 0.0;
@@ -2033,6 +2119,7 @@ char *as /* Alias string selector, NULL for none */
gmi->glumbcpf = 1.0; /* Fully compress grey axis at black end */
gmi->glumbexf = 1.0; /* Fully expand grey axis at black end */
gmi->glumknf = 0.0; /* No knee on grey mapping */
+ gmi->bph = gmm_bendBP; /* extent and bend */
gmi->gamcpf = 0.0; /* No gamut compression */
gmi->gamexf = 0.0; /* No gamut expansion */
gmi->gamcknf = 0.0; /* No knee in gamut compress */
@@ -2060,6 +2147,7 @@ char *as /* Alias string selector, NULL for none */
gmi->glumbcpf = 1.0; /* Fully compress grey axis at black end */
gmi->glumbexf = 1.0; /* Fully expand grey axis at black end */
gmi->glumknf = 1.0; /* Sigma knee in grey compress/expand */
+ gmi->bph = gmm_bendBP; /* extent and bend */
gmi->gamcpf = 1.0; /* Full gamut compression */
gmi->gamexf = 0.0; /* No gamut expansion */
gmi->gamcknf = 0.8; /* High Sigma knee in gamut compress */
@@ -2073,7 +2161,7 @@ char *as /* Alias string selector, NULL for none */
/* Don't align neutral axes, but perceptually compress out of gamut */
/* and map appearance space Jab to Jab. */
- no = 5;
+ no = 6;
gmi->as = "pa";
gmi->desc = "pa - Perceptual Apperance ";
gmi->icci = icPerceptual;
@@ -2085,6 +2173,7 @@ char *as /* Alias string selector, NULL for none */
gmi->glumbcpf = 1.0; /* Fully compress grey axis at black end */
gmi->glumbexf = 1.0; /* Fully expand grey axis at black end */
gmi->glumknf = 1.0; /* Sigma knee in grey compress/expand */
+ gmi->bph = gmm_bendBP; /* extent and bend */
gmi->gamcpf = 1.0; /* Full gamut compression */
gmi->gamexf = 0.0; /* No gamut expansion */
gmi->gamcknf = 0.8; /* High Sigma knee in gamut compress */
@@ -2098,7 +2187,7 @@ char *as /* Alias string selector, NULL for none */
/* Align neutral axes and perceptually map white and black points, */
/* perceptually compress and expand to match gamuts and map Jab to Jab. */
- no = 6;
+ no = 7;
gmi->as = "ms";
gmi->desc = "ms - Saturation";
gmi->icci = icSaturation;
@@ -2110,6 +2199,7 @@ char *as /* Alias string selector, NULL for none */
gmi->glumbcpf = 1.0; /* Fully compress grey axis at black end */
gmi->glumbexf = 1.0; /* Fully expand grey axis at black end */
gmi->glumknf = 1.0; /* Sigma knee in grey compress/expand */
+ gmi->bph = gmm_bendBP; /* extent and bend */
gmi->gamcpf = 1.0; /* Full gamut compression */
gmi->gamexf = 1.0; /* Full gamut expansion */
gmi->gamcknf = 1.0; /* High Sigma knee in gamut compress/expand */
@@ -2123,7 +2213,7 @@ char *as /* Alias string selector, NULL for none */
|| (as != NULL && stricmp(as,"s") == 0)) {
/* Same as "ms" but enhance saturation */
- no = 7;
+ no = 8;
gmi->as = "s";
gmi->desc = " s - Enhanced Saturation [ICC Saturation]";
gmi->icci = icSaturation;
@@ -2135,6 +2225,7 @@ char *as /* Alias string selector, NULL for none */
gmi->glumbcpf = 1.0; /* Fully compress grey axis at black end */
gmi->glumbexf = 1.0; /* Fully expand grey axis at black end */
gmi->glumknf = 1.0; /* Sigma knee in grey compress/expand */
+ gmi->bph = gmm_bendBP; /* extent and bend */
gmi->gamcpf = 1.0; /* Full gamut compression */
gmi->gamexf = 1.0; /* Full gamut expansion */
gmi->gamcknf = 1.0; /* High sigma knee in gamut compress */
@@ -2147,11 +2238,11 @@ char *as /* Alias string selector, NULL for none */
|| (as != NULL && stricmp(as,"al") == 0)) {
/* Map absolute L*a*b* to L*a*b* and clip out of gamut */
- no = 8;
+ no = 9;
gmi->as = "al";
gmi->desc = "al - Absolute Colorimetric (Lab)";
gmi->icci = icAbsoluteColorimetric;
- gmi->usecas = 0x0; /* Don't use appearance space, use L*a*b* */
+ gmi->usecas = 0x1; /* Don't use appearance space, use abs. L*a*b* */
gmi->usemap = 0; /* Don't use gamut mapping */
gmi->greymf = 0.0;
gmi->glumwcpf = 0.0;
@@ -2159,6 +2250,7 @@ char *as /* Alias string selector, NULL for none */
gmi->glumbcpf = 0.0;
gmi->glumbexf = 0.0;
gmi->glumknf = 0.0;
+ gmi->bph = gmm_noBPadpt; /* No BP adapation */
gmi->gamcpf = 0.0;
gmi->gamexf = 0.0;
gmi->gamcknf = 0.0;
@@ -2172,11 +2264,11 @@ char *as /* Alias string selector, NULL for none */
/* Align neutral axes and linearly map white point, then */
/* map L*a*b* to L*a*b* and clip out of gamut */
- no = 3;
+ no = 10;
gmi->as = "rl";
- gmi->desc = "rl - White Point Matched Appearance (Lab)";
+ gmi->desc = "rl - White Point Matched Colorimetric (Lab)";
gmi->icci = icRelativeColorimetric;
- gmi->usecas = 0x0; /* Don't use appearance space, use L*a*b* */
+ gmi->usecas = 0x0; /* Don't use appearance space, use relative L*a*b* */
gmi->usemap = 1; /* Use gamut mapping */
gmi->greymf = 1.0; /* And linearly map white point */
gmi->glumwcpf = 1.0;
@@ -2184,6 +2276,7 @@ char *as /* Alias string selector, NULL for none */
gmi->glumbcpf = 0.0;
gmi->glumbexf = 0.0;
gmi->glumknf = 0.0;
+ gmi->bph = gmm_noBPadpt; /* No BP adapation */
gmi->gamcpf = 0.0;
gmi->gamexf = 0.0;
gmi->gamcknf = 0.0;
@@ -2210,10 +2303,12 @@ icxGMappingIntent *gmi /* Gamut Mapping parameters to return */
printf(" Closest ICC intent = '%s'\n",icm2str(icmRenderingIntent,gmi->icci));
if ((gmi->usecas & 0xff) == 0)
- printf(" Not using Color Apperance Space\n");
+ printf(" Not using Color Apperance Space - using L*a*b*\n");
else if ((gmi->usecas & 0xff) == 1)
- printf(" Using Color Apperance Space\n");
+ printf(" Not using Color Apperance Space - using Absoute L*a*b*\n");
else if ((gmi->usecas & 0xff) == 2)
+ printf(" Using Color Apperance Space\n");
+ else if ((gmi->usecas & 0xff) == 3)
printf(" Using Absolute Color Apperance Space\n");
if ((gmi->usecas & 0x100) != 0)
@@ -2229,6 +2324,13 @@ icxGMappingIntent *gmi /* Gamut Mapping parameters to return */
printf(" Grey axis black compression factor %f\n", gmi->glumbcpf);
printf(" Grey axis black expansion factor %f\n", gmi->glumbexf);
printf(" Grey axis knee factor %f\n", gmi->glumknf);
+ printf(" Black point algorithm: ");
+ switch(gmi->bph) {
+ case gmm_clipBP: printf("Neutral axis no-adapt extend and clip\n"); break;
+ case gmm_BPadpt: printf("Neutral axis fully adapt\n"); break;
+ case gmm_bendBP: printf("Neutral axis no-adapt extend and bend\n"); break;
+ case gmm_noBPadpt: printf("Neutral axis no-adapt\n"); break;
+ }
printf(" Gamut compression factor %f\n", gmi->gamcpf);
printf(" Gamut expansion factor %f\n", gmi->gamexf);
printf(" Gamut compression knee factor %f\n", gmi->gamcknf);
@@ -3581,6 +3683,164 @@ void icxdpdiMulBy3x3Parm(
out[2] = ov[2];
}
+/* ------------------------------------------- */
+/* BT.1886 support */
+
+/* Compute technical gamma from effective gamma in BT.1886 style */
+
+/* Info for optimization */
+typedef struct {
+ double thyr; /* 50% input target */
+ double roo; /* 0% input target */
+} gam_fits;
+
+/* gamma + input offset function handed to powell() */
+static double gam_fit(void *dd, double *v) {
+ gam_fits *gf = (gam_fits *)dd;
+ double gamma = v[0];
+ double a, b;
+ double rv = 0.0;
+ double tt;
+
+ if (gamma < 0.0) {
+ rv += 100.0 * -gamma;
+ gamma = 1e-4;
+ }
+
+ tt = pow(gf->roo, 1.0/gamma);
+ b = tt/(1.0 - tt); /* Offset */
+ a = pow(1.0 - tt, gamma); /* Gain */
+
+ tt = a * pow((0.5 + b), gamma);
+ tt = tt - gf->thyr;
+ rv += tt * tt;
+
+ return rv;
+}
+
+/* Given the effective gamma and the output offset Y, */
+/* return the technical gamma needed for the correct 50% response. */
+double xicc_tech_gamma(
+ double egamma, /* effective gamma needed */
+ double off /* Output offset required */
+) {
+ gam_fits gf;
+ double op[1], sa[1], rv;
+
+ if (off <= 0.0) {
+ return egamma;
+ }
+
+ gf.thyr = pow(0.5, egamma); /* Advetised 50% target */
+ gf.roo = off;
+
+ op[0] = egamma;
+ sa[0] = 0.1;
+
+ if (powell(&rv, 1, op, sa, 1e-6, 500, gam_fit, (void *)&gf, NULL, NULL) != 0)
+ warning("Computing effective gamma and input offset is inaccurate");
+
+ return op[0];
+}
+
+
+/* Set the bt1886_info to a default do nothing state */
+void bt1886_setnop(bt1886_info *p) {
+ p->ingo = 0.0;
+ p->outsc = 1.0;
+ p->outL = 0.0;
+ p->tab[0] = 0.0;
+ p->tab[1] = 0.0;
+}
+
+/* Setup the bt1886_info for the given target */
+void bt1886_setup(bt1886_info *p, double *XYZbp, double gamma) {
+ double Lab[3], bkipow;
+ p->gamma = gamma;
+
+ icmXYZ2Lab(&icmD50, Lab, XYZbp);
+
+ p->outL = Lab[0]; /* For bp blend */
+ p->tab[0] = Lab[1]; /* a* b* correction needed */
+ p->tab[1] = Lab[2];
+
+ bkipow = pow(XYZbp[1], 1.0/p->gamma);
+ p->ingo = bkipow/(1.0 - bkipow); /* non-linear Y that makes out black point */
+ p->outsc = pow(1.0 - bkipow, p->gamma); /* Scale to restore 1 -> 1 */
+}
+
+/* Apply BT.1886 black offset and gamma curve to the XYZ out of the input profile. */
+/* Do this in the colorspace defined by the input profile matrix lookup, */
+/* so it will be relative XYZ. We assume that BT.1886 does a Rec709 to gamma */
+/* viewing adjustment, irrespective of the source profile transfer curve. */
+void bt1886_apply(bt1886_info *p, icmLuMatrix *lu, double *out, double *in) {
+ int j;
+ double vv;
+
+#ifdef DEBUG
+ printf("bt1886 XYZ in %f %f %f\n", in[0],in[1],in[2]);
+#endif
+
+ lu->bwd_matrix(lu, out, in);
+
+#ifdef DEBUG
+ printf("bt1886 RGB in %f %f %f\n", out[0],out[1],out[2]);
+#endif
+
+ for (j = 0; j < 3; j++) {
+ vv = out[j];
+
+ /* Convert linear light to Rec709 transfer curve */
+ if (vv < 0.018)
+ vv = 4.5 * vv;
+ else
+ vv = 1.099 * pow(vv, 0.45) - 0.099;
+
+ /* Apply input offset & re-scale, and then gamma of 2.4/custom gamma */
+ vv = vv + p->ingo;
+
+ if (vv > 0.0)
+ vv = p->outsc * pow(vv, p->gamma);
+
+ out[j] = vv;
+ }
+
+ lu->fwd_matrix(lu, out, out);
+
+#ifdef DEBUG
+ printf("bt1886 RGB bt.1886 %f %f %f\n", out[0],out[1],out[2]);
+#endif
+
+ icmXYZ2Lab(&icmD50, out, out);
+
+#ifdef DEBUG
+ printf("bt1886 Lab after Y adj. %f %f %f\n", out[0],out[1],out[2]);
+#endif
+
+ /* Blend ab to required black point offset p->tab[] as L approaches black. */
+ vv = (out[0] - p->outL)/(100.0 - p->outL); /* 0 at bp, 1 at wp */
+ vv = 1.0 - vv;
+
+ if (vv < 0.0)
+ vv = 0.0;
+ else if (vv > 1.0)
+ vv = 1.0;
+ vv = pow(vv, 40.0);
+ out[1] += vv * p->tab[0];
+ out[2] += vv * p->tab[1];
+
+#ifdef DEBUG
+ printf("bt1886 Lab after wp adj. %f %f %f\n", out[0],out[1],out[2]);
+#endif
+
+ icmLab2XYZ(&icmD50, out, out);
+
+#ifdef DEBUG
+ printf("bt1886 XYZ out %f %f %f\n", out[0],out[1],out[2]);
+#endif
+}
+
+/* - - - - - - - - - - */
#undef stricmp
diff --git a/xicc/xicc.h b/xicc/xicc.h
index 3969ebe..2e69ef1 100644
--- a/xicc/xicc.h
+++ b/xicc/xicc.h
@@ -183,20 +183,30 @@ typedef struct {
double Wxyz[3]; /* Reference/Adapted White XYZ (Y range 0.0 .. 1.0) */
double La; /* Adapting/Surround Luminance cd/m^2 */
double Yb; /* Relative Luminance of Background to reference white */
- double Lv; /* Luminance of white in the Viewing/Scene/Image field (cd/m^2) */
+ double Lv; /* Luminance of white in the Image/Scene/Viewing field (cd/m^2) */
/* Ignored if Ev is set to other than vc_none */
double Yf; /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
- double Fxyz[3]; /* The Flare white coordinates (typically the Ambient color) */
- /* Will be taken from Wxyz if Fxyz == 0.0 */
+ double Yg; /* Glare as a fraction of the ambient (Y range 0.0 .. 1.0) */
+ double Gxyz[3]; /* The Glare white coordinates (ie the Ambient color) */
+ /* will be taken from Wxyz if Gxyz <= 0.0 */
char *desc; /* Possible description of this VC */
} icxViewCond;
+/* Method of black point adaptation */
+typedef enum {
+ gmm_BPadpt = 0, /* Adapt source black point to destination */
+ gmm_noBPadpt = 1, /* Don't adapt black point to destination */
+ gmm_bendBP = 2, /* Don't adapt black point, bend it to dest. at end */
+ gmm_clipBP = 3 /* Don't adapt black point, clip it to dest. at end */
+} icx_BPmap;
+
/* Structure to convey gamut mapping intent */
typedef struct {
- int usecas; /* 0x0 Use Lab space */
- /* 0x1 Use Color Appearance Space */
- /* 0x2 Use Absolute Color Appearance Space */
- /* 0x101 Use Color Appearance Space with luminence scaling */
+ int usecas; /* 0x0 Use relative Lab space */
+ /* 0x1 Use Absolute Lab Space */
+ /* 0x2 Use Color Appearance Space */
+ /* 0x3 Use Absolute Color Appearance Space */
+ /* 0x102 Use Color Appearance Space with luminence scaling */
int usemap; /* NZ if Gamut mapping should be used, else clip */
double greymf; /* Grey axis hue matching factor, 0.0 - 1.0 */
double glumwcpf; /* Grey axis luminance white compression factor, 0.0 - 1.0 */
@@ -204,6 +214,7 @@ typedef struct {
double glumbcpf; /* Grey axis luminance black compression factor, 0.0 - 1.0 */
double glumbexf; /* Grey axis luminance black expansion factor, 0.0 - 1.0 */
double glumknf; /* Grey axis luminance knee factor, 0.0 - 1.0 */
+ icx_BPmap bph; /* Method of black point adapation */
double gamcpf; /* Gamut compression factor, 0.0 - 1.0 */
double gamexf; /* Gamut expansion factor, 0.0 - 1.0 */
double gamcknf; /* Gamut compression knee factor, 0.0 - 1.0 */
@@ -294,6 +305,7 @@ struct _xicc {
double smooth, /* RSPL smoothing factor, */
/* -ve if raw */
double avgdev, /* Avge Dev. of points */
+ double demph, /* cLut dark emphasis factor */
icxViewCond *vc, /* Viewing Condition - only */
/* used if pcsor == CIECAM. */
/* or ICX_CAM_CLIP flag. */
@@ -903,6 +915,32 @@ void icxdpdiMulBy3x3Parm(
double in[3] /* Input values */
);
+/* ------------------------------------------- */
+/* BT.1886 support */
+
+/* Convert an effective gamma given an offset into a technical gamma */
+double xicc_tech_gamma(double egamma, double off);
+
+typedef struct {
+ double ingo; /* input Y gamma offset for bt1886 */
+ double outsc; /* output Y scale for bt1886 */
+ double outL; /* output black point L value */
+ double tab[2]; /* Target ab offset value at zero input for bt1886 */
+ double gamma; /* bt.1886 technical gamma to apply */
+} bt1886_info;
+
+/* Set the bt1886_info to a default do nothing state */
+void bt1886_setnop(bt1886_info *p);
+
+/* Setup the bt1886_info for the given target */
+void bt1886_setup(bt1886_info *p, double *XYZbp, double gamma);
+
+/* Apply BT.1886 black offset and gamma curve to */
+/* the XYZ out of the input profile. */
+/* Do this in the colorspace defined by the input profile matrix lookup, */
+/* so it will be relative XYZ */
+void bt1886_apply(bt1886_info *p, icmLuMatrix *lu, double *out, double *in);
+
/* - - - - - - - - - - */
#include "xcal.h"
diff --git a/xicc/xicclu.c b/xicc/xicclu.c
index aa4a452..4987e65 100644
--- a/xicc/xicclu.c
+++ b/xicc/xicclu.c
@@ -3,7 +3,8 @@
* xicc lookup/test utility
*
* This program is the analog of icclu, but allows reverse lookup
- * of transforms by making use of xicc interpolation code.
+ * of transforms by making use of xicc interpolation code, + other
+ * more advanced features.
* (Based on the old xfmlu.c)
*
* Author: Graeme W. Gill
@@ -18,6 +19,8 @@
/* TTBD:
+ Add HSV as alternative to RGB ?
+
Can -ff and -fif be made to work with device link files ?
*/
@@ -41,9 +44,9 @@
void usage(char *diag) {
int i;
- fprintf(stderr,"Translate colors through an xicc, Version %s\n",ARGYLL_VERSION_STR);
+ fprintf(stderr,"Lookup ICC or CAL colors, Version %s\n",ARGYLL_VERSION_STR);
fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
- fprintf(stderr,"usage: xicclu [-options] profile\n");
+ fprintf(stderr,"usage: xicclu [-options] profile_or_cal\n");
if (diag != NULL)
fprintf(stderr,"Diagnostic: %s\n",diag);
fprintf(stderr," -v level Verbosity level 0 - 2 (default = 1)\n");
@@ -60,6 +63,15 @@ void usage(char *diag) {
fprintf(stderr," -p oride x = XYZ_PCS, X = XYZ * 100, l = Lab_PCS, L = LCh, y = Yxy\n");
fprintf(stderr," j = %s Appearance Jab, J = %s Appearance JCh\n",icxcam_description(cam_default),icxcam_description(cam_default));
fprintf(stderr," -s scale Scale device range 0.0 - scale rather than 0.0 - 1.0\n");
+ fprintf(stderr," -e flag Video encode device input as:\n");
+ fprintf(stderr," -E flag Video decode device output as:\n");
+ fprintf(stderr," n normal 0..1 full range RGB levels (default)\n");
+ fprintf(stderr," t (16-235)/255 \"TV\" RGB levels\n");
+ fprintf(stderr," 6 Rec601 YCbCr SD (16-235,240)/255 \"TV\" levels\n");
+ fprintf(stderr," 7 Rec709 1125/60Hz YCbCr HD (16-235,240)/255 \"TV\" levels\n");
+ fprintf(stderr," 5 Rec709 1250/50Hz YCbCr HD (16-235,240)/255 \"TV\" levels\n");
+ fprintf(stderr," 2 Rec2020 YCbCr UHD (16-235,240)/255 \"TV\" levels\n");
+ fprintf(stderr," C Rec2020 Constant Luminance YCbCr UHD (16-235,240)/255 \"TV\" levels\n");
fprintf(stderr," -k [zhxrlv] Black value target: z = zero K,\n");
fprintf(stderr," h = 0.5 K, x = max K, r = ramp K (def.)\n");
fprintf(stderr," l = extra PCS input is portion of K locus\n");
@@ -101,10 +113,11 @@ void usage(char *diag) {
fprintf(stderr," w:x:y Adapted white point as x, y\n");
fprintf(stderr," a:adaptation Adaptation luminance in cd.m^2 (default 50.0)\n");
fprintf(stderr," b:background Background %% of image luminance (default 20)\n");
- fprintf(stderr," l:scenewhite Scene white in cd.m^2 if surround = auto (default 250)\n");
- fprintf(stderr," f:flare Flare light %% of image luminance (default 1)\n");
- fprintf(stderr," f:X:Y:Z Flare color as XYZ (default media white, Abs: D50)\n");
- fprintf(stderr," f:x:y Flare color as x, y\n");
+ fprintf(stderr," l:imagewhite Image white in cd.m^2 if surround = auto (default 250)\n");
+ fprintf(stderr," f:flare Flare light %% of image luminance (default 0)\n");
+ fprintf(stderr," g:glare Flare light %% of ambient (default 1)\n");
+ fprintf(stderr," g:X:Y:Z Flare color as XYZ (default media white, Abs: D50)\n");
+ fprintf(stderr," g:x:y Flare color as x, y\n");
fprintf(stderr,"\n");
fprintf(stderr," The colors to be translated should be fed into standard in,\n");
fprintf(stderr," one input color per line, white space separated.\n");
@@ -141,9 +154,10 @@ int
main(int argc, char *argv[]) {
int fa,nfa; /* argument we're looking at */
char prof_name[MAXNAMEL+1];
- icmFile *fp;
- icc *icco;
- xicc *xicco;
+ icmFile *fp = NULL;
+ icc *icco = NULL;
+ xicc *xicco = NULL;
+ xcal *cal = NULL; /* If .cal rather than .icm/.icc, not NULL */
int doplot = 0; /* Do grey axis plot */
double pstart[3] = { -1000.0 }; /* Plot Lab/Jab PCS start point = white */
double pend[3] = { -1000.0 }; /* Plot Lab/Jab PCS end point = black */
@@ -157,11 +171,12 @@ main(int argc, char *argv[]) {
double vc_wXYZ[3] = {-1.0, -1.0, -1.0}; /* Adapted white override in XYZ */
double vc_wxy[2] = {-1.0, -1.0}; /* Adapted white override in x,y */
double vc_a = -1.0; /* Adapted luminance */
- double vc_b = -1.0; /* Background % overid */
+ double vc_b = -1.0; /* Background % overide */
double vc_l = -1.0; /* Scene luminance override */
- double vc_f = -1.0; /* Flare % overid */
- double vc_fXYZ[3] = {-1.0, -1.0, -1.0}; /* Flare color override in XYZ */
- double vc_fxy[2] = {-1.0, -1.0}; /* Flare color override in x,y */
+ double vc_f = -1.0; /* Flare % overide */
+ double vc_g = -1.0; /* Glare % overide */
+ double vc_gXYZ[3] = {-1.0, -1.0, -1.0}; /* Glare color override in XYZ */
+ double vc_gxy[2] = {-1.0, -1.0}; /* Glare color override in x,y */
int verb = 1;
int actual = 0;
int slocwarn = 0;
@@ -172,11 +187,13 @@ main(int argc, char *argv[]) {
int repLCh = 0; /* Report LCh */
int repXYZ100 = 0; /* Scale XYZ by 10 */
double scale = 0.0; /* Device value scale factor */
+ int in_tvenc; /* 1 to use RGB Video Level encoding, 2 = Rec601, 3 = Rec709 YCbCr */
+ int out_tvenc; /* 1 to use RGB Video Level encoding, 2 = Rec601, 3 = Rec709 YCbCr */
int rv = 0;
char buf[200];
double uin[MAX_CHAN], in[MAX_CHAN], out[MAX_CHAN], uout[MAX_CHAN];
- icxLuBase *luo, *aluo = NULL;
+ icxLuBase *luo = NULL, *aluo = NULL;
icColorSpaceSignature ins, outs; /* Type of input and output spaces */
int inn, outn; /* Number of components */
icmLuAlgType alg; /* Type of lookup algorithm */
@@ -194,6 +211,8 @@ main(int argc, char *argv[]) {
double Kstle1 = 0.0, Kstpo1 = 0.0, Kenle1 = 0.0, Kenpo1 = 0.0, Kshap1 = 0.0;
int invert = 0;
+ xslpoly *chlp = NULL;
+
#ifdef SPTEST
int sptest = 0;
warning("xicc/xicclu.c !!!! special rspl gamut sest code is compiled in !!!!\n");
@@ -285,6 +304,43 @@ main(int argc, char *argv[]) {
scale = atof(na);
if (scale <= 0.0) usage("Illegal scale value");
}
+ /* Video RGB encoding */
+ else if (argv[fa][1] == 'e'
+ || argv[fa][1] == 'E') {
+ int enc;
+ if (na == NULL) usage("Video encodong flag (-e/E) needs an argument");
+ switch (na[0]) {
+ case 'n': /* Normal */
+ enc = 0;
+ break;
+ case 't': /* TV 16 .. 235 */
+ enc = 1;
+ break;
+ case '6': /* Rec601 YCbCr */
+ enc = 2;
+ break;
+ case '7': /* Rec709 1150/60/2:1 YCbCr */
+ enc = 3;
+ break;
+ case '5': /* Rec709 1250/50/2:1 YCbCr (HD) */
+ enc = 4;
+ break;
+ case '2': /* Rec2020 Non-constant Luminance YCbCr (UHD) */
+ enc = 5;
+ break;
+ case 'C': /* Rec2020 Constant Luminance YCbCr (UHD) */
+ enc = 6;
+ break;
+ default:
+ usage("Video encoding (-E) argument not recognised");
+ }
+ if (argv[fa][1] == 'e')
+ in_tvenc = enc;
+ else
+ out_tvenc = enc;
+ fa = nfa;
+ }
+
/* function */
else if (argv[fa][1] == 'f') {
fa = nfa;
@@ -375,12 +431,14 @@ main(int argc, char *argv[]) {
repYxy = 0;
repLCh = 0;
repJCh = 0;
+ repXYZ100 = 0;
break;
case 'L':
pcsor = icSigLabData;
repYxy = 0;
repLCh = 1;
repJCh = 0;
+ repXYZ100 = 0;
break;
case 'y':
case 'Y':
@@ -388,18 +446,21 @@ main(int argc, char *argv[]) {
repYxy = 1;
repLCh = 0;
repJCh = 0;
+ repXYZ100 = 0;
break;
case 'j':
pcsor = icxSigJabData;
repYxy = 0;
repLCh = 0;
repJCh = 0;
+ repXYZ100 = 0;
break;
case 'J':
pcsor = icxSigJabData;
repYxy = 0;
repLCh = 0;
repJCh = 1;
+ repXYZ100 = 0;
break;
default:
usage("Unknown parameter after flag -i");
@@ -425,7 +486,8 @@ main(int argc, char *argv[]) {
}
/* Inking rule */
- else if (argv[fa][1] == 'k') {
+ else if (argv[fa][1] == 'k'
+ || argv[fa][1] == 'K') {
fa = nfa;
if (na == NULL) usage("No parameter after flag -k");
if (argv[fa][1] == 'k')
@@ -578,18 +640,22 @@ main(int argc, char *argv[]) {
vc_b = atof(na+2);
} else if (na[0] == 'l' || na[0] == 'L') {
if (na[1] != ':')
- usage("Viewing conditions (-[cd]l) missing ':'");
+ usage("Viewing conditions (-cl) missing ':'");
vc_l = atof(na+2);
} else if (na[0] == 'f' || na[0] == 'F') {
+ if (na[1] != ':')
+ usage("Viewing conditions (-cf) missing ':'");
+ vc_f = atof(na+2);
+ } else if (na[0] == 'g' || na[0] == 'G') {
double x, y, z;
if (sscanf(na+1,":%lf:%lf:%lf",&x,&y,&z) == 3) {
- vc_fXYZ[0] = x; vc_fXYZ[1] = y; vc_fXYZ[2] = z;
+ vc_gXYZ[0] = x; vc_gXYZ[1] = y; vc_gXYZ[2] = z;
} else if (sscanf(na+1,":%lf:%lf",&x,&y) == 2) {
- vc_fxy[0] = x; vc_fxy[1] = y;
+ vc_gxy[0] = x; vc_gxy[1] = y;
} else if (sscanf(na+1,":%lf",&x) == 1) {
- vc_f = x;
+ vc_g = x;
} else
- usage("Unrecognised parameters after -cf");
+ usage("Unrecognised parameters after -cg");
} else
usage("Unrecognised parameters after -c");
}
@@ -603,17 +669,17 @@ main(int argc, char *argv[]) {
if (fa >= argc || argv[fa][0] == '-') usage("Expecting profile file name");
strncpy(prof_name,argv[fa],MAXNAMEL); prof_name[MAXNAMEL] = '\000';
- if (doplot) {
-
- /* Force PCS to be Lab or Jab */
- repJCh = 0;
- repLCh = 0;
- if (pcsor != icxSigJabData)
- pcsor = icSigLabData;
+ if (slocwarn) {
+ if ((chlp = chrom_locus_poligon(0, icxOT_CIE_1931_2, 0)) == NULL)
+ error("chrom_locus_poligon failed");
+ }
- if ((invert == 0 && func != icmBwd)
- || (invert != 0 && func != icmFwd))
- error("Must use -fb or -fif for grey axis plot");
+ if (verb > 1) {
+ icmFile *op;
+ if ((op = new_icmFileStd_fp(stdout)) == NULL)
+ error ("Can't open stdout");
+ icco->header->dump(icco->header, op, 1);
+ op->del(op);
}
/* Open up the profile for reading */
@@ -623,248 +689,275 @@ main(int argc, char *argv[]) {
if ((icco = new_icc()) == NULL)
error ("Creation of ICC object failed");
- if ((rv = icco->read(icco,fp,0)) != 0)
- error ("%d, %s",rv,icco->err);
+ if ((rv = icco->read(icco,fp,0)) == 0) { /* ICC profile */
- if (doplot) {
- if (icco->header->deviceClass != icSigInputClass
- && icco->header->deviceClass != icSigDisplayClass
- && icco->header->deviceClass != icSigOutputClass)
- error("Profile must be a device profile to plot neutral axis");
- }
+ if (doplot) {
+
+ /* Force PCS to be Lab or Jab */
+ repJCh = 0;
+ repLCh = 0;
+ if (pcsor != icxSigJabData)
+ pcsor = icSigLabData;
+
+ if ((invert == 0 && func != icmBwd)
+ || (invert != 0 && func != icmFwd))
+ error("Must use -fb or -fif for grey axis plot");
+ }
- if (verb > 1) {
- icmFile *op;
- if ((op = new_icmFileStd_fp(stdout)) == NULL)
- error ("Can't open stdout");
- icco->header->dump(icco->header, op, 1);
- op->del(op);
- }
+ if (icco->header->cmmId == str2tag("argl"))
+ icco->allowclutPoints256 = 1;
+
+ if (doplot) {
+ if (icco->header->deviceClass != icSigInputClass
+ && icco->header->deviceClass != icSigDisplayClass
+ && icco->header->deviceClass != icSigOutputClass)
+ error("Profile must be a device profile to plot neutral axis");
+ }
- /* Wrap with an expanded icc */
- if ((xicco = new_xicc(icco)) == NULL)
- error ("Creation of xicc failed");
+ /* Wrap with an expanded icc */
+ if ((xicco = new_xicc(icco)) == NULL)
+ error ("Creation of xicc failed");
- /* Set the default ink limits if not set on command line */
- icxDefaultLimits(xicco, &ink.tlimit, tlimit, &ink.klimit, klimit);
+ /* Set the default ink limits if not set on command line */
+ icxDefaultLimits(xicco, &ink.tlimit, tlimit, &ink.klimit, klimit);
- if (verb > 1) {
- if (ink.tlimit >= 0.0)
- printf("Total ink limit assumed is %3.0f%%\n",100.0 * ink.tlimit);
- if (ink.klimit >= 0.0)
- printf("Black ink limit assumed is %3.0f%%\n",100.0 * ink.klimit);
- }
+ if (verb > 1) {
+ if (ink.tlimit >= 0.0)
+ printf("Total ink limit assumed is %3.0f%%\n",100.0 * ink.tlimit);
+ if (ink.klimit >= 0.0)
+ printf("Black ink limit assumed is %3.0f%%\n",100.0 * ink.klimit);
+ }
- ink.KonlyLmin = 0; /* Use normal black as locus Lmin */
-
- ink.c.Ksmth = ICXINKDEFSMTH; /* Default curve smoothing */
- ink.c.Kskew = ICXINKDEFSKEW; /* default curve skew */
- ink.x.Ksmth = ICXINKDEFSMTH;
- ink.x.Kskew = ICXINKDEFSKEW;
-
- if (inking == 0) { /* Use minimum */
- ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
- ink.c.Kstle = 0.0;
- ink.c.Kstpo = 0.0;
- ink.c.Kenpo = 1.0;
- ink.c.Kenle = 0.0;
- ink.c.Kshap = 1.0;
- } else if (inking == 1) { /* Use 0.5 */
- ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
- ink.c.Kstle = 0.5;
- ink.c.Kstpo = 0.0;
- ink.c.Kenpo = 1.0;
- ink.c.Kenle = 0.5;
- ink.c.Kshap = 1.0;
- } else if (inking == 2) { /* Use maximum */
- ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
- ink.c.Kstle = 1.0;
- ink.c.Kstpo = 0.0;
- ink.c.Kenpo = 1.0;
- ink.c.Kenle = 1.0;
- ink.c.Kshap = 1.0;
- } else if (inking == 3) { /* Use ramp */
- ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
- ink.c.Kstle = 0.0;
- ink.c.Kstpo = 0.0;
- ink.c.Kenpo = 1.0;
- ink.c.Kenle = 1.0;
- ink.c.Kshap = 1.0;
- } else if (inking == 4) { /* Use locus */
- ink.k_rule = icxKlocus;
- } else if (inking == 5) { /* Use K target */
- ink.k_rule = icxKvalue;
- } else if (inking == 6) { /* Use specified curve */
- ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
- ink.c.Kstle = Kstle;
- ink.c.Kstpo = Kstpo;
- ink.c.Kenpo = Kenpo;
- ink.c.Kenle = Kenle;
- ink.c.Kshap = Kshap;
- } else { /* Use dual curves */
- ink.k_rule = locus ? icxKl5l : icxKl5lk; /* Locus or value target */
- ink.c.Kstle = Kstle;
- ink.c.Kstpo = Kstpo;
- ink.c.Kenpo = Kenpo;
- ink.c.Kenle = Kenle;
- ink.c.Kshap = Kshap;
- ink.x.Kstle = Kstle1;
- ink.x.Kstpo = Kstpo1;
- ink.x.Kenpo = Kenpo1;
- ink.x.Kenle = Kenle1;
- ink.x.Kshap = Kshap1;
- }
+ ink.KonlyLmin = 0; /* Use normal black as locus Lmin */
+
+ ink.c.Ksmth = ICXINKDEFSMTH; /* Default curve smoothing */
+ ink.c.Kskew = ICXINKDEFSKEW; /* default curve skew */
+ ink.x.Ksmth = ICXINKDEFSMTH;
+ ink.x.Kskew = ICXINKDEFSKEW;
+
+ if (inking == 0) { /* Use minimum */
+ ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
+ ink.c.Kstle = 0.0;
+ ink.c.Kstpo = 0.0;
+ ink.c.Kenpo = 1.0;
+ ink.c.Kenle = 0.0;
+ ink.c.Kshap = 1.0;
+ } else if (inking == 1) { /* Use 0.5 */
+ ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
+ ink.c.Kstle = 0.5;
+ ink.c.Kstpo = 0.0;
+ ink.c.Kenpo = 1.0;
+ ink.c.Kenle = 0.5;
+ ink.c.Kshap = 1.0;
+ } else if (inking == 2) { /* Use maximum */
+ ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
+ ink.c.Kstle = 1.0;
+ ink.c.Kstpo = 0.0;
+ ink.c.Kenpo = 1.0;
+ ink.c.Kenle = 1.0;
+ ink.c.Kshap = 1.0;
+ } else if (inking == 3) { /* Use ramp */
+ ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
+ ink.c.Kstle = 0.0;
+ ink.c.Kstpo = 0.0;
+ ink.c.Kenpo = 1.0;
+ ink.c.Kenle = 1.0;
+ ink.c.Kshap = 1.0;
+ } else if (inking == 4) { /* Use locus */
+ ink.k_rule = icxKlocus;
+ } else if (inking == 5) { /* Use K target */
+ ink.k_rule = icxKvalue;
+ } else if (inking == 6) { /* Use specified curve */
+ ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
+ ink.c.Kstle = Kstle;
+ ink.c.Kstpo = Kstpo;
+ ink.c.Kenpo = Kenpo;
+ ink.c.Kenle = Kenle;
+ ink.c.Kshap = Kshap;
+ } else { /* Use dual curves */
+ ink.k_rule = locus ? icxKl5l : icxKl5lk; /* Locus or value target */
+ ink.c.Kstle = Kstle;
+ ink.c.Kstpo = Kstpo;
+ ink.c.Kenpo = Kenpo;
+ ink.c.Kenle = Kenle;
+ ink.c.Kshap = Kshap;
+ ink.x.Kstle = Kstle1;
+ ink.x.Kstpo = Kstpo1;
+ ink.x.Kenpo = Kenpo1;
+ ink.x.Kenle = Kenle1;
+ ink.x.Kshap = Kshap1;
+ }
- /* Setup the viewing conditions */
- if (xicc_enum_viewcond(xicco, &vc, -1, NULL, 0, NULL) == -999)
- error ("%d, %s",xicco->errc, xicco->err);
+ /* Setup the viewing conditions in case we need them */
+ if (xicc_enum_viewcond(xicco, &vc, -1, NULL, 0, NULL) == -999) {
+ if (camclip || pcsor == icxSigJabData) /* If it will be needed */
+ error("%d, %s",xicco->errc, xicco->err);
+ }
//xicc_dump_viewcond(&vc);
- if (vc_e != -1)
- if (xicc_enum_viewcond(xicco, &vc, vc_e, NULL, 0, NULL) == -999)
- error ("%d, %s",xicco->errc, xicco->err);
- if (vc_s >= 0)
- vc.Ev = vc_s;
- if (vc_wXYZ[1] > 0.0) {
- /* Normalise it to current media white */
- vc.Wxyz[0] = vc_wXYZ[0]/vc_wXYZ[1] * vc.Wxyz[1];
- vc.Wxyz[2] = vc_wXYZ[2]/vc_wXYZ[1] * vc.Wxyz[1];
- }
- if (vc_wxy[0] >= 0.0) {
- double x = vc_wxy[0];
- double y = vc_wxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
- double z = 1.0 - x - y;
- vc.Wxyz[0] = x/y * vc.Wxyz[1];
- vc.Wxyz[2] = z/y * vc.Wxyz[1];
- }
- if (vc_a >= 0.0)
- vc.La = vc_a;
- if (vc_b >= 0.0)
- vc.Yb = vc_b/100.0;
- if (vc_l >= 0.0)
- vc.Lv = vc_l;
- if (vc_f >= 0.0)
- vc.Yf = vc_f/100.0;
- if (vc_fXYZ[1] > 0.0) {
- /* Normalise it to current media white */
- vc.Fxyz[0] = vc_fXYZ[0]/vc_fXYZ[1] * vc.Fxyz[1];
- vc.Fxyz[2] = vc_fXYZ[2]/vc_fXYZ[1] * vc.Fxyz[1];
- }
- if (vc_fxy[0] >= 0.0) {
- double x = vc_fxy[0];
- double y = vc_fxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
- double z = 1.0 - x - y;
- vc.Fxyz[0] = x/y * vc.Fxyz[1];
- vc.Fxyz[2] = z/y * vc.Fxyz[1];
- }
+ if (vc_e != -1)
+ if (xicc_enum_viewcond(xicco, &vc, vc_e, NULL, 0, NULL) == -999)
+ error ("%d, %s",xicco->errc, xicco->err);
+ if (vc_s >= 0)
+ vc.Ev = vc_s;
+ if (vc_wXYZ[1] > 0.0) {
+ /* Normalise it to current media white */
+ vc.Wxyz[0] = vc_wXYZ[0]/vc_wXYZ[1] * vc.Wxyz[1];
+ vc.Wxyz[2] = vc_wXYZ[2]/vc_wXYZ[1] * vc.Wxyz[1];
+ }
+ if (vc_wxy[0] >= 0.0) {
+ double x = vc_wxy[0];
+ double y = vc_wxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
+ double z = 1.0 - x - y;
+ vc.Wxyz[0] = x/y * vc.Wxyz[1];
+ vc.Wxyz[2] = z/y * vc.Wxyz[1];
+ }
+ if (vc_a >= 0.0)
+ vc.La = vc_a;
+ if (vc_b >= 0.0)
+ vc.Yb = vc_b/100.0;
+ if (vc_l >= 0.0)
+ vc.Lv = vc_l;
+ if (vc_f >= 0.0)
+ vc.Yf = vc_f/100.0;
+ if (vc_g >= 0.0)
+ vc.Yg = vc_g/100.0;
+ if (vc_gXYZ[1] > 0.0) {
+ /* Normalise it to current media white */
+ vc.Gxyz[0] = vc_gXYZ[0]/vc_gXYZ[1] * vc.Gxyz[1];
+ vc.Gxyz[2] = vc_gXYZ[2]/vc_gXYZ[1] * vc.Gxyz[1];
+ }
+ if (vc_gxy[0] >= 0.0) {
+ double x = vc_gxy[0];
+ double y = vc_gxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
+ double z = 1.0 - x - y;
+ vc.Gxyz[0] = x/y * vc.Gxyz[1];
+ vc.Gxyz[2] = z/y * vc.Gxyz[1];
+ }
//xicc_dump_viewcond(&vc);
- /* Get a expanded color conversion object */
- if ((luo = xicco->get_luobj(xicco, 0
+ /* Get a expanded color conversion object */
+ if ((luo = xicco->get_luobj(xicco, 0
#ifdef USE_NEARCLIP
- | ICX_CLIP_NEAREST
+ | ICX_CLIP_NEAREST
#endif
- | (intsep ? ICX_INT_SEPARATE : 0)
- | (merge ? ICX_MERGE_CLUT : 0)
- | (camclip ? ICX_CAM_CLIP : 0)
- | ICX_FAST_SETUP
- , func, intent, pcsor, order, &vc, &ink)) == NULL)
- error ("%d, %s",xicco->errc, xicco->err);
-
- /* Get details of conversion (Arguments may be NULL if info not needed) */
- if (invert)
- luo->spaces(luo, &outs, &outn, &ins, &inn, &alg, NULL, NULL, NULL);
- else
- luo->spaces(luo, &ins, &inn, &outs, &outn, &alg, NULL, NULL, NULL);
-
- /* If we can do check on clipped values */
- if (actual != 0) {
- if (invert == 0) {
- if (func == icmFwd || func == icmBwd) {
- if ((aluo = xicco->get_luobj(xicco, ICX_CLIP_NEAREST,
- func == icmFwd ? icmBwd : icmFwd, intent, pcsor, order, &vc, &ink)) == NULL)
- error ("%d, %s",xicco->errc, xicco->err);
+ | (intsep ? ICX_INT_SEPARATE : 0)
+ | (merge ? ICX_MERGE_CLUT : 0)
+ | (camclip ? ICX_CAM_CLIP : 0)
+ | ICX_FAST_SETUP
+ , func, intent, pcsor, order, &vc, &ink)) == NULL)
+ error ("%d, %s",xicco->errc, xicco->err);
+
+ /* Get details of conversion (Arguments may be NULL if info not needed) */
+ if (invert)
+ luo->spaces(luo, &outs, &outn, &ins, &inn, &alg, NULL, NULL, NULL);
+ else
+ luo->spaces(luo, &ins, &inn, &outs, &outn, &alg, NULL, NULL, NULL);
+
+ /* If we can do check on clipped values */
+ if (actual != 0) {
+ if (invert == 0) {
+ if (func == icmFwd || func == icmBwd) {
+ if ((aluo = xicco->get_luobj(xicco, ICX_CLIP_NEAREST,
+ func == icmFwd ? icmBwd : icmFwd, intent, pcsor, order, &vc, &ink)) == NULL)
+ error ("%d, %s",xicco->errc, xicco->err);
+ }
+ } else {
+ aluo = luo; /* We can use the same one */
}
- } else {
- aluo = luo; /* We can use the same one */
}
- }
- /* More information */
- if (verb > 1) {
- int j;
- double inmin[MAX_CHAN], inmax[MAX_CHAN];
- double outmin[MAX_CHAN], outmax[MAX_CHAN];
-
- luo->get_native_ranges(luo, inmin, inmax, outmin,outmax);
- printf("Internal input value range: ");
- for (j = 0; j < inn; j++) {
- if (j > 0)
- fprintf(stdout," %f..%f",inmin[j], inmax[j]);
- else
- fprintf(stdout,"%f..%f",inmin[j], inmax[j]);
+ /* More information */
+ if (verb > 1) {
+ int j;
+ double inmin[MAX_CHAN], inmax[MAX_CHAN];
+ double outmin[MAX_CHAN], outmax[MAX_CHAN];
+
+ luo->get_native_ranges(luo, inmin, inmax, outmin,outmax);
+ printf("Internal input value range: ");
+ for (j = 0; j < inn; j++) {
+ if (j > 0)
+ fprintf(stdout," %f..%f",inmin[j], inmax[j]);
+ else
+ fprintf(stdout,"%f..%f",inmin[j], inmax[j]);
+ }
+ printf("\nInternal output value range: ");
+ for (j = 0; j < outn; j++) {
+ if (j > 0)
+ fprintf(stdout," %f..%f",outmin[j], outmax[j]);
+ else
+ fprintf(stdout,"%f..%f",outmin[j], outmax[j]);
+ }
+
+ luo->get_ranges(luo, inmin, inmax, outmin,outmax);
+ printf("\nInput value range: ");
+ for (j = 0; j < inn; j++) {
+ if (j > 0)
+ fprintf(stdout," %f..%f",inmin[j], inmax[j]);
+ else
+ fprintf(stdout,"%f..%f",inmin[j], inmax[j]);
+ }
+ printf("\nOutput value range: ");
+ for (j = 0; j < outn; j++) {
+ if (j > 0)
+ fprintf(stdout," %f..%f",outmin[j], outmax[j]);
+ else
+ fprintf(stdout,"%f..%f",outmin[j], outmax[j]);
+ }
+ printf("\n");
}
- printf("\nInternal output value range: ");
- for (j = 0; j < outn; j++) {
- if (j > 0)
- fprintf(stdout," %f..%f",outmin[j], outmax[j]);
- else
- fprintf(stdout,"%f..%f",outmin[j], outmax[j]);
+
+ if (repYxy) { /* report Yxy rather than XYZ */
+ if (ins == icSigXYZData)
+ ins = icSigYxyData;
+ if (outs == icSigXYZData)
+ outs = icSigYxyData;
}
- luo->get_ranges(luo, inmin, inmax, outmin,outmax);
- printf("\nInput value range: ");
- for (j = 0; j < inn; j++) {
- if (j > 0)
- fprintf(stdout," %f..%f",inmin[j], inmax[j]);
- else
- fprintf(stdout,"%f..%f",inmin[j], inmax[j]);
+ if (repJCh) { /* report JCh rather than Jab */
+ if (ins == icxSigJabData)
+ ins = icxSigJChData;
+ if (outs == icxSigJabData)
+ outs = icxSigJChData;
}
- printf("\nOutput value range: ");
- for (j = 0; j < outn; j++) {
- if (j > 0)
- fprintf(stdout," %f..%f",outmin[j], outmax[j]);
- else
- fprintf(stdout,"%f..%f",outmin[j], outmax[j]);
+ if (repLCh) { /* report LCh rather than Lab */
+ if (ins == icSigLabData)
+ ins = icxSigLChData;
+ if (outs == icSigLabData)
+ outs = icxSigLChData;
}
- printf("\n");
- }
- if (repYxy) { /* report Yxy rather than XYZ */
- if (ins == icSigXYZData)
- ins = icSigYxyData;
- if (outs == icSigXYZData)
- outs = icSigYxyData;
- }
+#ifdef SPTEST
+ if (sptest) {
+ icxLuLut *clu;
+ double cent[3] = { 50.0, 0.0, 0.0 };
- if (repJCh) { /* report JCh rather than Jab */
- if (ins == icxSigJabData)
- ins = icxSigJChData;
- if (outs == icxSigJabData)
- outs = icxSigJChData;
- }
- if (repLCh) { /* report LCh rather than Lab */
- if (ins == icSigLabData)
- ins = icxSigLChData;
- if (outs == icSigLabData)
- outs = icxSigLChData;
- }
+ if (luo->plu->ttype != icmLutType)
+ error("Special test only works on CLUT profiles");
-#ifdef SPTEST
- if (sptest) {
- icxLuLut *clu;
- double cent[3] = { 50.0, 0.0, 0.0 };
+ clu = (icxLuLut *)luo;
+
+ clu->clutTable->comp_gamut(clu->clutTable, cent, NULL, spoutf, clu, spioutf, clu);
+ rspl_gam_plot(clu->clutTable, "sp_test.wrl", sptest-1);
+ exit(0);
+ }
+#endif
+
+ } else { /* See if it's a .cal */
+ fp->del(fp);
+ fp = NULL;
- if (luo->plu->ttype != icmLutType)
- error("Special test only works on CLUT profiles");
+ if ((cal = new_xcal()) == NULL)
+ error("new_xcal failed");
- clu = (icxLuLut *)luo;
+ if ((cal->read(cal, prof_name)) != 0) {
+ error ("File '%s' is not an ICC or .cal file",prof_name);
+ }
- clu->clutTable->comp_gamut(clu->clutTable, cent, NULL, spoutf, clu, spioutf, clu);
- rspl_gam_plot(clu->clutTable, "sp_test.wrl", sptest-1);
- exit(0);
+ /* Get details of conversion (Arguments may be NULL if info not needed) */
+ outs = ins = cal->colspace;
+ outn = inn = cal->devchan;
}
-#endif
if (doplot) {
int i, j;
@@ -872,43 +965,64 @@ main(int argc, char *argv[]) {
double yy[6][XRES];
double start[3], end[3];
- /* Plot from white to black by default */
- luo->efv_wh_bk_points(luo, start, end, NULL);
+ if (cal != NULL) {
+ for (i = 0; i < XRES; i++) {
+ double ival = (double)i/(XRES-1.0);
- if (pstart[0] == -1000.0)
- icmCpy3(pstart, start);
+ for (j = 0; j < inn; j++)
+ in[j] = ival;
- if (pend[0] == -1000.0)
- icmCpy3(pend, end);
+ /* Do the conversion */
+ if (func == icmBwd || invert) {
+ if ((rv = cal->inv_interp(cal, out, in)) != 0)
+ error ("%d, %s",cal->errc,cal->err);
+ } else {
+ cal->interp(cal, out, in);
+ }
- if (verb) {
- printf("Plotting from white %f %f %f to black %f %f %f\n",
- pstart[0], pstart[1], pstart[2], pend[0], pend[1], pend[2]);
- }
- for (i = 0; i < XRES; i++) {
- double ival = (double)i/(XRES-1.0);
+ xx[i] = 100.0 * ival;
+ for (j = 0; j < outn; j++)
+ yy[j][i] = 100.0 * out[j];
+ }
+ } else {
+ /* Plot from white to black by default */
+ luo->efv_wh_bk_points(luo, start, end, NULL);
+
+ if (pstart[0] == -1000.0)
+ icmCpy3(pstart, start);
+
+ if (pend[0] == -1000.0)
+ icmCpy3(pend, end);
- /* Input is always Jab or Lab */
- in[0] = ival * pend[0] + (1.0 - ival) * pstart[0];
- in[1] = ival * pend[1] + (1.0 - ival) * pstart[1];
- in[2] = ival * pend[2] + (1.0 - ival) * pstart[2];
+ if (verb) {
+ printf("Plotting from white %f %f %f to black %f %f %f\n",
+ pstart[0], pstart[1], pstart[2], pend[0], pend[1], pend[2]);
+ }
+ for (i = 0; i < XRES; i++) {
+ double ival = (double)i/(XRES-1.0);
+
+ /* Input is always Jab or Lab */
+ in[0] = ival * pend[0] + (1.0 - ival) * pstart[0];
+ in[1] = ival * pend[1] + (1.0 - ival) * pstart[1];
+ in[2] = ival * pend[2] + (1.0 - ival) * pstart[2];
//in[1] = in[2] = 0.0;
- /* Do the conversion */
- if (invert) {
- if ((rv = luo->inv_lookup(luo, out, in)) > 1)
- error ("%d, %s",xicco->errc,xicco->err);
+ /* Do the conversion */
+ if (invert) {
+ if ((rv = luo->inv_lookup(luo, out, in)) > 1)
+ error ("%d, %s",xicco->errc,xicco->err);
//printf("~1 %f: %f %f %f -> %f %f %f %f\n", ival, in[0], in[1], in[2], out[0], out[1], out[2], out[3]);
- } else {
- if ((rv = luo->lookup(luo, out, in)) > 1)
- error ("%d, %s",xicco->errc,xicco->err);
- }
+ } else {
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",xicco->errc,xicco->err);
+ }
- xx[i] = 100.0 * ival;
- for (j = 0; j < outn; j++)
- yy[j][i] = 100.0 * out[j];
- }
+ xx[i] = 100.0 * ival;
+ for (j = 0; j < outn; j++)
+ yy[j][i] = 100.0 * out[j];
+ }
//fflush(stdout);
+ }
/* plot order: Black Red Green Blue Yellow Purple */
if (outs == icSigRgbData) {
@@ -941,7 +1055,6 @@ main(int argc, char *argv[]) {
}
}
-
} else {
if (slocwarn && outs != icSigXYZData
@@ -966,7 +1079,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;
uout[i] = out[i] = in[i] = uin[i] = strtod(bp, &nbp);
if (nbp == bp)
@@ -976,8 +1089,7 @@ main(int argc, char *argv[]) {
break;
/* If device data and scale */
- if(scale > 0.0
- && ins != icxSigJabData
+ if( ins != icxSigJabData
&& ins != icxSigJChData
&& ins != icSigXYZData
&& ins != icSigLabData
@@ -987,8 +1099,29 @@ main(int argc, char *argv[]) {
&& ins != icSigYxyData
&& ins != icSigHsvData
&& ins != icSigHlsData) {
- for (i = 0; i < MAX_CHAN; i++) {
- in[i] /= scale;
+ if (scale > 0.0) {
+ for (i = 0; i < MAX_CHAN; i++)
+ in[i] /= scale;
+ }
+ if (inn == 3 && in_tvenc != 0) {
+ if (in_tvenc == 1) { /* Video 16-235 range */
+ icmRGB_2_VidRGB(in, in);
+ } else if (in_tvenc == 2) { /* Rec601 YCbCr */
+ icmRec601_RGBd_2_YPbPr(in, in);
+ icmRecXXX_YPbPr_2_YCbCr(in, in);
+ } else if (in_tvenc == 3) { /* Rec709 YCbCr */
+ icmRec709_RGBd_2_YPbPr(in, in);
+ icmRecXXX_YPbPr_2_YCbCr(in, in);
+ } else if (out_tvenc == 4) { /* Rec709 1250/50/2:1 YCbCr */
+ icmRec709_50_RGBd_2_YPbPr(in, in);
+ icmRecXXX_YPbPr_2_YCbCr(in, in);
+ } else if (out_tvenc == 5) { /* Rec2020 Non-constant Luminance YCbCr */
+ icmRec2020_NCL_RGBd_2_YPbPr(in, in);
+ icmRecXXX_YPbPr_2_YCbCr(in, in);
+ } else if (out_tvenc == 6) { /* Rec2020 Non-constant Luminance YCbCr */
+ icmRec2020_CL_RGBd_2_YPbPr(in, in);
+ icmRecXXX_YPbPr_2_YCbCr(in, in);
+ }
}
}
@@ -999,7 +1132,7 @@ main(int argc, char *argv[]) {
}
if (repYxy && ins == icSigYxyData) {
- icmXYZ2Yxy(in, in);
+ icmYxy2XYZ(in, in);
}
/* JCh -> Jab & LCh -> Lab */
@@ -1012,14 +1145,25 @@ main(int argc, char *argv[]) {
}
/* Do conversion */
- if (invert) {
- for (j = 0; j < MAX_CHAN; j++)
- out[j] = in[j]; /* Carry any auxiliary value to out for lookup */
- if ((rv = luo->inv_lookup(luo, out, in)) > 1)
- error ("%d, %s",xicco->errc,xicco->err);
- } else {
- if ((rv = luo->lookup(luo, out, in)) > 1)
- error ("%d, %s",xicco->errc,xicco->err);
+ if (cal != NULL) { /* .cal */
+ if (func == icmBwd || invert) {
+ if ((rv = cal->inv_interp(cal, out, in)) != 0)
+ error ("%d, %s",cal->errc,cal->err);
+ } else {
+ cal->interp(cal, out, in);
+ rv = 0;
+ }
+
+ } else { /* ICC */
+ if (invert) {
+ for (j = 0; j < MAX_CHAN; j++)
+ out[j] = in[j]; /* Carry any auxiliary value to out for lookup */
+ if ((rv = luo->inv_lookup(luo, out, in)) > 1)
+ error ("%d, %s",xicco->errc,xicco->err);
+ } else {
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",xicco->errc,xicco->err);
+ }
}
if (slocwarn) {
@@ -1030,7 +1174,7 @@ main(int argc, char *argv[]) {
else
icmCpy3(xyz, out);
- outsloc = icx_outside_spec_locus(xyz, icxOT_CIE_1931_2);
+ outsloc = icx_outside_spec_locus(chlp, xyz);
}
/* Copy conversion out value so that we can create user values */
@@ -1044,7 +1188,7 @@ main(int argc, char *argv[]) {
}
if (repYxy && outs == icSigYxyData) {
- icmXYZ2Yxy(out, out);
+ icmXYZ2Yxy(uout, uout);
}
/* Jab -> JCh and Lab -> LCh */
@@ -1058,8 +1202,7 @@ main(int argc, char *argv[]) {
}
/* If device data and scale */
- if(scale > 0.0
- && outs != icxSigJabData
+ if( outs != icxSigJabData
&& outs != icxSigJChData
&& outs != icSigXYZData
&& outs != icSigLabData
@@ -1069,8 +1212,29 @@ main(int argc, char *argv[]) {
&& outs != icSigYxyData
&& outs != icSigHsvData
&& outs != icSigHlsData) {
- for (i = 0; i < MAX_CHAN; i++) {
- uout[i] *= scale;
+ if (outn == 3 && out_tvenc != 0) {
+ if (out_tvenc == 1) { /* Video 16-235 range */
+ icmVidRGB_2_RGB(uout, uout);
+ } else if (out_tvenc == 2) { /* Rec601 YCbCr */
+ icmRecXXX_YCbCr_2_YPbPr(uout, uout);
+ icmRec601_YPbPr_2_RGBd(uout, uout);
+ } else if (out_tvenc == 3) { /* Rec709 1150/60/2:1 YCbCr */
+ icmRecXXX_YCbCr_2_YPbPr(uout, uout);
+ icmRec709_YPbPr_2_RGBd(uout, uout);
+ } else if (out_tvenc == 4) { /* Rec709 1250/50/2:1 YCbCr */
+ icmRecXXX_YCbCr_2_YPbPr(uout, uout);
+ icmRec709_50_YPbPr_2_RGBd(uout, uout);
+ } else if (out_tvenc == 5) { /* Rec2020 Non-constant Luminance YCbCr */
+ icmRecXXX_YCbCr_2_YPbPr(uout, uout);
+ icmRec2020_NCL_YPbPr_2_RGBd(uout, uout);
+ } else if (out_tvenc == 6) { /* Rec2020 Non-constant Luminance YCbCr */
+ icmRecXXX_YCbCr_2_YPbPr(uout, uout);
+ icmRec2020_CL_YPbPr_2_RGBd(uout, uout);
+ }
+ }
+ if (scale > 0.0) {
+ for (i = 0; i < MAX_CHAN; i++)
+ uout[i] *= scale;
}
}
@@ -1082,8 +1246,11 @@ main(int argc, char *argv[]) {
else
fprintf(stdout,"%f",uin[j]);
}
- printf(" [%s] -> %s -> ", icx2str(icmColorSpaceSignature, ins),
- icm2str(icmLuAlg, alg));
+ if (cal != NULL)
+ printf(" [%s] -> ", icx2str(icmColorSpaceSignature, ins));
+ else
+ printf(" [%s] -> %s -> ", icx2str(icmColorSpaceSignature, ins),
+ icm2str(icmLuAlg, alg));
}
for (j = 0; j < outn; j++) {
@@ -1138,11 +1305,16 @@ main(int argc, char *argv[]) {
/* Done with lookup object */
if (aluo != NULL && aluo != luo)
luo->del(aluo);
- luo->del(luo);
-
- xicco->del(xicco); /* Expansion wrapper */
- icco->del(icco); /* Icc */
- fp->del(fp);
+ if (luo != NULL)
+ luo->del(luo);
+ if (cal != NULL)
+ cal->del(cal);
+ if (xicco != NULL)
+ xicco->del(xicco); /* Expansion wrapper */
+ if (icco != NULL)
+ icco->del(icco); /* Icc */
+ if (fp != NULL)
+ fp->del(fp);
return 0;
}
diff --git a/xicc/xlut.c b/xicc/xlut.c
index 21c286e..bbad934 100644
--- a/xicc/xlut.c
+++ b/xicc/xlut.c
@@ -56,8 +56,8 @@
do anything useful though, the chromatic adapation tag would
have to be used for absolute intent.
This may be the way of improving compatibility with other systems,
- but would break compatibility with existing Argyll profiles,
- unless special measures are taken:
+ and is needed for V4, but would break compatibility with existing
+ Argyll profiles, unless special measures are taken:
ie.
@@ -65,7 +65,7 @@
Create Bradford chromatic adapation matrix and store it in tag
Adapt all the readings using Bradford
Create white point and store it in tag (white point will be D50)
- Adapt all the readings to the white point using wrong Von-Kries (no change)
+ Adapt all the readings to the white point using wrong Von-Kries (== NOOP)
Store relative colorimetric cLUT
Set version >= 2.4
@@ -154,12 +154,16 @@
#undef DISABLE_KCURVE_FILTER /* [Undef] don't filter the Kcurve */
#undef REPORT_LOCUS_SEGMENTS /* [Undef[ Examine how many segments there are in aux inversion */
+#define XYZ_EXTRA_SMOOTH 20.0 /* Extra smoothing factor for XYZ profiles */
#define SHP_SMOOTH 1.0 /* Input shaper curve smoothing */
#define OUT_SMOOTH1 1.0 /* Output shaper curve smoothing for L*, X,Y,Z */
#define OUT_SMOOTH2 1.0 /* Output shaper curve smoothing for a*, b* */
#define CAMCLIPTRANS 1.0 /* [1.0] Cam clipping transition region Delta E */
-#define USECAMCLIPSPLINE /* [def] use spline blend */
+ /* Should this be smaller ? */
+#undef USECAMCLIPSPLINE /* [Und] use spline blend between PCS and Jab */
+
+#define CCJSCALE 2.0 /* [2.0] Amount to emphasize J delta E in in computing clip */
/*
* TTBD:
@@ -1322,6 +1326,7 @@ double *in /* Function input values to invert (== clut output' values) */
if (crv && clipd != NULL) {
+
/* Compute the clip DE */
for (cdist = 0.0, f = 0; f < fdi; f++) {
double tt;
@@ -1329,6 +1334,7 @@ double *in /* Function input values to invert (== clut output' values) */
cdist += tt * tt;
}
cdist = sqrt(cdist);
+ DBR(("Targ PCS %f %f %f Clipped %f %f %f cdist %f\n",tin[0],tin[1],tin[2],pp[0].v[0],pp[0].v[1],pp[0].v[2],cdist))
}
nsoln &= RSPL_NOSOLNS; /* Get number of solutions */
@@ -1368,6 +1374,7 @@ double *in /* Function input values to invert (== clut output' values) */
for (f = 0; f < fdi; f++) /* Transfer CAM targ */
cpp.v[f] = tin[f];
+ cpp.v[0] *= CCJSCALE;
/* Make sure that the auxiliar value is initialized, */
/* even though it shouldn't have any effect, since should clipp. */
@@ -1376,7 +1383,6 @@ double *in /* Function input values to invert (== clut output' values) */
cpp.p[e] = 0.5;
}
}
-
if (p->clutTable->di > fdi) { /* ie. CMYK->Lab, there will be ambiguity */
nsoln = p->cclutTable->rev_interp(
@@ -1404,12 +1410,14 @@ double *in /* Function input values to invert (== clut output' values) */
}
/* Compute the CAM clip distances */
+ cpp.v[0] /= CCJSCALE;
for (cdist = 0.0, f = 0; f < fdi; f++) {
double tt;
tt = cpp.v[f] - tin[f];
cdist += tt * tt;
}
cdist = sqrt(cdist);
+ DBR(("Targ CAM %f %f %f Clipped %f %f %f cdist %f\n",tin[0],tin[1],tin[2],cpp.v[0],cpp.v[1],cpp.v[2],cdist))
/* Use magic number to set blend distance, and compute a blend factor. */
/* Blend over 1 delta E, otherwise the Lab & CAM02 divergence can result */
@@ -1425,20 +1433,21 @@ double *in /* Function input values to invert (== clut output' values) */
/* Blend between solution values for PCS and CAM clip result. */
/* We're hoping that the solutions are close, and expect them to be */
/* that way when we're close to the gamut surface (since the cell */
- /* vertex values should be exact, irrespective of which space they're */
- /* in), but weird things could happen away from the surface. Weird */
- /* things can happen anyway with "clip to nearest", since this is not */
+ /* vertex values should be exact, irrespective of which interpolation */
+ /* space they're in), but weird things could happen away from the surface. */
+ /* Weird things can happen anyway with "clip to nearest", since this is not */
/* guaranteed to be a smooth function, depending on the gamut surface */
/* geometry, without taking some precaution such as clipping to a */
/* convex hull "wrapper" to create a clip vector, which we're not */
/* current doing. */
DBR(("Clip blend between device:\n"))
- DBR(("Lab: %f %f %f and\n",pp[0].p[0], pp[0].p[1], pp[0].p[2]))
- DBR(("Jab: %f %f %f\n",cpp.p[0], cpp.p[1], cpp.p[2]))
+ DBR(("Lab: %s & ",icmPdv(p->clutTable->di, pp[0].p)))
+ DBR(("Jab: %s ",icmPdv(p->clutTable->di, cpp.p)))
for (e = 0; e < p->clutTable->di; e++) {
out[e] = (1.0 - bf) * pp[0].p[e] + bf * cpp.p[e];
}
+ DBR((" = %s\n",icmPdv(p->clutTable->di, out)))
/* Not CAM clip case */
} else {
@@ -1459,6 +1468,7 @@ double *in /* Function input values to invert (== clut output' values) */
for (i = 0; i < nsoln; i++) {
double ss;
+ DBR(("Soln %d: %s\n",i, icmPdv(p->clutTable->di, pp[i].p)))
for (ss = 0.0, e = 0; e < p->clutTable->di; e++) {
double tt;
tt = pp[i].p[e] - p->licent[e];
@@ -1469,6 +1479,16 @@ double *in /* Function input values to invert (== clut output' values) */
}
}
}
+#ifndef NEVER
+ // ~~99 average them
+ for (i = 1; i < nsoln; i++) {
+ for (e = 0; e < p->clutTable->di; e++)
+ pp[0].p[e] += pp[i].p[e];
+ }
+ for (e = 0; e < p->clutTable->di; e++)
+ pp[0].p[e] /= (double)nsoln;
+ bsoln = 0;
+#endif
//printf("~1 chose %d\n",bsoln);
i = bsoln;
}
@@ -2289,8 +2309,8 @@ fprintf(stderr,"~1 Internal optimised 4D separations not yet implemented!\n");
else
xicc_enum_viewcond(xicp, &p->vc, -1, NULL, 0, NULL); /* Use a default */
p->cam = new_icxcam(cam_default);
- p->cam->set_view(p->cam, p->vc.Ev, p->vc.Wxyz, p->vc.La, p->vc.Yb, p->vc.Lv, p->vc.Yf,
- p->vc.Fxyz, XICC_USE_HK);
+ p->cam->set_view(p->cam, p->vc.Ev, p->vc.Wxyz, p->vc.La, p->vc.Yb, p->vc.Lv,
+ p->vc.Yf, p->vc.Yg, p->vc.Gxyz, XICC_USE_HK);
} else {
p->cam = NULL;
}
@@ -2477,10 +2497,12 @@ icxLuLut_clut_camclip_func(
luluto->output(luluto, out, out);
luluto->out_abs(luluto, out, out);
p->cam->XYZ_to_cam(p->cam, out, out);
+ out[0] *= CCJSCALE;
}
/* Initialise the additional CAM space clut rspl, used to compute */
/* reverse lookup CAM clipping results when the camclip flag is set. */
+/* We scale J by CCJSCALE, to give a more L* preserving clip direction */
/* return error code. */
static int
icxLuLut_init_clut_camclip(
@@ -2490,7 +2512,7 @@ icxLuLut *p) {
/* Setup so clut contains transform to CAM Jab */
/* (camclip is only used in fwd or invfwd direction lookup) */
double cmin[3], cmax[3];
- cmin[0] = 0.0; cmax[0] = 100.0; /* Nominal Jab output ranges */
+ cmin[0] = 0.0; cmax[0] = CCJSCALE * 100.0; /* Nominal Jab output ranges */
cmin[1] = -128.0; cmax[1] = 128.0;
cmin[2] = -128.0; cmax[2] = 128.0;
@@ -2835,6 +2857,7 @@ double dispLuminance, /* > 0.0 if display luminance value and is kno
double wpscale, /* > 0.0 if white point is to be scaled */
double smooth, /* RSPL smoothing factor, -ve if raw */
double avgdev, /* reading Average Deviation as a prop. of the input range */
+double demph, /* dark emphasis factor for cLUT grid res. */
icxViewCond *vc, /* Viewing Condition (NULL if not using CAM) */
icxInk *ink, /* inking details (NULL for default) */
int quality /* Quality metric, 0..3 */
@@ -2906,15 +2929,18 @@ int quality /* Quality metric, 0..3 */
/* (This is for rspl scattered data fitting smoothness adjustment) */
/* (This could do with more tuning) */
- /* For some unknown reason XYZ seems masively under-smoothed, so bump it up */
+ /* XYZ display models are under-smoothed, because the mapping is typically */
+ /* very "straight", and the lack of tension reduces any noise reduction effect. */
+ /* !!! This probably means that we should switch to 3rd order smoothness criteria !! */
+ /* We apply an arbitrary correction here */
if (p->pcs == icSigXYZData) {
- oavgdev[0] = 1.0 * 0.60 * avgdev;
- oavgdev[1] = 1.0 * 1.00 * avgdev;
- oavgdev[2] = 1.0 * 0.60 * avgdev;
+ oavgdev[0] = XYZ_EXTRA_SMOOTH * 0.70 * avgdev;
+ oavgdev[1] = XYZ_EXTRA_SMOOTH * 1.00 * avgdev;
+ oavgdev[2] = XYZ_EXTRA_SMOOTH * 0.70 * avgdev;
} else if (p->pcs == icSigLabData) {
oavgdev[0] = 1.00 * avgdev;
- oavgdev[1] = 0.60 * avgdev;
- oavgdev[2] = 0.60 * avgdev;
+ oavgdev[1] = 0.70 * avgdev;
+ oavgdev[2] = 0.70 * avgdev;
} else { /* Hmmm */
for (f = 0; f < p->outputChan; f++)
oavgdev[f] = avgdev;
@@ -3348,7 +3374,7 @@ int quality /* Quality metric, 0..3 */
if (xf->fit(xf, xfflags, p->inputChan, p->outputChan,
rsplflags, wp, dwhite, wpscale, dgwhite,
ipoints, nodp, skm, in_min, in_max, gres, out_min, out_max,
- smooth, oavgdev, iord, sord, oord, shp_smooth, out_smooth, tcomb,
+ smooth, oavgdev, demph, iord, sord, oord, shp_smooth, out_smooth, tcomb,
(void *)p, xfit_to_de2, xfit_to_dde2) != 0) {
p->pp->errc = 2;
sprintf(p->pp->err,"xfit fitting failed");
@@ -3821,7 +3847,8 @@ int quality /* Quality metric, 0..3 */
NULL, NULL, /* Use default Maximum range of Dev' values */
set_clut, /* Dev' -> PCS' transfer function */
NULL, NULL, /* Use default Maximum range of PCS' values */
- set_output /* Linear output transform PCS'->PCS */
+ set_output, /* Linear output transform PCS'->PCS */
+ NULL, NULL /* No APXLS */
) != 0)
error("Setting 16 bit %s->%s Lut failed: %d, %s",
icm2str(icmColorSpaceSignature, h->colorSpace),
@@ -3843,8 +3870,8 @@ int quality /* Quality metric, 0..3 */
else
xicc_enum_viewcond(xicp, &p->vc, -1, NULL, 0, NULL); /* Use a default */
p->cam = new_icxcam(cam_default);
- p->cam->set_view(p->cam, p->vc.Ev, p->vc.Wxyz, p->vc.La, p->vc.Yb, p->vc.Lv, p->vc.Yf,
- p->vc.Fxyz, XICC_USE_HK);
+ p->cam->set_view(p->cam, p->vc.Ev, p->vc.Wxyz, p->vc.La, p->vc.Yb, p->vc.Lv,
+ p->vc.Yf, p->vc.Yg, p->vc.Gxyz, XICC_USE_HK);
if (flags & ICX_VERBOSE)
printf("Done A to B table creation\n");
diff --git a/xicc/xlutfix.c b/xicc/xlutfix.c
index 6497688..a0c0e4e 100644
--- a/xicc/xlutfix.c
+++ b/xicc/xlutfix.c
@@ -368,7 +368,8 @@ void (*outfunc)(void *cbctx, double *out, double *in)
inmin, inmax,
xif_set_clut,
clutmin, clutmax,
- xif_set_output);
+ xif_set_output,
+ NULL, NULL);
return rv;
}
@@ -540,7 +541,8 @@ printf("~1 doing the first pass\n");
inmin, inmax,
xif_set_clut,
clutmin, clutmax,
- xif_set_output);
+ xif_set_output,
+ NULL, NULL);
#if defined(SAVE_TRACE) || defined(USE_TRACE)
fclose(xcs.tf);
@@ -594,7 +596,8 @@ printf("~1 updatding the icc\n");
inmin, inmax,
xif_set_clut,
clutmin, clutmax,
- xif_set_output);
+ xif_set_output,
+ NULL, NULL);
free(xcs.fhi);
free(xcs.g);
diff --git a/xicc/xmatrix.c b/xicc/xmatrix.c
index a194314..53db237 100644
--- a/xicc/xmatrix.c
+++ b/xicc/xmatrix.c
@@ -42,9 +42,9 @@
#define XSHAPE_OFFG 0.1 /* Input offset weights when ord 0 is gamma */
#define XSHAPE_OFFS 1.0 /* Input offset weights when ord 0 is shaper */
-#define XSHAPE_HW01 0.002 /* 0 & 1 harmonic weights */
-#define XSHAPE_HBREAK 4 /* Harmonic that has HWBR */
-#define XSHAPE_HWBR 0.8 /* Base weight of harmonics HBREAK up */
+#define XSHAPE_HW01 0.01 /* 0 & 1 harmonic weights */
+#define XSHAPE_HBREAK 3 /* Harmonic that has HWBR */
+#define XSHAPE_HWBR 0.5 /* Base weight of harmonics HBREAK up */
#define XSHAPE_HWINC 0.5 /* Increase in weight for each harmonic above HWBR */
#define XSHAPE_GAMTHR 0.01 /* Input threshold for linear slope below gamma power */
@@ -309,8 +309,8 @@ int dir /* 0 = fwd, 1 = bwd */
else
xicc_enum_viewcond(xicp, &p->vc, -1, NULL, 0, NULL); /* Use a default */
p->cam = new_icxcam(cam_default);
- p->cam->set_view(p->cam, p->vc.Ev, p->vc.Wxyz, p->vc.La, p->vc.Yb, p->vc.Lv, p->vc.Yf,
- p->vc.Fxyz, XICC_USE_HK);
+ p->cam->set_view(p->cam, p->vc.Ev, p->vc.Wxyz, p->vc.La, p->vc.Yb, p->vc.Lv,
+ p->vc.Yf, p->vc.Yg, p->vc.Gxyz, XICC_USE_HK);
} else {
p->cam = NULL;
}
@@ -560,7 +560,6 @@ double *v /* Pointer to parameters */
tt = v[11 + f];
if (f == 0 && p->shape0gam)
tt -= 1.0; /* default is linear */
- tt *= tt;
/* Weigh to suppress ripples */
if (f <= 1) { /* Use XSHAPE_HW01 */
w = XSHAPE_HW01;
@@ -568,15 +567,15 @@ double *v /* Pointer to parameters */
double bl = (f - 1.0)/(XSHAPE_HBREAK - 1.0);
w = (1.0 - bl) * XSHAPE_HW01 + bl * XSHAPE_HWBR * p->smooth;
} else { /* Use XSHAPE_HWBR * smooth */
- w = XSHAPE_HWBR + (f-XSHAPE_HBREAK) * XSHAPE_HWINC;
- w *= p->smooth;
+ w = XSHAPE_HWBR + (f-XSHAPE_HBREAK) * XSHAPE_HWINC * p->smooth;
}
+ tt *= tt;
tparam += w * tt;
}
return XSHAPE_MAG * tparam;
}
- /* Input ffset value */
+ /* Input offset value */
if (p->shape0gam)
w = XSHAPE_OFFG;
else
@@ -603,7 +602,6 @@ double *v /* Pointer to parameters */
w = (1.0 - bl) * XSHAPE_HW01 + bl * XSHAPE_HWBR * p->smooth;
} else {
w = XSHAPE_HWBR + (f-XSHAPE_HBREAK) * XSHAPE_HWINC * p->smooth;
- w *= p->smooth;
}
for (g = 0; g < 3; g++) {
tt = v[15 + 3 * f + g];
@@ -863,6 +861,10 @@ double scale /* Scale device values */
os->v[3] = 0.2; os->v[4] = 0.8; os->v[5] = 0.1;
os->v[6] = 0.02; os->v[7] = 0.15; os->v[8] = 1.3;
+ /* We try and take a homomorphic approach here, in an attempt */
+ /* to avoid getting trapped at a local minimum when a full */
+ /* set of shaper parameters are in play. */
+
/* Do a first pass just setting the matrix values */
os->isLinear = 1;
os->isGamma = 1;
@@ -889,17 +891,43 @@ double scale /* Scale device values */
#endif /* NEVER */
/* Now optimize again with shaper or gamma curves */
- if (!isLinear || isGamma) {
+ if (!isLinear) {
+ double scgamma;
/* Start from linear, which is what was assumed for the matrix fit, */
- /* and fit first with a single shared curve. */
+ /* and fit with a single shared gamma curve. */
os->isShTRC = 1;
- if (isGamma) { /* Just gamma curve */
- os->isLinear = 0;
- os->isGamma = 1;
- os->optdim = 10;
- os->v[9] = 1.0; /* Linear */
- } else { /* Creating input curves */
+ os->isLinear = 0;
+ os->isGamma = 1;
+ os->optdim = 10;
+ os->v[9] = 1.0; /* Linear */
+
+ /* Set search area to starting values */
+ for (j = 0; j < os->optdim; j++)
+ os->sa[j] = 0.2; /* Matrix, Gamma, Offsets, harmonics */
+
+ if (os->verb)
+ printf("Creating matrix and single gamma curve...\n");
+
+ if (powell(&rerr, os->optdim, os->v, os->sa, stopon, maxits,
+ mxoptfunc, (void *)os, mxprogfunc, (void *)os) != 0)
+ warning("Powell failed to converge, residual error = %f",rerr);
+
+ scgamma = os->v[9];
+ if (isShTRC && !isGamma) {
+
+#ifndef NEVER
+ if (os->verb) {
+ printf("Matrix = %f %f %f\n",os->v[0], os->v[1], os->v[2]);
+ printf(" %f %f %f\n",os->v[3], os->v[4], os->v[5]);
+ printf(" %f %f %f\n",os->v[6], os->v[7], os->v[8]);
+ printf("Gamma = %f\n",os->v[9]);
+ }
+#endif /* NEVER */
+
+ /* Do final optimisation using full curve capability */
+ /* and fit first with a single shared curve. */
+ os->isShTRC = 1;
os->isLinear = 0;
os->isGamma = 0;
os->optdim = 9 + 2 + os->norders; /* Matrix, offset + orders */
@@ -911,71 +939,96 @@ double scale /* Scale device values */
os->v[11] = 0.0; /* 0th Harmonic */
for (i = 12; i < os->optdim; i++)
os->v[i] = 0.0; /* Higher orders */
- }
- /* Set search area to starting values */
- for (j = 0; j < os->optdim; j++)
- os->sa[j] = 0.2; /* Matrix, Gamma, Offsets, harmonics */
+ /* Set search area to starting values */
+ for (j = 0; j < os->optdim; j++)
+ os->sa[j] = 0.2; /* Matrix, Gamma, Offsets, harmonics */
- if (os->verb)
- printf("Creating matrix and single curve...\n");
+ if (os->verb)
+ printf("Creating matrix and single shaper curve...\n");
- if (powell(&rerr, os->optdim, os->v, os->sa, stopon, maxits,
- mxoptfunc, (void *)os, mxprogfunc, (void *)os) != 0)
- warning("Powell failed to converge, residual error = %f",rerr);
+ if (powell(&rerr, os->optdim, os->v, os->sa, stopon, maxits,
+ mxoptfunc, (void *)os, mxprogfunc, (void *)os) != 0)
+ warning("Powell failed to converge, residual error = %f",rerr);
+
+ scgamma = os->v[9];
+
+ }
+
+ /* For multiple curves, continue fitting */
+ if (!isShTRC) {
+ double mcgamma[3];
#ifndef NEVER
- if (os->verb) {
- printf("Matrix = %f %f %f\n",os->v[0], os->v[1], os->v[2]);
- printf(" %f %f %f\n",os->v[3], os->v[4], os->v[5]);
- printf(" %f %f %f\n",os->v[6], os->v[7], os->v[8]);
- if (isGamma) { /* Creating input curves */
+ if (os->verb) {
+ printf("Matrix = %f %f %f\n",os->v[0], os->v[1], os->v[2]);
+ printf(" %f %f %f\n",os->v[3], os->v[4], os->v[5]);
+ printf(" %f %f %f\n",os->v[6], os->v[7], os->v[8]);
printf("Gamma = %f\n",os->v[9]);
- } else { /* Creating input curves */
- printf("Input offset = %f\n",os->v[9]);
- printf("Output offset = %f\n",os->v[10]);
- for (j = 0; j < os->norders; j++) {
- if (shape0gam && j == 0)
- printf("gamma = %f\n", os->v[11 + j]);
- else
- printf("%d harmonics = %f\n",j, os->v[11 + j]);
- }
}
- }
#endif /* NEVER */
- /* Now do the final optimisation with all curves */
- if (!isShTRC) {
+ /* Fit matrix + multi gamma curves */
os->isShTRC = 0;
- if (isGamma) { /* Just gamma curves */
- os->isLinear = 0;
- os->isGamma = 1;
- os->optdim = 12;
- os->v[9] = os->v[10] = os->v[11] = 1.0; /* Linear */
- } else { /* Creating input curves */
- os->isLinear = 0;
- os->isGamma = 0;
- os->optdim = 9 + 6 + 3 * os->norders; /* Matrix, offset + orders */
- os->v[9] = os->v[10] = os->v[11] = 0.0; /* Input offset */
- os->v[12] = os->v[13] = os->v[14] = 0.0; /* Output offset */
- if (shape0gam)
- os->v[15] = os->v[16] = os->v[17] = 1.0; /* Gamma */
- else
- os->v[15] = os->v[16] = os->v[17] = 0.0; /* 0th Harmonic */
- for (i = 18; i < os->optdim; i++)
- os->v[i] = 0.0; /* Higher orders */
- }
+ os->isLinear = 0;
+ os->isGamma = 1;
+ os->optdim = 12;
+ os->v[9] = os->v[10] = os->v[11] = scgamma; /* Single curve value */
/* Set search area to starting values */
for (j = 0; j < os->optdim; j++)
os->sa[j] = 0.2; /* Matrix, Gamma, Offsets, harmonics */
if (os->verb)
- printf("Creating matrix and curves...\n");
+ printf("Creating matrix and gamma curves...\n");
if (powell(&rerr, os->optdim, os->v, os->sa, stopon, maxits,
mxoptfunc, (void *)os, mxprogfunc, (void *)os) != 0)
warning("Powell failed to converge, residual error = %f",rerr);
+
+
+ mcgamma[0] = os->v[9];
+ mcgamma[1] = os->v[10];
+ mcgamma[2] = os->v[11];
+
+ if (!isGamma) {
+
+#ifndef NEVER
+ if (os->verb) {
+ printf("Matrix = %f %f %f\n",os->v[0], os->v[1], os->v[2]);
+ printf(" %f %f %f\n",os->v[3], os->v[4], os->v[5]);
+ printf(" %f %f %f\n",os->v[6], os->v[7], os->v[8]);
+ printf("Gamma = %f %f %f\n",os->v[9], os->v[10], os->v[11]);
+ }
+#endif /* NEVER */
+
+ /* Do final curves */
+ os->isShTRC = 0;
+ os->isLinear = 0;
+ os->isGamma = 0;
+ os->optdim = 9 + 6 + 3 * os->norders; /* Matrix, offset + orders */
+ os->v[9] = os->v[10] = os->v[11] = 0.0; /* Input offset */
+ os->v[12] = os->v[13] = os->v[14] = 0.0; /* Output offset */
+ if (shape0gam) {
+ os->v[15] = mcgamma[0];
+ os->v[16] = mcgamma[1];
+ os->v[17] = mcgamma[2];
+ } else
+ os->v[15] = os->v[16] = os->v[17] = 0.0; /* 0th Harmonic */
+ for (i = 18; i < os->optdim; i++)
+ os->v[i] = 0.0; /* Higher orders */
+
+ /* Set search area to starting values */
+ for (j = 0; j < os->optdim; j++)
+ os->sa[j] = 0.1; /* Matrix, Gamma, Offsets, harmonics */
+
+ if (os->verb)
+ printf("Creating matrix and curves...\n");
+
+ if (powell(&rerr, os->optdim, os->v, os->sa, stopon, maxits,
+ mxoptfunc, (void *)os, mxprogfunc, (void *)os) != 0)
+ warning("Powell failed to converge, residual error = %f",rerr);
+ }
}
}
if (os->clipprims) { /* Clip -ve primaries */
@@ -991,7 +1044,7 @@ double scale /* Scale device values */
printf(" %f %f %f\n",os->v[3], os->v[4], os->v[5]);
printf(" %f %f %f\n",os->v[6], os->v[7], os->v[8]);
if (!isLinear) { /* Creating input curves */
- if (isGamma) { /* Creating input curves */
+ if (os->isGamma) { /* Creating input curves */
if (isShTRC)
printf("Gamma = %f\n",os->v[9]);
else
@@ -1531,7 +1584,7 @@ double smooth /* Curve smoothing, nominally 1.0 */
}
/* If we are going to auto scale the WP to avoid clipping */
- /* values above the WP: (not important for matrix profiles ?) */
+ /* values above the WP: (not so important for matrix profiles ?) */
if ((p->flags & ICX_SET_WHITE_US) == ICX_SET_WHITE_US) {
double tw[3], bw[3];
icmXYZNumber _wp;
@@ -1813,12 +1866,28 @@ double smooth /* Curve smoothing, nominally 1.0 */
/* Matrix values */
{
icmXYZArray *wor, *wog, *wob;
+ double mat[3][3];
wor = pmlu->redColrnt;
wog = pmlu->greenColrnt;
wob = pmlu->blueColrnt;
- wor->data[0].X = os.v[0]; wor->data[0].Y = os.v[3]; wor->data[0].Z = os.v[6];
- wog->data[0].X = os.v[1]; wog->data[0].Y = os.v[4]; wog->data[0].Z = os.v[7];
- wob->data[0].X = os.v[2]; wob->data[0].Y = os.v[5]; wob->data[0].Z = os.v[8];
+
+ /* Copy to mat[RGB][XYZ] */
+ mat[0][0] = os.v[0];
+ mat[0][1] = os.v[3];
+ mat[0][2] = os.v[6];
+ mat[1][0] = os.v[1];
+ mat[1][1] = os.v[4];
+ mat[1][2] = os.v[7];
+ mat[2][0] = os.v[2];
+ mat[2][1] = os.v[5];
+ mat[2][2] = os.v[8];
+
+ /* Make sure rounding doesn't wreck white point */
+ quantizeRGBprimsS15Fixed16(mat);
+
+ wor->data[0].X = mat[0][0]; wor->data[0].Y = mat[0][1]; wor->data[0].Z = mat[0][2];
+ wog->data[0].X = mat[1][0]; wog->data[0].Y = mat[1][1]; wog->data[0].Z = mat[1][2];
+ wob->data[0].X = mat[2][0]; wob->data[0].Y = mat[2][1]; wob->data[0].Z = mat[2][2];
/* Load into pmlu matrix and inverse ??? */
}
diff --git a/xicc/xmono.c b/xicc/xmono.c
index f0ee04f..fe9c55e 100644
--- a/xicc/xmono.c
+++ b/xicc/xmono.c
@@ -261,7 +261,7 @@ int dir /* 0 = fwd, 1 = bwd */
if (pcsor == icxSigJabData) {
p->vc = *vc; /* Copy the structure */
p->cam = new_icxcam(cam_default);
- p->cam->set_view(p->cam, vc->Ev, vc->Wxyz, vc->La, vc->Yb, vc->Lv, vc->Yf, vc->Fxyz,
+ p->cam->set_view(p->cam, vc->Ev, vc->Wxyz, vc->La, vc->Yb, vc->Lv, vc->Yf, vc->Yg, vc->Gxyz,
XICC_USE_HK);
} else
p->cam = NULL;
diff --git a/xicc/xspect.c b/xicc/xspect.c
index a372d60..cc0ce85 100644
--- a/xicc/xspect.c
+++ b/xicc/xspect.c
@@ -23,16 +23,7 @@
/*
* TTBD:
*
- * If needed by ISO 13655-1009:
- * fwa_convert() function takes two illuminants:
- * first one is the measurement illumination to correct to,
- * the second is the assumed illumination spectrum for XYZ conversion.
- * so we compute the spectral reflectance as if the instrument had
- * one sort of practical illuminant (and taking into account FWA),
- * and then convert to D50 equivalent.
- * Need to bypass FWA if inst illum == simulated inst. illum.
- * Need to modify tools to allow optional param to -f which is
- * the simulated instrument illum, then make -i have -M0, -M1, -M2 options.
+ * Should add some more modern standard CMFs - see <http://www.cvrl.org/>
*
* [Does this make any sense though ? That is what's happening
* for a standard A illuminant instrument emitting D50 XYZ values,
@@ -52,6 +43,7 @@
#else
# include "numsup.h"
#endif
+#include "conv.h"
#include "xspect.h"
#define CLAMP_XYZ /* [def] Clamp XYZ to be >= 0.0 */
@@ -279,8 +271,9 @@ static int daylight_il(xspect *sp, double ct) {
double xd, yd;
double m1, m2;
- if (ct < 1000.0 || ct > 35000.0) /* Actually, accuracy is guaranteed from only 4000 - 25000 */
+ if (ct < 4000.0 || ct > 25000.0) { /* Only accurate down to 4000 */
return 1;
+ }
/* Compute chromaticity coordinates */
if (ct < 7000.0) {
@@ -306,6 +299,8 @@ static int daylight_il(xspect *sp, double ct) {
return 0;
}
+#endif /* !SALONEINSTLIB */
+
/* General temperature Planckian (black body) spectra */
/* Fill in the given xspect with the specified Planckian illuminant */
/* Return nz if temperature is out of range */
@@ -317,7 +312,6 @@ static int planckian_il(xspect *sp, double ct) {
return 1;
/* Set out targets */
-// sp->spec_n = 107; /* 5nm */
sp->spec_n = 531; /* 1nm */
sp->spec_wl_short = 300.0;
sp->spec_wl_long = 830;
@@ -337,6 +331,8 @@ static int planckian_il(xspect *sp, double ct) {
return 0;
}
+#ifndef SALONEINSTLIB
+
/* CIE F5 */
/* Fluorescent, Standard, 6350K, CRI 72 */
static xspect il_F5 = {
@@ -364,7 +360,7 @@ static xspect il_F5 = {
/* Fluorescent, Wide band 5000K, CRI 95 */
static xspect il_F8 = {
107, 300.0, 830.0, /* 109 bands from 300 to 830 nm in 5nm steps */
- 20.0, /* Arbitrary scale factor */
+ 30.0, /* Arbitrary scale factor */
{
/* 300 */ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
/* 340 */ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
@@ -388,7 +384,7 @@ static xspect il_F8 = {
/* Fluorescent, Narrow band 5000K, CRI 81 */
static xspect il_F10 = {
107, 300.0, 830.0, /* 109 bands from 300 to 830 nm in 5nm steps */
- 20.0, /* Arbitrary scale factor */
+ 30.0, /* Arbitrary scale factor */
{
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
@@ -503,28 +499,16 @@ double temp /* Optional temperature in degrees kelvin, for Dtemp and Ptemp *
return 0;
case icxIT_Dtemp:
return daylight_il(sp, temp);
+#endif
case icxIT_Ptemp:
return planckian_il(sp, temp);
-#endif
}
return 1;
}
-/* ------------- */
-
-/* Spectral locus poligon cache */
-typedef struct {
- int n; /* Number of spectral vertexes, 0 if uninit */
- double xmin, xmax, ymin, ymax; /* Boundint box */
- double tx[3], ty[3]; /* Fast inner triangle test, RGB */
- double be[3][3]; /* baricentric equations of triangle */
-// double eed[3]; /* Distance of triangle points to 0.3, 0.3 */
- double x[XSPECT_MAX_BANDS]; /* x value of vertex */
- double y[XSPECT_MAX_BANDS]; /* y value of vertex */
-} xslpoly;
/* ------------- */
-/* Observer Data and locus poligon cache */
+/* Observer Data */
/* Standard CIE 1931 2 degree */
static xspect ob_CIE_1931_2[3] = {
@@ -833,8 +817,6 @@ static xspect ob_CIE_1931_2[3] = {
}
};
-static xslpoly poly_CIE_1931_2 = { 0 };
-
/* Standard CIE 1964 10 degree */
static xspect ob_CIE_1964_10[3] = {
{
@@ -1142,8 +1124,6 @@ static xspect ob_CIE_1964_10[3] = {
}
};
-static xslpoly poly_CIE_1964_10 = { 0 };
-
#ifndef SALONEINSTLIB
/* Standard CIE 1964 10 degree observer, */
/* adjusted for compatibility with 2 degree observer. */
@@ -1454,8 +1434,6 @@ static xspect ob_CIE_1964_10c[3] = {
}
};
-static xslpoly poly_CIE_1964_10c = { 0 };
-
/* Judd & Voss 1978 2 degree */
static xspect ob_Judd_Voss_2[3] = {
{
@@ -1532,8 +1510,6 @@ static xspect ob_Judd_Voss_2[3] = {
}
};
-static xslpoly poly_Judd_Voss_2 = { 0 };
-
/* Stiles & Burch 1955 2 degree, */
/* rotated to align with 1931 XYZ space, */
@@ -1601,8 +1577,6 @@ static xspect ob_Stiles_Burch_2[3] = {
}
};
-static xslpoly poly_Stiles_Burch_2 = { 0 };
-
/* Shaw & Fairchild 1997 2 degree observer. */
/* From Mark Shaw's Masters thesis: */
/* "Evaluating the 1931 CIE Color Matching Functions" */
@@ -1666,8 +1640,898 @@ static xspect ob_Shaw_Fairchild_2[3] = {
}
};
-static xslpoly poly_Shaw_Fairchild_2 = { 0 };
+#ifdef NEVER
+CIE TC 1-36 proposed
+2-deg XYZ CMFs transformed from the CIE (2006) 2-deg LMS cone fundamentals
+
+390 3.769647E-03 4.146161E-04 1.847260E-02
+391 4.532416E-03 5.028333E-04 2.221101E-02
+392 5.446553E-03 6.084991E-04 2.669819E-02
+393 6.538868E-03 7.344436E-04 3.206937E-02
+394 7.839699E-03 8.837389E-04 3.847832E-02
+395 9.382967E-03 1.059646E-03 4.609784E-02
+396 1.120608E-02 1.265532E-03 5.511953E-02
+397 1.334965E-02 1.504753E-03 6.575257E-02
+398 1.585690E-02 1.780493E-03 7.822113E-02
+399 1.877286E-02 2.095572E-03 9.276013E-02
+400 2.214302E-02 2.452194E-03 1.096090E-01
+401 2.601285E-02 2.852216E-03 1.290077E-01
+402 3.043036E-02 3.299115E-03 1.512047E-01
+403 3.544325E-02 3.797466E-03 1.764441E-01
+404 4.109640E-02 4.352768E-03 2.049517E-01
+405 4.742986E-02 4.971717E-03 2.369246E-01
+406 5.447394E-02 5.661014E-03 2.725123E-01
+407 6.223612E-02 6.421615E-03 3.117820E-01
+408 7.070048E-02 7.250312E-03 3.547064E-01
+409 7.982513E-02 8.140173E-03 4.011473E-01
+410 8.953803E-02 9.079860E-03 4.508369E-01
+411 9.974848E-02 1.005608E-02 5.034164E-01
+412 1.104019E-01 1.106456E-02 5.586361E-01
+413 1.214566E-01 1.210522E-02 6.162734E-01
+414 1.328741E-01 1.318014E-02 6.760982E-01
+415 1.446214E-01 1.429377E-02 7.378822E-01
+416 1.566468E-01 1.545004E-02 8.013019E-01
+417 1.687901E-01 1.664093E-02 8.655573E-01
+418 1.808328E-01 1.785302E-02 9.295791E-01
+419 1.925216E-01 1.907018E-02 9.921293E-01
+420 2.035729E-01 2.027369E-02 1.051821E+00
+421 2.137531E-01 2.144805E-02 1.107509E+00
+422 2.231348E-01 2.260041E-02 1.159527E+00
+423 2.319245E-01 2.374789E-02 1.208869E+00
+424 2.403892E-01 2.491247E-02 1.256834E+00
+425 2.488523E-01 2.612106E-02 1.305008E+00
+426 2.575896E-01 2.739923E-02 1.354758E+00
+427 2.664991E-01 2.874993E-02 1.405594E+00
+428 2.753532E-01 3.016909E-02 1.456414E+00
+429 2.838921E-01 3.165145E-02 1.505960E+00
+430 2.918246E-01 3.319038E-02 1.552826E+00
+431 2.989200E-01 3.477912E-02 1.595902E+00
+432 3.052993E-01 3.641495E-02 1.635768E+00
+433 3.112031E-01 3.809569E-02 1.673573E+00
+434 3.169047E-01 3.981843E-02 1.710604E+00
+435 3.227087E-01 4.157940E-02 1.748280E+00
+436 3.288194E-01 4.337098E-02 1.787504E+00
+437 3.349242E-01 4.517180E-02 1.826609E+00
+438 3.405452E-01 4.695420E-02 1.863108E+00
+439 3.451688E-01 4.868718E-02 1.894332E+00
+440 3.482554E-01 5.033657E-02 1.917479E+00
+441 3.494153E-01 5.187611E-02 1.930529E+00
+442 3.489075E-01 5.332218E-02 1.934819E+00
+443 3.471746E-01 5.470603E-02 1.932650E+00
+444 3.446705E-01 5.606335E-02 1.926395E+00
+445 3.418483E-01 5.743393E-02 1.918437E+00
+446 3.390240E-01 5.885107E-02 1.910430E+00
+447 3.359926E-01 6.030809E-02 1.901224E+00
+448 3.324276E-01 6.178644E-02 1.889000E+00
+449 3.280157E-01 6.326570E-02 1.871996E+00
+450 3.224637E-01 6.472352E-02 1.848545E+00
+451 3.156225E-01 6.614749E-02 1.817792E+00
+452 3.078201E-01 6.757256E-02 1.781627E+00
+453 2.994771E-01 6.904928E-02 1.742514E+00
+454 2.909776E-01 7.063280E-02 1.702749E+00
+455 2.826646E-01 7.238339E-02 1.664439E+00
+456 2.747962E-01 7.435960E-02 1.629207E+00
+457 2.674312E-01 7.659383E-02 1.597360E+00
+458 2.605847E-01 7.911436E-02 1.568896E+00
+459 2.542749E-01 8.195345E-02 1.543823E+00
+460 2.485254E-01 8.514816E-02 1.522157E+00
+461 2.433039E-01 8.872657E-02 1.503611E+00
+462 2.383414E-01 9.266008E-02 1.486673E+00
+463 2.333253E-01 9.689723E-02 1.469595E+00
+464 2.279619E-01 1.013746E-01 1.450709E+00
+465 2.219781E-01 1.060145E-01 1.428440E+00
+466 2.151735E-01 1.107377E-01 1.401587E+00
+467 2.075619E-01 1.155111E-01 1.370094E+00
+468 1.992183E-01 1.203122E-01 1.334220E+00
+469 1.902290E-01 1.251161E-01 1.294275E+00
+470 1.806905E-01 1.298957E-01 1.250610E+00
+471 1.707154E-01 1.346299E-01 1.203696E+00
+472 1.604471E-01 1.393309E-01 1.154316E+00
+473 1.500244E-01 1.440235E-01 1.103284E+00
+474 1.395705E-01 1.487372E-01 1.051347E+00
+475 1.291920E-01 1.535066E-01 9.991789E-01
+476 1.189859E-01 1.583644E-01 9.473958E-01
+477 1.090615E-01 1.633199E-01 8.966222E-01
+478 9.951424E-02 1.683761E-01 8.473981E-01
+479 9.041850E-02 1.735365E-01 8.001576E-01
+480 8.182895E-02 1.788048E-01 7.552379E-01
+481 7.376817E-02 1.841819E-01 7.127879E-01
+482 6.619477E-02 1.896559E-01 6.725198E-01
+483 5.906380E-02 1.952101E-01 6.340976E-01
+484 5.234242E-02 2.008259E-01 5.972433E-01
+485 4.600865E-02 2.064828E-01 5.617313E-01
+486 4.006154E-02 2.121826E-01 5.274921E-01
+487 3.454373E-02 2.180279E-01 4.948809E-01
+488 2.949091E-02 2.241586E-01 4.642586E-01
+489 2.492140E-02 2.307302E-01 4.358841E-01
+490 2.083981E-02 2.379160E-01 4.099313E-01
+491 1.723591E-02 2.458706E-01 3.864261E-01
+492 1.407924E-02 2.546023E-01 3.650566E-01
+493 1.134516E-02 2.640760E-01 3.454812E-01
+494 9.019658E-03 2.742490E-01 3.274095E-01
+495 7.097731E-03 2.850680E-01 3.105939E-01
+496 5.571145E-03 2.964837E-01 2.948102E-01
+497 4.394566E-03 3.085010E-01 2.798194E-01
+498 3.516303E-03 3.211393E-01 2.654100E-01
+499 2.887638E-03 3.344175E-01 2.514084E-01
+500 2.461588E-03 3.483536E-01 2.376753E-01
+501 2.206348E-03 3.629601E-01 2.241211E-01
+502 2.149559E-03 3.782275E-01 2.107484E-01
+503 2.337091E-03 3.941359E-01 1.975839E-01
+504 2.818931E-03 4.106582E-01 1.846574E-01
+505 3.649178E-03 4.277595E-01 1.720018E-01
+506 4.891359E-03 4.453993E-01 1.596918E-01
+507 6.629364E-03 4.635396E-01 1.479415E-01
+508 8.942902E-03 4.821376E-01 1.369428E-01
+509 1.190224E-02 5.011430E-01 1.268279E-01
+510 1.556989E-02 5.204972E-01 1.176796E-01
+511 1.997668E-02 5.401387E-01 1.094970E-01
+512 2.504698E-02 5.600208E-01 1.020943E-01
+513 3.067530E-02 5.800972E-01 9.527993E-02
+514 3.674999E-02 6.003172E-01 8.890075E-02
+515 4.315171E-02 6.206256E-01 8.283548E-02
+516 4.978584E-02 6.409398E-01 7.700982E-02
+517 5.668554E-02 6.610772E-01 7.144001E-02
+518 6.391651E-02 6.808134E-01 6.615436E-02
+519 7.154352E-02 6.999044E-01 6.117199E-02
+520 7.962917E-02 7.180890E-01 5.650407E-02
+521 8.821473E-02 7.351593E-01 5.215121E-02
+522 9.726978E-02 7.511821E-01 4.809566E-02
+523 1.067504E-01 7.663143E-01 4.431720E-02
+524 1.166192E-01 7.807352E-01 4.079734E-02
+525 1.268468E-01 7.946448E-01 3.751912E-02
+526 1.374060E-01 8.082074E-01 3.446846E-02
+527 1.482471E-01 8.213817E-01 3.163764E-02
+528 1.593076E-01 8.340701E-01 2.901901E-02
+529 1.705181E-01 8.461711E-01 2.660364E-02
+530 1.818026E-01 8.575799E-01 2.438164E-02
+531 1.931090E-01 8.682408E-01 2.234097E-02
+532 2.045085E-01 8.783061E-01 2.046415E-02
+533 2.161166E-01 8.879907E-01 1.873456E-02
+534 2.280650E-01 8.975211E-01 1.713788E-02
+535 2.405015E-01 9.071347E-01 1.566174E-02
+536 2.535441E-01 9.169947E-01 1.429644E-02
+537 2.671300E-01 9.269295E-01 1.303702E-02
+538 2.811351E-01 9.366731E-01 1.187897E-02
+539 2.954164E-01 9.459482E-01 1.081725E-02
+540 3.098117E-01 9.544675E-01 9.846470E-03
+541 3.241678E-01 9.619834E-01 8.960687E-03
+542 3.384319E-01 9.684390E-01 8.152811E-03
+543 3.525786E-01 9.738289E-01 7.416025E-03
+544 3.665839E-01 9.781519E-01 6.744115E-03
+545 3.804244E-01 9.814106E-01 6.131421E-03
+546 3.940988E-01 9.836669E-01 5.572778E-03
+547 4.076972E-01 9.852081E-01 5.063463E-03
+548 4.213484E-01 9.863813E-01 4.599169E-03
+549 4.352003E-01 9.875357E-01 4.175971E-03
+550 4.494206E-01 9.890228E-01 3.790291E-03
+551 4.641616E-01 9.910811E-01 3.438952E-03
+552 4.794395E-01 9.934913E-01 3.119341E-03
+553 4.952180E-01 9.959172E-01 2.829038E-03
+554 5.114395E-01 9.980205E-01 2.565722E-03
+555 5.280233E-01 9.994608E-01 2.327186E-03
+556 5.448696E-01 9.999930E-01 2.111280E-03
+557 5.618898E-01 9.997557E-01 1.915766E-03
+558 5.790137E-01 9.989839E-01 1.738589E-03
+559 5.961882E-01 9.979123E-01 1.577920E-03
+560 6.133784E-01 9.967737E-01 1.432128E-03
+561 6.305897E-01 9.957356E-01 1.299781E-03
+562 6.479223E-01 9.947115E-01 1.179667E-03
+563 6.654866E-01 9.935534E-01 1.070694E-03
+564 6.833782E-01 9.921156E-01 9.718623E-04
+565 7.016774E-01 9.902549E-01 8.822531E-04
+566 7.204110E-01 9.878596E-01 8.010231E-04
+567 7.394495E-01 9.849324E-01 7.273884E-04
+568 7.586285E-01 9.815036E-01 6.606347E-04
+569 7.777885E-01 9.776035E-01 6.001146E-04
+570 7.967750E-01 9.732611E-01 5.452416E-04
+571 8.154530E-01 9.684764E-01 4.954847E-04
+572 8.337389E-01 9.631369E-01 4.503642E-04
+573 8.515493E-01 9.571062E-01 4.094455E-04
+574 8.687862E-01 9.502540E-01 3.723345E-04
+575 8.853376E-01 9.424569E-01 3.386739E-04
+576 9.011588E-01 9.336897E-01 3.081396E-04
+577 9.165278E-01 9.242893E-01 2.804370E-04
+578 9.318245E-01 9.146707E-01 2.552996E-04
+579 9.474524E-01 9.052333E-01 2.324859E-04
+580 9.638388E-01 8.963613E-01 2.117772E-04
+581 9.812596E-01 8.883069E-01 1.929758E-04
+582 9.992953E-01 8.808462E-01 1.759024E-04
+583 1.017343E+00 8.736445E-01 1.603947E-04
+584 1.034790E+00 8.663755E-01 1.463059E-04
+585 1.051011E+00 8.587203E-01 1.335031E-04
+586 1.065522E+00 8.504295E-01 1.218660E-04
+587 1.078421E+00 8.415047E-01 1.112857E-04
+588 1.089944E+00 8.320109E-01 1.016634E-04
+589 1.100320E+00 8.220154E-01 9.291003E-05
+590 1.109767E+00 8.115868E-01 8.494468E-05
+591 1.118438E+00 8.007874E-01 7.769425E-05
+592 1.126266E+00 7.896515E-01 7.109247E-05
+593 1.133138E+00 7.782053E-01 6.507936E-05
+594 1.138952E+00 7.664733E-01 5.960061E-05
+595 1.143620E+00 7.544785E-01 5.460706E-05
+596 1.147095E+00 7.422473E-01 5.005417E-05
+597 1.149464E+00 7.298229E-01 4.590157E-05
+598 1.150838E+00 7.172525E-01 4.211268E-05
+599 1.151326E+00 7.045818E-01 3.865437E-05
+600 1.151033E+00 6.918553E-01 3.549661E-05
+601 1.150002E+00 6.791009E-01 3.261220E-05
+602 1.148061E+00 6.662846E-01 2.997643E-05
+603 1.144998E+00 6.533595E-01 2.756693E-05
+604 1.140622E+00 6.402807E-01 2.536339E-05
+605 1.134757E+00 6.270066E-01 2.334738E-05
+606 1.127298E+00 6.135148E-01 2.150221E-05
+607 1.118342E+00 5.998494E-01 1.981268E-05
+608 1.108033E+00 5.860682E-01 1.826500E-05
+609 1.096515E+00 5.722261E-01 1.684667E-05
+610 1.083928E+00 5.583746E-01 1.554631E-05
+611 1.070387E+00 5.445535E-01 1.435360E-05
+612 1.055934E+00 5.307673E-01 1.325915E-05
+613 1.040592E+00 5.170130E-01 1.225443E-05
+614 1.024385E+00 5.032889E-01 1.133169E-05
+615 1.007344E+00 4.895950E-01 1.048387E-05
+616 9.895268E-01 4.759442E-01 0.000000E+00
+617 9.711213E-01 4.623958E-01 0.000000E+00
+618 9.523257E-01 4.490154E-01 0.000000E+00
+619 9.333248E-01 4.358622E-01 0.000000E+00
+620 9.142877E-01 4.229897E-01 0.000000E+00
+621 8.952798E-01 4.104152E-01 0.000000E+00
+622 8.760157E-01 3.980356E-01 0.000000E+00
+623 8.561607E-01 3.857300E-01 0.000000E+00
+624 8.354235E-01 3.733907E-01 0.000000E+00
+625 8.135565E-01 3.609245E-01 0.000000E+00
+626 7.904565E-01 3.482860E-01 0.000000E+00
+627 7.664364E-01 3.355702E-01 0.000000E+00
+628 7.418777E-01 3.228963E-01 0.000000E+00
+629 7.171219E-01 3.103704E-01 0.000000E+00
+630 6.924717E-01 2.980865E-01 0.000000E+00
+631 6.681600E-01 2.861160E-01 0.000000E+00
+632 6.442697E-01 2.744822E-01 0.000000E+00
+633 6.208450E-01 2.631953E-01 0.000000E+00
+634 5.979243E-01 2.522628E-01 0.000000E+00
+635 5.755410E-01 2.416902E-01 0.000000E+00
+636 5.537296E-01 2.314809E-01 0.000000E+00
+637 5.325412E-01 2.216378E-01 0.000000E+00
+638 5.120218E-01 2.121622E-01 0.000000E+00
+639 4.922070E-01 2.030542E-01 0.000000E+00
+640 4.731224E-01 1.943124E-01 0.000000E+00
+641 4.547417E-01 1.859227E-01 0.000000E+00
+642 4.368719E-01 1.778274E-01 0.000000E+00
+643 4.193121E-01 1.699654E-01 0.000000E+00
+644 4.018980E-01 1.622841E-01 0.000000E+00
+645 3.844986E-01 1.547397E-01 0.000000E+00
+646 3.670592E-01 1.473081E-01 0.000000E+00
+647 3.497167E-01 1.400169E-01 0.000000E+00
+648 3.326305E-01 1.329013E-01 0.000000E+00
+649 3.159341E-01 1.259913E-01 0.000000E+00
+650 2.997374E-01 1.193120E-01 0.000000E+00
+651 2.841189E-01 1.128820E-01 0.000000E+00
+652 2.691053E-01 1.067113E-01 0.000000E+00
+653 2.547077E-01 1.008052E-01 0.000000E+00
+654 2.409319E-01 9.516653E-02 0.000000E+00
+655 2.277792E-01 8.979594E-02 0.000000E+00
+656 2.152431E-01 8.469044E-02 0.000000E+00
+657 2.033010E-01 7.984009E-02 0.000000E+00
+658 1.919276E-01 7.523372E-02 0.000000E+00
+659 1.810987E-01 7.086061E-02 0.000000E+00
+660 1.707914E-01 6.671045E-02 0.000000E+00
+661 1.609842E-01 6.277360E-02 0.000000E+00
+662 1.516577E-01 5.904179E-02 0.000000E+00
+663 1.427936E-01 5.550703E-02 0.000000E+00
+664 1.343737E-01 5.216139E-02 0.000000E+00
+665 1.263808E-01 4.899699E-02 0.000000E+00
+666 1.187979E-01 4.600578E-02 0.000000E+00
+667 1.116088E-01 4.317885E-02 0.000000E+00
+668 1.047975E-01 4.050755E-02 0.000000E+00
+669 9.834835E-02 3.798376E-02 0.000000E+00
+670 9.224597E-02 3.559982E-02 0.000000E+00
+671 8.647506E-02 3.334856E-02 0.000000E+00
+672 8.101986E-02 3.122332E-02 0.000000E+00
+673 7.586514E-02 2.921780E-02 0.000000E+00
+674 7.099633E-02 2.732601E-02 0.000000E+00
+675 6.639960E-02 2.554223E-02 0.000000E+00
+676 6.206225E-02 2.386121E-02 0.000000E+00
+677 5.797409E-02 2.227859E-02 0.000000E+00
+678 5.412533E-02 2.079020E-02 0.000000E+00
+679 5.050600E-02 1.939185E-02 0.000000E+00
+680 4.710606E-02 1.807939E-02 0.000000E+00
+681 4.391411E-02 1.684817E-02 0.000000E+00
+682 4.091411E-02 1.569188E-02 0.000000E+00
+683 3.809067E-02 1.460446E-02 0.000000E+00
+684 3.543034E-02 1.358062E-02 0.000000E+00
+685 3.292138E-02 1.261573E-02 0.000000E+00
+686 3.055672E-02 1.170696E-02 0.000000E+00
+687 2.834146E-02 1.085608E-02 0.000000E+00
+688 2.628033E-02 1.006476E-02 0.000000E+00
+689 2.437465E-02 9.333376E-03 0.000000E+00
+690 2.262306E-02 8.661284E-03 0.000000E+00
+691 2.101935E-02 8.046048E-03 0.000000E+00
+692 1.954647E-02 7.481130E-03 0.000000E+00
+693 1.818727E-02 6.959987E-03 0.000000E+00
+694 1.692727E-02 6.477070E-03 0.000000E+00
+695 1.575417E-02 6.027677E-03 0.000000E+00
+696 1.465854E-02 5.608169E-03 0.000000E+00
+697 1.363571E-02 5.216691E-03 0.000000E+00
+698 1.268205E-02 4.851785E-03 0.000000E+00
+699 1.179394E-02 4.512008E-03 0.000000E+00
+700 1.096778E-02 4.195941E-03 0.000000E+00
+701 1.019964E-02 3.902057E-03 0.000000E+00
+702 9.484317E-03 3.628371E-03 0.000000E+00
+703 8.816851E-03 3.373005E-03 0.000000E+00
+704 8.192921E-03 3.134315E-03 0.000000E+00
+705 7.608750E-03 2.910864E-03 0.000000E+00
+706 7.061391E-03 2.701528E-03 0.000000E+00
+707 6.549509E-03 2.505796E-03 0.000000E+00
+708 6.071970E-03 2.323231E-03 0.000000E+00
+709 5.627476E-03 2.153333E-03 0.000000E+00
+710 5.214608E-03 1.995557E-03 0.000000E+00
+711 4.831848E-03 1.849316E-03 0.000000E+00
+712 4.477579E-03 1.713976E-03 0.000000E+00
+713 4.150166E-03 1.588899E-03 0.000000E+00
+714 3.847988E-03 1.473453E-03 0.000000E+00
+715 3.569452E-03 1.367022E-03 0.000000E+00
+716 3.312857E-03 1.268954E-03 0.000000E+00
+717 3.076022E-03 1.178421E-03 0.000000E+00
+718 2.856894E-03 1.094644E-03 0.000000E+00
+719 2.653681E-03 1.016943E-03 0.000000E+00
+720 2.464821E-03 9.447269E-04 0.000000E+00
+721 2.289060E-03 8.775171E-04 0.000000E+00
+722 2.125694E-03 8.150438E-04 0.000000E+00
+723 1.974121E-03 7.570755E-04 0.000000E+00
+724 1.833723E-03 7.033755E-04 0.000000E+00
+725 1.703876E-03 6.537050E-04 0.000000E+00
+726 1.583904E-03 6.078048E-04 0.000000E+00
+727 1.472939E-03 5.653435E-04 0.000000E+00
+728 1.370151E-03 5.260046E-04 0.000000E+00
+729 1.274803E-03 4.895061E-04 0.000000E+00
+730 1.186238E-03 4.555970E-04 0.000000E+00
+731 1.103871E-03 4.240548E-04 0.000000E+00
+732 1.027194E-03 3.946860E-04 0.000000E+00
+733 9.557493E-04 3.673178E-04 0.000000E+00
+734 8.891262E-04 3.417941E-04 0.000000E+00
+735 8.269535E-04 3.179738E-04 0.000000E+00
+736 7.689351E-04 2.957441E-04 0.000000E+00
+737 7.149425E-04 2.750558E-04 0.000000E+00
+738 6.648590E-04 2.558640E-04 0.000000E+00
+739 6.185421E-04 2.381142E-04 0.000000E+00
+740 5.758303E-04 2.217445E-04 0.000000E+00
+741 5.365046E-04 2.066711E-04 0.000000E+00
+742 5.001842E-04 1.927474E-04 0.000000E+00
+743 4.665005E-04 1.798315E-04 0.000000E+00
+744 4.351386E-04 1.678023E-04 0.000000E+00
+745 4.058303E-04 1.565566E-04 0.000000E+00
+746 3.783733E-04 1.460168E-04 0.000000E+00
+747 3.526892E-04 1.361535E-04 0.000000E+00
+748 3.287199E-04 1.269451E-04 0.000000E+00
+749 3.063998E-04 1.183671E-04 0.000000E+00
+750 2.856577E-04 1.103928E-04 0.000000E+00
+751 2.664108E-04 1.029908E-04 0.000000E+00
+752 2.485462E-04 9.611836E-05 0.000000E+00
+753 2.319529E-04 8.973323E-05 0.000000E+00
+754 2.165300E-04 8.379694E-05 0.000000E+00
+755 2.021853E-04 7.827442E-05 0.000000E+00
+756 1.888338E-04 7.313312E-05 0.000000E+00
+757 1.763935E-04 6.834142E-05 0.000000E+00
+758 1.647895E-04 6.387035E-05 0.000000E+00
+759 1.539542E-04 5.969389E-05 0.000000E+00
+760 1.438270E-04 5.578862E-05 0.000000E+00
+761 1.343572E-04 5.213509E-05 0.000000E+00
+762 1.255141E-04 4.872179E-05 0.000000E+00
+763 1.172706E-04 4.553845E-05 0.000000E+00
+764 1.095983E-04 4.257443E-05 0.000000E+00
+765 1.024685E-04 3.981884E-05 0.000000E+00
+766 9.584715E-05 3.725877E-05 0.000000E+00
+767 8.968316E-05 3.487467E-05 0.000000E+00
+768 8.392734E-05 3.264765E-05 0.000000E+00
+769 7.853708E-05 3.056140E-05 0.000000E+00
+770 7.347551E-05 2.860175E-05 0.000000E+00
+771 6.871576E-05 2.675841E-05 0.000000E+00
+772 6.425257E-05 2.502943E-05 0.000000E+00
+773 6.008292E-05 2.341373E-05 0.000000E+00
+774 5.620098E-05 2.190914E-05 0.000000E+00
+775 5.259870E-05 2.051259E-05 0.000000E+00
+776 4.926279E-05 1.921902E-05 0.000000E+00
+777 4.616623E-05 1.801796E-05 0.000000E+00
+778 4.328212E-05 1.689899E-05 0.000000E+00
+779 4.058715E-05 1.585309E-05 0.000000E+00
+780 3.806114E-05 1.487243E-05 0.000000E+00
+781 3.568818E-05 1.395085E-05 0.000000E+00
+782 3.346023E-05 1.308528E-05 0.000000E+00
+783 3.137090E-05 1.227327E-05 0.000000E+00
+784 2.941371E-05 1.151233E-05 0.000000E+00
+785 2.758222E-05 1.080001E-05 0.000000E+00
+786 2.586951E-05 1.013364E-05 0.000000E+00
+787 2.426701E-05 9.509919E-06 0.000000E+00
+788 2.276639E-05 8.925630E-06 0.000000E+00
+789 2.136009E-05 8.377852E-06 0.000000E+00
+790 2.004122E-05 7.863920E-06 0.000000E+00
+791 1.880380E-05 7.381539E-06 0.000000E+00
+792 1.764358E-05 6.929096E-06 0.000000E+00
+793 1.655671E-05 6.505136E-06 0.000000E+00
+794 1.553939E-05 6.108221E-06 0.000000E+00
+795 1.458792E-05 5.736935E-06 0.000000E+00
+796 1.369853E-05 5.389831E-06 0.000000E+00
+797 1.286705E-05 5.065269E-06 0.000000E+00
+798 1.208947E-05 4.761667E-06 0.000000E+00
+799 1.136207E-05 4.477561E-06 0.000000E+00
+800 1.068141E-05 4.211597E-06 0.000000E+00
+801 1.004411E-05 3.962457E-06 0.000000E+00
+802 9.446399E-06 3.728674E-06 0.000000E+00
+803 8.884754E-06 3.508881E-06 0.000000E+00
+804 8.356050E-06 3.301868E-06 0.000000E+00
+805 7.857521E-06 3.106561E-06 0.000000E+00
+806 7.386996E-06 2.922119E-06 0.000000E+00
+807 6.943576E-06 2.748208E-06 0.000000E+00
+808 6.526548E-06 2.584560E-06 0.000000E+00
+809 6.135087E-06 2.430867E-06 0.000000E+00
+810 5.768284E-06 2.286786E-06 0.000000E+00
+811 5.425069E-06 2.151905E-06 0.000000E+00
+812 5.103974E-06 2.025656E-06 0.000000E+00
+813 4.803525E-06 1.907464E-06 0.000000E+00
+814 4.522350E-06 1.796794E-06 0.000000E+00
+815 4.259166E-06 1.693147E-06 0.000000E+00
+816 4.012715E-06 1.596032E-06 0.000000E+00
+817 3.781597E-06 1.504903E-06 0.000000E+00
+818 3.564496E-06 1.419245E-06 0.000000E+00
+819 3.360236E-06 1.338600E-06 0.000000E+00
+820 3.167765E-06 1.262556E-06 0.000000E+00
+821 2.986206E-06 1.190771E-06 0.000000E+00
+822 2.814999E-06 1.123031E-06 0.000000E+00
+823 2.653663E-06 1.059151E-06 0.000000E+00
+824 2.501725E-06 9.989507E-07 0.000000E+00
+825 2.358723E-06 9.422514E-07 0.000000E+00
+826 2.224206E-06 8.888804E-07 0.000000E+00
+827 2.097737E-06 8.386690E-07 0.000000E+00
+828 1.978894E-06 7.914539E-07 0.000000E+00
+829 1.867268E-06 7.470770E-07 0.000000E+00
+830 1.762465E-06 7.053860E-07 0.000000E+00
+
+CIE TC 1-36 proposed
+10-deg XYZ CMFs transformed from the CIE (2006) 2-deg LMS cone fundamentals
+
+390 2.952420E-03 4.076779E-04 1.318752E-02
+391 3.577275E-03 4.977769E-04 1.597879E-02
+392 4.332146E-03 6.064754E-04 1.935758E-02
+393 5.241609E-03 7.370040E-04 2.343758E-02
+394 6.333902E-03 8.929388E-04 2.835021E-02
+395 7.641137E-03 1.078166E-03 3.424588E-02
+396 9.199401E-03 1.296816E-03 4.129467E-02
+397 1.104869E-02 1.553159E-03 4.968641E-02
+398 1.323262E-02 1.851463E-03 5.962964E-02
+399 1.579791E-02 2.195795E-03 7.134926E-02
+400 1.879338E-02 2.589775E-03 8.508254E-02
+401 2.226949E-02 3.036799E-03 1.010753E-01
+402 2.627978E-02 3.541926E-03 1.195838E-01
+403 3.087862E-02 4.111422E-03 1.408647E-01
+404 3.611890E-02 4.752618E-03 1.651644E-01
+405 4.204986E-02 5.474207E-03 1.927065E-01
+406 4.871256E-02 6.285034E-03 2.236782E-01
+407 5.612868E-02 7.188068E-03 2.582109E-01
+408 6.429866E-02 8.181786E-03 2.963632E-01
+409 7.319818E-02 9.260417E-03 3.381018E-01
+410 8.277331E-02 1.041303E-02 3.832822E-01
+411 9.295327E-02 1.162642E-02 4.316884E-01
+412 1.037137E-01 1.289884E-02 4.832440E-01
+413 1.150520E-01 1.423442E-02 5.379345E-01
+414 1.269771E-01 1.564080E-02 5.957740E-01
+415 1.395127E-01 1.712968E-02 6.568187E-01
+416 1.526661E-01 1.871265E-02 7.210459E-01
+417 1.663054E-01 2.038394E-02 7.878635E-01
+418 1.802197E-01 2.212935E-02 8.563391E-01
+419 1.941448E-01 2.392985E-02 9.253017E-01
+420 2.077647E-01 2.576133E-02 9.933444E-01
+421 2.207911E-01 2.760156E-02 1.059178E+00
+422 2.332355E-01 2.945513E-02 1.122832E+00
+423 2.452462E-01 3.133884E-02 1.184947E+00
+424 2.570397E-01 3.327575E-02 1.246476E+00
+425 2.688989E-01 3.529554E-02 1.308674E+00
+426 2.810677E-01 3.742705E-02 1.372628E+00
+427 2.933967E-01 3.967137E-02 1.437661E+00
+428 3.055933E-01 4.201998E-02 1.502449E+00
+429 3.173165E-01 4.446166E-02 1.565456E+00
+430 3.281798E-01 4.698226E-02 1.624940E+00
+431 3.378678E-01 4.956742E-02 1.679488E+00
+432 3.465097E-01 5.221219E-02 1.729668E+00
+433 3.543953E-01 5.491387E-02 1.776755E+00
+434 3.618655E-01 5.766919E-02 1.822228E+00
+435 3.693084E-01 6.047429E-02 1.867751E+00
+436 3.770107E-01 6.332195E-02 1.914504E+00
+437 3.846850E-01 6.619271E-02 1.961055E+00
+438 3.918591E-01 6.906185E-02 2.005136E+00
+439 3.980192E-01 7.190190E-02 2.044296E+00
+440 4.026189E-01 7.468288E-02 2.075946E+00
+441 4.052637E-01 7.738452E-02 2.098231E+00
+442 4.062482E-01 8.003601E-02 2.112591E+00
+443 4.060660E-01 8.268524E-02 2.121427E+00
+444 4.052283E-01 8.538745E-02 2.127239E+00
+445 4.042529E-01 8.820537E-02 2.132574E+00
+446 4.034808E-01 9.118925E-02 2.139093E+00
+447 4.025362E-01 9.431041E-02 2.144815E+00
+448 4.008675E-01 9.751346E-02 2.146832E+00
+449 3.979327E-01 1.007349E-01 2.142250E+00
+450 3.932139E-01 1.039030E-01 2.128264E+00
+451 3.864108E-01 1.069639E-01 2.103205E+00
+452 3.779513E-01 1.099676E-01 2.069388E+00
+453 3.684176E-01 1.129992E-01 2.030030E+00
+454 3.583473E-01 1.161541E-01 1.988178E+00
+455 3.482214E-01 1.195389E-01 1.946651E+00
+456 3.383830E-01 1.232503E-01 1.907521E+00
+457 3.288309E-01 1.273047E-01 1.870689E+00
+458 3.194977E-01 1.316964E-01 1.835578E+00
+459 3.103345E-01 1.364178E-01 1.801657E+00
+460 3.013112E-01 1.414586E-01 1.768440E+00
+461 2.923754E-01 1.468003E-01 1.735338E+00
+462 2.833273E-01 1.524002E-01 1.701254E+00
+463 2.739463E-01 1.582021E-01 1.665053E+00
+464 2.640352E-01 1.641400E-01 1.625712E+00
+465 2.534221E-01 1.701373E-01 1.582342E+00
+466 2.420135E-01 1.761233E-01 1.534439E+00
+467 2.299346E-01 1.820896E-01 1.482544E+00
+468 2.173617E-01 1.880463E-01 1.427438E+00
+469 2.044672E-01 1.940065E-01 1.369876E+00
+470 1.914176E-01 1.999859E-01 1.310576E+00
+471 1.783672E-01 2.060054E-01 1.250226E+00
+472 1.654407E-01 2.120981E-01 1.189511E+00
+473 1.527391E-01 2.183041E-01 1.129050E+00
+474 1.403439E-01 2.246686E-01 1.069379E+00
+475 1.283167E-01 2.312426E-01 1.010952E+00
+476 1.167124E-01 2.380741E-01 9.541809E-01
+477 1.056121E-01 2.451798E-01 8.995253E-01
+478 9.508569E-02 2.525682E-01 8.473720E-01
+479 8.518206E-02 2.602479E-01 7.980093E-01
+480 7.593120E-02 2.682271E-01 7.516389E-01
+481 6.733159E-02 2.765005E-01 7.082645E-01
+482 5.932018E-02 2.850035E-01 6.673867E-01
+483 5.184106E-02 2.936475E-01 6.284798E-01
+484 4.486119E-02 3.023319E-01 5.911174E-01
+485 3.836770E-02 3.109438E-01 5.549619E-01
+486 3.237296E-02 3.194105E-01 5.198843E-01
+487 2.692095E-02 3.278683E-01 4.862772E-01
+488 2.204070E-02 3.365263E-01 4.545497E-01
+489 1.773951E-02 3.456176E-01 4.249955E-01
+490 1.400745E-02 3.554018E-01 3.978114E-01
+491 1.082291E-02 3.660893E-01 3.730218E-01
+492 8.168996E-03 3.775857E-01 3.502618E-01
+493 6.044623E-03 3.896960E-01 3.291407E-01
+494 4.462638E-03 4.021947E-01 3.093356E-01
+495 3.446810E-03 4.148227E-01 2.905816E-01
+496 3.009513E-03 4.273539E-01 2.726773E-01
+497 3.090744E-03 4.398206E-01 2.555143E-01
+498 3.611221E-03 4.523360E-01 2.390188E-01
+499 4.491435E-03 4.650298E-01 2.231335E-01
+500 5.652072E-03 4.780482E-01 2.078158E-01
+501 7.035322E-03 4.915173E-01 1.930407E-01
+502 8.669631E-03 5.054224E-01 1.788089E-01
+503 1.060755E-02 5.197057E-01 1.651287E-01
+504 1.290468E-02 5.343012E-01 1.520103E-01
+505 1.561956E-02 5.491344E-01 1.394643E-01
+506 1.881640E-02 5.641302E-01 1.275353E-01
+507 2.256923E-02 5.792416E-01 1.163771E-01
+508 2.694456E-02 5.944264E-01 1.061161E-01
+509 3.199910E-02 6.096388E-01 9.682266E-02
+510 3.778185E-02 6.248296E-01 8.852389E-02
+511 4.430635E-02 6.399656E-01 8.118263E-02
+512 5.146516E-02 6.550943E-01 7.463132E-02
+513 5.912224E-02 6.702903E-01 6.870644E-02
+514 6.714220E-02 6.856375E-01 6.327834E-02
+515 7.538941E-02 7.012292E-01 5.824484E-02
+516 8.376697E-02 7.171103E-01 5.353812E-02
+517 9.233581E-02 7.330917E-01 4.914863E-02
+518 1.011940E-01 7.489041E-01 4.507511E-02
+519 1.104362E-01 7.642530E-01 4.131175E-02
+520 1.201511E-01 7.788199E-01 3.784916E-02
+521 1.303960E-01 7.923410E-01 3.467234E-02
+522 1.411310E-01 8.048510E-01 3.175471E-02
+523 1.522944E-01 8.164747E-01 2.907029E-02
+524 1.638288E-01 8.273520E-01 2.659651E-02
+525 1.756832E-01 8.376358E-01 2.431375E-02
+526 1.878114E-01 8.474653E-01 2.220677E-02
+527 2.001621E-01 8.568868E-01 2.026852E-02
+528 2.126822E-01 8.659242E-01 1.849246E-02
+529 2.253199E-01 8.746041E-01 1.687084E-02
+530 2.380254E-01 8.829552E-01 1.539505E-02
+531 2.507787E-01 8.910274E-01 1.405450E-02
+532 2.636778E-01 8.989495E-01 1.283354E-02
+533 2.768607E-01 9.068753E-01 1.171754E-02
+534 2.904792E-01 9.149652E-01 1.069415E-02
+535 3.046991E-01 9.233858E-01 9.753000E-03
+536 3.196485E-01 9.322325E-01 8.886096E-03
+537 3.352447E-01 9.412862E-01 8.089323E-03
+538 3.513290E-01 9.502378E-01 7.359131E-03
+539 3.677148E-01 9.587647E-01 6.691736E-03
+540 3.841856E-01 9.665325E-01 6.083223E-03
+541 4.005312E-01 9.732504E-01 5.529423E-03
+542 4.166669E-01 9.788415E-01 5.025504E-03
+543 4.325420E-01 9.832867E-01 4.566879E-03
+544 4.481063E-01 9.865720E-01 4.149405E-03
+545 4.633109E-01 9.886887E-01 3.769336E-03
+546 4.781440E-01 9.897056E-01 3.423302E-03
+547 4.927483E-01 9.899849E-01 3.108313E-03
+548 5.073315E-01 9.899624E-01 2.821650E-03
+549 5.221315E-01 9.900731E-01 2.560830E-03
+550 5.374170E-01 9.907500E-01 2.323578E-03
+551 5.534217E-01 9.922826E-01 2.107847E-03
+552 5.701242E-01 9.943837E-01 1.911867E-03
+553 5.874093E-01 9.966221E-01 1.734006E-03
+554 6.051269E-01 9.985649E-01 1.572736E-03
+555 6.230892E-01 9.997775E-01 1.426627E-03
+556 6.410999E-01 9.999440E-01 1.294325E-03
+557 6.590659E-01 9.992200E-01 1.174475E-03
+558 6.769436E-01 9.978793E-01 1.065842E-03
+559 6.947143E-01 9.961934E-01 9.673215E-04
+560 7.123849E-01 9.944304E-01 8.779264E-04
+561 7.299978E-01 9.927831E-01 7.967847E-04
+562 7.476478E-01 9.911578E-01 7.231502E-04
+563 7.654250E-01 9.893925E-01 6.563501E-04
+564 7.834009E-01 9.873288E-01 5.957678E-04
+565 8.016277E-01 9.848127E-01 5.408385E-04
+566 8.201041E-01 9.817253E-01 4.910441E-04
+567 8.386843E-01 9.780714E-01 4.459046E-04
+568 8.571936E-01 9.738860E-01 4.049826E-04
+569 8.754652E-01 9.692028E-01 3.678818E-04
+570 8.933408E-01 9.640545E-01 3.342429E-04
+571 9.106772E-01 9.584409E-01 3.037407E-04
+572 9.273554E-01 9.522379E-01 2.760809E-04
+573 9.432502E-01 9.452968E-01 2.509970E-04
+574 9.582244E-01 9.374773E-01 2.282474E-04
+575 9.721304E-01 9.286495E-01 2.076129E-04
+576 9.849237E-01 9.187953E-01 1.888948E-04
+577 9.970067E-01 9.083014E-01 1.719127E-04
+578 1.008907E+00 8.976352E-01 1.565030E-04
+579 1.021163E+00 8.872401E-01 1.425177E-04
+580 1.034327E+00 8.775360E-01 1.298230E-04
+581 1.048753E+00 8.687920E-01 1.182974E-04
+582 1.063937E+00 8.607474E-01 1.078310E-04
+583 1.079166E+00 8.530233E-01 9.832455E-05
+584 1.093723E+00 8.452535E-01 8.968787E-05
+585 1.106886E+00 8.370838E-01 8.183954E-05
+586 1.118106E+00 8.282409E-01 7.470582E-05
+587 1.127493E+00 8.187320E-01 6.821991E-05
+588 1.135317E+00 8.086352E-01 6.232132E-05
+589 1.141838E+00 7.980296E-01 5.695534E-05
+590 1.147304E+00 7.869950E-01 5.207245E-05
+591 1.151897E+00 7.756040E-01 4.762781E-05
+592 1.155582E+00 7.638996E-01 4.358082E-05
+593 1.158284E+00 7.519157E-01 3.989468E-05
+594 1.159934E+00 7.396832E-01 3.653612E-05
+595 1.160477E+00 7.272309E-01 3.347499E-05
+596 1.159890E+00 7.145878E-01 3.068400E-05
+597 1.158259E+00 7.017926E-01 2.813839E-05
+598 1.155692E+00 6.888866E-01 2.581574E-05
+599 1.152293E+00 6.759103E-01 2.369574E-05
+600 1.148163E+00 6.629035E-01 2.175998E-05
+601 1.143345E+00 6.498911E-01 1.999179E-05
+602 1.137685E+00 6.368410E-01 1.837603E-05
+603 1.130993E+00 6.237092E-01 1.689896E-05
+604 1.123097E+00 6.104541E-01 1.554815E-05
+605 1.113846E+00 5.970375E-01 1.431231E-05
+606 1.103152E+00 5.834395E-01 1.318119E-05
+607 1.091121E+00 5.697044E-01 1.214548E-05
+608 1.077902E+00 5.558892E-01 1.119673E-05
+609 1.063644E+00 5.420475E-01 1.032727E-05
+610 1.048485E+00 5.282296E-01 9.530130E-06
+611 1.032546E+00 5.144746E-01 8.798979E-06
+612 1.015870E+00 5.007881E-01 8.128065E-06
+613 9.984859E-01 4.871687E-01 7.512160E-06
+614 9.804227E-01 4.736160E-01 6.946506E-06
+615 9.617111E-01 4.601308E-01 6.426776E-06
+616 9.424119E-01 4.467260E-01 0.000000E+00
+617 9.227049E-01 4.334589E-01 0.000000E+00
+618 9.027804E-01 4.203919E-01 0.000000E+00
+619 8.828123E-01 4.075810E-01 0.000000E+00
+620 8.629581E-01 3.950755E-01 0.000000E+00
+621 8.432731E-01 3.828894E-01 0.000000E+00
+622 8.234742E-01 3.709190E-01 0.000000E+00
+623 8.032342E-01 3.590447E-01 0.000000E+00
+624 7.822715E-01 3.471615E-01 0.000000E+00
+625 7.603498E-01 3.351794E-01 0.000000E+00
+626 7.373739E-01 3.230562E-01 0.000000E+00
+627 7.136470E-01 3.108859E-01 0.000000E+00
+628 6.895336E-01 2.987840E-01 0.000000E+00
+629 6.653567E-01 2.868527E-01 0.000000E+00
+630 6.413984E-01 2.751807E-01 0.000000E+00
+631 6.178723E-01 2.638343E-01 0.000000E+00
+632 5.948484E-01 2.528330E-01 0.000000E+00
+633 5.723600E-01 2.421835E-01 0.000000E+00
+634 5.504353E-01 2.318904E-01 0.000000E+00
+635 5.290979E-01 2.219564E-01 0.000000E+00
+636 5.083728E-01 2.123826E-01 0.000000E+00
+637 4.883006E-01 2.031698E-01 0.000000E+00
+638 4.689171E-01 1.943179E-01 0.000000E+00
+639 4.502486E-01 1.858250E-01 0.000000E+00
+640 4.323126E-01 1.776882E-01 0.000000E+00
+641 4.150790E-01 1.698926E-01 0.000000E+00
+642 3.983657E-01 1.623822E-01 0.000000E+00
+643 3.819846E-01 1.550986E-01 0.000000E+00
+644 3.657821E-01 1.479918E-01 0.000000E+00
+645 3.496358E-01 1.410203E-01 0.000000E+00
+646 3.334937E-01 1.341614E-01 0.000000E+00
+647 3.174776E-01 1.274401E-01 0.000000E+00
+648 3.017298E-01 1.208887E-01 0.000000E+00
+649 2.863684E-01 1.145345E-01 0.000000E+00
+650 2.714900E-01 1.083996E-01 0.000000E+00
+651 2.571632E-01 1.025007E-01 0.000000E+00
+652 2.434102E-01 9.684588E-02 0.000000E+00
+653 2.302389E-01 9.143944E-02 0.000000E+00
+654 2.176527E-01 8.628318E-02 0.000000E+00
+655 2.056507E-01 8.137687E-02 0.000000E+00
+656 1.942251E-01 7.671708E-02 0.000000E+00
+657 1.833530E-01 7.229404E-02 0.000000E+00
+658 1.730097E-01 6.809696E-02 0.000000E+00
+659 1.631716E-01 6.411549E-02 0.000000E+00
+660 1.538163E-01 6.033976E-02 0.000000E+00
+661 1.449230E-01 5.676054E-02 0.000000E+00
+662 1.364729E-01 5.336992E-02 0.000000E+00
+663 1.284483E-01 5.016027E-02 0.000000E+00
+664 1.208320E-01 4.712405E-02 0.000000E+00
+665 1.136072E-01 4.425383E-02 0.000000E+00
+666 1.067579E-01 4.154205E-02 0.000000E+00
+667 1.002685E-01 3.898042E-02 0.000000E+00
+668 9.412394E-02 3.656091E-02 0.000000E+00
+669 8.830929E-02 3.427597E-02 0.000000E+00
+670 8.281010E-02 3.211852E-02 0.000000E+00
+671 7.761208E-02 3.008192E-02 0.000000E+00
+672 7.270064E-02 2.816001E-02 0.000000E+00
+673 6.806167E-02 2.634698E-02 0.000000E+00
+674 6.368176E-02 2.463731E-02 0.000000E+00
+675 5.954815E-02 2.302574E-02 0.000000E+00
+676 5.564917E-02 2.150743E-02 0.000000E+00
+677 5.197543E-02 2.007838E-02 0.000000E+00
+678 4.851788E-02 1.873474E-02 0.000000E+00
+679 4.526737E-02 1.747269E-02 0.000000E+00
+680 4.221473E-02 1.628841E-02 0.000000E+00
+681 3.934954E-02 1.517767E-02 0.000000E+00
+682 3.665730E-02 1.413473E-02 0.000000E+00
+683 3.412407E-02 1.315408E-02 0.000000E+00
+684 3.173768E-02 1.223092E-02 0.000000E+00
+685 2.948752E-02 1.136106E-02 0.000000E+00
+686 2.736717E-02 1.054190E-02 0.000000E+00
+687 2.538113E-02 9.775050E-03 0.000000E+00
+688 2.353356E-02 9.061962E-03 0.000000E+00
+689 2.182558E-02 8.402962E-03 0.000000E+00
+690 2.025590E-02 7.797457E-03 0.000000E+00
+691 1.881892E-02 7.243230E-03 0.000000E+00
+692 1.749930E-02 6.734381E-03 0.000000E+00
+693 1.628167E-02 6.265001E-03 0.000000E+00
+694 1.515301E-02 5.830085E-03 0.000000E+00
+695 1.410230E-02 5.425391E-03 0.000000E+00
+696 1.312106E-02 5.047634E-03 0.000000E+00
+697 1.220509E-02 4.695140E-03 0.000000E+00
+698 1.135114E-02 4.366592E-03 0.000000E+00
+699 1.055593E-02 4.060685E-03 0.000000E+00
+700 9.816228E-03 3.776140E-03 0.000000E+00
+701 9.128517E-03 3.511578E-03 0.000000E+00
+702 8.488116E-03 3.265211E-03 0.000000E+00
+703 7.890589E-03 3.035344E-03 0.000000E+00
+704 7.332061E-03 2.820496E-03 0.000000E+00
+705 6.809147E-03 2.619372E-03 0.000000E+00
+706 6.319204E-03 2.430960E-03 0.000000E+00
+707 5.861036E-03 2.254796E-03 0.000000E+00
+708 5.433624E-03 2.090489E-03 0.000000E+00
+709 5.035802E-03 1.937586E-03 0.000000E+00
+710 4.666298E-03 1.795595E-03 0.000000E+00
+711 4.323750E-03 1.663989E-03 0.000000E+00
+712 4.006709E-03 1.542195E-03 0.000000E+00
+713 3.713708E-03 1.429639E-03 0.000000E+00
+714 3.443294E-03 1.325752E-03 0.000000E+00
+715 3.194041E-03 1.229980E-03 0.000000E+00
+716 2.964424E-03 1.141734E-03 0.000000E+00
+717 2.752492E-03 1.060269E-03 0.000000E+00
+718 2.556406E-03 9.848854E-04 0.000000E+00
+719 2.374564E-03 9.149703E-04 0.000000E+00
+720 2.205568E-03 8.499903E-04 0.000000E+00
+721 2.048294E-03 7.895158E-04 0.000000E+00
+722 1.902113E-03 7.333038E-04 0.000000E+00
+723 1.766485E-03 6.811458E-04 0.000000E+00
+724 1.640857E-03 6.328287E-04 0.000000E+00
+725 1.524672E-03 5.881375E-04 0.000000E+00
+726 1.417322E-03 5.468389E-04 0.000000E+00
+727 1.318031E-03 5.086349E-04 0.000000E+00
+728 1.226059E-03 4.732403E-04 0.000000E+00
+729 1.140743E-03 4.404016E-04 0.000000E+00
+730 1.061495E-03 4.098928E-04 0.000000E+00
+731 9.877949E-04 3.815137E-04 0.000000E+00
+732 9.191847E-04 3.550902E-04 0.000000E+00
+733 8.552568E-04 3.304668E-04 0.000000E+00
+734 7.956433E-04 3.075030E-04 0.000000E+00
+735 7.400120E-04 2.860718E-04 0.000000E+00
+736 6.880980E-04 2.660718E-04 0.000000E+00
+737 6.397864E-04 2.474586E-04 0.000000E+00
+738 5.949726E-04 2.301919E-04 0.000000E+00
+739 5.535291E-04 2.142225E-04 0.000000E+00
+740 5.153113E-04 1.994949E-04 0.000000E+00
+741 4.801234E-04 1.859336E-04 0.000000E+00
+742 4.476245E-04 1.734067E-04 0.000000E+00
+743 4.174846E-04 1.617865E-04 0.000000E+00
+744 3.894221E-04 1.509641E-04 0.000000E+00
+745 3.631969E-04 1.408466E-04 0.000000E+00
+746 3.386279E-04 1.313642E-04 0.000000E+00
+747 3.156452E-04 1.224905E-04 0.000000E+00
+748 2.941966E-04 1.142060E-04 0.000000E+00
+749 2.742235E-04 1.064886E-04 0.000000E+00
+750 2.556624E-04 9.931439E-05 0.000000E+00
+751 2.384390E-04 9.265512E-05 0.000000E+00
+752 2.224525E-04 8.647225E-05 0.000000E+00
+753 2.076036E-04 8.072780E-05 0.000000E+00
+754 1.938018E-04 7.538716E-05 0.000000E+00
+755 1.809649E-04 7.041878E-05 0.000000E+00
+756 1.690167E-04 6.579338E-05 0.000000E+00
+757 1.578839E-04 6.148250E-05 0.000000E+00
+758 1.474993E-04 5.746008E-05 0.000000E+00
+759 1.378026E-04 5.370272E-05 0.000000E+00
+760 1.287394E-04 5.018934E-05 0.000000E+00
+761 1.202644E-04 4.690245E-05 0.000000E+00
+762 1.123502E-04 4.383167E-05 0.000000E+00
+763 1.049725E-04 4.096780E-05 0.000000E+00
+764 9.810596E-05 3.830123E-05 0.000000E+00
+765 9.172477E-05 3.582218E-05 0.000000E+00
+766 8.579861E-05 3.351903E-05 0.000000E+00
+767 8.028174E-05 3.137419E-05 0.000000E+00
+768 7.513013E-05 2.937068E-05 0.000000E+00
+769 7.030565E-05 2.749380E-05 0.000000E+00
+770 6.577532E-05 2.573083E-05 0.000000E+00
+771 6.151508E-05 2.407249E-05 0.000000E+00
+772 5.752025E-05 2.251704E-05 0.000000E+00
+773 5.378813E-05 2.106350E-05 0.000000E+00
+774 5.031350E-05 1.970991E-05 0.000000E+00
+775 4.708916E-05 1.845353E-05 0.000000E+00
+776 4.410322E-05 1.728979E-05 0.000000E+00
+777 4.133150E-05 1.620928E-05 0.000000E+00
+778 3.874992E-05 1.520262E-05 0.000000E+00
+779 3.633762E-05 1.426169E-05 0.000000E+00
+780 3.407653E-05 1.337946E-05 0.000000E+00
+781 3.195242E-05 1.255038E-05 0.000000E+00
+782 2.995808E-05 1.177169E-05 0.000000E+00
+783 2.808781E-05 1.104118E-05 0.000000E+00
+784 2.633581E-05 1.035662E-05 0.000000E+00
+785 2.469630E-05 9.715798E-06 0.000000E+00
+786 2.316311E-05 9.116316E-06 0.000000E+00
+787 2.172855E-05 8.555201E-06 0.000000E+00
+788 2.038519E-05 8.029561E-06 0.000000E+00
+789 1.912625E-05 7.536768E-06 0.000000E+00
+790 1.794555E-05 7.074424E-06 0.000000E+00
+791 1.683776E-05 6.640464E-06 0.000000E+00
+792 1.579907E-05 6.233437E-06 0.000000E+00
+793 1.482604E-05 5.852035E-06 0.000000E+00
+794 1.391527E-05 5.494963E-06 0.000000E+00
+795 1.306345E-05 5.160948E-06 0.000000E+00
+796 1.226720E-05 4.848687E-06 0.000000E+00
+797 1.152279E-05 4.556705E-06 0.000000E+00
+798 1.082663E-05 4.283580E-06 0.000000E+00
+799 1.017540E-05 4.027993E-06 0.000000E+00
+800 9.565993E-06 3.788729E-06 0.000000E+00
+801 8.995405E-06 3.564599E-06 0.000000E+00
+802 8.460253E-06 3.354285E-06 0.000000E+00
+803 7.957382E-06 3.156557E-06 0.000000E+00
+804 7.483997E-06 2.970326E-06 0.000000E+00
+805 7.037621E-06 2.794625E-06 0.000000E+00
+806 6.616311E-06 2.628701E-06 0.000000E+00
+807 6.219265E-06 2.472248E-06 0.000000E+00
+808 5.845844E-06 2.325030E-06 0.000000E+00
+809 5.495311E-06 2.186768E-06 0.000000E+00
+810 5.166853E-06 2.057152E-06 0.000000E+00
+811 4.859511E-06 1.935813E-06 0.000000E+00
+812 4.571973E-06 1.822239E-06 0.000000E+00
+813 4.302920E-06 1.715914E-06 0.000000E+00
+814 4.051121E-06 1.616355E-06 0.000000E+00
+815 3.815429E-06 1.523114E-06 0.000000E+00
+816 3.594719E-06 1.435750E-06 0.000000E+00
+817 3.387736E-06 1.353771E-06 0.000000E+00
+818 3.193301E-06 1.276714E-06 0.000000E+00
+819 3.010363E-06 1.204166E-06 0.000000E+00
+820 2.837980E-06 1.135758E-06 0.000000E+00
+821 2.675365E-06 1.071181E-06 0.000000E+00
+822 2.522020E-06 1.010243E-06 0.000000E+00
+823 2.377511E-06 9.527779E-07 0.000000E+00
+824 2.241417E-06 8.986224E-07 0.000000E+00
+825 2.113325E-06 8.476168E-07 0.000000E+00
+826 1.992830E-06 7.996052E-07 0.000000E+00
+827 1.879542E-06 7.544361E-07 0.000000E+00
+828 1.773083E-06 7.119624E-07 0.000000E+00
+829 1.673086E-06 6.720421E-07 0.000000E+00
+830 1.579199E-06 6.345380E-07 0.000000E+00
+#endif /* NEVER */
#endif /* !SALONEINSTLIB */
/* Return pointers to three xpsects with a standard observer weighting curves */
@@ -1745,36 +2609,6 @@ char *standardObserverDescription(icxObserverType obType) {
return "Unknown observer";
}
-/* Return a pointer to the spectral locus poligon */
-/* return NULL on failure. */
-static xslpoly *spectral_locus_poligon(
-icxObserverType obType /* Type of observer */
-) {
- switch (obType) {
- case icxOT_custom:
- return NULL;
- case icxOT_none:
- return NULL;
- case icxOT_default:
- case icxOT_CIE_1931_2:
- return &poly_CIE_1931_2;
- case icxOT_CIE_1964_10:
- return &poly_CIE_1964_10;
-#ifndef SALONEINSTLIB
- case icxOT_Stiles_Burch_2:
- return &poly_Stiles_Burch_2;
- case icxOT_Judd_Voss_2:
- return &poly_Judd_Voss_2;
- case icxOT_CIE_1964_10c:
- return &poly_CIE_1964_10c;
- case icxOT_Shaw_Fairchild_2:
- return &poly_Shaw_Fairchild_2;
-#endif /* !SALONEINSTLIB */
- default:
- return NULL;
- }
-}
-
#ifndef SALONEINSTLIB
/* ----------------------------------- */
@@ -2135,6 +2969,8 @@ int write_nxspect(char *fname, xspect *sp, int nspec, int type) {
sprintf(buf,"%f", sp->norm);
ocg->add_kword(ocg, 0, "SPECTRAL_NORM",buf, NULL);
+ /* Should we adda A COORD field for "CMF" and an INDEX field for "SPECT" ? */
+
/* Generate fields for spectral values */
for (i = 0; i < sp->spec_n; i++) {
int nm;
@@ -2172,6 +3008,7 @@ int write_nxspect(char *fname, xspect *sp, int nspec, int type) {
/* Up to nspec will be restored starting at offset off.. */
/* The number restored from the file will be written to *nret */
/* type: 0 = any, mask: 1 = SPECT, 2 = CMF, 4 = ccss */
+/* (Note that not all ccss information is read. Use ccss->read_ccss() for this. */
/* Return NZ on error */
/* (Would be nice to return an error message!) */
int read_nxspect(xspect *sp, char *fname, int *nret, int off, int nspec, int type) {
@@ -2454,6 +3291,44 @@ static int getval_raw_xspec_lin(xspect *sp, double *rv, double wl) {
return rc;
}
+/* Get a raw linearly interpolated spectrum value x 3. */
+/* Return NZ if value is valid, Z and last valid value */
+/* if outside the range */
+/* NOTE: Returned value isn't normalised by sp->norm */
+static int getval_raw_xspec3_lin(xspect *sp, double *rv, double wl) {
+ int i, rc = 1;
+ double f, w;
+
+ if (wl < sp[0].spec_wl_short) {
+ wl = sp[0].spec_wl_short;
+ rc = 0;
+ }
+
+ if (wl > sp[0].spec_wl_long) {
+ wl = sp[0].spec_wl_long;
+ rc = 0;
+ }
+
+ /* Compute fraction 0.0 - 1.0 out of known spectrum */
+ f = (wl - sp[0].spec_wl_short) / (sp[0].spec_wl_long - sp[0].spec_wl_short);
+ f *= (sp[0].spec_n - 1.0);
+ i = (int)floor(f); /* Base grid coordinate */
+
+ if (i < 0) /* Limit to valid cube base index range */
+ i = 0;
+ else if (i > (sp[0].spec_n - 2))
+ i = (sp[0].spec_n - 2);
+
+ w = f - (double)i; /* Interpolation weighting factor */
+
+ /* Compute interpolated value */
+ rv[0] = (1.0 - w) * sp[0].spec[i] + w * sp[0].spec[i+1];
+ rv[1] = (1.0 - w) * sp[1].spec[i] + w * sp[1].spec[i+1];
+ rv[2] = (1.0 - w) * sp[2].spec[i] + w * sp[2].spec[i+1];
+
+ return rc;
+}
+
#ifdef NEVER /* Nearest neighbor resampler, for testing */
/* Get a raw nearest-neighbor interpolated spectrum value. */
/* Return NZ if value is valid, Z and last valid value */
@@ -2587,6 +3462,7 @@ void xspect2xspect(xspect *dst, xspect *targ, xspect *src) {
/* Given an emission spectrum, set the UV output to the given level. */
/* The shape of the UV is taken from FWA1_stim, and the level is */
/* with respect to the Y of the input spectrum. */
+/* The output range is extended to accomodate the UV wavelengths */
void xsp_setUV(xspect *out, xspect *in, double uvlevel) {
int i, xs, xe;
double ww, avg;
@@ -2594,7 +3470,7 @@ void xsp_setUV(xspect *out, xspect *in, double uvlevel) {
cin = *in;
- /* Compute the average of the input spetrum */
+ /* Compute the average of the input spectrum */
for (avg = 0.0, i = 0; i < cin.spec_n; i++)
avg += cin.spec[i];
avg /= cin.spec_n;
@@ -2618,6 +3494,9 @@ void xsp_setUV(xspect *out, xspect *in, double uvlevel) {
getval_raw_xspec_lin(&cin, &inv, ww);
getval_raw_xspec_lin(&FWA1_stim, &uvv, ww);
+ /* Input illuminant with no Uv */
+ out->spec[i] = inv;
+
/* Taper measured illum out */
bl = (ww - FWA1_stim.spec_wl_short)/(FWA1_stim.spec_wl_long - FWA1_stim.spec_wl_short);
bl = bl < 0.0 ? 0.0 : (bl > 1.0 ? 1.0 : bl);
@@ -3735,8 +4614,9 @@ xspect *in /* Spectrum to be converted */
/* are used, also consistent with CIE and ANSI CGATS recommendations. */
out[j] = 0.0;
for (ww = p->observer[j].spec_wl_short; ww <= p->observer[j].spec_wl_long; ww += 1.0) {
- double I, O, S;
- getval_xspec(&p->illuminant, &I, ww);
+ double I = 1.0, O, S;
+ if (!p->isemis)
+ getval_xspec(&p->illuminant, &I, ww);
getval_xspec(&p->observer[j], &O, ww);
getval_xspec(in, &S, ww);
if (j == 1)
@@ -3788,7 +4668,7 @@ icxIllumeType ilType, /* Illuminant */
xspect *custIllum, /* Optional custom illuminant */
icxObserverType obType, /* Observer */
xspect custObserver[3], /* Optional custom observer */
-icColorSpaceSignature rcs, /* Return color space, icSigXYZData or icSigLabData */
+icColorSpaceSignature rcs, /* Return color space, icSigXYZData or D50 icSigLabData */
/* ** Must be icSigXYZData if SALONEINSTLIB ** */
icxClamping clamp /* NZ to clamp XYZ/Lab to be +ve */
) {
@@ -3924,9 +4804,10 @@ icxClamping clamp /* NZ to clamp XYZ/Lab to be +ve */
#ifndef SALONEINSTLIB
+
/* -------------------------------------------------------- */
-/* Return the spectrum locus rangefor the given observer */
+/* Return the spectrum locus range for the given observer */
/* return 0 on sucecss, nz if observer not known */
int icx_spectrum_locus_range(double *min_wl, double *max_wl, icxObserverType obType) {
xspect *sp[3];
@@ -3947,7 +4828,7 @@ int icx_spectrum_locus_range(double *min_wl, double *max_wl, icxObserverType obT
int icx_spectrum_locus(double xyz[3], double wl, icxObserverType obType) {
xspect *sp[3];
- DBGF((DBGA,"icx_spectrum_locus got obs %d wl %f\n",obType, wl));
+ DBGF((DBGA,"icx_chrom_locus got obs %d wl %f\n",obType, wl));
if (standardObserver(sp, obType))
return 1;
@@ -3966,113 +4847,1033 @@ int icx_spectrum_locus(double xyz[3], double wl, icxObserverType obType) {
return 0;
}
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Pre-calculated spectral locuses of Daylight and Plankian at 5 Mired intervals */
+/* These aren't actually spectrum, they are XYZ values */
+/* indexed by temperature in Mired */
+
+static xspect illoc_Daylight_CIE_1931_2[3] = {
+ {
+ 69, 60.000000, 400.000000,
+ 1.0,
+ {
+ 0.970635, 0.968292, 0.966045, 0.963906, 0.961884,
+ 0.959990, 0.958232, 0.956618, 0.955155, 0.953850,
+ 0.952710, 0.951740, 0.950945, 0.950330, 0.949899,
+ 0.949656, 0.949604, 0.949747, 0.950085, 0.950619,
+ 0.951352, 0.952283, 0.953413, 0.954741, 0.956265,
+ 0.957984, 0.959896, 0.961999, 0.964289, 0.966764,
+ 0.969419, 0.972251, 0.975254, 0.978425, 0.981759,
+ 0.985248, 0.988889, 0.992674, 0.996597, 1.000651,
+ 1.004828, 1.009121, 1.013522, 1.018021, 1.022611,
+ 1.027281, 1.032021, 1.036822, 1.041673, 1.046562,
+ 1.051478, 1.056409, 1.061342, 1.066265, 1.071163,
+ 1.076024, 1.080834, 1.085577, 1.090239, 1.094805,
+ 1.099259, 1.103586, 1.107771, 1.111796, 1.115647,
+ 1.119306, 1.122759, 1.125989, 1.128981
+ }
+ },
+ {
+ 69, 60.000000, 400.000000,
+ 1.0,
+ {
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000
+ }
+ },
+ {
+ 69, 60.000000, 400.000000,
+ 1.0,
+ {
+ 1.787622, 1.747661, 1.707659, 1.667701, 1.627864,
+ 1.588223, 1.548842, 1.509780, 1.471093, 1.432827,
+ 1.395026, 1.357727, 1.320965, 1.284767, 1.249158,
+ 1.214160, 1.179790, 1.146125, 1.113225, 1.081024,
+ 1.049542, 1.018793, 0.988790, 0.959541, 0.931050,
+ 0.903320, 0.876351, 0.850140, 0.824684, 0.799977,
+ 0.776012, 0.752780, 0.730272, 0.708479, 0.687390,
+ 0.666992, 0.647275, 0.628227, 0.609834, 0.592085,
+ 0.574967, 0.558466, 0.542572, 0.527270, 0.512549,
+ 0.498396, 0.484799, 0.471746, 0.459226, 0.447227,
+ 0.435739, 0.424749, 0.414250, 0.404229, 0.394677,
+ 0.385586, 0.376946, 0.368749, 0.360986, 0.353651,
+ 0.346735, 0.340234, 0.334139, 0.328447, 0.323151,
+ 0.318248, 0.313734, 0.309607, 0.305862
+ }
+ }
+};
+
+static xspect illoc_Plankian_CIE_1931_2[3] = {
+ {
+ 189, 60.000000, 1000.000000,
+ 1.0,
+ {
+ 0.990017, 0.987458, 0.985018, 0.982708, 0.980540,
+ 0.978522, 0.976663, 0.974970, 0.973449, 0.972105,
+ 0.970942, 0.969965, 0.969174, 0.968572, 0.968159,
+ 0.967936, 0.967902, 0.968056, 0.968396, 0.968922,
+ 0.969629, 0.970517, 0.971581, 0.972819, 0.974227,
+ 0.975803, 0.977542, 0.979441, 0.981495, 0.983702,
+ 0.986058, 0.988559, 0.991200, 0.993979, 0.996891,
+ 0.999934, 1.003102, 1.006394, 1.009804, 1.013331,
+ 1.016970, 1.020718, 1.024572, 1.028529, 1.032586,
+ 1.036739, 1.040986, 1.045325, 1.049751, 1.054263,
+ 1.058857, 1.063532, 1.068285, 1.073113, 1.078013,
+ 1.082985, 1.088024, 1.093130, 1.098299, 1.103530,
+ 1.108822, 1.114170, 1.119575, 1.125034, 1.130544,
+ 1.136105, 1.141715, 1.147371, 1.153072, 1.158817,
+ 1.164604, 1.170431, 1.176297, 1.182201, 1.188140,
+ 1.194115, 1.200122, 1.206161, 1.212231, 1.218329,
+ 1.224456, 1.230610, 1.236790, 1.242994, 1.249221,
+ 1.255470, 1.261741, 1.268032, 1.274342, 1.280670,
+ 1.287015, 1.293376, 1.299753, 1.306144, 1.312548,
+ 1.318965, 1.325393, 1.331833, 1.338282, 1.344741,
+ 1.351208, 1.357683, 1.364165, 1.370653, 1.377146,
+ 1.383645, 1.390147, 1.396654, 1.403162, 1.409674,
+ 1.416186, 1.422700, 1.429214, 1.435728, 1.442241,
+ 1.448753, 1.455263, 1.461771, 1.468275, 1.474777,
+ 1.481274, 1.487767, 1.494255, 1.500738, 1.507215,
+ 1.513686, 1.520150, 1.526607, 1.533057, 1.539498,
+ 1.545932, 1.552356, 1.558772, 1.565178, 1.571574,
+ 1.577960, 1.584336, 1.590701, 1.597054, 1.603397,
+ 1.609727, 1.616045, 1.622351, 1.628644, 1.634924,
+ 1.641190, 1.647444, 1.653683, 1.659908, 1.666119,
+ 1.672315, 1.678497, 1.684663, 1.690814, 1.696949,
+ 1.703069, 1.709173, 1.715260, 1.721331, 1.727386,
+ 1.733423, 1.739444, 1.745447, 1.751433, 1.757402,
+ 1.763353, 1.769286, 1.775201, 1.781098, 1.786976,
+ 1.792836, 1.798677, 1.804500, 1.810304, 1.816088,
+ 1.821853, 1.827600, 1.833326, 1.839033, 1.844721,
+ 1.850388, 1.856036, 1.861664, 1.867272, 1.872859,
+ 1.878426, 1.883973, 1.889500, 1.895006
+ }
+ },
+ {
+ 189, 60.000000, 1000.000000,
+ 1.0,
+ {
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000
+ }
+ },
+ {
+ 189, 60.000000, 1000.000000,
+ 1.0,
+ {
+ 1.807443, 1.767939, 1.728424, 1.688977, 1.649677,
+ 1.610595, 1.571797, 1.533345, 1.495294, 1.457694,
+ 1.420591, 1.384023, 1.348026, 1.312628, 1.277856,
+ 1.243731, 1.210269, 1.177483, 1.145385, 1.113980,
+ 1.083273, 1.053267, 1.023960, 0.995350, 0.967433,
+ 0.940204, 0.913655, 0.887779, 0.862566, 0.838007,
+ 0.814090, 0.790805, 0.768140, 0.746084, 0.724623,
+ 0.703745, 0.683438, 0.663689, 0.644486, 0.625814,
+ 0.607663, 0.590019, 0.572870, 0.556203, 0.540006,
+ 0.524267, 0.508975, 0.494117, 0.479683, 0.465661,
+ 0.452040, 0.438809, 0.425958, 0.413476, 0.401354,
+ 0.389581, 0.378149, 0.367047, 0.356266, 0.345798,
+ 0.335634, 0.325764, 0.316182, 0.306879, 0.297847,
+ 0.289078, 0.280566, 0.272302, 0.264279, 0.256492,
+ 0.248932, 0.241594, 0.234472, 0.227558, 0.220848,
+ 0.214334, 0.208013, 0.201877, 0.195922, 0.190142,
+ 0.184533, 0.179089, 0.173806, 0.168679, 0.163703,
+ 0.158875, 0.154189, 0.149641, 0.145229, 0.140947,
+ 0.136792, 0.132760, 0.128847, 0.125050, 0.121366,
+ 0.117792, 0.114323, 0.110958, 0.107692, 0.104523,
+ 0.101449, 0.098466, 0.095571, 0.092763, 0.090038,
+ 0.087394, 0.084829, 0.082340, 0.079926, 0.077583,
+ 0.075310, 0.073104, 0.070964, 0.068888, 0.066874,
+ 0.064919, 0.063023, 0.061184, 0.059399, 0.057667,
+ 0.055987, 0.054357, 0.052775, 0.051240, 0.049752,
+ 0.048307, 0.046906, 0.045546, 0.044226, 0.042946,
+ 0.041704, 0.040499, 0.039330, 0.038195, 0.037095,
+ 0.036027, 0.034990, 0.033985, 0.033009, 0.032062,
+ 0.031144, 0.030252, 0.029387, 0.028548, 0.027734,
+ 0.026943, 0.026177, 0.025432, 0.024710, 0.024009,
+ 0.023329, 0.022669, 0.022029, 0.021407, 0.020804,
+ 0.020219, 0.019651, 0.019099, 0.018564, 0.018044,
+ 0.017540, 0.017051, 0.016576, 0.016115, 0.015668,
+ 0.015233, 0.014811, 0.014402, 0.014005, 0.013619,
+ 0.013245, 0.012881, 0.012528, 0.012186, 0.011853,
+ 0.011530, 0.011217, 0.010912, 0.010617, 0.010329,
+ 0.010051, 0.009780, 0.009517, 0.009262, 0.009014,
+ 0.008773, 0.008540, 0.008313, 0.008092
+ }
+ }
+};
+
+static xspect illoc_Daylight_CIE_1964_10[3] = {
+ {
+ 69, 60.000000, 400.000000,
+ 1.0,
+ {
+ 0.949535, 0.948408, 0.947363, 0.946408, 0.945551,
+ 0.944800, 0.944161, 0.943639, 0.943243, 0.942976,
+ 0.942844, 0.942853, 0.943006, 0.943308, 0.943763,
+ 0.944374, 0.945146, 0.946079, 0.947174, 0.948434,
+ 0.949861, 0.951455, 0.953217, 0.955146, 0.957242,
+ 0.959504, 0.961930, 0.964519, 0.967268, 0.970175,
+ 0.973237, 0.976450, 0.979812, 0.983317, 0.986963,
+ 0.990743, 0.994653, 0.998688, 1.002842, 1.007108,
+ 1.011480, 1.015951, 1.020514, 1.025161, 1.029883,
+ 1.034672, 1.039519, 1.044415, 1.049348, 1.054309,
+ 1.059288, 1.064271, 1.069249, 1.074207, 1.079135,
+ 1.084018, 1.088844, 1.093597, 1.098265, 1.102832,
+ 1.107284, 1.111605, 1.115781, 1.119796, 1.123634,
+ 1.127280, 1.130718, 1.133933, 1.136910
+ }
+ },
+ {
+ 69, 60.000000, 400.000000,
+ 1.0,
+ {
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000
+ }
+ },
+ {
+ 69, 60.000000, 400.000000,
+ 1.0,
+ {
+ 1.736104, 1.699156, 1.662051, 1.624867, 1.587679,
+ 1.550555, 1.513559, 1.476750, 1.440182, 1.403904,
+ 1.367961, 1.332394, 1.297239, 1.262528, 1.228292,
+ 1.194555, 1.161341, 1.128728, 1.096782, 1.065445,
+ 1.034742, 1.004693, 0.975315, 0.946623, 0.918626,
+ 0.891332, 0.864747, 0.838873, 0.813712, 0.789262,
+ 0.765521, 0.742485, 0.720149, 0.698506, 0.677549,
+ 0.657271, 0.637662, 0.618713, 0.600415, 0.582756,
+ 0.565728, 0.549318, 0.533516, 0.518310, 0.503690,
+ 0.489644, 0.476161, 0.463230, 0.450839, 0.438977,
+ 0.427633, 0.416797, 0.406457, 0.396605, 0.387228,
+ 0.378318, 0.369864, 0.361858, 0.354290, 0.347151,
+ 0.340434, 0.334131, 0.328233, 0.322735, 0.317629,
+ 0.312911, 0.308575, 0.304616, 0.301031
+ }
+ }
+};
+
+static xspect illoc_Plankian_CIE_1964_10[3] = {
+ {
+ 189, 60.000000, 1000.000000,
+ 1.0,
+ {
+ 0.974241, 0.972539, 0.970952, 0.969490, 0.968163,
+ 0.966977, 0.965941, 0.965060, 0.964340, 0.963785,
+ 0.963398, 0.963183, 0.963140, 0.963272, 0.963578,
+ 0.964058, 0.964713, 0.965540, 0.966539, 0.967707,
+ 0.969041, 0.970540, 0.972201, 0.974020, 0.975994,
+ 0.978121, 0.980396, 0.982816, 0.985378, 0.988077,
+ 0.990911, 0.993876, 0.996967, 1.000182, 1.003517,
+ 1.006968, 1.010533, 1.014206, 1.017986, 1.021868,
+ 1.025851, 1.029929, 1.034101, 1.038362, 1.042711,
+ 1.047144, 1.051659, 1.056252, 1.060921, 1.065663,
+ 1.070476, 1.075357, 1.080304, 1.085314, 1.090384,
+ 1.095514, 1.100699, 1.105939, 1.111231, 1.116573,
+ 1.121962, 1.127398, 1.132878, 1.138400, 1.143962,
+ 1.149564, 1.155202, 1.160875, 1.166582, 1.172320,
+ 1.178090, 1.183888, 1.189713, 1.195565, 1.201441,
+ 1.207340, 1.213262, 1.219204, 1.225165, 1.231144,
+ 1.237140, 1.243152, 1.249179, 1.255219, 1.261272,
+ 1.267336, 1.273410, 1.279494, 1.285586, 1.291686,
+ 1.297792, 1.303903, 1.310020, 1.316140, 1.322264,
+ 1.328390, 1.334517, 1.340646, 1.346774, 1.352902,
+ 1.359028, 1.365152, 1.371274, 1.377392, 1.383506,
+ 1.389615, 1.395720, 1.401819, 1.407911, 1.413996,
+ 1.420075, 1.426145, 1.432207, 1.438260, 1.444304,
+ 1.450337, 1.456361, 1.462374, 1.468376, 1.474367,
+ 1.480346, 1.486312, 1.492266, 1.498207, 1.504134,
+ 1.510048, 1.515948, 1.521834, 1.527705, 1.533561,
+ 1.539402, 1.545228, 1.551037, 1.556831, 1.562609,
+ 1.568370, 1.574114, 1.579842, 1.585552, 1.591245,
+ 1.596920, 1.602578, 1.608217, 1.613839, 1.619442,
+ 1.625026, 1.630592, 1.636139, 1.641668, 1.647177,
+ 1.652666, 1.658137, 1.663587, 1.669019, 1.674430,
+ 1.679822, 1.685193, 1.690544, 1.695876, 1.701187,
+ 1.706477, 1.711747, 1.716997, 1.722226, 1.727434,
+ 1.732621, 1.737788, 1.742934, 1.748058, 1.753162,
+ 1.758245, 1.763306, 1.768347, 1.773366, 1.778364,
+ 1.783341, 1.788296, 1.793230, 1.798143, 1.803034,
+ 1.807904, 1.812753, 1.817580, 1.822386, 1.827170,
+ 1.831933, 1.836674, 1.841394, 1.846092
+ }
+ },
+ {
+ 189, 60.000000, 1000.000000,
+ 1.0,
+ {
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+ 1.000000, 1.000000, 1.000000, 1.000000
+ }
+ },
+ {
+ 189, 60.000000, 1000.000000,
+ 1.0,
+ {
+ 1.775958, 1.738417, 1.700795, 1.663169, 1.625615,
+ 1.588201, 1.550992, 1.514049, 1.477427, 1.441177,
+ 1.405344, 1.369969, 1.335088, 1.300732, 1.266930,
+ 1.233704, 1.201074, 1.169056, 1.137662, 1.106903,
+ 1.076786, 1.047315, 1.018493, 0.990321, 0.962796,
+ 0.935916, 0.909677, 0.884073, 0.859098, 0.834744,
+ 0.811003, 0.787866, 0.765323, 0.743365, 0.721980,
+ 0.701159, 0.680891, 0.661164, 0.641968, 0.623290,
+ 0.605120, 0.587446, 0.570257, 0.553542, 0.537290,
+ 0.521489, 0.506129, 0.491199, 0.476688, 0.462586,
+ 0.448883, 0.435567, 0.422630, 0.410062, 0.397852,
+ 0.385993, 0.374473, 0.363285, 0.352420, 0.341868,
+ 0.331622, 0.321672, 0.312013, 0.302634, 0.293529,
+ 0.284689, 0.276109, 0.267780, 0.259696, 0.251849,
+ 0.244234, 0.236843, 0.229670, 0.222710, 0.215956,
+ 0.209402, 0.203043, 0.196873, 0.190887, 0.185079,
+ 0.179445, 0.173979, 0.168676, 0.163533, 0.158544,
+ 0.153704, 0.149010, 0.144457, 0.140042, 0.135759,
+ 0.131606, 0.127578, 0.123672, 0.119884, 0.116210,
+ 0.112648, 0.109194, 0.105845, 0.102598, 0.099449,
+ 0.096397, 0.093437, 0.090567, 0.087785, 0.085088,
+ 0.082473, 0.079938, 0.077480, 0.075098, 0.072788,
+ 0.070550, 0.068380, 0.066276, 0.064237, 0.062260,
+ 0.060345, 0.058488, 0.056688, 0.054943, 0.053252,
+ 0.051613, 0.050025, 0.048485, 0.046993, 0.045547,
+ 0.044145, 0.042787, 0.041470, 0.040194, 0.038958,
+ 0.037760, 0.036598, 0.035473, 0.034382, 0.033325,
+ 0.032300, 0.031308, 0.030346, 0.029413, 0.028510,
+ 0.027634, 0.026786, 0.025963, 0.025166, 0.024394,
+ 0.023646, 0.022921, 0.022218, 0.021537, 0.020877,
+ 0.020237, 0.019618, 0.019017, 0.018435, 0.017871,
+ 0.017324, 0.016795, 0.016281, 0.015784, 0.015302,
+ 0.014835, 0.014382, 0.013943, 0.013518, 0.013106,
+ 0.012706, 0.012319, 0.011944, 0.011581, 0.011228,
+ 0.010887, 0.010556, 0.010235, 0.009925, 0.009623,
+ 0.009331, 0.009048, 0.008774, 0.008508, 0.008251,
+ 0.008001, 0.007759, 0.007524, 0.007297, 0.007077,
+ 0.006863, 0.006656, 0.006455, 0.006261
+ }
+ }
+};
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Fast but slightly less accurate CCT support */
+
+/* Context for optimiser callback */
+typedef struct {
+ xspect *iloc; /* Locus to match to */
+ double xyz[3]; /* Target XYZ */
+ icmXYZNumber XYZ; /* Target as XYZ number for DE wp */
+ xsp2cie *conv; /* Means of converting spectrum to XYZ */
+ int viscct; /* nz to use visual best match color temperature */
+} cct2ctx;
+
+static double cct2_func(void *fdata, double tp[]) {
+ cct2ctx *x = (cct2ctx *)fdata;
+ double xyz[3]; /* Current value */
+ double lab1[3], lab2[3];
+ xspect sp;
+ double rv = 0.0;
+ icmXYZNumber *wp = &x->XYZ;
+
+ /* Get XYZ for given temp in Mired. */
+ /* Will clip to limits of locus */
+ getval_raw_xspec3_lin(x->iloc, xyz, tp[0]);
+
+ xyz[0] /= xyz[1];
+ xyz[2] /= xyz[1];
+ xyz[1] /= xyz[1];
+
+ /* Compute the color difference to the target */
+ if (x->viscct) {
+ /* Use modern CIEDE2000 color difference - gives a better visual match */
+ icmXYZ2Lab(wp, lab1, x->xyz);
+ icmXYZ2Lab(wp, lab2, xyz);
+ rv = icmCIE2Ksq(lab1, lab2);
+ } else {
+ /* Use original CIE 1960 UCS space color difference */
+ icmXYZ21960UCS(lab1, x->xyz);
+ icmXYZ21960UCS(lab2, xyz);
+ rv = icmLabDEsq(lab1, lab2);
+ }
+
+//a1logd(g_log, 1, " cct2_func returning %f for temp = %f\n",rv,1e6/tp[0]);
+//DBGF((DBGA,"returning %f for temp = %f\n",rv,tp[0]));
+ return rv;
+
+}
+
+/* Given a choice of temperature dependent illuminant (icxIT_Dtemp or icxIT_Ptemp), */
+/* return the closest correlated color temperature to the XYZ. */
+/* An observer type can be chosen for interpretting the spectrum of the input and */
+/* the illuminant. */
+/* Return -1.0 on erorr */
+double icx_XYZ2ill_ct2(
+double txyz[3], /* If not NULL, return the XYZ of the locus temperature */
+icxIllumeType ilType, /* Type of illuminant, icxIT_Dtemp or icxIT_Ptemp */
+icxObserverType obType, /* Observer, CIE_1931_2 or CIE_1964_10 */
+double xyz[3], /* Input XYZ value */
+int viscct /* nz to use visual CIEDE2000, 0 to use CCT CIE 1960 UCS. */
+) {
+ cct2ctx x; /* Context for callback */
+ double cp[1], s[1];
+ double rv;
+ int i;
+ double tc, ber, bct = 0.0;
+
+ x.viscct = viscct;
+
+ if (ilType != icxIT_Dtemp && ilType != icxIT_Ptemp)
+ return -1.0;
+ if (obType != icxOT_CIE_1931_2 && obType != icxOT_CIE_1964_10)
+ return -1.0;
+
+ /* Locus to use */
+ if (obType == icxOT_CIE_1931_2) {
+ if (ilType == icxIT_Dtemp) {
+ x.iloc = illoc_Daylight_CIE_1931_2;
+ } else {
+ x.iloc = illoc_Plankian_CIE_1931_2;
+ }
+ } else {
+ if (ilType == icxIT_Dtemp) {
+ x.iloc = illoc_Daylight_CIE_1964_10;
+ } else {
+ x.iloc = illoc_Plankian_CIE_1964_10;
+ }
+ }
+
+ icmAry2Ary(x.xyz, xyz);
+
+ /* Normalise target */
+ x.xyz[0] /= x.xyz[1];
+ x.xyz[2] /= x.xyz[1];
+ x.xyz[1] /= x.xyz[1];
+
+ /* Convert to XYZ number for DE wp */
+ icmAry2XYZ(x.XYZ, x.xyz);
+
+ /* Do some start samples, to avoid getting trapped in local minima */
+ for (ber = 1e9, i = 0; i < 6; i++) {
+ double er;
+ tc = x.iloc[0].spec_wl_short
+ + i/(6-1.0) * (x.iloc[0].spec_wl_long - x.iloc[0].spec_wl_short);
+ if ((er = cct2_func((void *)&x, &tc)) < ber) {
+ ber = er;
+ bct = tc;
+ }
+//a1logd(g_log, 1, " starting tc = %f, err = %f\n",1e6/tc,er);
+//DBGF((DBGA,"tc = %f, er = %f\n",1e6/tc,er));
+ }
+ cp[0] = bct;
+ s[0] = 20.0;
+
+ /* Locate the CCT in Mired */
+ if (powell(&rv, 1, cp, s, 0.01, 1000, cct2_func, (void *)&x, NULL, NULL) != 0) {
+ x.conv->del(x.conv);
+ return -1.0;
+ }
+
+ if(txyz != NULL) {
+ /* Return the closest value on the locus */
+ getval_raw_xspec3_lin(x.iloc, txyz, cp[0]);
+ txyz[0] /= txyz[1];
+ txyz[2] /= txyz[1];
+ txyz[1] /= txyz[1];
+ }
+
+//a1logd(g_log, 1, " returning %f with error %f delta E94 %f\n",1e6/cp[0],sqrt(rv));
+//DBGF((DBGA,"returning %f with error %f delta E94 %f\n",cp[0],sqrt(rv)));
+ return 1e6/cp[0];
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Spectral and illuminant chromaticity locus support */
+
+/* All the nomenclature is for spectral locus, */
+/* but we use the same mechanism for a Daylight or */
+/* Plankian illuminant locus, substituting temp/Mired for wavelength/nm */
+
+/* Chromaticity locus poligon vertex */
+typedef struct {
+ double xy[2]; /* xy, u'v' value */
+ double xy_n[2]; /* xy, u'v' inwards normal direction */
+ double dist; /* Accumulated distance */
+ double rgb[3]; /* Representative color at this point */
+} xslvtx;
+
+/* Vertex bounding box */
+typedef struct {
+ int ix; /* Starting index of vertex */
+ int n; /* Number of vertexes in box */
+ double xmin;
+ double ymin;
+ double xmax;
+ double ymax;
+} xslbbx;
+
+#define SLOC_BBXN 19 /* 19 vertexes per bounding box */
+
+/* Chromaticity locus poligon cache */
+struct _xslpoly {
+ int sp; /* 0 = Spectral Locus, 1 = Daylight, 2 = Plankian */
+ icxObserverType obType; /* Type of observer */
+ int uv; /* 0 = xy, 1 = u'v' space */
+ int n; /* Number of vertexes, 0 if uninit */
+ double wl_short; /* First reading wavelength in nm (shortest)/ smallest Mired */
+ double wl_long; /* Last reading wavelength in nm (longest)/ largest Mired */
+ double xmin, xmax, ymin, ymax; /* xy Boundint box */
+ double tx[3], ty[3]; /* xy Fast inner triangle test, RGB (spectral locus) */
+ double be[3][3]; /* xy baricentric equations of triangle (spectral locus) */
+// double eed[3]; /* xy Distance of triangle points to 0.3, 0.3 (spectral locus) */
+ xslvtx v[XSPECT_MAX_BANDS]; /* vertex values */
+ int nbb; /* Number of bounding boxes */
+ xslbbx bb[XSPECT_MAX_BANDS/SLOC_BBXN + 1]; /* Bounding boxes */
+ double d_max; /* Maximum distance */
+ double rv[XSPECT_MAX_BANDS]; /* distance to wl reverse lookup */
+};
+
/* Init a xslpoly */
/* Return nz on error */
-static int icx_init_locus_poly(icxObserverType obType) {
- xslpoly *poly;
+static int icx_init_locus_poly(
+ xslpoly *p
+) {
+// static CRITICAL_SECTION lock = { NULL, -1 };
+ static amutex_static(lock);
- if ((poly = spectral_locus_poligon(obType)) == NULL)
- return 1;
+// InitializeCriticalSection(&(lock));
+
+ /* Prevent threads trying to multiply initialise the xslpoly */
+// EnterCriticalSection(&(lock));
+ amutex_lock(lock);
- /* Initialise (should have a mutex!) */
- if (poly->n == 0) {
- int i, j, c;
+ if (p->n == 0) {
+ int i0, in;
+ double wl_short, wl_long;
+ int ii, i, j, c;
double Yxy[3];
double xyz[3];
xspect *sp[3];
-double tt[3][3];
-
- if (standardObserver(sp, obType))
- return 3;
-
- poly->n = sp[0]->spec_n;
- poly->xmin = poly->ymin = 1e6;
- poly->xmax = poly->ymax = -1e6;
-
- for (i = 0; i < poly->n; i++) {
- xyz[0] = sp[0]->spec[i];
- xyz[1] = sp[1]->spec[i];
- xyz[2] = sp[2]->spec[i];
-
- icmXYZ2Yxy(Yxy, xyz);
-
- poly->x[i] = Yxy[1];
- poly->y[i] = Yxy[2];
- if (poly->x[i] < poly->xmin)
- poly->xmin = poly->x[i];
- if (poly->x[i] > poly->xmax)
- poly->xmax = poly->x[i];
- if (poly->y[i] < poly->ymin)
- poly->ymin = poly->y[i];
- if (poly->y[i] > poly->ymax)
- poly->ymax = poly->y[i];
+ double tt[3][3];
+ double dist = 0.0;
+
+ if (standardObserver(sp, p->obType)) {
+ amutex_unlock(lock);
+ return 1;
+ }
+
+ if (p->sp == 0) { /* If spectral locus */
+ i0 = 0;
+ in = sp[0]->spec_n;
+ wl_short = sp[0]->spec_wl_short;
+ wl_long = sp[0]->spec_wl_long;
+
+ /* Limit the range to 400 - 700, as the locus is not well behaved outside that */
+ if (wl_short < 400.0) {
+ i0 = (int)ceil(XSPECT_DIX(sp[0]->spec_wl_short, sp[0]->spec_wl_long, sp[0]->spec_n, 400.0));
+ wl_short = XSPECT_WL(sp[0]->spec_wl_short, sp[0]->spec_wl_long, sp[0]->spec_n, i0);
+ }
+ if (wl_long > 700.0) {
+ in = (int)ceil(XSPECT_DIX(sp[0]->spec_wl_short, sp[0]->spec_wl_long, sp[0]->spec_n, 700.0));
+ wl_long = XSPECT_WL(sp[0]->spec_wl_short, sp[0]->spec_wl_long, sp[0]->spec_n, in);
+ in++;
+ }
+
+ p->n = in - i0;
+ p->wl_short = wl_short;
+ p->wl_long = wl_long;
+ p->xmin = p->ymin = 1e6;
+ p->xmax = p->ymax = -1e6;
+
+ /* Compute xy, and accumulated distance along locus */
+ for (ii = 0, i = i0; i < in; i++, ii++) {
+ double wl = XSPECT_WL(p->wl_short, p->wl_long, p->n, ii);
+
+ xyz[0] = sp[0]->spec[i];
+ xyz[1] = sp[1]->spec[i];
+ xyz[2] = sp[2]->spec[i];
+
+ if (p->uv == 0)
+ icmXYZ2Yxy(Yxy, xyz);
+ else
+ icmXYZ21976UCS(Yxy, xyz);
+ p->v[ii].xy[0] = Yxy[1];
+ p->v[ii].xy[1] = Yxy[2];
+
+ if (ii == 0) {
+ p->v[ii].dist = 0.0;
+ } else {
+ double d0, d1;
+ d0 = p->v[ii].xy[0] - p->v[ii-1].xy[0];
+ d1 = p->v[ii].xy[1] - p->v[ii-1].xy[1];
+ dist += sqrt(d0 * d0 + d1 * d1);
+ p->v[ii].dist = dist;
+ }
+
+ /* Compute a display color */
+ icx_wl2RGB_ds(p->v[ii].rgb, wl, 0.1);
+
+//a1logd(g_log, 1, " [%d] = %f %f, dist %f\n",i,p->v[ii].xy[0],p->v[ii].xy[1],p->v[ii].dist);
+ if (Yxy[1] < p->xmin)
+ p->xmin = Yxy[1];
+ if (Yxy[1] > p->xmax)
+ p->xmax = Yxy[1];
+ if (Yxy[2] < p->ymin)
+ p->ymin = Yxy[2];
+ if (Yxy[2] > p->ymax)
+ p->ymax = Yxy[2];
+ }
+
+ } else { /* Daylight or Plankian locus */
+ xspect *iloc;
+ icxIllumeType ilType = p->sp == 1 ? icxIT_Dtemp : icxIT_Ptemp;
+
+ if (p->obType == icxOT_CIE_1931_2) {
+ if (ilType == icxIT_Dtemp) {
+ iloc = illoc_Daylight_CIE_1931_2;
+ } else {
+ iloc = illoc_Plankian_CIE_1931_2;
+ }
+ } else {
+ if (ilType == icxIT_Dtemp) {
+ iloc = illoc_Daylight_CIE_1964_10;
+ } else {
+ iloc = illoc_Plankian_CIE_1964_10;
+ }
+ }
+
+ i0 = 0;
+ in = iloc[0].spec_n;
+ wl_short = iloc[0].spec_wl_short;
+ wl_long = iloc[0].spec_wl_long;
+
+ p->n = in - i0;
+ p->wl_short = wl_short;
+ p->wl_long = wl_long;
+ p->xmin = p->ymin = 1e6;
+ p->xmax = p->ymax = -1e6;
+
+ /* Compute xy/u'v', and accumulated distance along locus */
+ for (ii = 0, i = i0; i < in; i++, ii++) {
+ double xyz[3];
+ double temp;
+
+ xyz[0] = iloc[0].spec[i];
+ xyz[1] = iloc[1].spec[i];
+ xyz[2] = iloc[2].spec[i];
+
+ if (p->uv == 0)
+ icmXYZ2Yxy(Yxy, xyz);
+ else
+ icmXYZ21976UCS(Yxy, xyz);
+ p->v[ii].xy[0] = Yxy[1];
+ p->v[ii].xy[1] = Yxy[2];
+
+ if (ii == 0) {
+ p->v[ii].dist = 0.0;
+ } else {
+ double d0, d1;
+ d0 = p->v[ii].xy[0] - p->v[ii-1].xy[0];
+ d1 = p->v[ii].xy[1] - p->v[ii-1].xy[1];
+ dist += sqrt(d0 * d0 + d1 * d1);
+ p->v[ii].dist = dist;
+ }
+
+ /* Compute a display color */
+ icx_XYZ2RGB_ds(p->v[ii].rgb, xyz, 0.1);
+
+//a1logd(g_log, 1, " [%d] Mired %f = %f %f, dist %f\n",i,XSPECT_WL(wl_short, wl_long, in, i),p->v[ii].xy[0],p->v[ii].xy[1],p->v[ii].dist);
+ if (Yxy[1] < p->xmin)
+ p->xmin = Yxy[1];
+ if (Yxy[1] > p->xmax)
+ p->xmax = Yxy[1];
+ if (Yxy[2] < p->ymin)
+ p->ymin = Yxy[2];
+ if (Yxy[2] > p->ymax)
+ p->ymax = Yxy[2];
+ }
}
- /* Select 3 points for inner triangle in RGB order */
- poly->tx[0] = poly->x[poly->n - 1];
- poly->ty[0] = poly->y[poly->n - 1];
-
- xyz[0] = value_xspect(sp[0], 517.0);
- xyz[1] = value_xspect(sp[1], 517.0);
- xyz[2] = value_xspect(sp[2], 517.0);
- icmXYZ2Yxy(Yxy, xyz);
- poly->tx[1] = Yxy[1];
- poly->ty[1] = Yxy[2];
-
- poly->tx[2] = poly->x[0];
- poly->ty[2] = poly->y[0];
-
- /* Compute distance from triangles to 0.3, 0.3 */
-// for (i = 0; i < 3; i++) {
-// poly->eed[i] = sqrt((poly->tx[i] - 0.3) * (poly->tx[i] - 0.3)
-// + (poly->ty[i] - 0.3) * (poly->ty[i] - 0.3));
-// }
-
- /* Compute baricentric equations */
- for (i = 0; i < 3; i++) {
- tt[0][i] = poly->tx[i];
- tt[1][i] = poly->ty[i];
- tt[2][i] = 1.0;
+ /* Compute bounding boxes */
+ for (i = ii = 0; i < p->n;) {
+ int m;
+ p->bb[ii].ix = i;
+ p->bb[ii].xmin = p->bb[ii].ymin = 1e6;
+ p->bb[ii].xmax = p->bb[ii].ymax = -1e6;
+ for (m = 0; m < SLOC_BBXN && i < p->n; i++, m++) {
+ if (p->v[i].xy[0] < p->bb[ii].xmin)
+ p->bb[ii].xmin = p->v[i].xy[0];
+ if (p->v[i].xy[1] < p->bb[ii].ymin)
+ p->bb[ii].ymin = p->v[i].xy[1];
+ if (p->v[i].xy[0] > p->bb[ii].xmax)
+ p->bb[ii].xmax = p->v[i].xy[0];
+ if (p->v[i].xy[1] > p->bb[ii].ymax)
+ p->bb[ii].ymax = p->v[i].xy[1];
+ }
+ p->bb[ii++].n = m;
}
- if (icmInverse3x3(poly->be, tt))
- error("icx_init_locus_poly: Matrix inversion failed");
+ p->nbb = ii;
- /* Compute baricentric of 0.3 0.3 */
- /* (Not currently used. How to move center to 0.3 0.3 ?? */
-// for (i = 0; i < 3; i++)
-// poly->eed[i] = poly->be[i][0] * 0.3 + poly->be[i][1] * 0.3 + poly->be[i][2];
+//for (i = 0; i < p->nbb; i++)
+//a1logd(g_log, 1,"bb %d: n = %d, bb %f %f %f %f",i,p->bb[i].n, p->bb[i].xmin,p->bb[i].xmax,p->bb[i].ymin,p->bb[i].ymax);
+
+ /* Compute reverse lookup of distance to wavelength/temp */
+ {
+ p->d_max = p->v[p->n-1].dist;
+
+//a1logd(g_log, 1,"d_max = %f\n",p->d_max);
+
+ p->rv[0] = 0.0;
+ for (i = 1; i < XSPECT_MAX_BANDS; i++)
+ p->rv[i] = -1.0;
+
+ /* Create search start points */
+ for (i = 0; i < p->n; i++) {
+ int ix;
+ ix = (int)floor(XSPECT_DIX(0.0, p->d_max, XSPECT_MAX_BANDS, p->v[i].dist));
+ if (p->rv[ix] < 0.0 || (double)i < p->rv[ix]) {
+ p->rv[ix] = (double)i;
+//a1logd(g_log, 1,"ix %d dist %f start ix %d\n",ix,p->v[i].dist,i);
+ }
+ }
+
+ /* Go through start points and create interpolated points */
+ for (i = XSPECT_MAX_BANDS-2; i > 0; i--) {
+ double d, wl0, d0, wl1, d1, bl, wl;
+ int j, ix;
+
+ d = XSPECT_WL(0.0, p->d_max, XSPECT_MAX_BANDS, i); /* Distance of this cell */
+
+ /* Find a search start point - skip any empty slots */
+ for (j = i; j >= 0; j--) {
+ ix = (int)p->rv[j];
+ if (ix >= 0)
+ break;
+ }
+ if (j < 0)
+ ix = 0;
+
+ for (; ix >= 0; ix--) {
+ d0 = p->v[ix].dist;
+ if (d0 <= d)
+ break;
+ }
+ if (ix < 0)
+ ix = 0;
+ wl0 = XSPECT_WL(wl_short, wl_long, p->n, ix);
+
+ /* Locate the fwd point after this entries distance */
+ for (j = ix+1; j < p->n; j++) {
+ d1 = p->v[j].dist;
+ if (d1 >= d)
+ break;
+ }
+ if (j >= p->n) {
+ d1 = p->d_max;
+ j--;
+ if (ix == j) {
+ ix--;
+ wl0 = XSPECT_WL(wl_short, wl_long, p->n, ix);
+ d0 = p->v[ix].dist;
+ }
+ }
+ wl1 = XSPECT_WL(wl_short, wl_long, p->n, j);
+
+ /* Linearly interpolate for this entries distance */
+ bl = (d - d0)/(d1 - d0);
+//a1logd(g_log, 1,"rv ix %d, d %f, bl %f, ixs %d - %d, ds %f - %f, wls %f - %f\n",i,d,bl, ix,j,d0,d1,wl0,wl1);
+ wl = (1.0 - bl) * wl0 + bl * wl1;
+ p->rv[i] = wl;
+ }
+ p->rv[0] = wl_short;
+ p->rv[XSPECT_MAX_BANDS-1] = wl_long;
+
+//for (i = 0; i < XSPECT_MAX_BANDS; i++)
+// a1logd(g_log, 1,"rv %d = %f\n",i,p->rv[i]);
+ }
+
+ /* Compute outward normals, and delta wl/delta dist */
+ for (i = 0; i < p->n; i++) {
+ int span = 1;
+ double pn[2], mm;
+
+ if (i < 50) /* Hack to straighten up 400nm */
+ span = 20;
+
+ i0 = i - span;
+ in = i + span;
+ if (i0 < 0) {
+ i0 = 0;
+ in = i0 + 2 * span;
+ }
+ if (in > (p->n-1)) {
+ in = (p->n-1);
+ i0 = in - 2 * span;
+ }
+ pn[0] = p->v[in].xy[1] - p->v[i0].xy[1];
+ pn[1] = -(p->v[in].xy[0] - p->v[i0].xy[0]);
+
+// a1logd(g_log, 1,"i0 = %d, in = %d\n",i0, in);
+// a1logd(g_log, 1,"i0 %d = %f %f\n",i0,p->v[i0].xy[0],p->v[i0].xy[1]);
+// a1logd(g_log, 1,"in %d = %f %f\n",in,p->v[in].xy[0],p->v[i0].xy[1]);
+// a1logd(g_log, 1,"pn = %f %f\n",pn[0],pn[1]);
+
+ mm = sqrt(pn[0] * pn[0] + pn[1] * pn[1]);
+ pn[0] /= mm;
+ pn[1] /= mm;
+
+ p->v[i].xy_n[0] = pn[0];
+ p->v[i].xy_n[1] = pn[1];
+ }
+
+#ifdef NEVER
+ /* Compute v2 sub sampled values */
+#endif
+
+ if (p->sp == 0) { /* If spectral locus */
+ /* Select 3 points for inner triangle in RGB order */
+ p->tx[0] = p->v[p->n - 1].xy[0];
+ p->ty[0] = p->v[p->n - 1].xy[1];
+
+ xyz[0] = value_xspect(sp[0], 517.0);
+ xyz[1] = value_xspect(sp[1], 517.0);
+ xyz[2] = value_xspect(sp[2], 517.0);
+ if (p->uv == 0)
+ icmXYZ2Yxy(Yxy, xyz);
+ else
+ icmXYZ21976UCS(Yxy, xyz);
+ p->tx[1] = Yxy[1];
+ p->ty[1] = Yxy[2];
+
+ p->tx[2] = p->v[0].xy[0];
+ p->ty[2] = p->v[0].xy[1];
+
+ /* Compute distance from triangles to 0.3, 0.3 */
+// for (i = 0; i < 3; i++) {
+// p->eed[i] = sqrt((p->tx[i] - 0.3) * (p->tx[i] - 0.3)
+// + (p->ty[i] - 0.3) * (p->ty[i] - 0.3));
+// }
+
+ /* Compute baricentric equations */
+ for (i = 0; i < 3; i++) {
+ tt[0][i] = p->tx[i];
+ tt[1][i] = p->ty[i];
+ tt[2][i] = 1.0;
+ }
+ if (icmInverse3x3(p->be, tt)) {
+ a1loge(g_log, 2, "icx_init_locus_poly: Matrix inversion failed");
+ amutex_unlock(lock);
+ return 2;
+ }
+
+ /* Compute baricentric of 0.3 0.3 */
+ /* (Not currently used. How to move center to 0.3 0.3 ?? */
+// for (i = 0; i < 3; i++)
+// p->eed[i] = p->be[i][0] * 0.3 + p->be[i][1] * 0.3 + p->be[i][2];
+ }
}
+ amutex_unlock(lock);
return 0;
}
+/* Spectral locus */
+static xslpoly splo_CIE_1931_2_xy = { 0, icxOT_CIE_1931_2, 0, 0 };
+static xslpoly splo_CIE_1931_2_uv = { 0, icxOT_CIE_1931_2, 1, 0 };
+static xslpoly splo_CIE_1964_10_xy = { 0, icxOT_CIE_1964_10, 0, 0 };
+static xslpoly splo_CIE_1964_10_uv = { 0, icxOT_CIE_1964_10, 1, 0 };
+static xslpoly splo_Stiles_Burch_2_xy = { 0, icxOT_Stiles_Burch_2, 0, 0 };
+static xslpoly splo_Stiles_Burch_2_uv = { 0, icxOT_Stiles_Burch_2, 1, 0 };
+static xslpoly splo_Judd_Voss_2_xy = { 0, icxOT_Judd_Voss_2, 0, 0 };
+static xslpoly splo_Judd_Voss_2_uv = { 0, icxOT_Judd_Voss_2, 1, 0 };
+static xslpoly splo_CIE_1964_10c_xy = { 0, icxOT_CIE_1964_10c, 0, 0 };
+static xslpoly splo_CIE_1964_10c_uv = { 0, icxOT_CIE_1964_10c, 1, 0 };
+static xslpoly splo_Shaw_Fairchild_2_xy = { 0, icxOT_Shaw_Fairchild_2, 0, 0 };
+static xslpoly splo_Shaw_Fairchild_2_uv = { 0, icxOT_Shaw_Fairchild_2, 1, 0 };
+
+/* Illuminant locus */
+static xslpoly illo_D_CIE_1931_2_xy = { 1, icxOT_CIE_1931_2, 0, 0 };
+static xslpoly illo_D_CIE_1931_2_uv = { 1, icxOT_CIE_1931_2, 1, 0 };
+static xslpoly illo_D_CIE_1964_10_xy = { 1, icxOT_CIE_1964_10, 0, 0 };
+static xslpoly illo_D_CIE_1964_10_uv = { 1, icxOT_CIE_1964_10, 1, 0 };
+static xslpoly illo_P_CIE_1931_2_xy = { 2, icxOT_CIE_1931_2, 0, 0 };
+static xslpoly illo_P_CIE_1931_2_uv = { 2, icxOT_CIE_1931_2, 1, 0 };
+static xslpoly illo_P_CIE_1964_10_xy = { 2, icxOT_CIE_1964_10, 0, 0 };
+static xslpoly illo_P_CIE_1964_10_uv = { 2, icxOT_CIE_1964_10, 1, 0 };
+
+/* Return a pointer to the chromaticity locus poligon */
+/* return NULL on failure. */
+xslpoly *chrom_locus_poligon(
+icxLocusType loty, /* Locus type, 1 = spectral, 2 = Daylight, 3 = Plankian */
+icxObserverType obType, /* Type of observer */
+int uv /* 0 = xy, 1 = u'v' space */
+) {
+ xslpoly *rv = NULL;
+
+ if (loty == icxLT_none)
+ return NULL;
+
+ switch (obType) {
+ case icxOT_default:
+ case icxOT_CIE_1931_2:
+ if (uv == 0) {
+ if (loty == icxLT_spectral)
+ rv = &splo_CIE_1931_2_xy;
+ else if (loty == icxLT_daylight)
+ rv = &illo_D_CIE_1931_2_xy;
+ else if (loty == icxLT_plankian)
+ rv = &illo_P_CIE_1931_2_xy;
+ } else {
+ if (loty == icxLT_spectral)
+ rv = &splo_CIE_1931_2_uv;
+ else if (loty == icxLT_daylight)
+ rv = &illo_D_CIE_1931_2_uv;
+ else if (loty == icxLT_plankian)
+ rv = &illo_P_CIE_1931_2_uv;
+ }
+ break;
+ case icxOT_CIE_1964_10:
+ if (uv == 0) {
+ if (loty == icxLT_spectral)
+ rv = &splo_CIE_1964_10_xy;
+ else if (loty == icxLT_daylight)
+ rv = &illo_D_CIE_1964_10_xy;
+ else if (loty == icxLT_plankian)
+ rv = &illo_P_CIE_1964_10_xy;
+ } else {
+ if (loty == icxLT_spectral)
+ rv = &splo_CIE_1964_10_uv;
+ else if (loty == icxLT_daylight)
+ rv = &illo_D_CIE_1964_10_uv;
+ else if (loty == icxLT_plankian)
+ rv = &illo_P_CIE_1964_10_uv;
+ }
+ break;
+ default:
+ rv = NULL;
+ }
+ if (rv == NULL)
+ return rv;
+
+
+ if (rv->n == 0 && icx_init_locus_poly(rv))
+ return NULL;
+
+ return rv;
+}
+
+
/* Determine whether the given XYZ is outside the spectrum locus */
/* Return 0 if within locus */
/* Return 1 if outside locus */
-/* Return 2 if unknown (bad observer) */
-int icx_outside_spec_locus(double xyz[3], icxObserverType obType) {
+int icx_outside_spec_locus(xslpoly *p, double xyz[3]) {
int i, j, c;
xslpoly *poly;
double Yxy[3];
- if ((poly = spectral_locus_poligon(obType)) == NULL)
- return 2;
-
- /* Init poly if needed */
- if (poly->n == 0 && icx_init_locus_poly(obType))
- return 2;
-
icmXYZ2Yxy(Yxy, xyz);
/* Quick test - bounding box */
- if (Yxy[1] < poly->xmin || Yxy[1] > poly->xmax
- || Yxy[2] < poly->ymin || Yxy[2] > poly->ymax)
+ if (Yxy[1] < p->xmin || Yxy[1] > p->xmax
+ || Yxy[2] < p->ymin || Yxy[2] > p->ymax)
return 1;
/* Quick test - inner triangle */
for (c = 1, i = 0, j = 3-1; i < 3; j = i++) {
- if ( ((poly->ty[i] > Yxy[2]) != (poly->ty[j] > Yxy[2]))
- && (Yxy[1] < (poly->tx[j] - poly->tx[i]) * (Yxy[2] - poly->ty[i])
- / (poly->ty[j] - poly->ty[i]) + poly->tx[i]) )
+ if ( ((p->ty[i] > Yxy[2]) != (p->ty[j] > Yxy[2]))
+ && (Yxy[1] < (p->tx[j] - p->tx[i]) * (Yxy[2] - p->ty[i])
+ / (p->ty[j] - p->ty[i]) + p->tx[i]) )
c = !c;
}
if (c == 0)
@@ -4080,91 +5881,52 @@ int icx_outside_spec_locus(double xyz[3], icxObserverType obType) {
/* Do point in poligon test */
/* (This could be speeded up in many ways) */
- for (c = 1, i = 0, j = poly->n-1; i < poly->n; j = i++) {
- if ( ((poly->y[i] > Yxy[2]) != (poly->y[j] > Yxy[2]))
- && (Yxy[1] < (poly->x[j] - poly->x[i]) * (Yxy[2] - poly->y[i])
- / (poly->y[j] - poly->y[i]) + poly->x[i]) )
+ for (c = 1, i = 0, j = p->n-1; i < p->n; j = i++) {
+ if ( ((p->v[i].xy[1] > Yxy[2]) != (p->v[j].xy[1] > Yxy[2]))
+ && (Yxy[1] < (p->v[j].xy[0] - p->v[i].xy[0]) * (Yxy[2] - p->v[i].xy[1])
+ / (p->v[j].xy[1] - p->v[i].xy[1]) + p->v[i].xy[0]) )
c = !c;
}
return c;
}
-/* Return an aproximate RGB value for coloring within the spectrum locus */
-void icx_spec_locus_color(double rgb[3], double xyz[3], icxObserverType obType) {
- int i, j;
- xslpoly *poly;
- double Yxy[3];
- double dtt[3]; /* Distances to triangle points */
- double v[3];
- double max;
-
- if ((poly = spectral_locus_poligon(obType)) == NULL)
- return;
-
- /* Init poly if needed */
- if (poly->n == 0 && icx_init_locus_poly(obType))
- return;
-
- icmXYZ2Yxy(Yxy, xyz);
-
- /* Compute the baricentric coord for the input point, */
- for (max = -1e6, i = 0; i < 3; i++) {
- v[i] = poly->be[i][0] * Yxy[1] + poly->be[i][1] * Yxy[2] + poly->be[i][2];
- if (v[i] < 0.0)
- v[i] = 0.0;
- else if (v[i] > 1.0)
- v[i] = 1.0;
-
- /* Normalise to put wp at 0.3 0.3 */
- // ~~99
-
- v[i] = pow(v[i], 1.0/2.2);
-
- if (v[i] > max)
- max = v[i];
- }
-
- for (i = 0; i < 3; i++) {
- rgb[i] = v[i]/max;
- }
-}
-
/* -------------------------------------------------------- */
+
/* Status T log10 weightings */
-/* CMYV */
+/* CMY + ISO V */
static xspect denT[4] = {
{
44, 340.0, 770.0, /* 44 bands from 340 to 770 nm in 10nm steps */
1.0, /* Log10 Scale factor */
{
- 0.000,
- 0.000, 0.000, 0.000, 0.000, 0.000,
- 0.000, 0.000, 0.000, 0.000, 0.000,
- 0.000, 0.000, 0.000, 0.000, 0.000,
- 0.000, 0.000, 0.000, 0.000, 0.000,
- 0.000, 0.500, 1.778, 2.653, 4.477,
+ -10.0,
+ -10.0, -10.0, -10.0, -10.0, -10.0,
+ -10.0, -10.0, -10.0, -10.0, -10.0,
+ -10.0, -10.0, -10.0, -10.0, -10.0,
+ -10.0, -10.0, -10.0, -10.0, -10.0,
+ -10.0, 0.500, 1.778, 2.653, 4.477,
5.000, 4.929, 4.740, 4.398, 4.000,
3.699, 3.176, 2.699, 2.477, 2.176,
- 1.699, 1.000, 0.500, 0.000, 0.000,
- 0.000, 0.000, 0.000
+ 1.699, 1.000, 0.500, -10.0, -10.0,
+ -10.0, -10.0, -10.0
}
},
{
44, 340.0, 770.0, /* 44 bands from 340 to 770 nm in 10nm steps */
1.0, /* Log10 Scale factor */
{
- 0.000,
- 0.000, 0.000, 0.000, 0.000, 0.000,
- 0.000, 0.000, 0.000, 0.000, 0.000,
- 0.000, 0.000, 0.500, 3.000, 3.699,
+ -10.0,
+ -10.0, -10.0, -10.0, -10.0, -10.0,
+ -10.0, -10.0, -10.0, -10.0, -10.0,
+ -10.0, -10.0, 0.500, 3.000, 3.699,
4.447, 4.833, 4.964, 5.000, 4.944,
4.820, 4.623, 4.342, 3.954, 3.398,
- 2.845, 1.954, 1.000, 0.500, 0.000,
- 0.000, 0.000, 0.000, 0.000, 0.000,
- 0.000, 0.000, 0.000, 0.000, 0.000,
- 0.000, 0.000, 0.000
+ 2.845, 1.954, 1.000, 0.500, -10.0,
+ -10.0, -10.0, -10.0, -10.0, -10.0,
+ -10.0, -10.0, -10.0, -10.0, -10.0,
+ -10.0, -10.0, -10.0
}
},
{
@@ -4176,19 +5938,19 @@ static xspect denT[4] = {
3.778, 4.230, 4.602, 4.778, 4.914,
4.973, 5.000, 4.987, 4.929, 4.813,
4.602, 4.255, 3.699, 2.301, 1.602,
- 0.500, 0.000, 0.000, 0.000, 0.000,
- 0.000, 0.000, 0.000, 0.000, 0.000,
- 0.000, 0.000, 0.000, 0.000, 0.000,
- 0.000, 0.000, 0.000, 0.000, 0.000,
- 0.000, 0.000, 0.000
+ 0.500, -10.0, -10.0, -10.0, -10.0,
+ -10.0, -10.0, -10.0, -10.0, -10.0,
+ -10.0, -10.0, -10.0, -10.0, -10.0,
+ -10.0, -10.0, -10.0, -10.0, -10.0,
+ -10.0, -10.0, -10.0
}
},
{
44, 340.0, 770.0, /* 44 bands from 340 to 770 nm in 10nm steps */
1.0, /* Log10 Scale factor */
{
- 0.000,
- 0.000, 0.000, 0.000, 0.000, 0.000,
+ -10.0,
+ -10.0, -10.0, -10.0, -10.0, -10.0,
0.500, 1.332, 1.914, 2.447, 2.881,
3.090, 3.346, 3.582, 3.818, 4.041,
4.276, 4.513, 4.702, 4.825, 4.905,
@@ -4201,6 +5963,7 @@ static xspect denT[4] = {
}
};
+
/* Given a reflectance or transmition spectral product, (Relative */
/* to the scale factor), return status T CMYV log10 density values */
void xsp_Tdensity(
@@ -4226,10 +5989,10 @@ xspect *in /* Spectral product to be converted */
out[j] += S * W;
}
out[j] /= sum; /* Normalise */
- if (out[j] < 0.00001)
- out[j] = 0.00001; /* Just to be sure we don't get silly values */
- else if (out[j] > 1.0)
- out[j] = 1.0;
+ if (out[j] < 1e-8)
+ out[j] = 1e-8; /* Just to be sure we don't get silly values */
+ else if (out[j] > 2.0)
+ out[j] = 2.0;
out[j] = -log10(out[j]); /* Convert to density */
}
@@ -4294,17 +6057,17 @@ double *in /* Input XYZ values */
}
}
-/* Given an XYZ value, */
-/* return approximate sRGB values */
+/* Given an XYZ value, return sRGB values. */
+/* This is a little slow if wp used */
void icx_XYZ2sRGB(
-double *out, /* Return aproximate CMYV log10 density */
+double *out, /* Return approximate sRGB values */
double *wp, /* Input XYZ white point (may be NULL) */
double *in /* Input XYZ values */
) {
int i, j;
double XYZ[3];
- double d65[3] = { 0.950543, 1.0, 1.089303 };
- double mat[3][3] = {
+ double d65[3] = { 0.950543, 1.0, 1.089303 }; /* D65 */
+ double mat[3][3] = { /* sRGB absolute XYZ->RGB ? */
{ 3.2406, -1.5372, -0.4986 },
{ -0.9689, 1.8758, 0.0415 },
{ 0.0557, -0.2040, 1.0570 }
@@ -4312,8 +6075,13 @@ double *in /* Input XYZ values */
/* Do a simple Von Kries between input white point and D65 */
if (wp != NULL) {
- for (j = 0; j < 3; j++)
- XYZ[j] = d65[j] * in[j]/wp[j];
+ icmXYZNumber dst, src;
+ double vkmat[3][3];
+
+ icmAry2XYZ(src, wp);
+ icmAry2XYZ(dst, d65);
+ icmChromAdaptMatrix(ICM_CAM_BRADFORD | ICM_CAM_BRADFORD, dst, src, vkmat);
+ icmMulBy3x3(XYZ, vkmat, in);
} else {
for (j = 0; j < 3; j++)
XYZ[j] = in[j];
@@ -4341,6 +6109,87 @@ double *in /* Input XYZ values */
}
}
+/* Given an XYZ value, return approximate RGB value */
+/* Desaurate to white by the given amount */
+void icx_XYZ2RGB_ds(
+double *out, /* Return approximate sRGB values */
+double *in, /* Input XYZ */
+double desat /* 0.0 = full saturation, 1.0 = white */
+) {
+ int i, j;
+ double mat[3][3] = { /* XYZ to D65 sRGB */
+ { 1.490715, -0.075680, -0.313279 }, /* Triangle that occupies spectrum locus */
+ { -0.492678, 1.364383, 0.095391 },
+ { 0.049610, -0.137386, 1.001080 }
+ };
+ double white[3] = { 1.0, 1.0, 1.0 } ;
+ double max;
+
+ /* Normalize */
+ in[0] /= in[1];
+ in[2] /= in[1];
+ in[1] = 1.0;
+
+//a1logd(g_log, 1,"icx_XYZ2sRGB_ds: norm XYZ %f %f %f\n", in[0], in[1], in[2]);
+
+ /* Convert to sRGB cromaticities */
+ for (i = 0; i < 3; i++) {
+ out[i] = 0.0;
+ for (j = 0; j < 3; j++) {
+ out[i] += in[j] * mat[i][j];
+ }
+ }
+//a1logd(g_log, 1,"icx_XYZ2sRGB_ds: raw RGB %f %f %f\n", out[0], out[1], out[2]);
+
+ /* Clip */
+ max = -1e6;
+ for (i = 0; i < 3; i++) {
+ if (out[i] > max)
+ max = out[i];
+ }
+ for (i = 0; i < 3; i++) {
+ out[i] /= max;
+ if (out[i] < 0.0)
+ out[i] = 0.0;
+ }
+//a1logd(g_log, 1,"icx_XYZ2sRGB_ds: clip RGB %f %f %f\n", out[0], out[1], out[2]);
+
+ /* Desaturate */
+ icmBlend3(out, out, white, desat);
+
+//a1logd(g_log, 1,"icx_XYZ2sRGB_ds: desat RGB %f %f %f\n", out[0], out[1], out[2]);
+
+ /* Apply gamma */
+ for (j = 0; j < 3; j++) {
+ if (out[j] <= (0.03928/12.92)) {
+ out[j] *= 12.92;
+ if (out[j] < 0.0)
+ out[j] = 0.0;
+ } else {
+ out[j] = pow(out[j], 1.0/2.4) * 1.055 - 0.055;
+ if (out[j] > 1.0)
+ out[j] = 1.0;
+ }
+ }
+
+//a1logd(g_log, 1,"icx_XYZ2sRGB_ds: final RGB %f %f %f\n", out[0], out[1], out[2]);
+}
+
+/* Given a wavelengthm return approximate RGB value */
+/* Desaurate to white by the given amount */
+void icx_wl2RGB_ds(
+double *out, /* Return approximate sRGB values */
+double wl, /* Input wavelength in nm */
+double desat /* 0.0 = full saturation, 1.0 = white */
+) {
+ double XYZ[3];
+
+ icx_spectrum_locus(XYZ, wl, icxOT_CIE_1931_2);
+//a1logd(g_log, 1,"cx_wl2sRGB_ds: wl %f -> XYZ %f %f %f\n",wl, XYZ[0], XYZ[1], XYZ[2]);
+
+ icx_XYZ2RGB_ds(out, XYZ, desat);
+}
+
/* ------------------- */
#ifdef NEVER /* Deprecated */
@@ -4348,11 +6197,16 @@ double *in /* Input XYZ values */
/* Given a daylight color temperature in degrees K, */
/* return the corresponding XYZ value (standard 2 degree observer) */
void icx_DTEMP2XYZ(
-double *out, /* Return XYZ value with Y == 1 */
+double *out, /* Return XYZ value with Y == 1, -1 on error */
double ct /* Input temperature in degrees K */
) {
double Yxy[3];
+ if (ct < 2500.0 || ct > 25000.0) { /* Only accurate down to 4000 */
+ out[0] = out[1] = out[2] = -1.0;
+ return;
+ }
+
//DBGF((DBGA,"computing temperature %f\n",ct));
/* Compute chromaticity coordinates */
if (ct < 7000.0) {
@@ -4465,7 +6319,6 @@ static double cct_func(void *fdata, double tp[]) {
/* return the closest correlated color temperature to the given spectrum or XYZ. */
/* An observer type can be chosen for interpretting the spectrum of the input and */
/* the illuminant. */
-/* Note we can use CIEDE2000, rather than the traditional L*u*v* 2/3 space for CCT */
/* Return -1 on erorr */
double icx_XYZ2ill_ct(
double txyz[3], /* If not NULL, return the XYZ of the locus temperature */
@@ -4705,6 +6558,137 @@ xspect *sample /* Illuminant sample to compute CRI of */
return cri;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Compute Australian Radiation Protection and Nuclear Safety Agency (ARPANSA) */
+/* Exposure to Ultraviolet Radiation exposure limits from a spectrum in mw/m-2/nm. */
+/* To be accurate, the spectrum must capture any significant */
+/* exposure wavelengths between 180 - 400 nm */
+
+/* Raw RSE from Table 1 of "Radiation Protection Series No. 12 December 2006" */
+struct {
+ double wl; /* Wavelength */
+ double rse; /* Relative Spectral Effectiveness */
+} raw_rse[57] = {
+ { 180.0, 0.012 },
+ { 190.0, 0.019 },
+ { 200.0, 0.030 },
+ { 205.0, 0.051 },
+ { 210.0, 0.075 },
+ { 215.0, 0.095 },
+ { 220.0, 0.120 },
+ { 225.0, 0.150 },
+ { 230.0, 0.190 },
+ { 235.0, 0.240 },
+ { 240.0, 0.300 },
+ { 245.0, 0.360 },
+ { 250.0, 0.430 },
+ { 254.0, 0.500 },
+ { 255.0, 0.520 },
+ { 260.0, 0.650 },
+ { 265.0, 0.810 },
+ { 270.0, 1.000 },
+ { 275.0, 0.960 },
+ { 280.0, 0.880 },
+ { 285.0, 0.770 },
+ { 290.0, 0.640 },
+ { 295.0, 0.540 },
+ { 297.0, 0.460 },
+ { 300.0, 0.300 },
+ { 303.0, 0.120 },
+ { 305.0, 0.060 },
+ { 308.0, 0.026 },
+ { 310.0, 0.015 },
+ { 313.0, 0.006 },
+ { 315.0, 0.003 },
+ { 316.0, 0.0024 },
+ { 317.0, 0.0020 },
+ { 318.0, 0.0016 },
+ { 319.0, 0.0012 },
+ { 320.0, 0.0010 },
+ { 322.0, 0.00067 },
+ { 323.0, 0.00054 },
+ { 325.0, 0.00050 },
+ { 328.0, 0.00044 },
+ { 330.0, 0.00041 },
+ { 333.0, 0.00037 },
+ { 335.0, 0.00034 },
+ { 340.0, 0.00028 },
+ { 345.0, 0.00024 },
+ { 350.0, 0.00020 },
+ { 355.0, 0.00016 },
+ { 360.0, 0.00013 },
+ { 365.0, 0.00011 },
+ { 370.0, 0.000093 },
+ { 375.0, 0.000077 },
+ { 380.0, 0.000064 },
+ { 385.0, 0.000053 },
+ { 390.0, 0.000044 },
+ { 395.0, 0.000036 },
+ { 400.0, 0.000030 }
+};
+
+/* Compute 1nm sampling rse from raw table using linear interpolation */
+static void compute_rse(xspect *dst) {
+ int i;
+
+ dst->spec_n = 221;
+ dst->spec_wl_short = 180.0;
+ dst->spec_wl_long = 400.0;
+ dst->norm = 1.0;
+
+ /* Linearly interpolate between each raw point */
+ for (i = 0; i < (57-1); i++) {
+ int j, n, ix;
+
+ n = (int)(raw_rse[i+1].wl - raw_rse[i].wl + 0.5);
+ for (j = 0; j <= n; j++) {
+ double bl = j/(double)n;
+ double wl = raw_rse[i].wl + j;
+
+ ix = XSPECT_XIX(dst, wl);
+ dst->spec[ix] = (1.0 - bl) * raw_rse[i].rse + bl * raw_rse[i+1].rse;
+
+//a1logd(g_log, 1,"UV rse ix %d wl %f rse = %f\n",ix,wl,dst->spec[ix]);
+ }
+ }
+}
+
+xspect ARPANSA_rse = { 0 };
+
+/* Return the maximum 24 hour exposure in seconds. */
+/* Maximum return value is 8 hours */
+/* Returns -1.0 if the source sample doesn't go down to at least 350 nm */
+double icx_ARPANSA_UV_exp(
+xspect *sample /* Illuminant sample to compute UV_exp of */
+) {
+ double wl_short, wl_long;
+ double effwpsm; /* Effective Watt/m^2 */
+ double wl;
+ double secs;
+ if (ARPANSA_rse.spec_n == 0)
+ compute_rse(&ARPANSA_rse);
+
+ wl_short = ARPANSA_rse.spec_wl_short;
+ wl_long = ARPANSA_rse.spec_wl_long;
+
+ if (sample->spec_wl_short > wl_short)
+ wl_short = sample->spec_wl_short;
+
+ if (wl_short > 350.0)
+ return -1.0;
+
+ effwpsm = 0.0;
+ for (wl = wl_short; wl <= (wl_long + 1e-6); wl++)
+ effwpsm += value_xspect(sample, wl) * value_xspect(&ARPANSA_rse, wl);
+
+ effwpsm /= 1000.0; /* Convert to W from mW */
+
+ secs = 30.0/effwpsm;
+
+ if (secs > (8 * 60 * 60)) /* Limit to 8 hours */
+ secs = 8 * 60 * 60;
+
+ return secs;
+}
#endif /* !SALONEINSTLIB */
diff --git a/xicc/xspect.h b/xicc/xspect.h
index caca8b2..e3adc1f 100644
--- a/xicc/xspect.h
+++ b/xicc/xspect.h
@@ -82,7 +82,7 @@ typedef struct {
/* Given a wavelength and address of an xspect, compute the nearest index */
#define XSPECT_XIX(PXSP, WL) \
-((int)floor(XSPECT_DIX(PXSP, WL) + 0.5))
+((int)floor(XSPECT_XDIX(PXSP, WL) + 0.5))
#ifndef SALONEINSTLIB
@@ -137,8 +137,8 @@ typedef enum {
icxIT_F10 = 11, /* Fluorescent Narrow Band 5000K, CRI 81 */
icxIT_Spectrocam = 12, /* Spectrocam Xenon Lamp */
icxIT_Dtemp = 13, /* Daylight at specified temperature */
- icxIT_Ptemp = 14 /* Planckian at specified temperature */
#endif /* !SALONEINSTLIB*/
+ icxIT_Ptemp = 14 /* Planckian at specified temperature */
} icxIllumeType;
/* Fill in an xpsect with a standard illuminant spectrum */
@@ -146,7 +146,7 @@ typedef enum {
int standardIlluminant(
xspect *sp, /* Xspect to fill in */
icxIllumeType ilType, /* Type of illuminant */
-double temp); /* Optional temperature in degrees kelvin, for Dtemp and Ptemp */
+double temp); /* Optional temperature in degrees kelvin, For Dtemp and Ptemp */
/* Given an emission spectrum, set the UV output to the given level. */
/* The shape of the UV is taken from FWA1_stim, and the level is */
@@ -176,6 +176,7 @@ int standardObserver(xspect *sp[3], icxObserverType obType);
/* Return a string describing the standard observer */
char *standardObserverDescription(icxObserverType obType);
+
/* Clamping state */
typedef enum {
icxNoClamp = 0, /* Don't clamp XYZ/Lab to +ve */
@@ -287,12 +288,27 @@ xsp2cie *new_xsp2cie(
icxObserverType obType, /* Observer */
xspect custObserver[3],
- icColorSpaceSignature rcs, /* Return color space, icSigXYZData or icSigLabData */
+ icColorSpaceSignature rcs, /* Return color space, icSigXYZData or D50 icSigLabData */
/* ** Must be icSigXYZData if SALONEINSTLIB ** */
icxClamping clamp /* NZ to clamp XYZ/Lab to be +ve */
);
#ifndef SALONEINSTLIB
+
+/* --------------------------- */
+/* Given a choice of temperature dependent illuminant (icxIT_Dtemp or icxIT_Ptemp), */
+/* return the closest correlated color temperature to the XYZ. */
+/* An observer type can be chosen for interpretting the spectrum of the input and */
+/* the illuminant. */
+/* Return -1.0 on erorr */
+double icx_XYZ2ill_ct2(
+double txyz[3], /* If not NULL, return the XYZ of the locus temperature */
+icxIllumeType ilType, /* Type of illuminant, icxIT_Dtemp or icxIT_Ptemp */
+icxObserverType obType, /* Observer, CIE_1931_2 or CIE_1964_10 */
+double xyz[3], /* Input XYZ value */
+int viscct /* nz to use visual CIEDE2000, 0 to use CCT CIE 1960 UCS. */
+);
+
/* --------------------------- */
/* Spectrum locus */
@@ -301,19 +317,32 @@ xsp2cie *new_xsp2cie(
int icx_spectrum_locus_range(double *min_wl, double *max_wl, icxObserverType obType);
/* Return an XYZ that is on the spectrum locus for the given observer. */
-/* wl is the input wavelength in the range icx_spectrum_locus_range(), */
+/* wl is the input wavelength in the range icx_chrom_locus_range(), */
/* and return clipped result if outside this range. */
/* Return nz if observer unknown. */
int icx_spectrum_locus(double xyz[3], double in, icxObserverType obType);
-/* Determine whether the given XYZ is outside the spectrum locus */
+/* - - - - - - - - - - - - - - */
+/* Chromaticity locus support */
+
+typedef struct _xslpoly xslpoly;
+
+typedef enum {
+ icxLT_none = 0,
+ icxLT_spectral = 1,
+ icxLT_daylight = 2,
+ icxLT_plankian = 3
+} icxLocusType;
+
+/* Return a pointer to the chromaticity locus object */
+/* return NULL on failure. */
+xslpoly *chrom_locus_poligon(icxLocusType locus_type, icxObserverType obType, int cspace);
+
+
+/* Determine whether the given XYZ is outside the chromaticity locus */
/* Return 0 if within locus */
/* Return 1 if outside locus */
-/* Return 2 if unknown (bad observer) */
-int icx_outside_spec_locus(double xyz[3], icxObserverType obType);
-
-/* Return an aproximate RGB value for coloring within the spectrum locus */
-void icx_spec_locus_color(double rgb[3], double xyz[3], icxObserverType obType);
+int icx_outside_spec_locus(xslpoly *p, double xyz[3]);
/* --------------------------- */
/* Density and other functions */
@@ -346,6 +375,21 @@ double *wp, /* Input XYZ white point (may be NULL) */
double *in /* Input XYZ values */
);
+/* Given an XYZ value, return approximate RGB value */
+/* Desaurate to white by the given amount */
+void icx_XYZ2RGB_ds(
+double *out, /* Return approximate sRGB values */
+double *in, /* Input XYZ */
+double desat /* 0.0 = full saturation, 1.0 = white */
+);
+
+/* Given a wavelengthm return approximate RGB value */
+/* Desaurate to white by the given amount */
+void icx_wl2RGB_ds(
+double *out, /* Return approximate sRGB values */
+double wl, /* Input wavelength in nm */
+double desat /* 0.0 = full saturation, 1.0 = white */
+);
/* Given an illuminant definition and an observer model, return */
@@ -356,7 +400,7 @@ double xyz[3], /* Return XYZ value with Y == 1 */
icxObserverType obType, /* Observer */
xspect custObserver[3], /* Optional custom observer */
icxIllumeType ilType, /* Type of illuminant */
-double ct, /* Input temperature in degrees K */
+double temp, /* Input temperature in degrees K */
xspect *custIllum); /* Optional custom illuminant */
@@ -364,7 +408,6 @@ xspect *custIllum); /* Optional custom illuminant */
/* return the closest correlated color temperature to the given spectrum or XYZ. */
/* An observer type can be chosen for interpretting the spectrum of the input and */
/* the illuminant. */
-/* Note we're using CICDE94, rather than the traditional L*u*v* 2/3 space for CCT */
/* Return -1 on erorr */
double icx_XYZ2ill_ct(
double txyz[3], /* If not NULL, return the XYZ of the black body temperature */
@@ -383,6 +426,15 @@ double icx_CIE1995_CRI(
int *invalid, /* if not NULL, set to nz if invalid */
xspect *sample /* Illuminant sample to compute CRI of */
);
+
+
+/* Return the maximum 24 hour exposure in seconds. */
+/* Limit is 8 hours */
+/* Returns -1 if the source sample doesn't go down to at least 350 nm */
+double icx_ARPANSA_UV_exp(
+xspect *sample /* Illuminant sample to compute UV_exp of */
+);
+
#endif /* !SALONEINSTLIB*/
#ifdef __cplusplus