From 3db384424bd7398ffbb7a355cab8f15f3add009f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Sun, 2 Oct 2016 19:24:58 +0200 Subject: New upstream version 1.9.1+repack --- xicc/Jamfile | 13 +- xicc/cam02.c | 20 +- xicc/cam02.h | 8 +- xicc/cam02plot.c | 6 +- xicc/cam02ref.h | 13 +- xicc/cam02test.c | 18 +- xicc/ccmx.c | 11 +- xicc/ccmx.h | 15 +- xicc/ccss.c | 13 +- xicc/ccss.h | 16 +- xicc/ccttest.c | 11 +- xicc/extractttag.c | 1 - xicc/fakeCMY.c | 16 +- xicc/fbview.c | 5 +- xicc/iccgamut.c | 32 ++- xicc/mpplu.c | 12 +- xicc/revfix.c | 1 - xicc/specplot.c | 154 +++++++----- xicc/specsubsamp.c | 11 +- xicc/spectest.c | 2 +- xicc/spectest2.c | 2 +- xicc/tiffgamut.c | 30 ++- xicc/tiffgmts.c | 1 - xicc/xcal.c | 46 +++- xicc/xcal.h | 23 +- xicc/xcam.c | 7 +- xicc/xcam.h | 3 +- xicc/xcolorants.c | 10 +- xicc/xcolorants.h | 12 +- xicc/xdevlin.c | 2 +- xicc/xfbview.c | 177 ++++++++------ xicc/xfit.c | 22 +- xicc/xicc.c | 32 ++- xicc/xicc.h | 47 +++- xicc/xicclu.c | 91 +++++++- xicc/xlut.c | 669 ++++++++++++++++++++++++++++++++++++++++------------- xicc/xmatrix.c | 20 +- xicc/xmono.c | 4 +- xicc/xspect.c | 258 +++++++++++++++++---- xicc/xspect.h | 172 +++++++++----- 40 files changed, 1499 insertions(+), 507 deletions(-) (limited to 'xicc') 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 #include #include +#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 #include #include +#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 @@ -11,6 +11,9 @@ */ /* TTBD: + * + * Should reject device link profiles ? + * * */ @@ -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 */ ); /* - - - - - - - - - - */ @@ -929,6 +942,28 @@ void icxdpdiMulBy3x3Parm( double in[3] /* Input values */ ); +/* - - - - - - - - - - */ + +/* 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" 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: @@ -3818,11 +3816,38 @@ 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 -- cgit v1.2.3