summaryrefslogtreecommitdiff
path: root/xicc
diff options
context:
space:
mode:
Diffstat (limited to 'xicc')
-rw-r--r--xicc/Jamfile13
-rw-r--r--xicc/cam02.c20
-rw-r--r--xicc/cam02.h8
-rw-r--r--xicc/cam02plot.c6
-rw-r--r--xicc/cam02ref.h13
-rw-r--r--xicc/cam02test.c18
-rw-r--r--xicc/ccmx.c11
-rw-r--r--xicc/ccmx.h15
-rw-r--r--xicc/ccss.c13
-rw-r--r--xicc/ccss.h16
-rw-r--r--xicc/ccttest.c11
-rw-r--r--xicc/extractttag.c1
-rw-r--r--xicc/fakeCMY.c16
-rw-r--r--xicc/fbview.c5
-rw-r--r--xicc/iccgamut.c32
-rw-r--r--xicc/mpplu.c12
-rw-r--r--xicc/revfix.c1
-rw-r--r--xicc/specplot.c154
-rw-r--r--xicc/specsubsamp.c11
-rw-r--r--xicc/spectest.c2
-rw-r--r--xicc/spectest2.c2
-rw-r--r--xicc/tiffgamut.c30
-rw-r--r--xicc/tiffgmts.c1
-rw-r--r--xicc/xcal.c46
-rw-r--r--xicc/xcal.h23
-rw-r--r--xicc/xcam.c7
-rw-r--r--xicc/xcam.h3
-rw-r--r--xicc/xcolorants.c10
-rw-r--r--xicc/xcolorants.h12
-rw-r--r--xicc/xdevlin.c2
-rw-r--r--xicc/xfbview.c177
-rw-r--r--xicc/xfit.c22
-rw-r--r--xicc/xicc.c32
-rw-r--r--xicc/xicc.h47
-rw-r--r--xicc/xicclu.c91
-rw-r--r--xicc/xlut.c669
-rw-r--r--xicc/xmatrix.c20
-rw-r--r--xicc/xmono.c4
-rw-r--r--xicc/xspect.c258
-rw-r--r--xicc/xspect.h172
40 files changed, 1499 insertions, 507 deletions
diff --git a/xicc/Jamfile b/xicc/Jamfile
index 5a03901..169b471 100644
--- a/xicc/Jamfile
+++ b/xicc/Jamfile
@@ -124,16 +124,27 @@ Main monctest : monctest.c ;
#Main cam02vecplot : cam02vecplot.c : : : : : ../plot/libvrml ;
#Home = ' d:\usr\graeme ' and PWD = ' /src/argyll/xicc '
-if $(HOME) = "d:\\usr\\graeme" && $(PWD) = "/src/argyll/xicc" {
+if $(HOME) = "D:\\usr\\graeme" && $(PWD) = "/src/argyll/xicc" {
+ Echo "Creating test utilities" ;
#Create test TIFF file for cam02 conversion
Main cam02plot : cam02plot.c : : : $(TIFFINC) $(JPEGINC) : : ;
Main cam02logplot : cam02logplot.c : : : $(TIFFINC) $(JPEGINC) : : ;
Main cam02delplot : cam02delplot.c : : : $(TIFFINC) $(JPEGINC) : : ;
Main cam02vecplot : cam02vecplot.c : : : : : ../plot/libvrml ;
+ Main cusptest : cusptest.c : : : : : ../plot/libvrml ;
}
#Main t : t.c ;
+# diagnostic utility
+if [ GLOB [ NormPaths . ] : tennm.c ] {
+ Main tennm : tennm.c ;
+}
+
+if [ GLOB [ NormPaths . ] : cam16.c ] {
+ Main cam16vecplot : cam16vecplot.c cam16.c ;
+}
+
if $(BUILD_JUNK) {
LINKLIBS += ../render/librender ;
diff --git a/xicc/cam02.c b/xicc/cam02.c
index 376d2c8..a79aae1 100644
--- a/xicc/cam02.c
+++ b/xicc/cam02.c
@@ -197,7 +197,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 Yg, double Gxyz[3],
- int hk);
+ int hk, double hkscale);
static int XYZ_to_cam(struct _cam02 *s, double *Jab, double *xyz);
static int cam_to_XYZ(struct _cam02 *s, double *xyz, double *Jab);
@@ -224,6 +224,9 @@ cam02 *new_cam02(void) {
s->XYZ_to_cam = XYZ_to_cam;
s->cam_to_XYZ = cam_to_XYZ;
+ /* Initialise default parameters */
+ s->hkscale = 1.0;
+
/* Set default range handling limits */
s->nldlimit = NLDLIMIT;
s->nldicept = NLDICEPT;
@@ -288,7 +291,8 @@ double Yf, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) *
double Yg, /* Flare as a fraction of the adapting/surround (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-Kohlrausch effect */
+int hk, /* Flag, NZ to use Helmholtz-Kohlrausch effect */
+double hkscale /* HK effect scaling factor */
) {
double tt, t1, t2;
double tm[3][3];
@@ -378,6 +382,7 @@ int hk /* Flag, NZ to use Helmholtz-Kohlrausch effect */
s->Gxyz[2] = Wxyz[2];
}
s->hk = hk;
+ s->hkscale = hkscale;
/* The rgba vectors */
s->Va[0] = 1.0;
@@ -633,9 +638,10 @@ double XYZ[3]
TRACE(("XYZ inc flare = %f %f %f\n",xyz[0], xyz[1], xyz[2]))
- /* Spectrally sharpened cone responses, */
- /* Chromaticaly transformed sample value, */
- /* Transform from spectrally sharpened, to Hunt-Pointer_Estevez cone space. */
+ /* Transfor to spectrally sharpened cone responses, */
+ /* apply chromaticaly transform, */
+ /* and transform from spectrally sharpened to Hunt-Pointer_Estevez cone space, */
+ /* all in one step. */
icmMulBy3x3(rgbp, s->cc, xyz);
TRACE(("rgbp = %f %f %f\n", rgbp[0], rgbp[1], rgbp[2]))
@@ -949,7 +955,7 @@ double XYZ[3]
/* Helmholtz-Kohlrausch effect */
if (s->hk && J < 1.0) {
// double kk = C/300.0 * sin(DBL_PI * fabs(0.5 * (h - 90.0))/180.0);
- double kk = HHKR_MUL * C/300.0 * sin(DBL_PI * fabs(0.5 * (h - 90.0))/180.0);
+ double kk = s->hkscale * HHKR_MUL * C/300.0 * sin(DBL_PI * fabs(0.5 * (h - 90.0))/180.0);
if (kk > 1e-6) /* Limit kk to a reasonable range */
kk = 1.0/(s->hklimit + 1.0/kk);
JJ = J + (1.0 - (J > 0.0 ? J : 0.0)) * kk;
@@ -1045,7 +1051,7 @@ double Jab[3]
/* Undo Helmholtz-Kohlrausch effect */
if (s->hk && J < 1.0) {
// double kk = C/300.0 * sin(DBL_PI * fabs(0.5 * (h - 90.0))/180.0);
- double kk = HHKR_MUL * C/300.0 * sin(DBL_PI * fabs(0.5 * (h - 90.0))/180.0);
+ double kk = s->hkscale * HHKR_MUL * C/300.0 * sin(DBL_PI * fabs(0.5 * (h - 90.0))/180.0);
if (kk > 1e-6) /* Limit kk to a reasonable range */
kk = 1.0/(s->hklimit + 1.0/kk);
J = (JJ - kk)/(1.0 - kk);
diff --git a/xicc/cam02.h b/xicc/cam02.h
index 37443d3..9550bd5 100644
--- a/xicc/cam02.h
+++ b/xicc/cam02.h
@@ -7,7 +7,7 @@
* 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 a Viewing Flare+Glare
- * model, and the Helmholtz-Kohlraush effect, using the equation
+ * model, and the Helmholtz-Kohlrausch effect, using the equation
* the Bradford-Hunt 96C model as detailed in Mark Fairchilds
* book "Color Appearance Models".
*
@@ -142,7 +142,8 @@ struct _cam02 {
double Yg, /* Glare as a fraction of the adapting/surround (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 */
+ int hk, /* Flag, NZ to use Helmholtz-Kohlrausch effect */
+ double hkscale /* HK effect scaling factor */
);
/* Conversions. Return nz on error */
@@ -194,7 +195,8 @@ struct _cam02 {
double lA; /* JLIMIT Limited A */
/* Option flags, code not always enabled */
- int hk; /* Use Helmholtz-Kohlraush effect */
+ int hk; /* Use Helmholtz-Kohlrausch effect */
+ int hkscale; /* [1.0] Scale HK effect up/down from default */
int trace; /* Trace values through computation */
int retss; /* Return ss rather than Jab */
int range; /* (for cam02ref.h) return on range error */
diff --git a/xicc/cam02plot.c b/xicc/cam02plot.c
index f397e09..16b5aa5 100644
--- a/xicc/cam02plot.c
+++ b/xicc/cam02plot.c
@@ -577,7 +577,8 @@ main(int argc, char *argv[]) {
0.00, /* 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) */
white[4], /* The Glare color coordinates (typically the Ambient color) */
- use_hk /* use Helmholtz-Kohlraush flag */
+ use_hk, /* use Helmholtz-Kohlraush flag */
+ 1.0 /* HK scaling factor */
);
/* Setup cam to convert from Jab */
@@ -595,7 +596,8 @@ main(int argc, char *argv[]) {
0.00, /* 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) */
white[4], /* The Glare color coordinates (typically the Ambient color) */
- use_hk /* use Helmholtz-Kohlraush flag */
+ use_hk, /* use Helmholtz-Kohlraush flag */
+ 1.0 /* HK scaling factor */
);
/* Figure out the size of the raster */
diff --git a/xicc/cam02ref.h b/xicc/cam02ref.h
index 29f7d2a..28c3fd3 100644
--- a/xicc/cam02ref.h
+++ b/xicc/cam02ref.h
@@ -22,6 +22,8 @@
/* with the ICC convention (not 100.0 as assumed by the CIECAM spec.) */
/* Note that all whites are assumed to be normalised (ie. Y = 1.0) */
+#define HHKR_MUL 0.25
+
#undef DIAG /* Print internal value diagnostics for each conversion */
/* ---------------------------------- */
@@ -122,7 +124,8 @@ 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 Yg, double Gxyz[3], int hk);
+ double Yb, double La, double Lv, double Yf, double Yg, double Gxyz[3],
+ int hk, double hkscale);
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]);
@@ -160,7 +163,8 @@ double Lv, /* Luminence of white in the Viewing/Scene/Image field (cd/m^2) */
double Yf, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
double Yg, /* Glare as a fraction of the adapting/surround (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 */
+int hk, /* Flag, NZ to use Helmholtz-Kohlraush effect */
+double hkscale /* HK effect scaling factor */
) {
double tt;
@@ -185,6 +189,7 @@ int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
s->Gxyz[2] = Wxyz[2];
}
s->hk = hk;
+ s->hkscale = hkscale;
/* Compute the internal parameters by category */
switch(s->Ev) {
@@ -442,7 +447,7 @@ double XYZ[3]
/* Helmholtz-Kohlraush effect */
if (s->hk && J < 1.0) {
- double JJ, kk = C/300.0 * sin(DBL_PI * fabs(0.5 * (h - 90.0))/180.0);
+ double JJ, kk = s->hkscale * HHKR_MUL * C/300.0 * sin(DBL_PI * fabs(0.5 * (h - 90.0))/180.0);
if (kk > 0.9) /* Limit kk to a reasonable range */
kk = 0.9;
JJ = J + (1.0 - J) * kk;
@@ -508,7 +513,7 @@ double Jab[3]
/* Helmholtz-Kohlraush effect */
if (s->hk && J < 1.0) {
- double kk = C/300.0 * sin(DBL_PI * fabs(0.5 * (h - 90.0))/180.0);
+ double kk = s->hkscale * HHKR_MUL * C/300.0 * sin(DBL_PI * fabs(0.5 * (h - 90.0))/180.0);
if (kk > 0.9) /* Limit kk to a reasonable range */
kk = 0.9;
J = (J - kk)/(1.0 - kk);
diff --git a/xicc/cam02test.c b/xicc/cam02test.c
index eba27d6..fb7d98e 100644
--- a/xicc/cam02test.c
+++ b/xicc/cam02test.c
@@ -562,7 +562,8 @@ main(void) {
0.00, /* 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) */
sp_white[c],/* The Glare color coordinates (typically the Ambient color) */
- USE_HK /* use Helmholtz-Kohlraush flag */
+ USE_HK, /* use Helmholtz-Kohlraush flag */
+ 1.0 /* Normal Helmholtz-Kohlraush scale */
);
cam->set_view(
@@ -575,7 +576,8 @@ main(void) {
0.00, /* 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) */
sp_white[c],/* The Glare color coordinates (typically the Ambient color) */
- USE_HK /* use Helmholtz-Kohlraush flag */
+ USE_HK, /* use Helmholtz-Kohlraush flag */
+ 1.0 /* Normal Helmholtz-Kohlraush scale */
);
camr->nldlimit = cam->nldlimit;
@@ -716,7 +718,8 @@ main(void) {
0.00, /* 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) */
white[c], /* The Glare color coordinates (typically the Ambient color) */
- USE_HK /* use Helmholtz-Kohlraush flag */
+ USE_HK, /* use Helmholtz-Kohlraush flag */
+ 1.0 /* Normal Helmholtz-Kohlraush scale */
);
cam->set_view(
@@ -729,7 +732,8 @@ main(void) {
0.00, /* 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) */
white[c], /* The Glare color coordinates (typically the Ambient color) */
- USE_HK /* use Helmholtz-Kohlraush flag */
+ USE_HK, /* use Helmholtz-Kohlraush flag */
+ 1.0 /* Normal Helmholtz-Kohlraush scale */
);
/* Make reference return error where it's going to disagree with implementation */
@@ -950,7 +954,8 @@ main(void) {
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 */
+ USE_HK, /* use Helmholtz-Kohlraush flag */
+ 1.0 /* Normal Helmholtz-Kohlraush scale */
);
#ifdef INVTEST1
@@ -1132,7 +1137,8 @@ main(void) {
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 */
+ USE_HK, /* use Helmholtz-Kohlraush flag */
+ 1.0 /* Normal Helmholtz-Kohlraush scale */
);
#ifdef TESTINV1
diff --git a/xicc/ccmx.c b/xicc/ccmx.c
index 538f181..7206f31 100644
--- a/xicc/ccmx.c
+++ b/xicc/ccmx.c
@@ -3,6 +3,9 @@
* Argyll Color Correction System
* Colorimeter Correction Matrix
*
+ */
+
+/*
* Author: Graeme W. Gill
* Date: 19/8/2010
*
@@ -16,6 +19,7 @@
* on other libraries that are licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3.
*/
+
/*
* TTBD:
*/
@@ -34,7 +38,7 @@
#include "icc.h"
#else
#include "numsup.h"
-#include "conv.h"
+#include "sa_conv.h"
#endif
#include "cgats.h"
#include "disptechs.h"
@@ -146,7 +150,7 @@ char *outname /* Filename to write to */
static int buf_write_ccmx(
ccmx *p,
unsigned char **buf, /* Return allocated buffer */
-int *len /* Return length */
+size_t *len /* Return length */
) {
int rv;
cgats *ocg; /* CGATS structure */
@@ -353,7 +357,7 @@ char *inname /* Filename to read from */
static int buf_read_ccmx(
ccmx *p, /* This */
unsigned char *buf,
-int len
+size_t len
) {
int rv;
cgatsFile *fp;
@@ -685,6 +689,7 @@ int refrmode, /* Display refresh mode, -1 = unknown, 0 = n, 1 = yes */
int cbid, /* Display type calibration base index, 0 = unknown */
char *sel, /* UI selector characters - NULL for none */
char *refd, /* Reference spectrometer description (optional) */
+int oem, /* NZ if OEM source */
int npat, /* Number of samples in following arrays */
double (*refs)[3], /* Array of XYZ values from spectrometer */
double (*cols)[3] /* Array of XYZ values from colorimeter */
diff --git a/xicc/ccmx.h b/xicc/ccmx.h
index 94263e0..03d29d1 100644
--- a/xicc/ccmx.h
+++ b/xicc/ccmx.h
@@ -5,6 +5,9 @@
* Argyll Color Correction System
* Colorimeter Correction Matrix support.
*
+ */
+
+/*
* Author: Graeme W. Gill
* Date: 19/8/2010
*
@@ -19,6 +22,10 @@
*
*/
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
/*
* This object provides storage and application of a 3x3 XYZ
* corretion matrix suitable for corrected a particular
@@ -46,13 +53,13 @@ struct _ccmx {
/* write a CGATS .ccmx file to a memory buffer. */
/* return nz on error, with message in err[] */
- int (*buf_write_ccmx)(struct _ccmx *p, unsigned char **buf, int *len);
+ int (*buf_write_ccmx)(struct _ccmx *p, unsigned char **buf, size_t *len);
/* read from a CGATS .ccmx file */
int (*read_ccmx)(struct _ccmx *p, char *filename);
/* read from a CGATS .ccmx file from a memory buffer. */
- int (*buf_read_ccmx)(struct _ccmx *p, unsigned char *buf, int len);
+ int (*buf_read_ccmx)(struct _ccmx *p, unsigned char *buf, size_t len);
/* Correct an XYZ value */
void (*xform) (struct _ccmx *p,
@@ -83,6 +90,10 @@ struct _ccmx {
/* Create a new, uninitialised ccmx */
ccmx *new_ccmx(void);
+#ifdef __cplusplus
+ }
+#endif
+
#endif /* CCMX_H */
diff --git a/xicc/ccss.c b/xicc/ccss.c
index 75075c1..aae278e 100644
--- a/xicc/ccss.c
+++ b/xicc/ccss.c
@@ -2,7 +2,9 @@
/*
* Argyll Color Correction System
* Colorimeter Correction Matrix
- *
+ */
+
+/*
* Author: Graeme W. Gill
* Date: 9/8/2010
*
@@ -13,6 +15,7 @@
* see the License2.txt file for licencing details.
*/
+
/*
* TTBD:
*/
@@ -31,7 +34,7 @@
#include "icc.h"
#else
#include "numsup.h"
-#include "conv.h"
+#include "sa_conv.h"
#endif
#include "cgats.h"
#include "xspect.h"
@@ -201,7 +204,7 @@ char *outname /* Filename to write to */
static int buf_write_ccss(
ccss *p,
unsigned char **buf, /* Return allocated buffer */
-int *len /* Return length */
+size_t *len /* Return length */
) {
int rv;
cgats *ocg; /* CGATS structure */
@@ -231,7 +234,7 @@ int *len /* Return length */
}
/* Get the buffer the ccss has been written to */
- if (fp->get_buf(fp, buf, (size_t *)len)) {
+ if (fp->get_buf(fp, buf, len)) {
strcpy(p->err, "cgatsFileMem get_buf failed");
return 2;
}
@@ -434,7 +437,7 @@ char *inname /* Filename to read from */
static int buf_read_ccss(
ccss *p, /* This */
unsigned char *buf,
-int len
+size_t len
) {
int rv;
cgatsFile *fp;
diff --git a/xicc/ccss.h b/xicc/ccss.h
index 8258031..dbcfbc1 100644
--- a/xicc/ccss.h
+++ b/xicc/ccss.h
@@ -4,7 +4,9 @@
/*
* Argyll Color Correction System
* Colorimeter Calibration Spectral Set support.
- *
+ */
+
+/*
* Author: Graeme W. Gill
* Date: 18/8/2011
*
@@ -17,6 +19,10 @@
* Based on ccmx.h
*/
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
/*
* This object provides storage and application of emisive spectral
* samples that can be used to compute calibration for suitable
@@ -42,7 +48,7 @@ struct _ccss {
/* write a CGATS .ccss file to a memory buffer. */
/* return nz on error, with message in err[] */
- int (*buf_write_ccss)(struct _ccss *p, unsigned char **buf, int *len);
+ int (*buf_write_ccss)(struct _ccss *p, unsigned char **buf, size_t *len);
/* read from a CGATS .ccss file */
/* return nz on error, with message in err[] */
@@ -50,7 +56,7 @@ struct _ccss {
/* read from a CGATS .ccss file from a memory buffer. */
/* return nz on error, with message in err[] */
- int (*buf_read_ccss)(struct _ccss *p, unsigned char *buf, int len);
+ int (*buf_read_ccss)(struct _ccss *p, unsigned char *buf, size_t len);
/* Private: */
/* (All char * are owned by ccss) */
@@ -75,6 +81,10 @@ struct _ccss {
/* Create a new, uninitialised ccss */
ccss *new_ccss(void);
+#ifdef __cplusplus
+ }
+#endif
+
#endif /* CCSS_H */
diff --git a/xicc/ccttest.c b/xicc/ccttest.c
index 52bbdac..1b859b5 100644
--- a/xicc/ccttest.c
+++ b/xicc/ccttest.c
@@ -238,9 +238,18 @@ main(
#endif
if (in_name[0] != '\000') {
- if (read_xspect(&sp, in_name) != 0)
+ inst_meas_type mt;
+
+ if (read_xspect(&sp, &mt, in_name) != 0)
error ("Unable to read custom spectrum '%s'",in_name);
+ if (mt != inst_mrt_none
+ && mt != inst_mrt_emission
+ && mt != inst_mrt_ambient
+ && mt != inst_mrt_emission_flash
+ && mt != inst_mrt_ambient_flash)
+ error("Custom illuminant '%s' is wrong measurement type",in_name);
+
sprintf(buf, "File '%s'",in_name);
do_spec(buf, &sp);
diff --git a/xicc/extractttag.c b/xicc/extractttag.c
index 2c26bda..98c7835 100644
--- a/xicc/extractttag.c
+++ b/xicc/extractttag.c
@@ -32,7 +32,6 @@
#include "numlib.h"
#include "icc.h"
#include "xicc.h"
-#include "ui.h"
#define MXTGNMS 30
diff --git a/xicc/fakeCMY.c b/xicc/fakeCMY.c
index b27f2d1..0863cfb 100644
--- a/xicc/fakeCMY.c
+++ b/xicc/fakeCMY.c
@@ -31,7 +31,6 @@
#include "xicc.h"
#include "cgats.h"
#include "targen.h"
-#include "ui.h"
#define EXTRA_NEUTRAL 50 /* Crude way of increasing weighting */
@@ -257,14 +256,15 @@ main(
for (j = 0; j < 3; j++)
gc[j] = 0;
- for (;;) { /* For all grid points */
+ /* Add CMY grid surface points */
+ for (;;) { /* Generate all grid points */
/* Check if this position is on the CMY surface */
for (j = 0; j < 3; j++) {
if (gc[j] == 0 || gc[j] == (tres-1))
break;
}
- if (j < 3) { /* On the surface */
+ if (j < 3) { /* On the surface, so add a point */
/* Make sure there is room */
if (fxno >= fxlist_a) {
@@ -274,7 +274,7 @@ main(
}
for (j = 0; j < 3; j++)
fxlist[fxno].p[j] = gc[j]/(tres-1.0);
- fxlist[fxno].p[3] = 0.0;
+ fxlist[fxno].p[3] = 0.0; /* K = 0 */
fxno++;
}
@@ -289,7 +289,8 @@ main(
break; /* Done grid */
}
- for (i = 1; i < ((EXTRA_NEUTRAL * gres)-1); i++) { /* For all grey axis points */
+ /* Add neutral axis points */
+ for (i = 1; i < ((EXTRA_NEUTRAL * gres)-1); i++) {
/* Make sure there is room */
if (fxno >= fxlist_a) {
@@ -299,7 +300,7 @@ main(
}
for (j = 0; j < 3; j++)
fxlist[fxno].p[j] = i/((EXTRA_NEUTRAL * gres)-1.0);
- fxlist[fxno].p[3] = 0.0;
+ fxlist[fxno].p[3] = 0.0; /* K = 0 */
fxno++;
}
@@ -310,6 +311,9 @@ main(
//printf("~1 initial lookup %f %f %f -> %f %f %f\n", fxlist[i].p[0], fxlist[i].p[1], fxlist[i].p[2], fxlist[i].v[0], fxlist[i].v[1], fxlist[i].v[2]);
}
+ /* A CMYK black will be deeper than CMY0, so we need to figure out */
+ /* how to scale the initial values to fully occupy the CMYK gamut. */
+
/* Figure out the general scale at the black end */
{
double dval[4];
diff --git a/xicc/fbview.c b/xicc/fbview.c
index 07a9170..482ba5f 100644
--- a/xicc/fbview.c
+++ b/xicc/fbview.c
@@ -1,7 +1,10 @@
/*
+ * See xfbview !
+ *
* International Color Consortium Format Library (icclib)
* View the gamut clipping implemented by the bwd profile.
+ * abscol intent, asumes CMYK
*
* Author: Graeme W. Gill
* Date: 2000/12/8
@@ -58,7 +61,7 @@ double absdiff(double in1[3], double in2[3]) {
/* ---------------------------------------- */
void usage(void) {
- fprintf(stderr,"View bwd table clipping of an ICC file, , Version %s\n",ARGYLL_VERSION_STR);
+ fprintf(stderr,"View abscol bwd table clipping of an ICC file, , Version %s\n",ARGYLL_VERSION_STR);
fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
fprintf(stderr,"usage: fbtest [-v] infile\n");
fprintf(stderr," -v verbose\n");
diff --git a/xicc/iccgamut.c b/xicc/iccgamut.c
index 77f8fdd..ecbab7f 100644
--- a/xicc/iccgamut.c
+++ b/xicc/iccgamut.c
@@ -42,14 +42,13 @@
#include "gamut.h"
#include "counters.h"
#include "vrml.h"
-#include "ui.h"
static void diag_gamut(icxLuBase *p, double detail, int doaxes,
double tlimit, double klimit, char *outname);
void usage(char *diag) {
int i;
- fprintf(stderr,"Create Lab/Jab gamut plot Version %s\n",ARGYLL_VERSION_STR);
+ fprintf(stderr,"Create ICC profile Lab/Jab gamut & plot Version %s\n",ARGYLL_VERSION_STR);
fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
fprintf(stderr,"usage: iccgamut [options] profile\n");
if (diag != NULL)
@@ -89,7 +88,8 @@ void usage(char *diag) {
fprintf(stderr," g:glare Flare light %% of ambient (default %d)\n",XICC_DEFAULT_GLARE);
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," -x pcent Expand/compress gamut cylindrically by percent\n");
+ fprintf(stderr," -s Create special cube surface topology plot\n");
fprintf(stderr,"\n");
exit(1);
}
@@ -126,6 +126,7 @@ main(int argc, char *argv[]) {
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 */
+ double expand = 1.0; /* Expand gamut cylindrically */
icxLuBase *luo;
@@ -288,6 +289,18 @@ main(int argc, char *argv[]) {
usage("Parameter after flag -d seems out of range");
}
+ /* Expand gamut cylindrically */
+ else if (argv[fa][1] == 'x') {
+ double rr;
+ fa = nfa;
+ if (na == NULL) usage("No parameter after flag -x");
+ rr = atof(na)/100.0;
+
+ if (rr < 0.01 || rr > 100.0)
+ usage("-x ratio is out of range");
+ expand = rr;
+ }
+
/* Viewing conditions */
else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') {
fa = nfa;
@@ -489,6 +502,19 @@ main(int argc, char *argv[]) {
if ((gam = luo->get_gamut(luo, gamres)) == NULL)
error ("%d, %s",xicco->errc, xicco->err);
+ /* Expand gamut cylindrically */
+ if (expand != 1.0) {
+ gamut *xgam;
+
+ if ((xgam = new_gamut(1.0, 0, 0)) == NULL
+ || xgam->exp_cyl(xgam, gam, expand)) {
+ error ("Creating expanded gamut failed");
+ }
+
+ gam->del(gam);
+ gam = xgam;
+ }
+
if (gam->write_gam(gam, out_name))
error ("write gamut failed on '%s'",out_name);
diff --git a/xicc/mpplu.c b/xicc/mpplu.c
index 11ddb76..09fe18c 100644
--- a/xicc/mpplu.c
+++ b/xicc/mpplu.c
@@ -214,10 +214,20 @@ main(int argc, char *argv[]) {
spec = 1;
illum = icxIT_F10;
} else { /* Assume it's a filename */
+ inst_meas_type mt;
+
spec = 1;
illum = icxIT_custom;
- if (read_xspect(&cust_illum, na) != 0)
+ if (read_xspect(&cust_illum, &mt, na) != 0)
usage();
+
+ if (mt != inst_mrt_none
+ && mt != inst_mrt_emission
+ && mt != inst_mrt_ambient
+ && mt != inst_mrt_emission_flash
+ && mt != inst_mrt_ambient_flash) {
+ error("Custom illuminant '%s' is wrong measurement type",na);
+ }
}
}
diff --git a/xicc/revfix.c b/xicc/revfix.c
index 2add264..ee9662a 100644
--- a/xicc/revfix.c
+++ b/xicc/revfix.c
@@ -34,7 +34,6 @@
#include "aconfig.h"
#include "numlib.h"
#include "xicc.h"
-#include "ui.h"
#define USE_CAM_CLIP_OPT /* Clip in CAM Jab space rather than Lab */
#undef DEBUG /* Print each value changed */
diff --git a/xicc/specplot.c b/xicc/specplot.c
index 6f38f37..9e43856 100644
--- a/xicc/specplot.c
+++ b/xicc/specplot.c
@@ -38,6 +38,7 @@ static int do_spec(
char name[MAXGRAPHS][200],
xspect *sp,
int nsp, /* Number of sp */
+ inst_meas_type mt, /* Measurement type */
int dozero, /* Include zero in the range */
int douv, /* Do variation of added UV test */
double uvmin,
@@ -103,69 +104,95 @@ static int do_spec(
xsp_setUV(&tsp, &sp[k], uv);
}
- /* Compute XYZ of illuminant */
- if (icx_ill_sp2XYZ(xyz, icxOT_CIE_1931_2, NULL, icxIT_custom, 0, &tsp) != 0)
- warning("icx_sp_temp2XYZ returned error");
-
- icmXYZ2Yxy(Yxy, xyz);
- icmXYZ2Lab(&icmD50, Lab, xyz);
-
printf("Type = %s [%s]\n",name[k], color[k]);
- printf("XYZ = %f %f %f, x,y = %f %f\n", xyz[0], xyz[1], xyz[2], Yxy[1], Yxy[2]);
- printf("D50 L*a*b* = %f %f %f\n", Lab[0], Lab[1], Lab[2]);
-
-#ifndef NEVER
- /* Test density */
- {
- double dens[4];
-
- xsp_Tdensity(dens, &tsp);
-
- printf("CMYV density = %f %f %f %f\n", dens[0], dens[1], dens[2], dens[3]);
- }
-#endif
-
- /* Compute CCT */
- if ((cct = icx_XYZ2ill_ct(cct_xyz, icxIT_Ptemp, icxOT_CIE_1931_2, NULL, xyz, NULL, 0)) < 0)
- warning("Got bad cct\n");
-
- /* Compute VCT */
- if ((vct = icx_XYZ2ill_ct(vct_xyz, icxIT_Ptemp, icxOT_CIE_1931_2, NULL, xyz, NULL, 1)) < 0)
- warning("Got bad vct\n");
- printf("CCT = %f, VCT = %f\n",cct, vct);
-
- /* Compute CDT */
- if ((cct = icx_XYZ2ill_ct(cct_xyz, icxIT_Dtemp, icxOT_CIE_1931_2, NULL, xyz, NULL, 0)) < 0)
- warning("Got bad cct\n");
+ if (mt == inst_mrt_none
+ || mt == inst_mrt_emission
+ || mt == inst_mrt_ambient
+ || mt == inst_mrt_emission_flash
+ || mt == inst_mrt_ambient_flash) {
+
+ /* Compute XYZ of illuminant */
+ if (icx_ill_sp2XYZ(xyz, icxOT_CIE_1931_2, NULL, icxIT_custom, 0, &tsp) != 0)
+ warning("icx_ill_sp2XYZ returned error");
+
+ icmXYZ2Yxy(Yxy, xyz);
+ icmXYZ2Lab(&icmD50, Lab, xyz);
- /* Compute VDT */
- if ((vct = icx_XYZ2ill_ct(vct_xyz, icxIT_Dtemp, icxOT_CIE_1931_2, NULL, xyz, NULL, 1)) < 0)
- warning("Got bad vct\n");
+ printf("XYZ = %f %f %f, x,y = %f %f\n", xyz[0], xyz[1], xyz[2], Yxy[1], Yxy[2]);
+ printf("D50 L*a*b* = %f %f %f\n", Lab[0], Lab[1], Lab[2]);
+
+ /* Compute CCT */
+ if ((cct = icx_XYZ2ill_ct(cct_xyz, icxIT_Ptemp, icxOT_CIE_1931_2, NULL, xyz, NULL, 0)) < 0)
+ warning("Got bad cct\n");
+
+ /* Compute VCT */
+ if ((vct = icx_XYZ2ill_ct(vct_xyz, icxIT_Ptemp, icxOT_CIE_1931_2, NULL, xyz, NULL, 1)) < 0)
+ warning("Got bad vct\n");
+
+ printf("CCT = %f, VCT = %f\n",cct, vct);
+
+ /* Compute CDT */
+ if ((cct = icx_XYZ2ill_ct(cct_xyz, icxIT_Dtemp, icxOT_CIE_1931_2, NULL, xyz, NULL, 0)) < 0)
+ warning("Got bad cct\n");
+
+ /* Compute VDT */
+ if ((vct = icx_XYZ2ill_ct(vct_xyz, icxIT_Dtemp, icxOT_CIE_1931_2, NULL, xyz, NULL, 1)) < 0)
+ warning("Got bad vct\n");
+
+ printf("CDT = %f, VDT = %f\n",cct, vct);
+
+ {
+ int invalid = 0;
+ double RR[14];
+ double cri;
+ cri = icx_CIE1995_CRI(&invalid, RR, &tsp);
+ printf("CRI = %.1f [ R9 = %.1f ]%s\n",cri,RR[9-1],invalid ? " (Invalid)" : "");
+ }
+ {
+ int invalid = 0;
+ double tlci;
+ tlci = icx_EBU2012_TLCI(&invalid, &tsp);
+ printf("TLCI = %.1f%s\n",tlci,invalid ? " (Invalid)" : "");
+ }
+
+ /* Use modern color difference - gives a better visual match */
+ icmAry2XYZ(wp, vct_xyz);
+ icmXYZ2Lab(&wp, cct_lab, cct_xyz);
+ icmXYZ2Lab(&wp, vct_lab, vct_xyz);
+ de = icmCIE2K(cct_lab, vct_lab);
+ printf("CIEDE2000 Delta E = %f\n",de);
+
+ } else if (mt == inst_mrt_none
+ || mt == inst_mrt_reflective
+ || mt == inst_mrt_transmissive) {
+
+ printf("CIE values under D50 illuminant:\n");
- printf("CDT = %f, VDT = %f\n",cct, vct);
+ if (icx_sp2XYZ(xyz, icxOT_CIE_1931_2, NULL, icxIT_D50, 0, NULL, &tsp) != 0)
+ warning("icx_sp2XYZ returned error");
+
+ icmXYZ2Yxy(Yxy, xyz);
+ icmXYZ2Lab(&icmD50, Lab, xyz);
- {
- int invalid = 0;
- double RR[14];
- double cri;
- cri = icx_CIE1995_CRI(&invalid, RR, &tsp);
- printf("CRI = %.1f [ R9 = %.1f ]%s\n",cri,RR[9-1],invalid ? " (Invalid)" : "");
- }
- {
- int invalid = 0;
- double tlci;
- tlci = icx_EBU2012_TLCI(&invalid, &tsp);
- printf("TLCI = %.1f%s\n",tlci,invalid ? " (Invalid)" : "");
+ printf("XYZ = %f %f %f, x,y = %f %f\n", xyz[0], xyz[1], xyz[2], Yxy[1], Yxy[2]);
+ printf("D50 L*a*b* = %f %f %f\n", Lab[0], Lab[1], Lab[2]);
+
+#ifndef NEVER
+ /* Test density */
+ {
+ double dens[4];
+
+ xsp_Tdensity(dens, &tsp);
+
+ printf("CMYV density = %f %f %f %f\n", dens[0], dens[1], dens[2], dens[3]);
+ }
+#endif
+
+ } else {
+ printf("Unhandled measurement type '%s'\n",meas_type2str(mt));
}
- /* Use modern color difference - gives a better visual match */
- icmAry2XYZ(wp, vct_xyz);
- icmXYZ2Lab(&wp, cct_lab, cct_xyz);
- icmXYZ2Lab(&wp, vct_lab, vct_xyz);
- de = icmCIE2K(cct_lab, vct_lab);
- printf("CIEDE2000 Delta E = %f\n",de);
-
/* Plot spectrum out */
for (i = 0; i < XRES; i++) {
double ww;
@@ -285,13 +312,14 @@ main(
/* Until we run out */
for (;;) {
int i, nret, nreq;
+ inst_meas_type mt;
/* If we've got to the limit of each plot, */
- /* or at least one and there are no more files, */
- /* or at least one and we're not combining files and at start of a new file */
+ /* or at least one and we're not combining files and at start of a new file, */
+ /* or at least one and there are no more files */
if (nsp >= MAXGRAPHS || (nsp > 0 && ((!comb && soff == 0) || fa >= argc))) {
/* Plot what we've got */
- do_spec(buf, sp, nsp, zero, douv, uvmin, uvmax);
+ do_spec(buf, sp, nsp, mt, zero, douv, uvmin, uvmax);
nsp = 0;
}
@@ -300,9 +328,11 @@ main(
/* Read as many spectra from the file as possible */
nreq = MAXGRAPHS - nsp;
- if (read_nxspect(&sp[nsp], argv[fa], &nret, soff, nreq, 0) != 0) {
+
+ if (read_nxspect(&sp[nsp], &mt, argv[fa], &nret, soff, nreq, 0) != 0) {
error ("Unable to read custom spectrum, CMF or CCSS '%s'",argv[fa]);
}
+
for (i = 0; i < nret; i++) {
xspect_denorm(&sp[nsp + i]);
sprintf(buf[nsp + i],"File '%s' spect %d",argv[fa], soff + i);
@@ -353,7 +383,7 @@ main(
error ("standardIlluminant returned error for %d (%s)",ilType,inm);
strcpy(buf[0],inm);
- do_spec(buf, sp, 1, zero, douv, uvmin, uvmax);
+ do_spec(buf, sp, 1, inst_mrt_ambient, zero, douv, uvmin, uvmax);
}
/* For each material and illuminant */
@@ -375,7 +405,7 @@ main(
sprintf(buf[0], "%s at %f", k == 0 ? "Daylight" : "Black body", temp);
- do_spec(buf, sp, 1, zero, douv, uvmin, uvmax);
+ do_spec(buf, sp, 1, inst_mrt_ambient, zero, douv, uvmin, uvmax);
}
}
diff --git a/xicc/specsubsamp.c b/xicc/specsubsamp.c
index 4163ee2..f4ac1e4 100644
--- a/xicc/specsubsamp.c
+++ b/xicc/specsubsamp.c
@@ -91,9 +91,18 @@ main(
} else if (strcmp(na, "F10") == 0) {
illum = icxIT_F10;
} else { /* Assume it's a filename */
+ inst_meas_type mt;
+
illum = icxIT_custom;
- if (read_xspect(&cust_illum, na) != 0)
+ if (read_xspect(&cust_illum, &mt, na) != 0)
usage();
+
+ if (mt != inst_mrt_none
+ && mt != inst_mrt_emission
+ && mt != inst_mrt_ambient
+ && mt != inst_mrt_emission_flash
+ && mt != inst_mrt_ambient_flash)
+ error("Custom illuminant '%s' is wrong measurement type",na);
}
}
diff --git a/xicc/spectest.c b/xicc/spectest.c
index c340e98..bed19e0 100644
--- a/xicc/spectest.c
+++ b/xicc/spectest.c
@@ -33,8 +33,8 @@
#include "numlib.h"
#ifdef DOPLOT
#include "plot.h"
-#endif
#include "ui.h"
+#endif
/* Spectrolino filter "D65" illuminant */
diff --git a/xicc/spectest2.c b/xicc/spectest2.c
index 3fd574f..cec6359 100644
--- a/xicc/spectest2.c
+++ b/xicc/spectest2.c
@@ -31,8 +31,8 @@
#include "numlib.h"
#ifdef DOPLOT
#include "plot.h"
-#endif
#include "ui.h"
+#endif
/* Normal 'A' spectra, then UV filtered version */
diff --git a/xicc/tiffgamut.c b/xicc/tiffgamut.c
index e373fbd..38b6b6c 100644
--- a/xicc/tiffgamut.c
+++ b/xicc/tiffgamut.c
@@ -46,7 +46,6 @@
#include "xicc.h"
#include "sort.h"
#include "vrml.h"
-#include "ui.h"
#undef NOCAMGAM_CLIP /* No clip to CAM gamut before CAM lookup */
#undef DEBUG /* Dump filter cell contents */
@@ -99,6 +98,7 @@ void usage(void) {
fprintf(stderr," g:glare Flare light %% of ambient (default %d)\n",XICC_DEFAULT_GLARE);
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," -x pcent Expand/compress gamut cylindrically by percent\n");
fprintf(stderr," -O outputfile Override the default output filename.\n");
exit(1);
}
@@ -355,6 +355,7 @@ main(int argc, char *argv[]) {
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 */
+ double expand = 1.0; /* Expand gamut cylindrically by ratio */
icxLuBase *luo = NULL; /* Generic lookup object */
icColorSpaceSignature ins = icSigLabData, outs; /* Type of input and output spaces */
int inn, outn; /* Number of components */
@@ -581,6 +582,18 @@ main(int argc, char *argv[]) {
filter = 1;
}
+ /* Expand gamut cylindrically */
+ else if (argv[fa][1] == 'x') {
+ double rr;
+ fa = nfa;
+ if (na == NULL) usage();
+ rr = atof(na)/100.0;
+
+ if (rr < 0.01 || rr > 100.0)
+ usage();
+ expand = rr;
+ }
+
/* Output file name */
else if (argv[fa][1] == 'O') {
fa = nfa;
@@ -760,7 +773,7 @@ main(int argc, char *argv[]) {
error("new_icxcam failed");
cam->set_view(cam, vc.Ev, vc.Wxyz, vc.La, vc.Yb, vc.Lv, vc.Yf, vc.Yg, vc.Gxyz,
- XICC_USE_HK);
+ XICC_USE_HK, vc.hkscale);
}
/* Establish the PCS range if we are filtering */
@@ -1134,6 +1147,19 @@ main(int argc, char *argv[]) {
if (verb)
printf("Output Gamut file '%s'\n",out_name);
+ /* Expand gamut cylindrically */
+ if (expand != 1.0) {
+ gamut *xgam;
+
+ if ((xgam = new_gamut(1.0, 0, 0)) == NULL
+ || xgam->exp_cyl(xgam, gam, expand)) {
+ error ("Creating expanded gamut failed");
+ }
+
+ gam->del(gam);
+ gam = xgam;
+ }
+
/* Create the VRML/X3D file */
if (gam->write_gam(gam,out_name))
error ("write gamut failed on '%s'",out_name);
diff --git a/xicc/tiffgmts.c b/xicc/tiffgmts.c
index 446ea5c..592c2dd 100644
--- a/xicc/tiffgmts.c
+++ b/xicc/tiffgmts.c
@@ -42,7 +42,6 @@
#include "xicc.h"
#include "vrml.h"
#include "sort.h"
-#include "ui.h"
#define DE_SPACE 3 /* Delta E of spacing for output points */
#undef DEBUG_PLOT
diff --git a/xicc/xcal.c b/xicc/xcal.c
index 4400745..4a5b37e 100644
--- a/xicc/xcal.c
+++ b/xicc/xcal.c
@@ -1,8 +1,11 @@
/*
- * Argyll Color Correction System
* Calibration curve class.
+ */
+
+/*
*
+ * Argyll Color Correction System
* Author: Graeme W. Gill
* Date: 30/10/2005
*
@@ -26,10 +29,20 @@
#include <sys/types.h>
#include <time.h>
#include <string.h>
+#ifndef SALONEINSTLIB
#include "copyright.h"
#include "aconfig.h"
#include "numlib.h"
#include "xicc.h"
+#else
+#include "sa_config.h"
+#include "numsup.h"
+#include "rspl1.h"
+#include "cgats.h"
+#include "sa_conv.h"
+#include "xcolorants.h"
+#include "xcal.h"
+#endif /* SALONEINSTLIB */
#ifdef NT /* You'd think there might be some standards.... */
# ifndef __BORLANDC__
@@ -196,11 +209,14 @@ static int xcal_read_cgats(xcal *p, cgats *tcg, int table, char *filename) {
return 0;
}
+#ifndef SALONEINSTLIB
+
/* 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;
+ icmText *tx;
int res, i, j;
/* See if there is a vcgt tag */
@@ -229,8 +245,8 @@ int xcal_read_icc(xcal *p, icc *c) {
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);
+ if ((tx = (icmText *)c->read_tag(c, icSigCopyrightTag)) != NULL) {
+ p->xpi.copyright = strdup(tx->data);
}
/* Decide the lut resolution */
@@ -280,6 +296,8 @@ int xcal_read_icc(xcal *p, icc *c) {
return 0;
}
+#endif /* !SALONEINSTLIB */
+
/* Read a calibration file */
/* Return nz if this fails */
static int xcal_read(xcal *p, char *filename) {
@@ -342,7 +360,11 @@ static int xcal_write_cgats(xcal *p, cgats *tcg) {
else if (p->devclass == icSigDisplayClass)
tcg->add_kword(tcg, table, "DEVICE_CLASS","DISPLAY", NULL);
else {
+#ifdef SALONEINSTLIB
+ sprintf(p->err,"Unknown device class 0x%x",p->devclass);
+#else
sprintf(p->err,"Unknown device class '%s'",icm2str(icmProfileClassSignature,p->devclass));
+#endif
return p->errc = 1;
}
@@ -382,7 +404,7 @@ static int xcal_write_cgats(xcal *p, cgats *tcg) {
return p->errc = 2;
}
- calres = p->cals[0]->g.res[0];
+ calres = p->cals[0]->get_res(p->cals[0])[0];
for (i = 0; i < calres; i++) {
double vv = i/(calres-1.0);
@@ -448,6 +470,8 @@ static void xcal_interp(xcal *p, double *out, double *in) {
}
}
+#ifndef SALONEINSTLIB
+
#define MAX_INVSOLN 10 /* Rspl maximum reverse solutions */
/* Translate a value backwards through the curves. */
@@ -497,6 +521,8 @@ static int xcal_inv_interp(xcal *p, double *out, double *in) {
return rv;
}
+#endif /* !SALONEINSTLIB */
+
/* Translate a value through one of the curves */
static double xcal_interp_ch(xcal *p, int ch, double in) {
co tp;
@@ -509,6 +535,8 @@ static double xcal_interp_ch(xcal *p, int ch, double in) {
return tp.v[0];
}
+#ifndef SALONEINSTLIB
+
/* Translate a value backwards through one of the curves */
/* Return -1.0 if the inversion fails */
static double xcal_inv_interp_ch(xcal *p, int ch, double in) {
@@ -522,7 +550,7 @@ static double xcal_inv_interp_ch(xcal *p, int ch, double in) {
pp[0].v[0] = in;
- nsoln = p->cals[ch]->rev_interp (
+ nsoln = p->cals[ch]->rev_interp(
p->cals[ch], /* this */
RSPL_NEARCLIP, /* Clip to nearest (faster than vector) */
MAX_INVSOLN, /* Maximum number of solutions allowed for */
@@ -553,6 +581,8 @@ static double xcal_inv_interp_ch(xcal *p, int ch, double in) {
return pp[k].p[0];
}
+#endif /* !SALONEINSTLIB */
+
/* Delete an xcal */
static void xcal_del(xcal *p) {
int j;
@@ -583,14 +613,20 @@ xcal *new_xcal(void) {
/* Init method pointers */
p->del = xcal_del;
p->read_cgats = xcal_read_cgats;
+#ifndef SALONEINSTLIB
p->read_icc = xcal_read_icc;
+#endif /* !SALONEINSTLIB */
p->read = xcal_read;
p->write_cgats = xcal_write_cgats;
p->write = xcal_write;
p->interp = xcal_interp;
+#ifndef SALONEINSTLIB
p->inv_interp = xcal_inv_interp;
+#endif /* !SALONEINSTLIB */
p->interp_ch = xcal_interp_ch;
+#ifndef SALONEINSTLIB
p->inv_interp_ch = xcal_inv_interp_ch;
+#endif /* !SALONEINSTLIB */
return p;
}
diff --git a/xicc/xcal.h b/xicc/xcal.h
index f7c3fa9..c70b884 100644
--- a/xicc/xcal.h
+++ b/xicc/xcal.h
@@ -3,8 +3,11 @@
#define XCAL_H
/*
- * Argyll Color Correction System
* Calibration curve class.
+ */
+
+/*
+ * Argyll Color Correction System
*
* Author: Graeme W. Gill
* Date: 30/10/2005
@@ -20,6 +23,18 @@
* when computing ink limits.
*/
+#ifdef SALONEINSTLIB
+
+/* Sub-set of profile Creation Suplimental Information structure */
+struct _profxinf {
+ char *deviceMfgDesc; /* Manufacturer text description, NULL for none */
+ char *modelDesc; /* Model text description, NULL for none */
+ char *profDesc; /* Text profile description, NULL for default */
+ char *copyright; /* Copyrigh text, NULL for default */
+}; typedef struct _profxinf profxinf;
+
+#endif /* SALONEINSTLIB */
+
struct _xcal {
/* Public: */
@@ -29,9 +44,11 @@ struct _xcal {
/* Return nz if this fails (filename is for error messages) */
int (*read_cgats) (struct _xcal *p, cgats *cg, int table, char *filename);
+#ifndef SALONEINSTLIB
/* Read a calibration file from an ICC vcgt tag */
/* Return nz if this fails */
int (*read_icc) (struct _xcal *p, icc *c);
+#endif
/* Read a calibration file */
/* Return nz if this fails */
@@ -48,16 +65,20 @@ struct _xcal {
/* Translate values through the curves. */
void (*interp) (struct _xcal *p, double *out, double *in);
+#ifndef SALONEINSTLIB
/* Translate a value backwards through the curves. */
/* Return nz if the inversion fails */
int (*inv_interp) (struct _xcal *p, double *out, double *in);
+#endif
/* Translate a value through one of the curves */
double (*interp_ch) (struct _xcal *p, int ch, double in);
+#ifndef SALONEINSTLIB
/* Translate a value backwards through one of the curves */
/* Return -1.0 if the inversion fails */
double (*inv_interp_ch) (struct _xcal *p, int ch, double in);
+#endif
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 */
diff --git a/xicc/xcam.c b/xicc/xcam.c
index 6117cd1..0b46a24 100644
--- a/xicc/xcam.c
+++ b/xicc/xcam.c
@@ -24,7 +24,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 Yg, double Gxyz[3],
- int hk);
+ int hk, double hkscale);
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]);
static void settrace(struct _icxcam *s, int tracev);
@@ -128,7 +128,8 @@ double Lv, /* Luminance of white in the Viewing/Scene/Image field (cd/m^2) */
double Yf, /* Flare as a fraction of the reference white (Y range 0.0 .. 1.0) */
double Yg, /* Glare as a fraction of the adapting/surround (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 */
+int hk, /* Flag, NZ to use Helmholtz-Kohlraush effect */
+double hkscale /* HK effect scaling factor */
) {
s->Wxyz[0] = Wxyz[0];
s->Wxyz[1] = Wxyz[1];
@@ -141,7 +142,7 @@ int hk /* Flag, NZ to use Helmholtz-Kohlraush effect */
}
case cam_CIECAM02: {
cam02 *pp = (cam02 *)s->p;
- return pp->set_view(pp, Ev, Wxyz, La, Yb, Lv, Yf, Yg, Gxyz, hk);
+ return pp->set_view(pp, Ev, Wxyz, La, Yb, Lv, Yf, Yg, Gxyz, hk, hkscale);
}
default:
break;
diff --git a/xicc/xcam.h b/xicc/xcam.h
index 021c621..de4f5b4 100644
--- a/xicc/xcam.h
+++ b/xicc/xcam.h
@@ -51,7 +51,8 @@ struct _icxcam {
double Yf, /* Flare as a fraction of the reference white (range 0.0 .. 1.0) */
double Yg, /* Glare as a fraction of the adapting/surround (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 */
+ int hk, /* Flag, NZ to use Helmholtz-Kohlraush effect */
+ double hkscale /* HK effect scaling factor */
);
/* Conversions */
diff --git a/xicc/xcolorants.c b/xicc/xcolorants.c
index 84a7720..edc7e52 100644
--- a/xicc/xcolorants.c
+++ b/xicc/xcolorants.c
@@ -2,7 +2,9 @@
/*
* International Color Consortium color transform expanded support
* Known colorants support.
- *
+ */
+
+/*
* Author: Graeme W. Gill
* Date: 24/2/2002
* Version: 1.00
@@ -24,7 +26,12 @@
#include <stdlib.h>
#include <string.h>
#include <math.h>
+#ifndef SALONEINSTLIB
#include "icc.h"
+#else
+#include "numsup.h"
+#include "sa_conv.h"
+#endif
#include "xcolorants.h"
#include "sort.h"
@@ -610,7 +617,6 @@ inkmask mask /* Colorant combination mask */
}
-
/* - - - - - - - - - - - - - - - - - */
/* Approximate device colorant model */
diff --git a/xicc/xcolorants.h b/xicc/xcolorants.h
index 460c4b0..e328ad2 100644
--- a/xicc/xcolorants.h
+++ b/xicc/xcolorants.h
@@ -3,7 +3,9 @@
/*
* International Color Consortium color transform expanded support
* Known colorant definitions.
- *
+ */
+
+/*
* Author: Graeme W. Gill
* Date: 24/2/2002
* Version: 1.00
@@ -45,7 +47,9 @@
Another change would be to make xcolorants an object,
with dynamic colorant and colorant combination values.
These would be initialised to defaults, but could then
- be added to at run time.
+ be added to at run time, to support import/export to
+ other color data formats, and coping with arbitrary raster
+ files etc.
Handling the "colorant" of non-device type color channels
is also a challenge (ie., Lab, Hsv etc.)
@@ -250,6 +254,8 @@ inkmask icx_enum_colorant_comb(int no, char **desc);
/* return NZ if it does. */
int icx_colorant_comb_match_icc(inkmask mask, icColorSpaceSignature sig);
+#ifndef SALONEINSTLIB
+
/* Given an ICC colorspace signature, return the appropriate */
/* colorant combination mask. Return 0 if ambiguous signature. */
inkmask icx_icc_to_colorant_comb(icColorSpaceSignature sig, icProfileClassSignature deviceClass);
@@ -265,6 +271,8 @@ inkmask icx_icc_cv_to_colorant_comb(icColorSpaceSignature sig, icProfileClassSig
/* return 0 if there is no match */
icColorSpaceSignature icx_colorant_comb_to_icc(inkmask mask);
+#endif /* !SALONEINSTLIB */
+
/* --------------------------------------------------------- */
/* An aproximate device colorant model object lookup object: */
diff --git a/xicc/xdevlin.c b/xicc/xdevlin.c
index a5f7bd6..05b4317 100644
--- a/xicc/xdevlin.c
+++ b/xicc/xdevlin.c
@@ -88,7 +88,7 @@ double *in /* di output */
pp[0].v[0] = in[i];
cdir = p->clipc[i] - in[i]; /* Clip towards output range */
- nsoln = p->curves[i]->rev_interp (
+ nsoln = p->curves[i]->rev_interp(
p->curves[i], /* this */
0, /* No flags */
MAX_INVSOLN, /* Maximum number of solutions allowed for */
diff --git a/xicc/xfbview.c b/xicc/xfbview.c
index affdb53..3d7c665 100644
--- a/xicc/xfbview.c
+++ b/xicc/xfbview.c
@@ -12,6 +12,9 @@
/* TTBD:
*
+ * Should reject device link profiles ?
+ *
+ *
*/
#include <stdio.h>
@@ -26,9 +29,13 @@
#include "icc.h"
#include "xicc.h"
#include "vrml.h"
-#include "ui.h"
-#define RW 0.5 /* Device Delta */
+#define RW 0.5 /* PCS Delta */
+
+//#define FLAGS ICX_CLIP_NEAREST | ICX_CAM_CLIP | ICX_FAST_SETUP
+//#define FLAGS ICX_CLIP_NEAREST | ICX_CAM_CLIP
+//#define FLAGS ICX_CLIP_NEAREST | ICX_FAST_SETUP
+#define FLAGS ICX_CLIP_NEAREST
/* - - - - - - - - - - - - - */
@@ -63,13 +70,13 @@ void usage(void) {
fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
fprintf(stderr,"usage: fbtest [options] infile\n");
fprintf(stderr," -v verbose\n");
- fprintf(stderr," -f Show PCS target -> reference clipped PCS vectors\n");
- fprintf(stderr," -d Show PCS target -> average of device ref clippped PCS\n");
- fprintf(stderr," -b Show PCS target -> B2A lookup clipped PCS\n");
- fprintf(stderr," -e Show reference cliped PCS -> B2A lookup clipped PCS\n");
+ fprintf(stderr," -f Add PCS -inv(a2b)- device -a2b- PCS Vectors\n");
+ fprintf(stderr," -d Add ave(PCS + 4 x a*b* variations -inv(a2b)- device) -a2b- PCS\n");
+ fprintf(stderr," -b Add PCS -b2a- device -a2b- PCS Vectors\n");
+ fprintf(stderr," -e Add dif between inv(a2b) and b2a cliped vectors\n");
fprintf(stderr," -r res Resolution of test grid [Def 33]\n");
- fprintf(stderr," -g Do full grid, not just L = 0\n");
- fprintf(stderr," -c Do all values, not just clipped ones\n");
+ fprintf(stderr," -g Do full grid, not just cube\n");
+ fprintf(stderr," -a Do all values, not just clipped ones\n");
fprintf(stderr," -l tlimit set total ink limit, 0 - 400%% (estimate by default)\n");
fprintf(stderr," -L klimit set black ink limit, 0 - 100%% (estimate by default)\n");
exit(1);
@@ -90,7 +97,7 @@ main(
int dodelta = 0; /* Show device interp delta variation */
int dob2a = 0; /* Show B2A lookups */
int doeee = 0; /* Show clipped ref PCS to B2A clipped PCS */
- int dilzero = 1; /* Do just L = 0 plane */
+ int dogrid = 0; /* Do full grid, not just cube */
int doclip = 1; /* Do just clipped values */
char in_name[100];
char *xl, out_name[100];
@@ -98,6 +105,8 @@ main(
icc *rd_icco;
int rv = 0;
icColorSpaceSignature ins, outs; /* Type of input and output spaces */
+ int inn; /* Number of device values */
+ double abscale = 128.0;
error_program = argv[0];
@@ -122,31 +131,31 @@ main(
}
/* Verbosity */
- if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
+ if (argv[fa][1] == 'v') {
verb = 1;
}
/* Show reference */
- else if (argv[fa][1] == 'f' || argv[fa][1] == 'F') {
+ else if (argv[fa][1] == 'f') {
doref = 1;
}
/* Show device delta variation */
- else if (argv[fa][1] == 'd' || argv[fa][1] == 'D') {
+ else if (argv[fa][1] == 'd') {
dodelta = 1;
}
/* Show B2A table lookup */
- else if (argv[fa][1] == 'b' || argv[fa][1] == 'B') {
+ else if (argv[fa][1] == 'b') {
dob2a = 1;
}
/* Show reference clipped PCS to clipped B2A PCS */
- else if (argv[fa][1] == 'e' || argv[fa][1] == 'E') {
+ else if (argv[fa][1] == 'e') {
doeee = 1;
}
- /* Do the full grid, not just L = 0 */
- else if (argv[fa][1] == 'g' || argv[fa][1] == 'G') {
- dilzero = 0;
+ /* Do full grid, not just cube */
+ else if (argv[fa][1] == 'g') {
+ dogrid = 1;
}
/* Do all values, not just clipped ones */
- else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') {
+ else if (argv[fa][1] == 'a') {
doclip = 0;
}
/* Ink limit */
@@ -162,7 +171,7 @@ main(
}
/* Resolution */
- else if (argv[fa][1] == 'r' || argv[fa][1] == 'R') {
+ else if (argv[fa][1] == 'r') {
fa = nfa;
if (na == NULL) usage();
tres = atoi(na);
@@ -241,15 +250,16 @@ main(
ink.c.Kshap = 1.0; /* Linear transition */
/* Get a Device to PCS conversion object */
- if ((luo = xicco->get_luobj(xicco, ICX_CLIP_NEAREST, icmFwd, icAbsoluteColorimetric, icSigLabData, icmLuOrdNorm, NULL, &ink)) == NULL) {
- if ((luo = xicco->get_luobj(xicco, ICX_CLIP_NEAREST, icmFwd, icmDefaultIntent, icSigLabData, icmLuOrdNorm, NULL, &ink)) == NULL)
+ if ((luo = xicco->get_luobj(xicco, FLAGS, icmFwd, icAbsoluteColorimetric, icSigLabData, icmLuOrdNorm, NULL, &ink)) == NULL) {
+ if ((luo = xicco->get_luobj(xicco, FLAGS, icmFwd, icmDefaultIntent, icSigLabData, icmLuOrdNorm, NULL, &ink)) == NULL)
error ("%d, %s",rd_icco->errc, rd_icco->err);
}
/* Get details of conversion */
- luo->spaces(luo, &ins, NULL, &outs, NULL, NULL, NULL, NULL, NULL);
+ luo->spaces(luo, &ins, &inn, &outs, NULL, NULL, NULL, NULL, NULL);
- if (ins != icSigCmykData) {
- error("Expecting CMYK device");
+ if (ins != icSigCmykData
+ && ins != icSigRgbData) {
+ error("Expecting RGB or CMYK device");
}
if ((wrl = new_vrml(out_name, doaxes, vrml_lab)) == NULL) {
@@ -258,17 +268,20 @@ main(
}
/* ---------------------------------------------- */
- /* The PCS target -> Reference clipped vectors */
+ /* Clipping vectors of inverse a2b */
+ /* PCS -inv(a2b)- device -a2b- PCS Vectors */
+ /* PCS -> device -> PCS */
if (doref) {
double rgb[3];
if (verb)
- printf("Doing PCS target to reference clipped PCS Vectors\n");
+ printf("Adding PCS -inv(a2b)- device -a2b- PCS Vectors \n");
wrl->start_line_set(wrl, 0);
i = 0;
+ /* For range of PCS values */
for (coa[0] = 0; coa[0] < tres; coa[0]++) {
for (coa[1] = 0; coa[1] < tres; coa[1]++) {
for (coa[2] = 0; coa[2] < tres; coa[2]++) {
@@ -276,14 +289,21 @@ main(
double temp[4];
int rv1, rv2;
+ if (!dogrid
+ && coa[0] != 0 && coa[0] != (tres-1)
+ && coa[1] != 0 && coa[1] != (tres-1)
+ && coa[2] != 0 && coa[2] != (tres-1)) {
+ continue;
+ }
+
temp[0] = coa[0]/(tres-1.0);
temp[1] = coa[1]/(tres-1.0);
temp[2] = coa[2]/(tres-1.0);
/* PCS values */
in[0] = temp[0] * 100.0;
- in[1] = 200.0 * temp[1] -100.0;
- in[2] = 200.0 * temp[2] -100.0;
+ in[1] = abscale * 2.0 * (temp[1] - 0.5);
+ in[2] = abscale * 2.0 * (temp[2] - 0.5);
/* Do reference lookup */
@@ -307,8 +327,6 @@ main(
i++;
}
}
- if (dilzero)
- break;
}
if (verb)
@@ -328,18 +346,19 @@ main(
}
/* ---------------------------------------------- */
- /* The Device Delta lines */
- /* The PCS target -> clipped from average of surrounding device values, vectors */
+ /* Clipping vectors of average around PCS of inverse a2b */
+ /* Average of(PCS + 4 x a*b* variations -inv(a2b)- device) -a2b- PCS */
if (dodelta) {
double rgb[3];
if (verb)
- printf("Doing target PCS to average of 4 surrounding device to PCS Vectors\n");
+ printf("Adding ave(PCS + 4 x a*b* variations -inv(a2b)- device) -a2b- PCS\n");
wrl->start_line_set(wrl, 0);
i = 0;
+ /* For Lab values */
for (coa[0] = 0; coa[0] < tres; coa[0]++) {
for (coa[1] = 0; coa[1] < tres; coa[1]++) {
for (coa[2] = 0; coa[2] < tres; coa[2]++) {
@@ -349,13 +368,22 @@ main(
double dev0[4], dev1[4], dev2[4], dev3[4];
int rv1, rv2;
+ if (!dogrid
+ && coa[0] != 0
+ && coa[0] != (tres-1)
+ && coa[1] != 0
+ && coa[1] != (tres-1)
+ && coa[2] != 0
+ && coa[2] != (tres-1))
+ continue;
+
temp[0] = coa[0]/(tres-1.0);
temp[1] = coa[1]/(tres-1.0);
temp[2] = coa[2]/(tres-1.0);
in[0] = temp[0] * 100.0;
- in[1] = 200.0 * temp[1] -100.0;
- in[2] = 200.0 * temp[2] -100.0;
+ in[1] = abscale * 2.0 * (temp[1] - 0.5);
+ in[2] = abscale * 2.0 * (temp[2] - 0.5);
/* Do reference lookup */
@@ -366,7 +394,7 @@ main(
if (doclip && rv2 != 1) /* Not clip */
continue;
- /* Device -> PCS check value */
+ /* Device -> PCS check value - not used */
if ((rv1 = luo->lookup(luo, check, dev)) > 1)
error ("%d, %s",rd_icco->errc,rd_icco->err);
@@ -377,8 +405,8 @@ main(
temp[2] = (coa[2]-RW)/(tres-1.0);
in4[0] = temp[0] * 100.0;
- in4[1] = 200.0 * temp[1] -100.0;
- in4[2] = 200.0 * temp[2] -100.0;
+ in4[1] = abscale * 2.0 * (temp[1] - 0.5);
+ in4[2] = abscale * 2.0 * (temp[2] - 0.5);
/* PCS -> Device */
if ((rv2 = luo->inv_lookup(luo, dev0, in4)) > 1)
@@ -394,8 +422,8 @@ main(
temp[2] = (coa[2]-RW)/(tres-1.0);
in4[0] = temp[0] * 100.0;
- in4[1] = 200.0 * temp[1] -100.0;
- in4[2] = 200.0 * temp[2] -100.0;
+ in4[1] = abscale * 2.0 * (temp[1] - 0.5);
+ in4[2] = abscale * 2.0 * (temp[2] - 0.5);
/* PCS -> Device */
if ((rv2 = luo->inv_lookup(luo, dev1, in4)) > 1)
@@ -411,8 +439,8 @@ main(
temp[2] = (coa[2]+RW)/(tres-1.0);
in4[0] = temp[0] * 100.0;
- in4[1] = 200.0 * temp[1] -100.0;
- in4[2] = 200.0 * temp[2] -100.0;
+ in4[1] = abscale * 2.0 * (temp[1] - 0.5);
+ in4[2] = abscale * 2.0 * (temp[2] - 0.5);
/* PCS -> Device */
if ((rv2 = luo->inv_lookup(luo, dev2, in4)) > 1)
@@ -428,8 +456,8 @@ main(
temp[2] = (coa[2]+RW)/(tres-1.0);
in4[0] = temp[0] * 100.0;
- in4[1] = 200.0 * temp[1] -100.0;
- in4[2] = 200.0 * temp[2] -100.0;
+ in4[1] = abscale * 2.0 * (temp[1] - 0.5);
+ in4[2] = abscale * 2.0 * (temp[2] - 0.5);
/* PCS -> Device */
if ((rv2 = luo->inv_lookup(luo, dev3, in4)) > 1)
@@ -440,7 +468,7 @@ main(
adev[2] += 0.25 * dev3[2];
adev[3] += 0.25 * dev3[3];
- /* Device -> PCS */
+ /* Average device -> PCS */
if ((rv1 = luo->lookup(luo, out, adev)) > 1)
error ("%d, %s",rd_icco->errc,rd_icco->err);
@@ -452,8 +480,6 @@ main(
i++;
}
}
- if (dilzero)
- break;
}
if (verb)
@@ -473,7 +499,8 @@ main(
}
/* ---------------------------------------------- */
- /* The target PCS -> clipped PCS using B2A table vectore */
+ /* Clipping vectors of b2a */
+ /* PCS -b2a- device -a2b- PCS Vectors */
if (dob2a) {
double rgb[3];
@@ -482,18 +509,19 @@ main(
wrl->start_line_set(wrl, 0);
- /* Get a PCS to Device conversion object */
- if ((luoB = xicco->get_luobj(xicco, ICX_CLIP_NEAREST, icmBwd, icAbsoluteColorimetric,
+ /* Get a B2A (PCS to Device) conversion object */
+ if ((luoB = xicco->get_luobj(xicco, FLAGS, icmBwd, icAbsoluteColorimetric,
icSigLabData, icmLuOrdNorm, NULL, &ink)) == NULL) {
- if ((luoB = xicco->get_luobj(xicco, ICX_CLIP_NEAREST, icmBwd, icmDefaultIntent,
+ if ((luoB = xicco->get_luobj(xicco, FLAGS, icmBwd, icmDefaultIntent,
icSigLabData, icmLuOrdNorm, NULL, &ink)) == NULL)
error ("%d, %s",rd_icco->errc, rd_icco->err);
}
if (verb)
- printf("Doing target PCS to B2A clipped PCS Vectors\n");
+ printf("Adding PCS -b2a- device -a2b- PCS Vectors\n");
i = 0;
+ /* For PCS values */
for (coa[0] = 0; coa[0] < tres; coa[0]++) {
for (coa[1] = 0; coa[1] < tres; coa[1]++) {
for (coa[2] = 0; coa[2] < tres; coa[2]++) {
@@ -501,13 +529,22 @@ main(
double temp[4];
int rv1, rv2;
+ if (!dogrid
+ && coa[0] != 0
+ && coa[0] != (tres-1)
+ && coa[1] != 0
+ && coa[1] != (tres-1)
+ && coa[2] != 0
+ && coa[2] != (tres-1))
+ continue;
+
temp[0] = coa[0]/(tres-1.0);
temp[1] = coa[1]/(tres-1.0);
temp[2] = coa[2]/(tres-1.0);
in[0] = temp[0] * 100.0;
- in[1] = 200.0 * temp[1] -100.0;
- in[2] = 200.0 * temp[2] -100.0;
+ in[1] = abscale * 2.0 * (temp[1] - 0.5);
+ in[2] = abscale * 2.0 * (temp[2] - 0.5);
/* PCS -> Device */
if ((rv2 = luoB->lookup(luoB, dev, in)) > 1)
@@ -528,8 +565,6 @@ main(
i++;
}
}
- if (dilzero)
- break;
}
if (verb)
@@ -551,7 +586,11 @@ main(
}
/* ---------------------------------------------- */
- /* The reference clipped PCS -> B2A clipped PCS vectore */
+ /* Difference between:
+ PCS -inv(a2b)- device -a2b- PCS
+ and
+ PCS -b2a- device -a2b- PCS
+ */
if (doeee) {
double rgb[3];
@@ -561,15 +600,15 @@ main(
wrl->start_line_set(wrl, 0);
/* Get a PCS to Device conversion object */
- if ((luoB = xicco->get_luobj(xicco, ICX_CLIP_NEAREST, icmBwd, icAbsoluteColorimetric,
+ if ((luoB = xicco->get_luobj(xicco, FLAGS, icmBwd, icAbsoluteColorimetric,
icSigLabData, icmLuOrdNorm, NULL, &ink)) == NULL) {
- if ((luoB = xicco->get_luobj(xicco, ICX_CLIP_NEAREST, icmBwd, icmDefaultIntent,
+ if ((luoB = xicco->get_luobj(xicco, FLAGS, icmBwd, icmDefaultIntent,
icSigLabData, icmLuOrdNorm, NULL, &ink)) == NULL)
error ("%d, %s",rd_icco->errc, rd_icco->err);
}
if (verb)
- printf("Doing reference clipped PCS to B2A table clipped PCS Vectors\n");
+ printf("Adding differenve between inv(a2b) and b2a cliped vectors\n");
i = 0;
for (coa[0] = 0; coa[0] < tres; coa[0]++) {
@@ -580,15 +619,24 @@ main(
double temp[4];
int rv1, rv2;
+ if (!dogrid
+ && coa[0] != 0
+ && coa[0] != (tres-1)
+ && coa[1] != 0
+ && coa[1] != (tres-1)
+ && coa[2] != 0
+ && coa[2] != (tres-1))
+ continue;
+
temp[0] = coa[0]/(tres-1.0);
temp[1] = coa[1]/(tres-1.0);
temp[2] = coa[2]/(tres-1.0);
in[0] = temp[0] * 100.0;
- in[1] = 200.0 * temp[1] -100.0;
- in[2] = 200.0 * temp[2] -100.0;
+ in[1] = abscale * 2.0 * (temp[1] - 0.5);
+ in[2] = abscale * 2.0 * (temp[2] - 0.5);
- /* Do reference lookup */
+ /* Do reference lookup using inverse a2b */
/* PCS -> Device */
if ((rv1 = luo->inv_lookup(luo, dev, in)) > 1)
error ("%d, %s",rd_icco->errc,rd_icco->err);
@@ -597,6 +645,7 @@ main(
if (luo->lookup(luo, check, dev) > 1)
error ("%d, %s",rd_icco->errc,rd_icco->err);
+
/* Do B2A table lookup */
/* PCS -> Device */
if ((rv2 = luoB->lookup(luoB, dev, in)) > 1)
@@ -617,8 +666,6 @@ main(
i++;
}
}
- if (dilzero)
- break;
}
if (verb)
diff --git a/xicc/xfit.c b/xicc/xfit.c
index d12919f..91afa64 100644
--- a/xicc/xfit.c
+++ b/xicc/xfit.c
@@ -1816,18 +1816,15 @@ static int xfit_fit(
icmAry2XYZ(_wp, p->wp);
- /* Absolute->Aprox. Relative Adaptation matrix */
- if (p->picc != NULL)
- p->picc->chromAdaptMatrix(p->picc, ICM_CAM_NONE, icmD50, _wp, p->fromAbs);
- else
- icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, _wp, p->fromAbs);
-
+ /* Absolute->Aprox. Relative Adaptation matrix and */
/* Aproximate relative to absolute conversion matrix */
- if (p->picc != NULL)
- p->picc->chromAdaptMatrix(p->picc, ICM_CAM_NONE, _wp, icmD50, p->toAbs);
- else
+ if (p->picc != NULL) {
+ p->picc->chromAdaptMatrix(p->picc, ICM_CAM_NONE, p->toAbs, p->fromAbs, icmD50, _wp);
+ } else {
+ icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, _wp, p->fromAbs);
icmChromAdaptMatrix(ICM_CAM_BRADFORD, _wp, icmD50, p->toAbs);
-
+ }
+
if (p->verb) {
double lab[3];
icmXYZ2Lab(&icmD50, lab, p->wp);
@@ -2664,7 +2661,7 @@ printf("~1 ipos[%d][%d] = %f\n",e,i,cv);
/* Matrix needed to correct approx rel wp to target D50 */
icmAry2XYZ(_wp, wcc.v); /* Aprox relative target white point */
if (p->picc != NULL) /* Correction */
- p->picc->chromAdaptMatrix(p->picc, ICM_CAM_NONE, icmD50, _wp, p->cmat);
+ p->picc->chromAdaptMatrix(p->picc, ICM_CAM_NONE, NULL, p->cmat, icmD50, _wp);
else
icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, _wp, p->cmat);
@@ -2687,8 +2684,7 @@ printf("~1 ipos[%d][%d] = %f\n",e,i,cv);
/* Fix absolute conversions to leave absolute response unchanged. */
icmAry2XYZ(_wp, wp); /* Actual white point */
if (p->picc != NULL) {
- p->picc->chromAdaptMatrix(p->picc, ICM_CAM_NONE, icmD50, _wp, p->fromAbs);
- p->picc->chromAdaptMatrix(p->picc, ICM_CAM_NONE, _wp, icmD50, p->toAbs);
+ p->picc->chromAdaptMatrix(p->picc, ICM_CAM_NONE, p->toAbs, p->fromAbs, icmD50, _wp);
} else {
icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, _wp, p->fromAbs);
icmChromAdaptMatrix(ICM_CAM_BRADFORD, _wp, icmD50, p->toAbs);
diff --git a/xicc/xicc.c b/xicc/xicc.c
index 058f492..9e35ffc 100644
--- a/xicc/xicc.c
+++ b/xicc/xicc.c
@@ -902,6 +902,18 @@ xicc *p
free (p);
}
+/* return nz if the intent implies Jab space */
+int xiccIsIntentJab(icRenderingIntent intent) {
+
+ if (intent == icxAppearance
+ || intent == icxAbsAppearance
+ || intent == icxPerceptualAppearance
+ || intent == icxAbsPerceptualAppearance
+ || intent == icxSaturationAppearance
+ || intent == icxAbsSaturationAppearance)
+ return 1;
+ return 0;
+}
/* Return an expanded lookup object, initialised */
/* from the icc. */
@@ -934,12 +946,7 @@ icxInk *ink /* inking details (NULL for default) */
//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
- || intent == icxAbsAppearance
- || intent == icxPerceptualAppearance
- || intent == icxAbsPerceptualAppearance
- || intent == icxSaturationAppearance
- || intent == icxAbsSaturationAppearance) {
+ if (xiccIsIntentJab(intent)) {
pcsor = icxSigJabData;
//printf("~1 pcsor = %s\n",tag2str(pcsor));
@@ -1365,6 +1372,7 @@ icxViewCond *vc /* Viewing parameters to return */
printf("Technology = %s\n",tag2str(tsig));
printf("deviceClass = %s\n",tag2str(devc));
printf("Transparency = %d\n",trans);
+ // hk ? hkscale ?
#endif
/* See if the viewing conditions are completely defined as ICC can do it */
@@ -1625,7 +1633,7 @@ icxViewCond *vc, /* Viewing parameters to return, May be NULL if desc is nz */
int no, /* Enumeration to return, -1 for default, -2 for none */
char *as, /* String alias to number, NULL if none */
int desc, /* NZ - Just return a description of this enumeration in vc */
-double *wp /* Provide white point if xicc is NULL */
+double *wp /* Provide XYZ white point if xicc is NULL */
) {
if (desc == 0) { /* We're setting the viewing condition */
@@ -1665,6 +1673,9 @@ double *wp /* Provide white point if xicc is NULL */
vc->Gxyz[0] = vc->Wxyz[0];
vc->Gxyz[1] = vc->Wxyz[1];
vc->Gxyz[2] = vc->Wxyz[2];
+
+ /* Default HK scaling factor = none */
+ vc->hkscale = 1.0;
}
/*
@@ -1919,6 +1930,7 @@ icxViewCond *vc
printf(" Flare to image ratio = %f\n",vc->Yf);
printf(" Glare to adapting/surround ratio = %f\n",vc->Yg);
printf(" Flare color = %f %f %f\n",vc->Gxyz[0], vc->Gxyz[1], vc->Gxyz[2]);
+ printf(" HK scaling = %f\n",vc->hkscale);
}
@@ -1997,6 +2009,8 @@ char *as /* Alias string selector, NULL for none */
fprintf(stderr,"!!!!!! Warning, USE_CAM is off in xicc.c !!!!!!\n");
#endif
+ gmi->hkscale = -1.0; /* Default is to not override viewing condition HK factor */
+
/* Assert default if no guidance given */
if (no == icxNoGMIntent && as == NULL)
no = icxDefaultGMIntent;
@@ -2209,7 +2223,6 @@ char *as /* Alias string selector, NULL for none */
gmi->desc = "lp - Luminance Preserving Perceptual";
gmi->icci = icPerceptual;
gmi->usecas = perccas; /* Appearance space */
-// gmi->usecas = 0; /* Lab space */
gmi->usemap = 1; /* Use gamut mapping */
gmi->greymf = 1.0; /* Fully align grey axis */
gmi->glumwcpf = 1.0; /* Fully compress grey axis at white end */
@@ -2226,6 +2239,7 @@ char *as /* Alias string selector, NULL for none */
gmi->gamlpwf = 1.0; /* Full Linear Preserving Perceptual wghtg. factor */
gmi->gamswf = 0.0; /* No Saturation weighting factor */
gmi->satenh = 0.0; /* No saturation enhancement */
+ gmi->hkscale = 0.2; /* Mostly disable HK appearance modeling */
}
else if (no == 8
|| (as != NULL && stricmp(as,"ms") == 0)) {
@@ -2389,6 +2403,8 @@ icxGMappingIntent *gmi /* Gamut Mapping parameters to return */
printf(" Gamut Saturation mapping weighting factor %f\n", gmi->gamswf);
printf(" Saturation enhancement factor %f\n", gmi->satenh);
}
+ if (gmi->hkscale >= 0.0)
+ printf(" HK scale override %f\n", gmi->hkscale);
}
/* ------------------------------------------------------ */
diff --git a/xicc/xicc.h b/xicc/xicc.h
index a5ecf95..e4db20d 100644
--- a/xicc/xicc.h
+++ b/xicc/xicc.h
@@ -172,11 +172,10 @@ typedef struct {
/* Structure to convey inverse lookup clip handling details */
struct _icxClip {
int nearclip; /* Flag - use near clipping not vector */
- int LabLike; /* Flag Its an Lab like colorspace */
+ int LabLike; /* Flag It's an Lab like colorspace */
int fdi; /* Dimentionality of clip vector */
- double ocent[MXDO]; /* base of center of clut output gamut */
- double ocentv[MXDO]; /* vector direction of clut output clip target line */
- double ocentl; /* clip target line length */
+ struct _icxCuspMap *cm; /* Cusp map for computing vector (if !NULL) */
+ double ocent[MXDO]; /* Default center of clut output gamut used if cm == NULL */
}; typedef struct _icxClip icxClip;
/* Structure to convey viewing conditions */
@@ -191,6 +190,7 @@ typedef struct {
double Yg; /* Glare as a fraction of the adapting/surround (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 */
+ double hkscale; /* [1.0] HK scaling factor */
char *desc; /* Possible description of this VC */
} icxViewCond;
@@ -227,6 +227,7 @@ typedef struct {
double gamlpwf; /* Gamut Lightness preserving perceptual Map whtg. factor, 0.0 - 1.0 */
double gamswf; /* Gamut Saturation Map weighting factor, 0.0 - 1.0 */
double satenh; /* Saturation enhancement value, 0.0 - Inf */
+ double hkscale; /* [1.0] Optional HK scaling factor */
char *as; /* Alias string (option name) */
char *desc; /* Possible description of this VC */
icRenderingIntent icci; /* Closest ICC intent */
@@ -431,6 +432,13 @@ xicc *new_xicc(icc *picc);
gamut * (*get_gamut) (struct _icxLuBase *plu, /* xicc lookup object */ \
double detail); /* gamut detail level, 0.0 = def */ \
\
+ /* Given an xicc lookup object, return an icxCuspMap object. */ \
+ /* Note that the PCS must be Lab or Jab. */ \
+ /* An icxLuLut type must be icmFwd, and the ink limit (if supplied) */ \
+ /* will be applied. */ \
+ /* Return NULL on error, check errc+err for reason */ \
+ struct _icxCuspMap *(*get_cuspmap)(struct _icxLuBase *p, int res); \
+ \
/* The following two functions expose the relative colorimetric native ICC PCS */ \
/* <--> absolute/CAM space transform, so that CAM based gamut compression */ \
/* can be applied in creating the ICC Lut tabls in profout.c. */ \
@@ -599,6 +607,10 @@ struct _icxLuLut {
/* ------------------------------------------------------------------------------ */
/* Utility declarations and functions */
+/* Utility that mirrors get_luobj intent handling: */
+/* return nz if the intent implies Jab space */
+int xiccIsIntentJab(icRenderingIntent intent);
+
/* Profile Creation Suplimental Information structure */
struct _profxinf {
icmSig manufacturer; /* Device manufacturer ICC Sig, 0 for default */
@@ -663,7 +675,7 @@ icxViewCond *vc, /* Viewing parameters to return, May be NULL if desc is nz */
int no, /* Enumeration to return, -1 for default, -2 for none */
char *as, /* String alias to number, NULL if none */
int desc, /* NZ - Just return a description of this enumeration in vc */
-double *wp /* Provide white point if xicc is NULL */
+double *wp /* Provide XYZ white point if xicc is NULL */
);
/* Debug: dump a Viewing Condition to standard out */
@@ -719,7 +731,8 @@ double icxMaxUnderlyingLimit(struct _xcal *cal, double ilimit);
double *icxClipVector(
icxClip *p, /* Clipping setup information */
double *in, /* Target point */
-double *cdirv /* Space for returned clip vector */
+double *cdirv, /* Returned clip vector */
+int safe /* Flag - return safe vector */
);
/* - - - - - - - - - - */
@@ -931,6 +944,28 @@ void icxdpdiMulBy3x3Parm(
/* - - - - - - - - - - */
+/* Cusp map - used for vector clipping in Lab like spaces */
+struct _icxCuspMap {
+ double Lmax[3]; /* Maximum L* value found */
+ double Lmin[3]; /* Minimum L* value found */
+ int res; /* Resolution of hue map */
+ double *L; /* L* value of cusp at hue angle */
+ double *C; /* C* value of cusp at hue angle */
+
+ /* Expand cusp map with given point */
+ void (*expand)(struct _icxCuspMap *p, double lab[3]);
+
+ /* Return the corresponding cusp location, given the source point */
+ void (*getCusp)(struct _icxCuspMap *p,double cuspLCh[3], double srcLab[3]);
+
+ /* We're done with CuspMap */
+ void (*del)(struct _icxCuspMap *p);
+
+}; typedef struct _icxCuspMap icxCuspMap;
+
+
+/* - - - - - - - - - - */
+
#include "xcal.h"
#endif /* XICC_H */
diff --git a/xicc/xicclu.c b/xicc/xicclu.c
index f8fb9d6..20c7d8b 100644
--- a/xicc/xicclu.c
+++ b/xicc/xicclu.c
@@ -40,7 +40,8 @@
#undef SPTEST /* Test rspl gamut surface code */
-#define USE_NEARCLIP /* Our usual expectation */
+#define USE_NEARCLIP /* [def] Our usual expectation */
+#define USE_FASTNNSETP /* [def] Make it more responsive */
#define XRES 128 /* Plotting resolution */
void usage(char *diag) {
@@ -54,10 +55,11 @@ void usage(char *diag) {
fprintf(stderr," -g Plot slice instead of looking colors up. (Default white to black)\n");
fprintf(stderr," -G s:L:a:b Override plot slice start with Lab or Jab co-ordinate \n");
fprintf(stderr," -G e:L:a:b Override plot slice end with Lab or Jab co-ordinate \n");
+ fprintf(stderr," -V Use 'vcgt' calibration tag from profile\n");
fprintf(stderr," -f function f = forward, b = backwards, g = gamut, p = preview\n");
fprintf(stderr," if = inverted forward, ib = inverted backwards\n");
fprintf(stderr," -i intent a = absolute, r = relative colorimetric\n");
- fprintf(stderr," p = perceptual, s = saturation\n");
+ fprintf(stderr," p = perceptual, s = saturation, A = disp. abs. measurements\n");
// fprintf(stderr," P = absolute perceptual, S = absolute saturation\n");
fprintf(stderr," -o order n = normal (priority: lut > matrix > monochrome)\n");
fprintf(stderr," r = reverse (priority: monochrome > matrix > lut)\n");
@@ -151,6 +153,10 @@ void spioutf(void *cbntx, double *out, double *in) {
#endif /* SPTEST */
+#ifndef USE_FASTNNSETP
+#pragma message("!!!!!!!!!!!! USE_FASTNNSETP turned off !!!!!!!!!")
+#endif
+
int
main(int argc, char *argv[]) {
int fa, nfa, mfa; /* argument we're looking at */
@@ -159,6 +165,7 @@ main(int argc, char *argv[]) {
icc *icco = NULL;
xicc *xicco = NULL;
xcal *cal = NULL; /* If .cal rather than .icm/.icc, not NULL */
+ int dovcgt = 0; /* Extract cal out of icc profile */
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 */
@@ -202,6 +209,8 @@ main(int argc, char *argv[]) {
/* Lookup parameters */
icmLookupFunc func = icmFwd; /* Default */
icRenderingIntent intent = icmDefaultIntent; /* Default */
+ int absmeas = 0; /* Show display absolute measurement in XYZ */
+ double dispLuminance = -1.0; /* Luminance value for absmeas */
icColorSpaceSignature pcsor = icmSigDefaultData; /* Default */
icmLookupOrder order = icmLuOrdNorm; /* Default */
int inking = 3; /* Default is ramp */
@@ -262,6 +271,11 @@ main(int argc, char *argv[]) {
}
}
+ /* Load ICC 'vcgt' tag as Cal */
+ else if (argv[fa][1] == 'V') {
+ dovcgt = 1;
+ }
+
/* Locus plot */
else if (argv[fa][1] == 'g') {
doplot = 1;
@@ -383,6 +397,7 @@ main(int argc, char *argv[]) {
else if (argv[fa][1] == 'i') {
if (na == NULL) usage("No parameter after flag -i");
fa = nfa;
+ absmeas = 0;
switch (na[0]) {
case 'p':
intent = icPerceptual;
@@ -396,6 +411,10 @@ main(int argc, char *argv[]) {
case 'a':
intent = icAbsoluteColorimetric;
break;
+ case 'A':
+ intent = icAbsoluteColorimetric;
+ absmeas = 1;
+ break;
/* Argyll special intents to check spaces underlying */
/* icxPerceptualAppearance & icxSaturationAppearance */
case 'P':
@@ -415,6 +434,7 @@ main(int argc, char *argv[]) {
fa = nfa;
switch (na[0]) {
case 'x':
+ /* (See also absmeas after options) */
pcsor = icSigXYZData;
repYxy = 0;
repLCh = 0;
@@ -676,6 +696,15 @@ main(int argc, char *argv[]) {
error("chrom_locus_poligon failed");
}
+ /* Force to XYZ PCS */
+ if (absmeas) {
+ pcsor = icSigXYZData;
+ repYxy = 0;
+ repLCh = 0;
+ repJCh = 0;
+ repXYZ100 = 0;
+ }
+
/* Open up the profile for reading */
if ((fp = new_icmFileStd_name(prof_name,"r")) == NULL)
error ("Can't open file '%s'",prof_name);
@@ -683,7 +712,26 @@ main(int argc, char *argv[]) {
if ((icco = new_icc()) == NULL)
error ("Creation of ICC object failed");
- if ((rv = icco->read(icco,fp,0)) == 0) { /* ICC profile */
+ if (dovcgt) {
+ if ((rv = icco->read(icco,fp,0)) != 0)
+ error ("Can't load ICC file '%s'",prof_name);
+
+ if ((cal = new_xcal()) == NULL)
+ error("new_xcal failed");
+
+ if ((cal->read_icc(cal, icco)) != 0) {
+ error ("ICC file '%s' doesn't contain a 'vcgt' tag",prof_name);
+ }
+
+ icco->del(icco);
+ fp->del(fp);
+ fp = NULL;
+
+ /* Get details of conversion (Arguments may be NULL if info not needed) */
+ outs = ins = cal->colspace;
+ outn = inn = cal->devchan;
+
+ } else if ((rv = icco->read(icco,fp,0)) == 0) { /* ICC profile */
if (doplot) {
@@ -838,7 +886,9 @@ main(int argc, char *argv[]) {
| (intsep ? ICX_INT_SEPARATE : 0)
| (merge ? ICX_MERGE_CLUT : 0)
| (camclip ? ICX_CAM_CLIP : 0)
+#ifdef USE_FASTNNSETP
| ICX_FAST_SETUP
+#endif
, func, intent, pcsor, order, &vc, &ink)) == NULL)
error ("%d, %s",xicco->errc, xicco->err);
@@ -937,7 +987,23 @@ main(int argc, char *argv[]) {
}
#endif
+ /* If we are doing display absolute measurement PCS */
+ if (absmeas) {
+ icmXYZArray *wo;
+ if ((wo = (icmXYZArray *)icco->read_tag(
+ icco, icSigLuminanceTag)) != NULL
+ && wo->ttype == icSigXYZArrayType) {
+ dispLuminance = wo->data[0].Y;
+ }
+ if (verb)
+ printf("Display Luminance = %f cd/m^2\n", dispLuminance);
+
+ if (dispLuminance <= 0.0)
+ error("Unable to read display luminance tag from '%s'\n",prof_name);
+ }
+
} else { /* See if it's a .cal */
+ icco->del(icco);
fp->del(fp);
fp = NULL;
@@ -953,6 +1019,11 @@ main(int argc, char *argv[]) {
outn = inn = cal->devchan;
}
+ /* Sanity check */
+ if (absmeas && ins != icSigXYZData && outs != icSigXYZData) {
+ error("Must be XYZ PCS space for display absolute measurement");
+ }
+
if (verb > 1 && icco != NULL) {
icmFile *op;
if ((op = new_icmFileStd_fp(stdout)) == NULL)
@@ -1147,6 +1218,13 @@ main(int argc, char *argv[]) {
in[2] = C * sin(3.14159265359/180.0 * h);
}
+ /* display absolute measurement */
+ if (absmeas && ins == icSigXYZData) {
+ in[0] /= dispLuminance;
+ in[1] /= dispLuminance;
+ in[2] /= dispLuminance;
+ }
+
/* Do conversion */
if (cal != NULL) { /* .cal */
if (func == icmBwd || invert) {
@@ -1184,6 +1262,13 @@ main(int argc, char *argv[]) {
for (i = 0; i < MAX_CHAN; i++)
uout[i] = out[i];
+ /* display absolute measurement */
+ if (absmeas && outs == icSigXYZData) {
+ uout[0] *= dispLuminance;
+ uout[1] *= dispLuminance;
+ uout[2] *= dispLuminance;
+ }
+
if (repXYZ100 && outs == icSigXYZData) {
uout[0] *= 100.0;
uout[1] *= 100.0;
diff --git a/xicc/xlut.c b/xicc/xlut.c
index 827dde8..a18956c 100644
--- a/xicc/xlut.c
+++ b/xicc/xlut.c
@@ -54,7 +54,7 @@
/*
- ~~~~~!!!!! This has all been fixed ?
+ !!!!! This has all been fixed ? !!!!!!
NOTE :- an alternative to the way display profile absolute is handled here
would be to always chromatically adapt the illuminant to D50, and encode
@@ -160,6 +160,9 @@
#undef DISABLE_KCURVE_FILTER /* [Undef] don't filter the Kcurve */
#undef REPORT_LOCUS_SEGMENTS /* [Undef[ Examine how many segments there are in aux inversion */
+#undef FASTREVSETUP_NON_CAM /* [Undef] Use fast setup on innerm non-CAM lookup, if we're */
+ /* going to use CAM clip for nn lookup */
+
#define XYZ_EXTRA_SMOOTH 20.0 /* Extra smoothing factor for XYZ profiles */
/* !!! Note this is mainly due to smoothing being */
/* scaled by data range in rspl code !!! */
@@ -171,7 +174,10 @@
/* 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 */
+#define USELCHWEIGHT /* [def] Use LCh weighting for nn clip if possible */
+#define JCCWEIGHT 2.0 /* [2.0] Amount to emphasize J delta E in in computing clip */
+#define CCCWEIGHT 1.0 /* [1.0] Amount to emphasize C delta E in in computing clip */
+#define HCCWEIGHT 2.2 /* [2.2] Amount to emphasize H delta E in in computing clip */
/*
* TTBD:
@@ -429,7 +435,6 @@ icColorSpaceSignature is, /* Input space, XYZ or Lab */
double *out, double *in) {
icxLuLut *p = (icxLuLut *)pp;
-
/* Convert to the ICC PCS */
if (is == icSigLabData && p->natpcs == icSigXYZData) {
DBS(("fwd_relpcs_outpcs: Lab in = %s\n", icmPdv(p->inputChan, in)));
@@ -444,7 +449,7 @@ double *out, double *in) {
icmCpy3(out, in);
}
- /* Convert to absolute */
+ /* Convert to final PCS (i.e. absolute intent is set that way) */
((icmLuLut *)p->plu)->out_abs((icmLuLut *)p->plu, out, out);
DBS(("fwd_relpcs_outpcs: abs PCS = %s\n", icmPdv(p->inputChan, out)));
@@ -461,51 +466,98 @@ double *out, double *in) {
/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Components of inverse lookup, in order */
+static double sigfunc(double in, double pp) {
+ if (in < 0.5)
+ return pow(2 * in, pp) * 0.5;
+ else
+ return 1.0 - (pow(2 * (1.0 - in), pp) * 0.5);
+}
+
/* Utility function - compute the clip vector direction. */
/* return NULL if vector clip isn't used. */
double *icxClipVector(
icxClip *p, /* Clipping setup information */
double *in, /* Target point */
-double *cdirv /* Space for returned clip vector */
+double *cdirv, /* Returned clip vector */
+int safe /* Flag - return safe vector */
) {
int f;
if (p->nearclip != 0)
return NULL; /* Doing nearest clipping, not vector */
- /* Default is simple vector clip */
- for (f = 0; f < p->fdi; f++)
- cdirv[f] = p->ocent[f] - in[f]; /* Clip towards output gamut center */
+ if (p->cm == NULL) {
+ /* Default is simple vector clip */
+ for (f = 0; f < p->fdi; f++)
+ cdirv[f] = p->ocent[f] - in[f]; /* Clip towards output gamut center */
+
+ /* Else use CuspMap for more targeted vector */
+ } else {
+ double Cpow = 2.0; /* [2.0] 4.0 for less L change */
+ double Lsig = 2.5; /* [2.5] 1.6 for less L change */
+ double Lpow = 0.5; /* [0.5] 0.8 for less L change */
+ double Cratio = 0.9; /* [0.9] see cusptest.c */
+ double ratio;
+ double ss[3];
+ double inC;
+ double targ[3], cuspLCh[3];
- if (p->ocentl != 0.0) { /* Graduated vector clip */
- double cvl, nll;
+ inC = sqrt(in[1] * in[1] + in[2] * in[2]);
- /* Compute (negative) clip vector length */
- for (cvl = 0.0, f = 0; f < p->fdi; f++) {
- cvl += cdirv[f] * cdirv[f];
+ p->cm->getCusp(p->cm, cuspLCh, in);
+
+
+//icmLCh2Lab(targ, cuspLCh);
+//printf("\n~1 in %f %f %f -> cusp %f %f %f\n", in[0], in[1], in[2], targ[0], targ[1], targ[2]);
+//printf("~1 in %f %f %f -> cuspLCh %f %f %f\n", in[0], in[1], in[2], cuspLCh[0], cuspLCh[1], cuspLCh[2]);
+
+ /* Constrain clip vector to always be inwards pointing */
+ if (cuspLCh[1] > 0.90 * inC) {
+ cuspLCh[1] = 0.90 * inC;
+//printf("~1 Constrain cusp C: cuspLCh %f %f %f\n", cuspLCh[0], cuspLCh[1], cuspLCh[2]);
}
- cvl = sqrt(cvl);
- if (cvl > 1e-8) {
- /* Dot product of clip vector and clip center line */
- for (nll = 0.0, f = 0; f < p->fdi; f++)
- nll -= cdirv[f] * p->ocentv[f]; /* (Fix -ve clip vector) */
- nll /= (p->ocentl * p->ocentl); /* Normalised location along line */
-
- /* Limit to line */
- if (nll < 0.0)
- nll = 0.0;
- else if (nll > 1.0)
- nll = 1.0;
-
- if (p->LabLike) {
- /* Aim more towards center for saturated targets */
- double sat = sqrt(in[1] * in[1] + in[2] * in[2]);
- nll += sat/150.0 * (0.5 - nll);
- }
- /* Compute target clip direction */
- for (f = 0; f < p->fdi; f++)
- cdirv[f] = p->ocent[f] + nll * p->ocentv[f] - in[f];
+ ss[0] = in[0];
+ ss[1] = in[1];
+ ss[2] = in[2];
+
+ if (ss[0] < p->cm->Lmin[0])
+ ss[0] = p->cm->Lmin[0];
+ if (ss[0] > p->cm->Lmax[0])
+ ss[0] = p->cm->Lmax[0];
+
+ if (safe) {
+ targ[0] = cuspLCh[0]; /* L target is cusp L */
+ targ[1] = 0.0; /* Right on the neutral axis */
+
+ } else {
+ if (ss[0] >= cuspLCh[0]) {
+ ratio = (p->cm->Lmax[0] - ss[0])/(p->cm->Lmax[0] - cuspLCh[0]);
+ targ[0] = p->cm->Lmax[0] - sigfunc(pow(ratio, Lpow), Lsig)
+ * (p->cm->Lmax[0] - cuspLCh[0]);
+ targ[1] = pow(ratio, Cpow) * Cratio * cuspLCh[1];
+ } else {
+ ratio = (ss[0] - p->cm->Lmin[0])/(cuspLCh[0] - p->cm->Lmin[0]);
+ targ[0] = p->cm->Lmin[0] + sigfunc(pow(ratio, Lpow), Lsig)
+ * (cuspLCh[0] - p->cm->Lmin[0]);
+ targ[1] = pow(ratio, Cpow) * Cratio * cuspLCh[1];
+ }
}
+ targ[2] = cuspLCh[2]; /* h of in */
+
+//printf("~1 targLCH %f %f %f\n", targ[0], targ[1], targ[2]);
+ icmLCh2Lab(targ, targ);
+//printf("~1 targ %f %f %f\n", targ[0], targ[1], targ[2]);
+
+ /* line target point up with white and black point ? */
+ ratio = (ss[0] - p->cm->Lmin[0])/(p->cm->Lmax[0] - p->cm->Lmin[0]);
+ targ[1] += (1.0 - ratio) * p->cm->Lmin[1] + ratio * p->cm->Lmax[1];
+ targ[2] += (1.0 - ratio) * p->cm->Lmin[2] + ratio * p->cm->Lmax[2];
+
+//printf("~1 in %f %f %f -> targ %f %f %f\n", in[0], in[1], in[2], targ[0], targ[1], targ[2]);
+
+ /* Compute target clip direction */
+ for (f = 0; f < p->fdi; f++)
+ cdirv[f] = (targ[f] - in[f]);
}
return cdirv;
@@ -855,7 +907,7 @@ static double icxKcurveNF(double L, icxInkCurve *c) {
#define DBK(xxx)
#endif
-/* Same as above, but implement transition filters accross inflection points. */
+/* Same as above, but implement transition filters across inflection points. */
/* (The convolultion filter previously used could be */
/* re-instituted if something was done about compressing */
/* the filter at the boundaries so that the levels are met.) */
@@ -999,12 +1051,14 @@ double *clipd, /* If not NULL, return DE to gamut on clipi, 0 for not clip */
double *in /* Function input values to invert (== clut output' values) */
) {
co pp[MAX_INVSOLN]; /* Room for all the solutions found */
+ co upp; /* pp[0] value sent to rev_interp() for replay. */
int nsoln; /* Number of solutions found */
- double *cdir, cdirv[MXDO]; /* Clip vector direction and length */
+ double *cdir, cdirv[MXDO]; /* Clip vector direction and length/LCh weighting */
int e,f,i;
int fdi = p->clutTable->fdi;
int flags = 0; /* reverse interp flags */
int xflags = 0; /* extra clip/noclip flags */
+ int uflags = 0; /* flags value sent to rev_interp() for replay */
double tin[MXDO]; /* PCS value to be inverted */
double cdist = 0.0; /* clip DE */
int crv = 0; /* Return value - set to 1 if clipped */
@@ -1026,10 +1080,10 @@ double *in /* Function input values to invert (== clut output' values) */
/* Setup for reverse lookup */
for (f = 0; f < fdi; f++)
- pp[0].v[f] = in[f]; /* Target value */
+ upp.v[f] = pp[0].v[f] = in[f]; /* Target value */
/* Compute clip vector, if any */
- cdir = icxClipVector(&p->clip, in, cdirv);
+ cdir = icxClipVector(&p->clip, in, cdirv, 0);
if (p->clutTable->di > fdi) { /* ie. CMYK->Lab, there will be ambiguity */
double min[MXDI], max[MXDI]; /* Auxiliary locus range */
@@ -1084,7 +1138,7 @@ double *in /* Function input values to invert (== clut output' values) */
/* even though it won't have any effect. */
for (e = 0; e < p->clutTable->di; e++) {
if (p->auxm[e] != 0) {
- pp[0].p[e] = 0.5;
+ upp.p[e] = pp[0].p[e] = 0.5;
}
}
@@ -1123,7 +1177,7 @@ double *in /* Function input values to invert (== clut output' values) */
iv = min[e];
else if (iv > max[e])
iv = max[e];
- pp[0].p[e] = iv;
+ upp.p[e] = pp[0].p[e] = iv;
}
}
DBR(("inv_clut_aux: aux %f from auxt[] %f\n",pp[0].p[3],auxt[0]))
@@ -1137,7 +1191,7 @@ double *in /* Function input values to invert (== clut output' values) */
iv = min[e];
else if (iv > max[e])
iv = max[e];
- pp[0].p[e] = iv;
+ upp.p[e] = pp[0].p[e] = iv;
}
}
DBR(("inv_clut_aux: aux %f from out[0] K target %f min %f max %f\n",pp[0].p[3],out[3],min[3],max[3]))
@@ -1152,7 +1206,7 @@ double *in /* Function input values to invert (== clut output' values) */
iv = min[e];
else if (iv > max[e])
iv = max[e];
- pp[0].p[e] = iv;
+ upp.p[e] = pp[0].p[e] = iv;
}
}
DBR(("inv_clut_aux: aux %f from out[0] locus %f min %f max %f\n",pp[0].p[3],out[3],min[3],max[3]))
@@ -1195,7 +1249,7 @@ double *in /* Function input values to invert (== clut output' values) */
for (e = 0; e < p->clutTable->di; e++) {
if (p->auxm[e] != 0) {
- pp[0].p[e] = min[e] + rv * (max[e] - min[e]);
+ upp.p[e] = pp[0].p[e] = min[e] + rv * (max[e] - min[e]);
}
}
DBR(("inv_clut_aux: aux %f from locus %f min %f max %f\n",pp[0].p[3],rv,min[3],max[3]))
@@ -1209,7 +1263,7 @@ double *in /* Function input values to invert (== clut output' values) */
iv = min[e];
else if (iv > max[e])
iv = max[e];
- pp[0].p[e] = iv;
+ upp.p[e] = pp[0].p[e] = iv;
}
}
DBR(("inv_clut_aux: aux %f from out[0] K target %f min %f max %f\n",pp[0].p[3],rv,min[3],max[3]))
@@ -1241,7 +1295,7 @@ double *in /* Function input values to invert (== clut output' values) */
ii = 1.0;
ii = (1.0 - ii) * rv + ii * rv2;/* Blend between locus rule curves */
/* Out ink from output locus */
- pp[0].p[e] = min[e] + ii * (max[e] - min[e]);
+ upp.p[e] = pp[0].p[e] = min[e] + ii * (max[e] - min[e]);
} else {
double iv;
iv = out[e]; /* Input K level */
@@ -1249,7 +1303,7 @@ double *in /* Function input values to invert (== clut output' values) */
iv = rv;
else if (iv > rv2)
iv = rv2;
- pp[0].p[e] = iv;
+ upp.p[e] = pp[0].p[e] = iv;
}
}
}
@@ -1270,7 +1324,7 @@ double *in /* Function input values to invert (== clut output' values) */
tv = max[e];
tc.p[0] = tv;
p->inputTable[e]->interp(p->inputTable[e], &tc);
- pp[0].p[e] = tc.v[0];
+ upp.p[e] = pp[0].p[e] = tc.v[0];
}
}
@@ -1291,16 +1345,18 @@ double *in /* Function input values to invert (== clut output' values) */
tin[f] = pp[0].v[f];
}
+ uflags = RSPL_MAXAUX | flags | xflags; /* Combine all the flags */
+
/* Find reverse solution with target auxiliaries */
/* We choose the closest aux at or above the target */
/* to try and avoid glitches near black due to */
/* possible forked black locuses. */
nsoln = p->clutTable->rev_interp(
p->clutTable, /* rspl object */
- RSPL_MAXAUX | flags | xflags, /* Combine all the flags */
+ uflags,
MAX_INVSOLN, /* Maxumum solutions to return */
p->auxm, /* Auxiliary input chanel mask */
- cdir, /* Clip vector direction and length */
+ cdir, /* Clip vector direction/LCh weighting */
pp); /* Input target and output solutions */
/* returned solutions in pp[0..retval-1].p[] */
@@ -1312,13 +1368,15 @@ double *in /* Function input values to invert (== clut output' values) */
tin[f] = pp[0].v[f];
}
+ uflags = flags; /* No extra flags */
+
/* Color spaces don't need auxiliaries to choose from solution locus */
nsoln = p->clutTable->rev_interp(
p->clutTable, /* rspl object */
- flags, /* No extra flags */
+ uflags,
MAX_INVSOLN, /* Maxumum solutions to return */
NULL, /* No auxiliary input targets */
- cdir, /* Clip vector direction and length */
+ cdir, /* Clip vector direction/LCh weighting */
pp); /* Input target and output solutions */
/* returned solutions in pp[0..retval-1].p[] */
}
@@ -1343,8 +1401,9 @@ double *in /* Function input values to invert (== clut output' values) */
DBR(("inv_clut_aux got %d rev_interp solutions, clipflag = %d\n",nsoln,crv))
/* If we clipped and we should clip in CAM Jab space, compute reverse */
- /* clip solution using our additional CAM space. */
- /* (Note that we don't support vector clip in CAM space at the moment) */
+ /* clip solution using our additional CAM space rspl. */
+ /* Note that we don't support vector clip in CAM space at the moment */
+ /* - icxLuLut_init_clut_camclip() doesn't setup CAM rspl for vector clip. */
if (crv != 0 && p->camclip && p->nearclip) {
co cpp; /* Alternate CAM space solution */
double bf; /* Blend factor */
@@ -1375,7 +1434,6 @@ 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. */
@@ -1391,7 +1449,7 @@ double *in /* Function input values to invert (== clut output' values) */
flags | xflags | RSPL_WILLCLIP, /* Combine all the flags + clip ?? */
1, /* Maximum solutions to return */
p->auxm, /* Auxiliary input chanel mask */
- cdir, /* Clip vector direction and length */
+ cdir, /* Clip vector direction/ LCh weighting */
&cpp); /* Input target and output solutions */
} else {
@@ -1400,7 +1458,7 @@ double *in /* Function input values to invert (== clut output' values) */
flags | RSPL_WILLCLIP, /* Because we know it will clip ?? */
1, /* Maximum solutions to return */
NULL, /* No auxiliary input targets */
- cdir, /* Clip vector direction and length */
+ cdir, /* Clip vector direction/LCh weighting */
&cpp); /* Input target and output solutions */
}
@@ -1411,7 +1469,6 @@ 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];
@@ -1453,12 +1510,59 @@ double *in /* Function input values to invert (== clut output' values) */
/* Not CAM clip case */
} else {
+ /* If vector clip, replay with simple vector */
+ if (nsoln == 0 && p->nearclip == 0) {
+//printf("~1 !!!!!!!!!!!!!! Vector clip failed - trying safe replay !!!!!!!!!!!!!!!1\n");
+
+ // Reset pp[0] target values and auiliaries
+ for (e = 0; e < p->clutTable->di; e++)
+ pp[0].p[e] = upp.p[e];
+ for (f = 0; f < fdi; f++)
+ pp[0].v[f] = upp.v[f];
+
+ /* Compute safe clip vector */
+ cdir = icxClipVector(&p->clip, in, cdirv, 1);
+
+ /* Replay the lookup using safer vector */
+ nsoln = p->clutTable->rev_interp(
+ p->clutTable, /* rspl object */
+ uflags,
+ MAX_INVSOLN, /* Maxumum solutions to return */
+ NULL, /* No auxiliary input targets */
+ cdir, /* Clip vector direction and length */
+ pp); /* Input target and output solutions */
+ /* returned solutions in pp[0..retval-1].p[] */
+ nsoln &= RSPL_NOSOLNS; /* Get number of solutions */
+
+ if (nsoln == 0) { /* Hmm */
+//printf("~1 !!!!!!!!!!!!!! Vector clip failed again - using nn replay !!!!!!!!!!!!!!!1\n");
+ // Reset pp[0] target values and auiliaries
+ for (e = 0; e < p->clutTable->di; e++)
+ pp[0].p[e] = upp.p[e];
+ for (f = 0; f < fdi; f++)
+ pp[0].v[f] = upp.v[f];
+
+ /* Replay the lookup as a nearclip, to guarantee a solution */
+ nsoln = p->clutTable->rev_interp(
+ p->clutTable, /* rspl object */
+ RSPL_NEARCLIP | RSPL_NONNSETUP,
+ MAX_INVSOLN, /* Maxumum solutions to return */
+ NULL, /* No auxiliary input targets */
+ NULL, /* No LCh weighting */
+ pp); /* Input target and output solutions */
+ /* returned solutions in pp[0..retval-1].p[] */
+ nsoln &= RSPL_NOSOLNS; /* Get number of solutions */
+ }
+ }
+
if (nsoln == 1) { /* Exactly one solution */
i = 0;
} else if (nsoln == 0) { /* Zero solutions. This is unexpected. */
double in_v[MXDO];
p->output(p, in_v, pp[0].v); /* Get ICC inverse input values */
p->out_abs(p, in_v, in_v);
+ if (p->nearclip == 0)
+ a1logd(g_log,0,"Clip dst %f %f %f\n",pp[0].v[0]+cdir[0], pp[0].v[1]+cdir[1], pp[0].v[2]+cdir[2]);
error("Unexpected failure to find reverse solution for input to output table for value %f %f %f (ICC input %f %f %f)",pp[0].v[0],pp[0].v[1],pp[0].v[2], in_v[0], in_v[1], in_v[2]);
return 2;
} else { /* Multiple solutions */
@@ -1625,21 +1729,22 @@ int icxLuLut_inv_input(icxLuLut *p, double *out, double *in) {
int i,j;
int nsoln; /* Number of solutions found */
co pp[MAX_INVSOLN]; /* Room for all the solutions found */
- double cdir;
+// double cdir;
DBR(("inv_input got DEV' %f %f %f %f\n",in[0],in[1],in[2],in[3]))
for (i = 0; i < p->inputChan; i++) {
pp[0].p[0] = p->inputClipc[i];
pp[0].v[0] = in[i];
- cdir = p->inputClipc[i] - in[i]; /* Clip towards output range */
+// cdir = p->inputClipc[i] - in[i]; /* Clip towards output range */
nsoln = p->inputTable[i]->rev_interp (
p->inputTable[i], /* this */
RSPL_NEARCLIP, /* Clip to nearest (faster than vector) */
MAX_INVSOLN, /* Maximum number of solutions allowed for */
NULL, /* No auxiliary input targets */
- &cdir, /* Clip vector direction and length */
+ NULL, /* No LCH weight because this is 1D */
+// &cdir, /* Clip vector direction and length */
pp); /* Input and output values */
if (nsoln & RSPL_DIDCLIP)
@@ -1837,6 +1942,7 @@ icxLuBase *pp
/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
static gamut *icxLuLutGamut(icxLuBase *plu, double detail);
+static icxCuspMap *icxLuLutCuspMap(icxLuBase *plu, int res);
/* Do the basic icxLuLut creation and initialisation */
static icxLuLut *
@@ -1860,6 +1966,7 @@ alloc_icxLuLut(
p->get_ranges = icxLu_get_ranges;
p->efv_wh_bk_points = icxLuEfv_wh_bk_points;
p->get_gamut = icxLuLutGamut;
+ p->get_cuspmap = icxLuLutCuspMap;
p->fwd_relpcs_outpcs = icxLuLut_fwd_relpcs_outpcs;
p->bwd_outpcs_relpcs = icxLuLut_bwd_outpcs_relpcs;
p->nearclip = 0; /* Set flag defaults */
@@ -2077,14 +2184,8 @@ icxLuLut *p /* Object being initialised */
p->clip.nearclip = 1;
} else { /* Vector clip */
- /* !!!! NOTE NOTE NOTE !!!! */
- /* We should re-write this to avoid calling p->clutTable->rev_interp(), */
- /* since this sets up all the rev acceleration tables for two calls, */
- /* if this lut is being setup from scattered data, and never used */
- /* for rev lookup */
icColorSpaceSignature clutos = p->natos;
- fprintf(stderr, "!!!!! setup_clip_icxLuLut with vector clip - possibly unnecessary rev setup !!!!\n");
p->clip.nearclip = 0;
p->clip.LabLike = 0;
p->clip.fdi = p->clutTable->fdi;
@@ -2092,95 +2193,25 @@ icxLuLut *p /* Object being initialised */
switch(clutos) {
case icxSigJabData:
case icSigLabData: {
- co pp; /* Room for all the solutions found */
- int nsoln; /* Number of solutions found */
- double cdir[MXDO]; /* Clip vector direction and length */
p->clip.LabLike = 1;
- /* Find high clip target */
- for (i = 0; i < p->inputChan; i++)
- pp.p[i] = 0.0; /* Set aux values */
- pp.v[0] = 105.0; pp.v[1] = pp.v[2] = 0.0; /* PCS Target value */
- cdir[0] = cdir[1] = cdir[2] = 0.0; /* Clip Target */
-
- p->inv_output(p, pp.v, pp.v); /* Compensate for output curve */
- p->inv_output(p, cdir, cdir);
-
- cdir[0] -= pp.v[0]; /* Clip vector */
- cdir[1] -= pp.v[1];
- cdir[2] -= pp.v[2];
-
- /* PCS -> Device with clipping */
- nsoln = p->clutTable->rev_interp(
- p->clutTable, /* rspl object */
- 0, /* No hint flags - might be in gamut, might vector clip */
- 1, /* Maxumum solutions to return */
- p->auxm, /* Auxiliary input targets */
- cdir, /* Clip vector direction and length */
- &pp); /* Input target and output solutions */
- /* returned solutions in pp[0..retval-1].p[] */
- nsoln &= RSPL_NOSOLNS; /* Get number of solutions */
-
- if (nsoln != 1) {
- p->pp->errc = 2;
- sprintf(p->pp->err,"Failed to find high clip target for Lab space");
- return p->pp->errc;
- }
-
- p->clip.ocent[0] = pp.v[0] - 0.001; /* Got main target */
- p->clip.ocent[1] = pp.v[1];
- p->clip.ocent[2] = pp.v[2];
-
- /* Find low clip target */
- pp.v[0] = -5.0; pp.v[1] = pp.v[2] = 0.0; /* PCS Target value */
- cdir[0] = 100.0; cdir[1] = cdir[2] = 0.0; /* Clip Target */
-
- p->inv_output(p, pp.v, pp.v); /* Compensate for output curve */
- p->inv_output(p, cdir, cdir);
- cdir[0] -= pp.v[0]; /* Clip vector */
- cdir[1] -= pp.v[1];
- cdir[2] -= pp.v[2];
-
- /* PCS -> Device with clipping */
- nsoln = p->clutTable->rev_interp(
- p->clutTable, /* rspl object */
- RSPL_WILLCLIP, /* Since there was no locus, we expect to have to clip */
- 1, /* Maxumum solutions to return */
- NULL, /* No auxiliary input targets */
- cdir, /* Clip vector direction and length */
- &pp); /* Input target and output solutions */
- /* returned solutions in pp[0..retval-1].p[] */
- nsoln &= RSPL_NOSOLNS; /* Get number of solutions */
- if (nsoln != 1) {
- p->pp->errc = 2;
- sprintf(p->pp->err,"Failed to find low clip target for Lab space");
- return p->pp->errc;
- }
-
- p->clip.ocentv[0] = pp.v[0] + 0.001 - p->clip.ocent[0]; /* Raw line vector */
- p->clip.ocentv[1] = pp.v[1] - p->clip.ocent[1];
- p->clip.ocentv[2] = pp.v[2] - p->clip.ocent[2];
-
- /* Compute vectors length */
- for (p->clip.ocentl = 0.0, i = 0; i < 3; i++)
- p->clip.ocentl += p->clip.ocentv[i] * p->clip.ocentv[i];
- p->clip.ocentl = sqrt(p->clip.ocentl);
- if (p->clip.ocentl <= 1e-8)
- p->clip.ocentl = 0.0;
-
+ /* Create a CuspMap to point vectors towards */
+ /* (Don't make it too fine, or there will be dips) */
+ p->clip.cm = p->get_cuspmap((icxLuBase *)p, 30);
break;
}
case icSigXYZData:
// ~~~~~~1 need to add this.
+ warning("xlut.c: setup_clip_icxLuLut() icSigXYZData case not implemented!");
+ /* Fall through */
default:
/* Do a crude approximation, that may not work. */
p->clutTable->get_out_range(p->clutTable, tmin, tmax);
- for (i = 0; i < p->clutTable->fdi; i++) {
+ for (i = 0; i < p->clutTable->fdi; i++)
p->clip.ocent[i] = (tmin[i] + tmax[i])/2.0;
- }
- p->clip.ocentl = 0.0;
+
break;
}
}
@@ -2306,13 +2337,13 @@ fprintf(stderr,"~1 Internal optimised 4D separations not yet implemented!\n");
/* Init the CAM model if it will be used */
if (pcsor == icxSigJabData || p->camclip) {
- if (vc != NULL) /* One has been provided */
+ if (vc != NULL) /* One has been provided */
p->vc = *vc; /* Copy the structure */
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.Yg, p->vc.Gxyz, XICC_USE_HK);
+ p->vc.Yf, p->vc.Yg, p->vc.Gxyz, XICC_USE_HK, p->vc.hkscale);
} else {
p->cam = NULL;
}
@@ -2330,8 +2361,8 @@ fprintf(stderr,"~1 Internal optimised 4D separations not yet implemented!\n");
p->pcs = pcsor;
if (xicp->pp->header->deviceClass == icSigAbstractClass) {
- p->ins = pcsor;
- p->outs = pcsor;
+ p->ins = pcsor;
+ p->outs = pcsor;
} else if (xicp->pp->header->deviceClass != icSigLinkClass) {
if (func == icmBwd || func == icmGamut || func == icmPreview)
@@ -2412,13 +2443,21 @@ fprintf(stderr,"~1 Internal optimised 4D separations not yet implemented!\n");
/* ------------------------------- */
{
int gres[MXDI];
+ int xflags = 0;
for (i = 0; i < p->inputChan; i++)
gres[i] = p->lut->clutPoints;
+#ifdef FASTREVSETUP_NON_CAM
+ /* Don't fill in nnrev array if we aren't going to use it */
+ if (p->camclip && p->nearclip)
+ xflags = RSPL_FASTREVSETUP;
+#endif
+
/* Create rspl based multi-dim table */
if ((p->clutTable = new_rspl((p->fastsetup ? RSPL_FASTREVSETUP : RSPL_NOFLAGS)
- | (flags & ICX_VERBOSE ? RSPL_VERBOSE : RSPL_NOFLAGS),
+ | (flags & ICX_VERBOSE ? RSPL_VERBOSE : RSPL_NOFLAGS)
+ | xflags,
p->inputChan, p->outputChan)) == NULL) {
p->pp->errc = 2;
sprintf(p->pp->err,"Creation of clut table rspl failed");
@@ -2438,6 +2477,17 @@ fprintf(stderr,"~1 Internal optimised 4D separations not yet implemented!\n");
}
+#ifdef USELCHWEIGHT
+ /* If we are not doing camclip, but our output is an Lab like space, */
+ /* then apply lchw weighting anyway. */
+ if (!p->camclip && (p->outs == icSigLabData || p->outs == icxSigJabData)) {
+ double lchw[MXRO] = { JCCWEIGHT, CCCWEIGHT, HCCWEIGHT };
+
+ /* Set the Nearest Neighbor clipping Weighting */
+ p->clutTable->rev_set_lchw(p->clutTable, lchw);
+ }
+#endif /* USELCHWEIGHT */
+
/* clut clipping is setup separately */
}
@@ -2500,22 +2550,24 @@ 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. */
+/* We weight the CAM nn clipping, to give a more L* and H* preserving clip direction. */
+/* Return error code. */
+/* (We are assuming nearest clipping - we aren't setting up properly for */
+/* vector clipping) */
static int
icxLuLut_init_clut_camclip(
icxLuLut *p) {
int e, gres[MXDI];
+ double lchw[MXRO] = { JCCWEIGHT, CCCWEIGHT, HCCWEIGHT };
/* 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] = CCJSCALE * 100.0; /* Nominal Jab output ranges */
+ cmin[0] = 0.0; cmax[0] = 100.0; /* Nominal Jab output ranges */
cmin[1] = -128.0; cmax[1] = 128.0;
cmin[2] = -128.0; cmax[2] = 128.0;
@@ -2536,6 +2588,11 @@ icxLuLut *p) {
return p->pp->errc;
}
+#ifdef USELCHWEIGHT
+ /* Set the Nearest Neighbor clipping Weighting */
+ p->cclutTable->rev_set_lchw(p->cclutTable, lchw);
+#endif /* USELCHWEIGHT */
+
for (e = 0; e < p->inputChan; e++)
gres[e] = p->lut->clutPoints;
@@ -3899,7 +3956,7 @@ int quality /* Quality metric, 0..3 */
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.Yg, p->vc.Gxyz, XICC_USE_HK);
+ p->vc.Yf, p->vc.Yg, p->vc.Gxyz, XICC_USE_HK, p->vc.hkscale);
if (flags & ICX_VERBOSE)
printf("Done A to B table creation\n");
@@ -3912,7 +3969,7 @@ int quality /* Quality metric, 0..3 */
/* ========================================================== */
-/* Context for creating gamut boundary points fro, xicc */
+/* Context for creating gamut boundary points from, xicc */
typedef struct {
gamut *g; /* Gamut being created */
icxLuLut *x; /* xLut we are working from */
@@ -4096,9 +4153,9 @@ double detail /* gamut detail level, 0.0 = def */
}
/* Scan only device surface */
- for (m1 = 0; m1 < inn; m1++) { /* Choose first coord to scan */
+ for (m1 = 0; m1 < (inn-1); m1++) { /* Choose first coord to scan */
if (co[m1] != 0)
- continue; /* Not at lower corner */
+ continue; /* Not at lower corner */
for (m2 = m1 + 1; m2 < inn; m2++) { /* Choose second coord to scan */
int x, y;
@@ -4216,7 +4273,7 @@ double detail /* gamut detail level, 0.0 = def */
default:
break;
}
- if ((cx.flu = p->get_luobj(p, ICX_CLIP_NEAREST , icmFwd, intent, pcs, icmLuOrdNorm,
+ if ((cx.flu = p->get_luobj(p, ICX_CLIP_NEAREST, icmFwd, intent, pcs, icmLuOrdNorm,
&plu->vc, NULL)) == NULL) {
return NULL; /* oops */
}
@@ -4314,6 +4371,304 @@ double detail /* gamut detail level, 0.0 = def */
return gam;
}
+/* ========================================================== */
+/* Cusp Map finding code. */
+/* ========================================================== */
+
+/* Context for creating Cusp Map points from, xicc */
+typedef struct {
+ icxCuspMap *cm; /* Cusp Map being created */
+ icxLuLut *x; /* xLut we are working from */
+ icxLuBase *flu; /* Forward xlookup */
+ double in[MAX_CHAN]; /* Device input values */
+} lutcuspmapctx;
+
+/* Function to pass to rspl to create cusp map from */
+/* forward xLut transform grid points. */
+static void
+lutfwdcuspmap_func(
+ void *pp, /* lutcuspmapctx structure */
+ double *out, /* output' value at clut grid point (ie. PCS' value) */
+ double *in /* input' value at clut grid point (ie. device' value) */
+) {
+ lutcuspmapctx *p = (lutcuspmapctx *)pp;
+ double pcso[3]; /* PCS output value */
+
+ /* Figure if we are over the ink limit. */
+ if ( (p->x->ink.tlimit >= 0.0 || p->x->ink.klimit >= 0.0)
+ && icxLimitD(p->x, in) > 0.0) {
+ int i;
+ double sf;
+
+ /* We are, so use the bracket search to discover a scale */
+ /* for the clut input' value that will put us on the ink limit. */
+
+ for (i = 0; i < p->x->inputChan; i++)
+ p->in[i] = in[i];
+
+ if (zbrent(&sf, 0.0, 1.0, 1e-4, icxLimitFind, pp) != 0) {
+ return; /* Give up */
+ }
+
+ /* Compute ink limit value */
+ for (i = 0; i < p->x->inputChan; i++)
+ p->in[i] = sf * in[i];
+
+ /* Compute the clut output for this clut input */
+ p->x->clut(p->x, pcso, p->in);
+ p->x->output(p->x, pcso, pcso);
+ p->x->out_abs(p->x, pcso, pcso);
+ } else { /* No ink limiting */
+ /* Convert the clut PCS' values to PCS output values */
+ p->x->output(p->x, pcso, out);
+ p->x->out_abs(p->x, pcso, pcso);
+ }
+
+ /* Expand the usp map surface with this point */
+ p->cm->expand(p->cm, pcso);
+
+ /* Leave out[] unchanged */
+}
+
+/* Expand cusp map with given point */
+/* (We use a segmentned maxima approach) */
+static void cuspmap_expand(icxCuspMap *p, double lab[3]) {
+ double h, C;
+ int ix;
+
+ /* Hue angle 0.0 .. 1.0 */
+ h = (0.5/3.14159265359) * atan2(lab[2], lab[1]);
+ h = (h < 0.0) ? h + 1.0 : h;
+
+ /* Slot index 0..res-1 */
+ ix = (int)floor(h * p->res + 0.5);
+ if (ix >= p->res)
+ ix -= p->res;
+
+ /* Chromanance */
+ C = sqrt(lab[1] * lab[1] + lab[2] * lab[2]);
+
+ /* New point at this angle with largest chromanance */
+ if (C > p->C[ix]) {
+ p->C[ix] = C;
+ p->L[ix] = lab[0];
+ }
+
+ /* Tracl min * max L */
+ if (lab[0] > p->Lmax[0])
+ icmCpy3(p->Lmax, lab);
+ if (lab[0] < p->Lmin[0])
+ icmCpy3(p->Lmin, lab);
+}
+
+/* Interpolate over any gaps in map */
+static void cuspmap_complete(icxCuspMap *p) {
+ int i, j, k;
+
+// printf("cuspmap list before fixups:\n");
+// for (i = 0; i < p->res; i++) {
+// printf(" %d: C = %f, L = %f\n",i, p->C[i], p->L[i]);
+// }
+
+ /* First check if there are any entries at all */
+ for (i = 0; i < p->res; i++) {
+ if (p->C[i] >= 0.0)
+ break;
+ }
+
+ if (i >= p->res) /* Nothing there - give up */
+ return;
+
+ /* See if there are any gaps */
+ j = -1;
+ for (i = 0; i < p->res; i++) {
+//printf("~1 checking %d\n",i);
+ if (p->C[i] >= 0.0) {
+ j = i; /* Last valid slot */
+//printf("~1 last valid %d\n",j);
+
+ /* Got a gap */
+ } else {
+ int ii = i;
+//printf("~1 found gap at %d\n",i);
+ if (j < 0) { /* No previous */
+//printf("~1 no previous\n");
+ for (j = p->res-1; j >= 0; j--) {
+ if (p->C[j] >= 0.0)
+ break;
+ }
+ }
+//printf("~1 got previous %d\n",j);
+ /* Find next, even if it's behind us */
+ for (k = i+1; k != i; k = (k+1) % p->res) {
+ if (p->C[k] >= 0.0)
+ break;
+ }
+//printf("~1 got next %d\n",k);
+
+ /* Interpolate between them */
+ for (; i != k; i = (i+1) % p->res) {
+ double prop;
+ int bb, tt;
+ bb = k > i ? k - i : k + p->res - i;
+ tt = k > j ? k - j : k + p->res - j;
+ prop = (double)bb/(double)tt;
+ p->C[i] = prop * p->C[j] + (1.0 - prop) * p->C[k];
+ p->L[i] = prop * p->L[j] + (1.0 - prop) * p->L[k];
+//printf("~1 interpolating %d with weight %f from %d and %d\n",i,prop,j,k);
+ }
+ if (k > ii) {
+ i = k; /* Continue from next valid */
+//printf("~1 continuing from %d\n",i);
+ } else {
+//printf("~1 we've looped back\n");
+ break; /* We've looped back */
+ }
+ }
+ }
+
+// printf("cuspmap list after fixups:\n");
+// for (i = 0; i < p->res; i++) {
+// printf(" %d: C = %f, L = %f\n",i, p->C[i], p->L[i]);
+// }
+}
+
+/* Return the corresponding cusp location, given the source point */
+static void cuspmap_getCusp(icxCuspMap *p,double cuspLCh[3], double srcLab[3]) {
+ double h, C;
+ int ix, ix0, ix1;
+
+ /* Hue angle 0.0 .. 1.0 */
+ h = (0.5/3.14159265359) * atan2(srcLab[2], srcLab[1]);
+ h = (h < 0.0) ? h + 1.0 : h;
+
+//printf("~1 getCusp in %f %f %f, h = %f\n", srcLab[0], srcLab[1], srcLab[2],h);
+
+ /* Slot index 0..res-1 */
+ ix = (int)floor(h * p->res + 0.5);
+ if (ix >= p->res)
+ ix -= p->res;
+
+ /* Indexes each side */
+ ix0 = ix > 0 ? ix-1 : p->res-1;
+ ix1 = ix < (p->res-1) ? ix+1 : 0;
+
+ cuspLCh[0] = p->L[ix];
+ cuspLCh[1] = p->C[ix];
+ if (cuspLCh[1] > p->C[ix0]) /* Be conservative with C value */
+ cuspLCh[1] = p->C[ix0];
+ if (cuspLCh[1] > p->C[ix1])
+ cuspLCh[1] = p->C[ix1];
+ cuspLCh[2] = 360.0 * h;
+
+//printf("~1 index %d, L %f C %f h %f\n", ix, cuspLCh[0], cuspLCh[1], cuspLCh[2]);
+}
+
+/* We're done with CuspMap */
+static void cuspmap_del(icxCuspMap *p) {
+ if (p != NULL) {
+ if (p->L != NULL)
+ free(p->L);
+ if (p->C != NULL)
+ free(p->C);
+ free(p);
+ }
+}
+
+/* Given an xicc lookup object, return an icxCuspMap object. */
+/* Note that the PCS must be Lab or Jab. */
+/* An icxLuLut type must be icmFwd, and the ink limit (if supplied) */
+/* will be applied. */
+/* Return NULL on error, check errc+err for reason */
+static icxCuspMap *icxLuLutCuspMap(
+icxLuBase *plu, /* this */
+int res /* Hue resolution */
+) {
+ xicc *p = plu->pp; /* parent xicc */
+ icxLuLut *luluto = (icxLuLut *)plu; /* Lookup xLut type object */
+ icColorSpaceSignature ins, pcs, outs;
+ icmLookupFunc func;
+ icRenderingIntent intent;
+ int inn, outn;
+ lutcuspmapctx cx;
+ icxCuspMap *cm;
+ int i;
+
+ /* get some details */
+ plu->spaces(plu, &ins, &inn, &outs, &outn, NULL, &intent, &func, &pcs);
+
+ if (func != icmFwd) {
+ p->errc = 1;
+ sprintf(p->err,"Creating CuspMap for anything other than Device -> PCS is not supported.");
+ return NULL;
+ }
+
+ if (pcs != icSigLabData && pcs != icxSigJabData) {
+ p->errc = 1;
+ sprintf(p->err,"Creating CuspMap PCS of other than Lab or Jab is not supported.");
+ return NULL;
+ }
+
+ cx.cm = cm = (icxCuspMap *) calloc(1, sizeof(icxCuspMap));
+ cx.x = luluto;
+
+ if (cx.cm == NULL) {
+ p->errc = 2;
+ sprintf(p->err,"Malloc of icxCuspMap failed");
+ return NULL;
+ }
+
+ if ((cm->L = (double *)malloc(sizeof(double) * res)) == NULL) {
+ free(cm);
+ p->errc = 2;
+ sprintf(p->err,"Malloc of icxCuspMap failed");
+ return NULL;
+ }
+
+ if ((cm->C = (double *)malloc(sizeof(double) * res)) == NULL) {
+ free(cm->L);
+ free(cm);
+ p->errc = 2;
+ sprintf(p->err,"Malloc of icxCuspMap failed");
+ return NULL;
+ }
+
+ cm->res = res;
+ cm->expand = cuspmap_expand;
+ cm->getCusp = cuspmap_getCusp;
+ cm->del = cuspmap_del;
+
+ for (i = 0; i < res; i++) {
+ cm->L[i] = -1.0;
+ cm->C[i] = -1.0;
+ }
+ cm->Lmax[0] = -1.0;
+ cm->Lmin[0] = 101.0;
+
+ /* Scan through grid, expanding the CuspMap. */
+ luluto->clutTable->scan_rspl(
+ luluto->clutTable, /* this */
+ RSPL_NOFLAGS, /* Combination of flags */
+ (void *)&cx, /* Opaque function context */
+ lutfwdcuspmap_func /* Function to set from */
+ );
+
+ cm->Lmax[0] -= 0.1; /* Make sure tips intersect */
+ cm->Lmin[0] += 0.1;
+
+ for (i = 0; i < res; i++) {
+ if (cm->L[i] > cm->Lmax[0])
+ cm->L[i] = cm->Lmax[0];
+ if (cm->L[i] < cm->Lmin[0])
+ cm->L[i] = cm->Lmin[0];
+ }
+
+ /* Fill in any gaps */
+ cuspmap_complete(cm);
+
+ return cm;
+}
+
/* ----------------------------------------------- */
#ifdef DEBUG
#undef DEBUG /* Limit extent to this file */
diff --git a/xicc/xmatrix.c b/xicc/xmatrix.c
index 3d4fca6..c18ef1c 100644
--- a/xicc/xmatrix.c
+++ b/xicc/xmatrix.c
@@ -223,6 +223,7 @@ double *out, double *in) {
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static gamut *icxLuMatrixGamut(icxLuBase *plu, double detail);
+static icxCuspMap *icxLuMatrixCuspMap(icxLuBase *plu, int res) { return NULL; };
/* Do the basic icxLuMatrix creation and initialisation */
static icxLuMatrix *
@@ -246,6 +247,7 @@ alloc_icxLuMatrix(
p->get_ranges = icxLu_get_ranges;
p->efv_wh_bk_points = icxLuEfv_wh_bk_points;
p->get_gamut = icxLuMatrixGamut;
+ p->get_cuspmap = icxLuMatrixCuspMap;
p->fwd_relpcs_outpcs = icxLuMatrix_fwd_relpcs_outpcs;
p->bwd_outpcs_relpcs = icxLuMatrix_bwd_outpcs_relpcs;
@@ -315,7 +317,7 @@ int dir /* 0 = fwd, 1 = bwd */
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.Yg, p->vc.Gxyz, XICC_USE_HK);
+ p->vc.Yf, p->vc.Yg, p->vc.Gxyz, XICC_USE_HK, p->vc.hkscale);
} else {
p->cam = NULL;
}
@@ -1136,7 +1138,7 @@ static void icxMM_force_exact(icxMatrixModel *p, double *targ, double *rgb) {
icmAry2XYZ(_ap, axyz);
icmAry2XYZ(_tp, txyz);
if (p->picc != NULL)
- p->picc->chromAdaptMatrix(p->picc, ICM_CAM_NONE, _tp, _ap, cmat);
+ p->picc->chromAdaptMatrix(p->picc, ICM_CAM_NONE, NULL, cmat, _tp, _ap);
else
icmChromAdaptMatrix(ICM_CAM_BRADFORD, _tp, _ap, cmat);
@@ -1508,11 +1510,9 @@ double smooth /* Curve smoothing, nominally 1.0 */
icmXYZNumber _wp;
icmAry2XYZ(_wp, wp);
- /* Absolute->Aprox. Relative Adaptation matrix */
- icco->chromAdaptMatrix(icco, ICM_CAM_NONE, icmD50, _wp, fromAbs);
-
+ /* Absolute->Aprox. Relative Adaptation matrix, and */
/* Aproximate relative to absolute conversion matrix */
- icco->chromAdaptMatrix(icco, ICM_CAM_NONE, _wp, icmD50, toAbs);
+ icco->chromAdaptMatrix(icco, ICM_CAM_NONE, toAbs, fromAbs, icmD50, _wp);
}
} else {
@@ -1618,7 +1618,7 @@ printf(" set black %d w = %f\n", nodp,rpoints[nodp].w);
/* Matrix needed to correct aprox white to target D50 */
icmAry2XYZ(_wp, aw); /* Aprox relative target white point */
- icco->chromAdaptMatrix(icco, ICM_CAM_NONE, icmD50, _wp, cmat); /* Correction */
+ icco->chromAdaptMatrix(icco, ICM_CAM_NONE, NULL, cmat, icmD50, _wp); /* Correction */
/* Compute the current absolute white point */
icmMulBy3x3(wp, toAbs, aw);
@@ -1628,8 +1628,7 @@ printf(" set black %d w = %f\n", nodp,rpoints[nodp].w);
/* Fix relative conversions to leave absolute response unchanged. */
icmAry2XYZ(_wp, wp); /* Actual white point */
- icco->chromAdaptMatrix(icco, ICM_CAM_NONE, icmD50, _wp, fromAbs);
- icco->chromAdaptMatrix(icco, ICM_CAM_NONE, _wp, icmD50, toAbs);
+ icco->chromAdaptMatrix(icco, ICM_CAM_NONE, toAbs, fromAbs, icmD50, _wp);
if (flags & ICX_VERBOSE) {
double tw[3];
@@ -1723,8 +1722,7 @@ printf(" set black %d w = %f\n", nodp,rpoints[nodp].w);
/* Fix absolute conversions to leave absolute response unchanged. */
icmAry2XYZ(_wp, wp); /* Actual white point */
- icco->chromAdaptMatrix(icco, ICM_CAM_NONE, icmD50, _wp, fromAbs);
- icco->chromAdaptMatrix(icco, ICM_CAM_NONE, _wp, icmD50, toAbs);
+ icco->chromAdaptMatrix(icco, ICM_CAM_NONE, toAbs, fromAbs, icmD50, _wp);
}
/* Look up the actual black point */
diff --git a/xicc/xmono.c b/xicc/xmono.c
index fe9c55e..d1f091b 100644
--- a/xicc/xmono.c
+++ b/xicc/xmono.c
@@ -194,6 +194,7 @@ double *out, double *in) {
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static gamut *icxLuMonoGamut(icxLuBase *plu, double detail);
+static icxCuspMap *icxLuMonoCuspMap(icxLuBase *plu, int res) { return NULL; };
static icxLuBase *
new_icxLuMono(
@@ -222,6 +223,7 @@ int dir /* 0 = fwd, 1 = bwd */
p->get_ranges = icxLu_get_ranges;
p->efv_wh_bk_points = icxLuEfv_wh_bk_points;
p->get_gamut = icxLuMonoGamut;
+ p->get_cuspmap = icxLuMonoCuspMap;
p->fwd_relpcs_outpcs = icxLuMono_fwd_relpcs_outpcs;
p->bwd_outpcs_relpcs = icxLuMono_bwd_outpcs_relpcs;
p->nearclip = 0; /* Set flag defaults */
@@ -262,7 +264,7 @@ int dir /* 0 = fwd, 1 = bwd */
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->Yg, vc->Gxyz,
- XICC_USE_HK);
+ XICC_USE_HK, vc->hkscale);
} else
p->cam = NULL;
diff --git a/xicc/xspect.c b/xicc/xspect.c
index 6738113..9df298e 100644
--- a/xicc/xspect.c
+++ b/xicc/xspect.c
@@ -42,6 +42,7 @@
# include "plot.h" /* For debugging */
#else
# include "numsup.h"
+# include "sa_conv.h"
#endif
#include "conv.h"
#include "xspect.h"
@@ -291,7 +292,6 @@ static xspect il_D65 = {
}
};
-#ifndef SALONEINSTLIB
/* General temperature Daylight spectra (Using OLDER CIE 1960 u,v CCT) */
/* 300 - 830nm ub 5nm intervals. */
/* Fill in the given xspect with the specified daylight illuminant */
@@ -511,8 +511,6 @@ static int daylight_il(xspect *sp, double ct) {
return 0;
}
-#endif /* !SALONEINSTLIB */
-
/* General temperature Planckian (black body) spectra using CIE 15:2004 */
/* Fill in the given xspect with the specified Planckian illuminant */
/* normalised so that 560nm = 100. */
@@ -744,11 +742,11 @@ double temp /* Optional temperature in degrees kelvin, for Dtemp and Ptemp *
case icxIT_Spectrocam:
*sp = il_Spectrocam;
return 0;
+#endif
case icxIT_ODtemp:
return daylight_old_il(sp, temp);
case icxIT_Dtemp:
return daylight_il(sp, temp);
-#endif
case icxIT_OPtemp:
return planckian_old_il(sp, temp);
case icxIT_Ptemp:
@@ -3819,10 +3817,37 @@ static xspect FWA1_emit = {
#endif /* STOCKFWA */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Return a string describing the inst_meas_type */
+char *meas_type2str(inst_meas_type mt) {
+
+ switch (mt) {
+ case inst_mrt_none:
+ return "None";
+ case inst_mrt_emission:
+ return "Emission";
+ case inst_mrt_ambient:
+ return "Ambient";
+ case inst_mrt_emission_flash:
+ return "Emission Flash";
+ case inst_mrt_ambient_flash:
+ return "Ambient Flash";
+ case inst_mrt_reflective:
+ return "Reflective";
+ case inst_mrt_transmissive:
+ return "Transmissive";
+ case inst_mrt_sensitivity:
+ return "Sensitivity";
+ case inst_mrt_frequency:
+ return "Frequency";
+ }
+ return "Unknown";
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* save a set of spectrum to a CGATS file */
/* type 0 = SPECT, 1 = CMF */
/* Return NZ on error */
-int write_nxspect(char *fname, xspect *sp, int nspec, int type) {
+int write_nxspect(char *fname, inst_meas_type mt, xspect *sp, int nspec, int type) {
char buf[100];
time_t clk = time(0);
struct tm *tsp = localtime(&clk);
@@ -3844,6 +3869,33 @@ int write_nxspect(char *fname, xspect *sp, int nspec, int type) {
atm[strlen(atm)-1] = '\000'; /* Remove \n from end */
ocg->add_kword(ocg, 0, "CREATED",atm, NULL);
+ if (mt != inst_mrt_none) {
+ char *tag = NULL;
+ switch (mt) {
+ case inst_mrt_emission:
+ tag = "EMISSION"; break;
+ case inst_mrt_ambient:
+ tag = "AMBIENT"; break;
+ case inst_mrt_emission_flash:
+ tag = "EMISSION_FLASH"; break;
+ case inst_mrt_ambient_flash:
+ tag = "AMBIENT_FLASH"; break;
+ case inst_mrt_reflective:
+ tag = "REFLECTIVE"; break;
+ case inst_mrt_transmissive:
+ tag = "TRANSMISSIVE"; break;
+ case inst_mrt_sensitivity:
+ tag = "SENSITIVITY"; break;
+ default:
+ break;
+ }
+ if (tag != NULL)
+ ocg->add_kword(ocg, 0, "MEAS_TYPE",tag, NULL);
+ }
+
+ sprintf(buf,"%d", sp->spec_n);
+ ocg->add_kword(ocg, 0, "SPECTRAL_BANDS",buf, NULL);
+
sprintf(buf,"%d", sp->spec_n);
ocg->add_kword(ocg, 0, "SPECTRAL_BANDS",buf, NULL);
sprintf(buf,"%f", sp->spec_wl_short);
@@ -3889,13 +3941,14 @@ int write_nxspect(char *fname, xspect *sp, int nspec, int type) {
}
/* Restore a set of spectrum from a CGATS file. */
-/* Up to nspec will be restored starting at offset off.. */
+/* 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 */
+/* File 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) {
+int read_nxspect(xspect *sp, inst_meas_type *mt, char *fname,
+ int *nret, int off, int nspec, int type) {
cgats *icg; /* input cgats structure */
char buf[100];
int sflds[XSPECT_MAX_BANDS];
@@ -3931,6 +3984,26 @@ int read_nxspect(xspect *sp, char *fname, int *nret, int off, int nspec, int typ
return 1;
}
+ if (mt != NULL && (ii = icg->find_kword(icg, 0, "MEAS_TYPE")) >= 0) {
+
+ if (strcmp(icg->t[0].kdata[ii], "EMISSION") == 0)
+ *mt = inst_mrt_emission;
+ else if (strcmp(icg->t[0].kdata[ii], "AMBIENT") == 0)
+ *mt = inst_mrt_ambient;
+ else if (strcmp(icg->t[0].kdata[ii], "EMISSION_FLASH") == 0)
+ *mt = inst_mrt_emission_flash;
+ else if (strcmp(icg->t[0].kdata[ii], "AMBIENT_FLASH") == 0)
+ *mt = inst_mrt_ambient_flash;
+ else if (strcmp(icg->t[0].kdata[ii], "REFLECTIVE") == 0)
+ *mt = inst_mrt_reflective;
+ else if (strcmp(icg->t[0].kdata[ii], "TRANSMISSIVE") == 0)
+ *mt = inst_mrt_transmissive;
+ else if (strcmp(icg->t[0].kdata[ii], "SENSITIVITY") == 0)
+ *mt = inst_mrt_sensitivity;
+ else
+ *mt = inst_mrt_none;
+ }
+
if ((ii = icg->find_kword(icg, 0, "SPECTRAL_BANDS")) < 0) {
DBG ("Input file doesn't contain keyword SPECTRAL_BANDS\n");
icg->del(icg);
@@ -3980,12 +4053,12 @@ int read_nxspect(xspect *sp, char *fname, int *nret, int off, int nspec, int typ
}
/* Read all the spectra */
- for (j = off; j < nspec && j < icg->t[0].nsets; j++) {
+ for (j = off; j < (off + nspec) && j < icg->t[0].nsets; j++) {
- XSPECT_COPY_INFO(&sp[j], &proto);
+ XSPECT_COPY_INFO(&sp[j-off], &proto);
for (i = 0; i < proto.spec_n; i++) {
- sp[j].spec[i] = *((double *)icg->t[0].fdata[j][sflds[i]]);
+ sp[j-off].spec[i] = *((double *)icg->t[0].fdata[j][sflds[i]]);
}
}
if (nret != NULL)
@@ -4000,17 +4073,17 @@ int read_nxspect(xspect *sp, char *fname, int *nret, int off, int nspec, int typ
/* save a spectrum to a CGATS file */
/* Return NZ on error */
-int write_xspect(char *fname, xspect *sp) {
- return write_nxspect(fname, sp, 1, 0);
+int write_xspect(char *fname, inst_meas_type mt, xspect *sp) {
+ return write_nxspect(fname, mt, sp, 1, 0);
}
/* restore a spectrum from a CGATS file */
/* Return NZ on error */
/* (Would be nice to return an error message!) */
-int read_xspect(xspect *sp, char *fname) {
+int read_xspect(xspect *sp, inst_meas_type *mt, char *fname) {
int rv, nret;
- if ((rv = read_nxspect(sp, fname, &nret, 0, 1, 1)) != 0)
+ if ((rv = read_nxspect(sp, mt, fname, &nret, 0, 1, 1)) != 0)
return rv;
if (nret != 1) {
DBG("Didn't read one spectra\n");
@@ -4024,19 +4097,25 @@ int read_xspect(xspect *sp, char *fname) {
/* save a set of 3 spectrum to a CGATS CMF file */
/* Return NZ on error */
int write_cmf(char *fname, xspect sp[3]) {
- return write_nxspect(fname, sp, 3, 1);
+ return write_nxspect(fname, inst_mrt_sensitivity, sp, 3, 1);
}
/* restore a spectrum from a CGATS file */
/* Return NZ on error */
/* (Would be nice to return an error message!) */
int read_cmf(xspect sp[3], char *fname) {
+ inst_meas_type mt;
int rv, nret;
- if ((rv = read_nxspect(sp, fname, &nret, 0, 3, 2)) != 0) {
+ /* Hmm. Should we check inst_meas_type is none || sensitivity ? */
+ if ((rv = read_nxspect(sp, &mt, fname, &nret, 0, 3, 2)) != 0) {
DBG("read_nxspect failed\n");
return rv;
}
+ if (mt != inst_mrt_none && mt != inst_mrt_sensitivity) {
+ DBG("read_nxspect - wrong measurement type\n");
+ return 1;
+ }
if (nret != 3) {
DBG("Didn't read three spectra\n");
return 1;
@@ -4050,7 +4129,7 @@ int read_cmf(xspect sp[3], char *fname) {
/* Get a raw 3rd order polinomial interpolated spectrum value. */
-/* Return NZ if value is valid, Z and last valid value */
+/* Return NZ if value is valid, Z and xtrapolated value */
/* if outside the range */
/* NOTE: Returned value isn't normalised by sp->norm */
static int getval_raw_xspec_poly3(xspect *sp, double *rv, double xw) {
@@ -4072,32 +4151,28 @@ static int getval_raw_xspec_poly3(xspect *sp, double *rv, double xw) {
rc = 0;
}
- /* Compute fraction 0.0 - 1.0 out of known spectrum */
+ /* Compute fraction 0.0 - 1.0 out of known spectrum. */
+ /* Place it so that the target wavelength lands in middle section */
+ /* of Lagrange basis points. */
spcing = (sp->spec_wl_long - sp->spec_wl_short)/(sp->spec_n-1.0);
f = (xw - sp->spec_wl_short) / (sp->spec_wl_long - sp->spec_wl_short);
f *= (sp->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->spec_n - 2))
- i = (sp->spec_n - 2);
+ if (i < 1) /* Limit to valid Lagrange basis index range, */
+ i = 1; /* and extrapolate from that at ends. */
+ else if (i > (sp->spec_n - 3))
+ i = (sp->spec_n - 3);
/* Setup the surrounding values */
x[0] = sp->spec_wl_short + (i-1) * spcing;
- if (i == 0)
- y[0] = sp->spec[i];
- else
- y[0] = sp->spec[i-1];
+ y[0] = sp->spec[i-1];
x[1] = sp->spec_wl_short + i * spcing;
y[1] = sp->spec[i];
x[2] = sp->spec_wl_short + (i+1) * spcing;
y[2] = sp->spec[i+1];
x[3] = sp->spec_wl_short + (i+2) * spcing;
- if ((i+2) < sp->spec_n)
- y[3] = sp->spec[i+2];
- else
- y[3] = sp->spec[i+1];
+ y[3] = sp->spec[i+2];
#ifndef NEVER
/* Compute interpolated value using Lagrange: */
@@ -4346,13 +4421,14 @@ void xspect2xspect(xspect *dst, xspect *targ, xspect *src) {
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Plot up to 3 spectra */
-void xspect_plot(xspect *sp1, xspect *sp2, xspect *sp3) {
- double xx[XSPECT_MAX_BANDS];
- double y1[XSPECT_MAX_BANDS];
- double y2[XSPECT_MAX_BANDS];
- double y3[XSPECT_MAX_BANDS];
+void xspect_plot_w(xspect *sp1, xspect *sp2, xspect *sp3, int wait) {
+ static double xx[XSPECT_MAX_BANDS];
+ static double y1[XSPECT_MAX_BANDS];
+ static double y2[XSPECT_MAX_BANDS];
+ static double y3[XSPECT_MAX_BANDS];
int j;
double wl, wlshort, wllong;
+ double ymax = 0.0;
if (sp1 == NULL)
return;
@@ -4384,14 +4460,28 @@ void xspect_plot(xspect *sp1, xspect *sp2, xspect *sp3) {
#endif
xx[j] = wl;
y1[j] = value_xspect(sp1, wl);
- if (sp2 != NULL)
+ if (y1[j] > ymax)
+ ymax = y1[j];
+ if (sp2 != NULL) {
y2[j] = value_xspect(sp2, wl);
- if (sp3 != NULL)
+ if (y2[j] > ymax)
+ ymax = y2[j];
+ }
+ if (sp3 != NULL) {
y3[j] = value_xspect(sp3, wl);
+ if (y3[j] > ymax)
+ ymax = y3[j];
+ }
}
- do_plot(xx, y1, sp2 != NULL ? y2 : NULL, sp3 != NULL ? y3 : NULL, j);
+// do_plot(xx, y1, sp2 != NULL ? y2 : NULL, sp3 != NULL ? y3 : NULL, j);
+ do_plot_x(xx, y1, sp2 != NULL ? y2 : NULL, sp3 != NULL ? y3 : NULL, j,
+ wait, 0.0, -1.0, 0.0, ymax, 1.0);
}
+/* Plot up to 3 spectra & wait for key */
+void xspect_plot(xspect *sp1, xspect *sp2, xspect *sp3) {
+ xspect_plot_w(sp1, sp2, sp3, 1);
+}
/* Plot up to 10 spectra */
void xspect_plot10(xspect *sp, int n) {
@@ -5644,12 +5734,10 @@ xspect *in /* Spectrum to be converted */
#endif /* CLAMP_XYZ */
}
-#ifndef SALONEINSTLIB
/* If Lab is target, convert to D50 Lab */
if (p->doLab) {
icmXYZ2Lab(&icmD50, out, out);
}
-#endif /* !SALONEINSTLIB */
if (sout != NULL) {
*sout = *in; /* Structure copy */
@@ -5682,9 +5770,9 @@ xsp2cie *p
/* Create and return a new spectral conversion object */
xsp2cie *new_xsp2cie(
icxIllumeType ilType, /* Illuminant */
-xspect *custIllum, /* Optional custom illuminant */
+xspect *custIllum, /* Custom illuminant if ilType == icxIT_custom */
icxObserverType obType, /* Observer */
-xspect custObserver[3], /* Optional custom observer */
+xspect custObserver[3], /* Custom observer if obType == icxOT_custom */
icColorSpaceSignature rcs, /* Return color space, icSigXYZData or D50 icSigLabData */
/* ** Must be icSigXYZData if SALONEINSTLIB ** */
icxClamping clamp /* NZ to clamp XYZ/Lab to be +ve */
@@ -7781,6 +7869,7 @@ int viscct /* nz to use visual CIEDE2000, 0 to use CCT CIE 1960 UCS. */
/* An observer type can be chosen for interpretting the spectrum of the input and */
/* the illuminant. */
/* Return -1.0 on erorr */
+/* (Faster, slightly less accurate version of icx_XYZ2ill_ct()) */
double icx_XYZ2ill_ct2(
double txyz[3], /* If not NULL, return the XYZ of the locus temperature */
icxIllumeType ilType, /* Type of illuminant, icxIT_[O]Dtemp or icxIT_[O]Ptemp */
@@ -7828,6 +7917,7 @@ int viscct /* nz to use visual CIEDE2000, 0 to use CCT CIE 1960 UCS. */
/* An observer type can be chosen for interpretting the spectrum of the input and */
/* the illuminant. */
/* Return xyz[0] = -1.0 on erorr */
+/* (Faster, slightly less accrurate alternative to standardIlluminant()) */
void icx_ill_ct2XYZ(
double xyz[3], /* Return the XYZ value */
icxIllumeType ilType, /* Type of illuminant, icxIT_[O]Dtemp or icxIT_[O]Ptemp */
@@ -8780,9 +8870,44 @@ double ct /* Input temperature in degrees K */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/* Given a reflectance/transmittance spectrum, */
+/* an illuminant definition and an observer model, return */
+/* the XYZ value for that spectrum. */
+/* Return 0 on sucess, 1 on error */
+/* (One shot version of xsp2cie etc.) */
+int icx_sp2XYZ(
+double xyz[3], /* Return XYZ value */
+icxObserverType obType, /* Observer */
+xspect custObserver[3], /* Optional custom observer */
+icxIllumeType ilType, /* Type of illuminant, icxIT_[O]Dtemp or icxIT_[O]Ptemp */
+double ct, /* Input temperature in degrees K */
+xspect *custIllum, /* Optional custom illuminant */
+xspect *sp /* Spectrum to be converted */
+) {
+ xspect ill; /* Xspect to fill in */
+ xsp2cie *conv; /* Means of converting spectrum to XYZ */
+
+ if (ilType == icxIT_custom)
+ ill = *custIllum;
+ else if (standardIlluminant(&ill, ilType, ct) != 0)
+ return 1;
+
+ if ((conv = new_xsp2cie(icxIT_custom, &ill, obType, custObserver, icSigXYZData, 1)) == NULL)
+ return 1;
+
+ conv->convert(conv, xyz, sp);
+
+ conv->del(conv);
+
+ return 0;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
/* Given an illuminant definition and an observer model, return */
/* the normalised XYZ value for that spectrum. */
/* Return 0 on sucess, 1 on error */
+/* (One shot version of xsp2cie etc.) */
int icx_ill_sp2XYZ(
double xyz[3], /* Return XYZ value with Y == 1 */
icxObserverType obType, /* Observer */
@@ -8816,9 +8941,9 @@ xspect *custIllum /* Optional custom illuminant */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Aproximate CCT using polinomial. No good < 3000K */
-/* Use this for sanity check */
#ifdef NEVER
-static double aprox_CCT(double xyz[3]) {
+/* Hernndez-Andrs, Lee & Romero exponential approximation (more fragile) */
+static double aprox_CCT_Yxy(double Yxy[3]) {
double xe = 0.3366;
double ye = 0.1735;
double A0 = -949.86315;
@@ -8828,29 +8953,62 @@ static double aprox_CCT(double xyz[3]) {
double t2 = 0.20039;
double A3 = 0.00004;
double t3 = 0.07125;
- double Yxy[3];
double n;
double cct;
- icmXYZ2Yxy(Yxy, xyz);
n = (Yxy[1] - xe)/(Yxy[2] - ye);
cct = A0 + A1 * exp(-n/t1) + A2 * exp(-n/t2) + A3 * exp(-n/t3);
return cct;
}
#else
-static double aprox_CCT(double xyz[3]) {
- double Yxy[3];
+/* McCamy's cubic approximation: (more robust) */
+static double aprox_CCT_Yxy(double Yxy[3]) {
double n;
double cct;
- icmXYZ2Yxy(Yxy, xyz);
n = (Yxy[1] - 0.3320)/(Yxy[2] - 0.1858);
cct = -449.0 * n * n * n + 3525.0 * n * n - 6823.3 * n + 5520.33;
return cct;
}
#endif
+double aprox_CCT(double xyz[3]) {
+ double Yxy[3];
+
+ icmXYZ2Yxy(Yxy, xyz);
+
+ return aprox_CCT_Yxy(Yxy);
+}
+
+/* Aproximate x,y from CCT using Kim et al's cubic spline. */
+/* Invalid < 1667 and > 25000 */
+/* (Doesn't set Yxy[0]) */
+void aprox_plankian(double Yxy[3], double ct) {
+ double t1 = 1e3/ct;
+ double t2 = t1 * t1;
+ double t3 = t2 * t1;
+ double xc, xc2, xc3, yc;
+
+ if (ct <= 4000.0)
+ xc = -0.2661239 * t3 - 0.2343580 * t2 + 0.8776956 * t1 + 0.179910;
+ else
+ xc = -3.0258469 * t3 + 2.1070379 * t2 + 0.2226347 * t2 + 0.240390;
+
+ xc2 = xc * xc;
+ xc3 = xc2 * xc;
+
+ if (ct <= 2222.0)
+ yc = -1.1063814 * xc3 - 1.34811020 * xc2 + 2.18555832 * xc - 0.20219683;
+ else if (ct <= 4000.0)
+ yc = -0.9549476 * xc3 - 1.37418593 * xc2 + 2.09137015 * xc - 0.16748867;
+ else
+ yc = 3.0817580 * xc3 - 5.87338670 * xc2 + 3.75112997 * xc - 0.37001483;
+
+ Yxy[1] = xc;
+ Yxy[2] = yc;
+}
+
/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Full precision CCT */
diff --git a/xicc/xspect.h b/xicc/xspect.h
index 3e0caeb..3dd99a5 100644
--- a/xicc/xspect.h
+++ b/xicc/xspect.h
@@ -27,7 +27,7 @@
#ifndef SALONEINSTLIB
#include "icc.h" /* icclib ICC definitions */
#else /* SALONEINSTLIB */
-#include "conv.h" /* fake icclib ICC definitions */
+#include "sa_conv.h" /* fake icclib ICC definitions */
#endif /* SALONEINSTLIB */
#ifdef __cplusplus
@@ -36,6 +36,24 @@
/* ------------------------------------------------------------------------------ */
+/* Type of measurement result */
+typedef enum { /* XYZ units, Spectral units */
+ inst_mrt_none = 0, /* Not set */
+ inst_mrt_emission = 1, /* cd/m^2, mW/(m^2.sr.nm) */
+ inst_mrt_ambient = 2, /* Lux mW/(m^2.nm) */
+ inst_mrt_emission_flash = 3, /* cd.s/m^2, mW.s/(m^2.sr.nm) */
+ inst_mrt_ambient_flash = 4, /* Lux.s mW.s/(m^2.nm) */
+ inst_mrt_reflective = 5, /* %, %/nm */
+ inst_mrt_transmissive = 6, /* %, %/nm */
+ inst_mrt_sensitivity = 7, /* %, %/nm */
+ inst_mrt_frequency = 8 /* Hz */
+} inst_meas_type;
+
+/* Return a string describing the inst_meas_type */
+char *meas_type2str(inst_meas_type mt);
+
+/* ------------------------------------------------------------------------------ */
+
/* Structure for conveying spectral information */
/* NOTE :- should ditch norm, and replace it by */
@@ -87,8 +105,8 @@ typedef struct {
#ifndef SALONEINSTLIB
/* Single spectrum utility functions. Return NZ if error */
-int write_xspect(char *fname, xspect *s);
-int read_xspect(xspect *sp, char *fname);
+int write_xspect(char *fname, inst_meas_type mt, xspect *s);
+int read_xspect(xspect *sp, inst_meas_type *mt, char *fname);
/* CMF utility functions. Return NZ if error */
int write_cmf(char *fname, xspect cmf[3]);
@@ -96,11 +114,12 @@ int read_cmf(xspect cmf[3], char *fname);
/* Save a set of nspec spectrum to a CGATS file. Return NZ if error */
/* type 0 = SPECT, 1 = CMF */
-int write_nxspect(char *fname, xspect *sp, int nspec, int type);
+int write_nxspect(char *fname, inst_meas_type mt, xspect *sp, int nspec, int type);
/* Restore a set of up to nspec spectrum from a CGATS file. Return NZ if error */
/* type = any, 1 = SPECT, 2 = CMF, 3 = both */
-int read_nxspect(xspect *sp, char *fname, int *nret, int off, int nspec, int type);
+int read_nxspect(xspect *sp, inst_meas_type *mt,
+ char *fname, int *nret, int off, int nspec, int type);
#endif /* !SALONEINSTLIB*/
@@ -115,6 +134,9 @@ void xspect_denorm(xspect *sp);
void xspect2xspect(xspect *dst, xspect *targ, xspect *src);
/* Plot up to 3 spectra */
+void xspect_plot_w(xspect *sp1, xspect *sp2, xspect *sp3, int wait);
+
+/* Plot up to 3 spectra & wait for key */
void xspect_plot(xspect *sp1, xspect *sp2, xspect *sp3);
/* Plot up to 10 spectra */
@@ -145,9 +167,9 @@ typedef enum {
icxIT_F8 = 12, /* Fluorescent, Broad Band 5000K, CRI 95 */
icxIT_F10 = 13, /* Fluorescent Narrow Band 5000K, CRI 81 */
icxIT_Spectrocam = 14, /* Spectrocam Xenon Lamp */
+#endif /* !SALONEINSTLIB*/
icxIT_ODtemp = 15, /* Daylight at specified temperature */
icxIT_Dtemp = 16, /* 15:2004 Daylight at specified temperature */
-#endif /* !SALONEINSTLIB*/
icxIT_OPtemp = 17, /* Planckian at specified temperature */
icxIT_Ptemp = 18 /* 15:2004 Planckian at specified temperature */
} icxIllumeType;
@@ -317,10 +339,10 @@ struct _xsp2cie {
xsp2cie *new_xsp2cie(
icxIllumeType ilType, /* Observer Illuminant to use */
- xspect *custIllum,
+ xspect *custIllum, /* Custom illuminant if ilType == icxIT_custom */
icxObserverType obType, /* Observer */
- xspect custObserver[3],
+ xspect custObserver[3], /* Custom observer if obType == icxOT_custom */
icColorSpaceSignature rcs, /* Return color space, icSigXYZData or D50 icSigLabData */
/* ** Must be icSigXYZData if SALONEINSTLIB ** */
icxClamping clamp /* NZ to clamp XYZ/Lab to be +ve */
@@ -334,6 +356,7 @@ xsp2cie *new_xsp2cie(
/* An observer type can be chosen for interpretting the spectrum of the input and */
/* the illuminant. */
/* Return -1.0 on erorr */
+/* (Faster, slightly less accurate version of icx_XYZ2ill_ct()) */
double icx_XYZ2ill_ct2(
double txyz[3], /* If not NULL, return the XYZ of the locus temperature */
icxIllumeType ilType, /* Type of illuminant, icxIT_[O]Dtemp or icxIT_[O]Ptemp */
@@ -347,6 +370,7 @@ int viscct /* nz to use visual CIEDE2000, 0 to use CCT CIE 1960 UCS. */
/* An observer type can be chosen for interpretting the spectrum of the input and */
/* the illuminant. */
/* Return xyz[0] = -1.0 on erorr */
+/* (Faster, slightly less accrurate alternative to standardIlluminant()) */
void icx_ill_ct2XYZ(
double xyz[3], /* Return the XYZ value */
icxIllumeType ilType, /* Type of illuminant, icxIT_[O]Dtemp or icx[O]IT_Ptemp */
@@ -391,6 +415,86 @@ xslpoly *chrom_locus_poligon(icxLocusType locus_type, icxObserverType obType, in
/* Return 1 if outside locus */
int icx_outside_spec_locus(xslpoly *p, double xyz[3]);
+/* ------------------------------------------------- */
+
+/* Given a reflectance/transmittance spectrum, */
+/* an illuminant definition and an observer model, return */
+/* the XYZ value for that spectrum. */
+/* Return 0 on sucess, 1 on error */
+/* (One shot version of xsp2cie etc.) */
+int icx_sp2XYZ(
+double xyz[3], /* Return XYZ value */
+icxObserverType obType, /* Observer */
+xspect custObserver[3], /* Optional custom observer */
+icxIllumeType ilType, /* Type of illuminant, icxIT_[O]Dtemp or icxIT_[O]Ptemp */
+double ct, /* Input temperature in degrees K */
+xspect *custIllum, /* Optional custom illuminant */
+xspect *sp /* Spectrum to be converted */
+);
+
+/* ------------------------------------------------- */
+/* Color temperature and CRI */
+
+/* Given an illuminant definition and an observer model, return */
+/* the normalised XYZ value for that spectrum. */
+/* Return 0 on sucess, 1 on error */
+/* (One shot version of xsp2cie etc.) */
+int icx_ill_sp2XYZ(
+double xyz[3], /* Return XYZ value with Y == 1 */
+icxObserverType obType, /* Observer */
+xspect custObserver[3], /* Optional custom observer */
+icxIllumeType ilType, /* Type of illuminant */
+double temp, /* Input temperature in degrees K */
+xspect *custIllum); /* Optional custom illuminant */
+
+/* Given a choice of temperature dependent illuminant (icxIT_[O]Dtemp or icxIT_[O]Ptemp), */
+/* 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. */
+/* Return -1 on erorr */
+double icx_XYZ2ill_ct(
+double txyz[3], /* If not NULL, return the XYZ of the black body temperature */
+icxIllumeType ilType, /* Type of illuminant, icxIT_[O]Dtemp or icxIT_[O]Ptemp */
+icxObserverType obType, /* Observer */
+xspect custObserver[3], /* Optional custom observer */
+double xyz[3], /* Input XYZ value, NULL if spectrum intead */
+xspect *insp0, /* Input spectrum value, NULL if xyz[] instead */
+int viscct); /* nz to use visual CIEDE2000, 0 to use CCT CIE 1960 UCS. */
+
+/* Compute the CIE1995 CRI: Ra */
+/* Return < 0.0 on error */
+/* If invalid is not NULL, set it to nz if CRI */
+/* is invalid because the sample is not white enough. */
+double icx_CIE1995_CRI(
+int *invalid, /* if not NULL, set to nz if invalid */
+double cris[14], /* If not NULL, return the TCS01-14 CRI's */
+xspect *sample /* Illuminant sample to compute CRI of */
+);
+
+/* Compute the EBU TLCI-2012 Qa */
+/* Return < 0.0 on error */
+/* If invalid is not NULL, set it to nz if TLCI */
+/* is invalid because the sample is not white enough. */
+double icx_EBU2012_TLCI(
+int *invalid, /* if not NULL, set to nz if invalid */
+xspect *sample /* Illuminant sample to compute TLCI 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 */
+);
+
+/* Return a polinomial aproximation of CCT */
+double aprox_CCT(double xyz[3]);
+
+/* Aproximate x,y from CCT using Kim et al's cubic spline. */
+/* Invalid < 1667 and > 25000 */
+/* (Doesn't set Yxy[0]) */
+void aprox_plankian(double Yxy[3], double ct);
+
/* --------------------------- */
/* Density and other functions */
@@ -447,58 +551,6 @@ double desat /* 0.0 = full saturation, 1.0 = white */
);
-/* Given an illuminant definition and an observer model, return */
-/* the normalised XYZ value for that spectrum. */
-/* Return 0 on sucess, 1 on error */
-int icx_ill_sp2XYZ(
-double xyz[3], /* Return XYZ value with Y == 1 */
-icxObserverType obType, /* Observer */
-xspect custObserver[3], /* Optional custom observer */
-icxIllumeType ilType, /* Type of illuminant */
-double temp, /* Input temperature in degrees K */
-xspect *custIllum); /* Optional custom illuminant */
-
-
-/* Given a choice of temperature dependent illuminant (icxIT_[O]Dtemp or icxIT_[O]Ptemp), */
-/* 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. */
-/* Return -1 on erorr */
-double icx_XYZ2ill_ct(
-double txyz[3], /* If not NULL, return the XYZ of the black body temperature */
-icxIllumeType ilType, /* Type of illuminant, icxIT_[O]Dtemp or icxIT_[O]Ptemp */
-icxObserverType obType, /* Observer */
-xspect custObserver[3], /* Optional custom observer */
-double xyz[3], /* Input XYZ value, NULL if spectrum intead */
-xspect *insp0, /* Input spectrum value, NULL if xyz[] instead */
-int viscct); /* nz to use visual CIEDE2000, 0 to use CCT CIE 1960 UCS. */
-
-/* Compute the CIE1995 CRI: Ra */
-/* Return < 0.0 on error */
-/* If invalid is not NULL, set it to nz if CRI */
-/* is invalid because the sample is not white enough. */
-double icx_CIE1995_CRI(
-int *invalid, /* if not NULL, set to nz if invalid */
-double cris[14], /* If not NULL, return the TCS01-14 CRI's */
-xspect *sample /* Illuminant sample to compute CRI of */
-);
-
-/* Compute the EBU TLCI-2012 Qa */
-/* Return < 0.0 on error */
-/* If invalid is not NULL, set it to nz if TLCI */
-/* is invalid because the sample is not white enough. */
-double icx_EBU2012_TLCI(
-int *invalid, /* if not NULL, set to nz if invalid */
-xspect *sample /* Illuminant sample to compute TLCI 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