summaryrefslogtreecommitdiff
path: root/xicc/xicclu.c
diff options
context:
space:
mode:
Diffstat (limited to 'xicc/xicclu.c')
-rw-r--r--xicc/xicclu.c1149
1 files changed, 1149 insertions, 0 deletions
diff --git a/xicc/xicclu.c b/xicc/xicclu.c
new file mode 100644
index 0000000..aa4a452
--- /dev/null
+++ b/xicc/xicclu.c
@@ -0,0 +1,1149 @@
+
+/*
+ * xicc lookup/test utility
+ *
+ * This program is the analog of icclu, but allows reverse lookup
+ * of transforms by making use of xicc interpolation code.
+ * (Based on the old xfmlu.c)
+ *
+ * Author: Graeme W. Gill
+ * Date: 8/7/00
+ * Version: 1.00
+ *
+ * Copyright 1999, 2000 Graeme W. Gill
+ * All rights reserved.
+ * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
+ * see the License.txt file for licencing details.
+ */
+
+/* TTBD:
+
+ Can -ff and -fif be made to work with device link files ?
+
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <string.h>
+#include <math.h>
+#include "copyright.h"
+#include "aconfig.h"
+#include "numlib.h"
+#include "plot.h"
+#include "xicc.h"
+
+#undef SPTEST /* Test rspl gamut surface code */
+
+#define USE_NEARCLIP /* Our usual expectation */
+#define XRES 128 /* Plotting resolution */
+
+void usage(char *diag) {
+ int i;
+ fprintf(stderr,"Translate colors through an xicc, Version %s\n",ARGYLL_VERSION_STR);
+ fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
+ fprintf(stderr,"usage: xicclu [-options] profile\n");
+ if (diag != NULL)
+ fprintf(stderr,"Diagnostic: %s\n",diag);
+ fprintf(stderr," -v level Verbosity level 0 - 2 (default = 1)\n");
+ 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," -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 = 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");
+ fprintf(stderr," -p oride x = XYZ_PCS, X = XYZ * 100, l = Lab_PCS, L = LCh, y = Yxy\n");
+ fprintf(stderr," j = %s Appearance Jab, J = %s Appearance JCh\n",icxcam_description(cam_default),icxcam_description(cam_default));
+ fprintf(stderr," -s scale Scale device range 0.0 - scale rather than 0.0 - 1.0\n");
+ fprintf(stderr," -k [zhxrlv] Black value target: z = zero K,\n");
+ fprintf(stderr," h = 0.5 K, x = max K, r = ramp K (def.)\n");
+ fprintf(stderr," l = extra PCS input is portion of K locus\n");
+ fprintf(stderr," v = extra PCS input is K target value\n");
+ fprintf(stderr," -k p stle stpo enpo enle shape\n");
+ fprintf(stderr," stle: K level at White 0.0 - 1.0\n");
+ fprintf(stderr," stpo: start point of transition Wh 0.0 - Bk 1.0\n");
+ fprintf(stderr," enpo: End point of transition Wh 0.0 - Bk 1.0\n");
+ fprintf(stderr," enle: K level at Black 0.0 - 1.0\n");
+ fprintf(stderr," shape: 1.0 = straight, 0.0-1.0 concave, 1.0-2.0 convex\n");
+ fprintf(stderr," -k q stle0 stpo0 enpo0 enle0 shape0 stle2 stpo2 enpo2 enle2 shape2\n");
+ fprintf(stderr," Transfer extra PCS input to dual curve limits\n");
+ fprintf(stderr," -K parameters Same as -k, but target is K locus rather than K value itself\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");
+ fprintf(stderr," -a show actual target values if clipped\n");
+ fprintf(stderr," -u warn if output PCS is outside the spectrum locus\n");
+ fprintf(stderr," -m merge output processing into clut\n");
+ fprintf(stderr," -b use CAM Jab for clipping\n");
+// fprintf(stderr," -S Use internal optimised separation for inverse 4d [NOT IMPLEMENTED]\n");
+
+#ifdef SPTEST
+ fprintf(stderr," -w special gamut surface test PCS space\n");
+ fprintf(stderr," -W special gamut surface test, PCS' space\n");
+#endif
+ fprintf(stderr," -c viewcond set viewing conditions for CIECAM97s,\n");
+ fprintf(stderr," either an enumerated choice, or a parameter:value changes\n");
+ for (i = 0; ; i++) {
+ icxViewCond vc;
+ if (xicc_enum_viewcond(NULL, &vc, i, NULL, 1, NULL) == -999)
+ break;
+
+ fprintf(stderr," %s\n",vc.desc);
+ }
+
+ fprintf(stderr," s:surround n = auto, a = average, m = dim, d = dark,\n");
+ fprintf(stderr," c = transparency (default average)\n");
+ fprintf(stderr," w:X:Y:Z Adapted white point as XYZ (default media white, Abs: D50)\n");
+ fprintf(stderr," w:x:y Adapted white point as x, y\n");
+ fprintf(stderr," a:adaptation Adaptation luminance in cd.m^2 (default 50.0)\n");
+ fprintf(stderr," b:background Background %% of image luminance (default 20)\n");
+ fprintf(stderr," l:scenewhite Scene white in cd.m^2 if surround = auto (default 250)\n");
+ fprintf(stderr," f:flare Flare light %% of image luminance (default 1)\n");
+ fprintf(stderr," f:X:Y:Z Flare color as XYZ (default media white, Abs: D50)\n");
+ fprintf(stderr," f:x:y Flare color as x, y\n");
+ fprintf(stderr,"\n");
+ fprintf(stderr," The colors to be translated should be fed into standard in,\n");
+ fprintf(stderr," one input color per line, white space separated.\n");
+ fprintf(stderr," A line starting with a # will be ignored.\n");
+ fprintf(stderr," A line not starting with a number will terminate the program.\n");
+ fprintf(stderr," Use -v0 for just output colors.\n");
+ exit(1);
+}
+
+#ifdef SPTEST
+
+#pragma message("!!!!!!!!!!!! Experimental gamut boundary test !!!!!!!!!")
+
+/* Output curve function */
+void spoutf(void *cbntx, double *out, double *in) {
+ icxLuLut *clu = (icxLuLut *)cbntx;
+
+ clu->output(clu, out, in);
+ clu->out_abs(clu, out, out);
+}
+
+/* Inverse output curve function */
+void spioutf(void *cbntx, double *out, double *in) {
+ icxLuLut *clu = (icxLuLut *)cbntx;
+
+ clu->inv_out_abs(clu, out, in);
+ clu->inv_output(clu, out, out);
+}
+
+
+#endif /* SPTEST */
+
+int
+main(int argc, char *argv[]) {
+ int fa,nfa; /* argument we're looking at */
+ char prof_name[MAXNAMEL+1];
+ icmFile *fp;
+ icc *icco;
+ xicc *xicco;
+ 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 */
+ icxInk ink; /* Ink parameters */
+ double tlimit = -1.0; /* Total ink limit */
+ double klimit = -1.0; /* Black ink limit */
+ int intsep = 0;
+ icxViewCond vc; /* Viewing Condition for CIECAM97s */
+ int vc_e = -1; /* Enumerated viewing condition */
+ int vc_s = -1; /* Surround override */
+ double vc_wXYZ[3] = {-1.0, -1.0, -1.0}; /* Adapted white override in XYZ */
+ double vc_wxy[2] = {-1.0, -1.0}; /* Adapted white override in x,y */
+ double vc_a = -1.0; /* Adapted luminance */
+ double vc_b = -1.0; /* Background % overid */
+ double vc_l = -1.0; /* Scene luminance override */
+ double vc_f = -1.0; /* Flare % overid */
+ double vc_fXYZ[3] = {-1.0, -1.0, -1.0}; /* Flare color override in XYZ */
+ double vc_fxy[2] = {-1.0, -1.0}; /* Flare color override in x,y */
+ int verb = 1;
+ int actual = 0;
+ int slocwarn = 0;
+ int merge = 0;
+ int camclip = 0;
+ int repYxy = 0; /* Report Yxy */
+ int repJCh = 0; /* Report JCh */
+ int repLCh = 0; /* Report LCh */
+ int repXYZ100 = 0; /* Scale XYZ by 10 */
+ double scale = 0.0; /* Device value scale factor */
+ int rv = 0;
+ char buf[200];
+ double uin[MAX_CHAN], in[MAX_CHAN], out[MAX_CHAN], uout[MAX_CHAN];
+
+ icxLuBase *luo, *aluo = NULL;
+ icColorSpaceSignature ins, outs; /* Type of input and output spaces */
+ int inn, outn; /* Number of components */
+ icmLuAlgType alg; /* Type of lookup algorithm */
+
+ /* Lookup parameters */
+ icmLookupFunc func = icmFwd; /* Default */
+ icRenderingIntent intent = icmDefaultIntent; /* Default */
+ icColorSpaceSignature pcsor = icmSigDefaultData; /* Default */
+ icmLookupOrder order = icmLuOrdNorm; /* Default */
+ int inking = 3; /* Default is ramp */
+ int locus = 0; /* Default is K value */
+ /* K curve params */
+ double Kstle = 0.0, Kstpo = 0.0, Kenle = 0.0, Kenpo = 0.0, Kshap = 0.0;
+ /* K curve params (max) */
+ double Kstle1 = 0.0, Kstpo1 = 0.0, Kenle1 = 0.0, Kenpo1 = 0.0, Kshap1 = 0.0;
+ int invert = 0;
+
+#ifdef SPTEST
+ int sptest = 0;
+ warning("xicc/xicclu.c !!!! special rspl gamut sest code is compiled in !!!!\n");
+#endif
+
+ error_program = argv[0];
+
+ if (argc < 2)
+ usage("Too few arguments");
+
+ /* Process the arguments */
+ for(fa = 1;fa < argc;fa++) {
+ nfa = fa; /* skip to nfa if next argument is used */
+ if (argv[fa][0] == '-') { /* Look for any flags */
+ char *na = NULL; /* next argument after flag, null if none */
+
+ if (argv[fa][2] != '\000')
+ na = &argv[fa][2]; /* next is directly after flag */
+ else {
+ if ((fa+1) < argc) {
+ if (argv[fa+1][0] != '-') {
+ nfa = fa + 1;
+ na = argv[nfa]; /* next is seperate non-flag argument */
+ }
+ }
+ }
+
+ if (argv[fa][1] == '?')
+ usage("Requested usage");
+
+ /* Verbosity */
+ else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
+ fa = nfa;
+ if (na == NULL)
+ verb = 2;
+ else {
+ if (na[0] == '0')
+ verb = 0;
+ else if (na[0] == '1')
+ verb = 1;
+ else if (na[0] == '2')
+ verb = 2;
+ else
+ usage("Illegal verbosity level");
+ }
+ }
+
+ /* Locus plot */
+ else if (argv[fa][1] == 'g') {
+ doplot = 1;
+ }
+ /* Plot start or end override */
+ else if (argv[fa][1] == 'G') {
+ fa = nfa;
+ if (na == NULL) usage("No parameter after flag -G");
+ if (na[0] == 's' || na[0] == 'S') {
+ if (sscanf(na+1,":%lf:%lf:%lf",&pstart[0],&pstart[1],&pstart[2]) != 3)
+ usage("Unrecognised parameters after -Gs");
+ } else if (na[0] == 'e' || na[0] == 'E') {
+ if (sscanf(na+1,":%lf:%lf:%lf",&pend[0],&pend[1],&pend[2]) != 3)
+ usage("Unrecognised parameters after -Ge");
+ } else
+ usage("Unrecognised parameters after -G");
+ }
+ /* Actual target values */
+ else if (argv[fa][1] == 'a') {
+ actual = 1;
+ }
+ /* Warn if output is outside the spectrum locus */
+ else if (argv[fa][1] == 'u') {
+ slocwarn = 1;
+ }
+ /* Merge output */
+ else if (argv[fa][1] == 'm') {
+ merge = 1;
+ }
+ /* Use CAM Jab for clipping on reverse lookup */
+ else if (argv[fa][1] == 'b') {
+ camclip = 1;
+ }
+ /* Use optimised internal separation */
+ else if (argv[fa][1] == 'S') {
+ intsep = 1;
+ }
+ /* Device scale */
+ else if (argv[fa][1] == 's') {
+ fa = nfa;
+ if (na == NULL) usage("No parameter after flag -s");
+ scale = atof(na);
+ if (scale <= 0.0) usage("Illegal scale value");
+ }
+ /* function */
+ else if (argv[fa][1] == 'f') {
+ fa = nfa;
+ if (na == NULL) usage("No parameter after flag -f");
+ switch (na[0]) {
+ case 'f':
+ case 'F':
+ func = icmFwd;
+ break;
+ case 'b':
+ case 'B':
+ func = icmBwd;
+ break;
+ case 'g':
+ case 'G':
+ func = icmGamut;
+ break;
+ case 'p':
+ case 'P':
+ func = icmPreview;
+ break;
+ case 'i':
+ case 'I':
+ invert = 1;
+ if (na[1] == 'f' || na[1] == 'F')
+ func = icmFwd;
+ else if (na[1] == 'b' || na[1] == 'B')
+ func = icmBwd;
+ else
+ usage("Unknown parameter after flag -fi");
+ break;
+ default:
+ usage("Unknown parameter after flag -f");
+ }
+ }
+
+ /* Intent */
+ else if (argv[fa][1] == 'i') {
+ fa = nfa;
+ if (na == NULL) usage("No parameter after flag -i");
+ switch (na[0]) {
+ case 'p':
+ intent = icPerceptual;
+ break;
+ case 'r':
+ intent = icRelativeColorimetric;
+ break;
+ case 's':
+ intent = icSaturation;
+ break;
+ case 'a':
+ intent = icAbsoluteColorimetric;
+ break;
+ /* Argyll special intents to check spaces underlying */
+ /* icxPerceptualAppearance & icxSaturationAppearance */
+ case 'P':
+ intent = icmAbsolutePerceptual;
+ break;
+ case 'S':
+ intent = icmAbsoluteSaturation;
+ break;
+ default:
+ usage("Unknown parameter after flag -i");
+ }
+ }
+
+ /* PCS override */
+ else if (argv[fa][1] == 'p') {
+ fa = nfa;
+ if (na == NULL) usage("No parameter after flag -i");
+ switch (na[0]) {
+ case 'x':
+ pcsor = icSigXYZData;
+ repYxy = 0;
+ repLCh = 0;
+ repJCh = 0;
+ repXYZ100 = 0;
+ break;
+ case 'X':
+ pcsor = icSigXYZData;
+ repYxy = 0;
+ repLCh = 0;
+ repJCh = 0;
+ repXYZ100 = 1;
+ break;
+ case 'l':
+ pcsor = icSigLabData;
+ repYxy = 0;
+ repLCh = 0;
+ repJCh = 0;
+ break;
+ case 'L':
+ pcsor = icSigLabData;
+ repYxy = 0;
+ repLCh = 1;
+ repJCh = 0;
+ break;
+ case 'y':
+ case 'Y':
+ pcsor = icSigXYZData;
+ repYxy = 1;
+ repLCh = 0;
+ repJCh = 0;
+ break;
+ case 'j':
+ pcsor = icxSigJabData;
+ repYxy = 0;
+ repLCh = 0;
+ repJCh = 0;
+ break;
+ case 'J':
+ pcsor = icxSigJabData;
+ repYxy = 0;
+ repLCh = 0;
+ repJCh = 1;
+ break;
+ default:
+ usage("Unknown parameter after flag -i");
+ }
+ }
+
+ /* Search order */
+ else if (argv[fa][1] == 'o') {
+ fa = nfa;
+ if (na == NULL) usage("No parameter after flag -o");
+ switch (na[0]) {
+ case 'n':
+ case 'N':
+ order = icmLuOrdNorm;
+ break;
+ case 'r':
+ case 'R':
+ order = icmLuOrdRev;
+ break;
+ default:
+ usage("Unknown parameter after flag -o");
+ }
+ }
+
+ /* Inking rule */
+ else if (argv[fa][1] == 'k') {
+ fa = nfa;
+ if (na == NULL) usage("No parameter after flag -k");
+ if (argv[fa][1] == 'k')
+ locus = 0; /* K value target */
+ else
+ locus = 1; /* K locus target */
+ switch (na[0]) {
+ case 'z':
+ case 'Z':
+ inking = 0; /* Use minimum k */
+ break;
+ case 'h':
+ case 'H':
+ inking = 1; /* Use 0.5 k */
+ break;
+ case 'x':
+ case 'X':
+ inking = 2; /* Use maximum k */
+ break;
+ case 'r':
+ case 'R':
+ inking = 3; /* Use ramping K */
+ break;
+ case 'l':
+ case 'L':
+ inking = 4; /* Extra param is locus */
+ break;
+ case 'v':
+ case 'V':
+ inking = 5; /* Extra param is K target */
+ break;
+ case 'p':
+ case 'P':
+ case 'q':
+ case 'Q':
+ inking = 6; /* Use curve parameter */
+
+ ++fa;
+ if (fa >= argc) usage("Inking rule (-kp) expects more parameters");
+ Kstle = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc) usage("Inking rule (-kp) expects more parameters");
+ Kstpo = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc || argv[fa][0] == '-') usage("Inking rule (-kp) expects more parameters");
+ Kenpo = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc || argv[fa][0] == '-') usage("Inking rule (-kp) expects more parameters");
+ Kenle = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc || argv[fa][0] == '-') usage("Inking rule (-kp) expects more parameters");
+ Kshap = atof(argv[fa]);
+
+ if (na[0] == 'q' || na[0] == 'Q') {
+ inking = 7; /* Use transfer to dual curve parameter */
+
+ ++fa;
+ if (fa >= argc) usage("Inking rule (-kq) expects more parameters");
+ Kstle1 = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc) usage("Inking rule (-kq) expects more parameters");
+ Kstpo1 = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc || argv[fa][0] == '-') usage("Inking rule (-kq) expects more parameters");
+ Kenpo1 = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc) usage("Inking rule (-kq) expects more parameters");
+ Kenle1 = atof(argv[fa]);
+
+ ++fa;
+ if (fa >= argc || argv[fa][0] == '-') usage("Inking rule (-kq) expects more parameters");
+ Kshap1 = atof(argv[fa]);
+
+ }
+ break;
+ default:
+ usage("Unknown parameter after flag -k");
+ }
+ }
+
+ else if (argv[fa][1] == 'l') {
+ fa = nfa;
+ if (na == NULL) usage("No parameter after flag -l");
+ tlimit = atoi(na)/100.0;
+ }
+
+ else if (argv[fa][1] == 'L') {
+ fa = nfa;
+ if (na == NULL) usage("No parameter after flag -L");
+ klimit = atoi(na)/100.0;
+ }
+
+#ifdef SPTEST
+ else if (argv[fa][1] == 'w') {
+ sptest = 1;
+ }
+ else if (argv[fa][1] == 'W') {
+ sptest = 2;
+ }
+#endif
+ /* Viewing conditions */
+ else if (argv[fa][1] == 'c') {
+ fa = nfa;
+ if (na == NULL) usage("No parameter after flag -c");
+#ifdef NEVER
+ if (na[0] >= '0' && na[0] <= '9') {
+ vc_e = atoi(na);
+ } else
+#endif
+ if (na[1] != ':') {
+ if ((vc_e = xicc_enum_viewcond(NULL, NULL, -2, na, 1, NULL)) == -999)
+ usage("Urecognised Enumerated Viewing conditions");
+ } else if (na[0] == 's' || na[0] == 'S') {
+ if (na[1] != ':')
+ usage("Unrecognised parameters after -cs");
+ if (na[2] == 'n' || na[2] == 'N') {
+ vc_s = vc_none; /* Automatic using Lv */
+ } else if (na[2] == 'a' || na[2] == 'A') {
+ vc_s = vc_average;
+ } else if (na[2] == 'm' || na[2] == 'M') {
+ vc_s = vc_dim;
+ } else if (na[2] == 'd' || na[2] == 'D') {
+ vc_s = vc_dark;
+ } else if (na[2] == 'c' || na[2] == 'C') {
+ vc_s = vc_cut_sheet;
+ } else
+ usage("Unrecognised parameters after -cs:");
+ } else if (na[0] == 'w' || na[0] == 'W') {
+ double x, y, z;
+ if (sscanf(na+1,":%lf:%lf:%lf",&x,&y,&z) == 3) {
+ vc_wXYZ[0] = x; vc_wXYZ[1] = y; vc_wXYZ[2] = z;
+ } else if (sscanf(na+1,":%lf:%lf",&x,&y) == 2) {
+ vc_wxy[0] = x; vc_wxy[1] = y;
+ } else
+ usage("Unrecognised parameters after -cw");
+ } else if (na[0] == 'a' || na[0] == 'A') {
+ if (na[1] != ':')
+ usage("Unrecognised parameters after -ca");
+ vc_a = atof(na+2);
+ } else if (na[0] == 'b' || na[0] == 'B') {
+ if (na[1] != ':')
+ usage("Unrecognised parameters after -cb");
+ vc_b = atof(na+2);
+ } else if (na[0] == 'l' || na[0] == 'L') {
+ if (na[1] != ':')
+ usage("Viewing conditions (-[cd]l) missing ':'");
+ vc_l = atof(na+2);
+ } else if (na[0] == 'f' || na[0] == 'F') {
+ double x, y, z;
+ if (sscanf(na+1,":%lf:%lf:%lf",&x,&y,&z) == 3) {
+ vc_fXYZ[0] = x; vc_fXYZ[1] = y; vc_fXYZ[2] = z;
+ } else if (sscanf(na+1,":%lf:%lf",&x,&y) == 2) {
+ vc_fxy[0] = x; vc_fxy[1] = y;
+ } else if (sscanf(na+1,":%lf",&x) == 1) {
+ vc_f = x;
+ } else
+ usage("Unrecognised parameters after -cf");
+ } else
+ usage("Unrecognised parameters after -c");
+ }
+
+ else
+ usage("Unknown flag");
+ } else
+ break;
+ }
+
+ if (fa >= argc || argv[fa][0] == '-') usage("Expecting profile file name");
+ strncpy(prof_name,argv[fa],MAXNAMEL); prof_name[MAXNAMEL] = '\000';
+
+ if (doplot) {
+
+ /* Force PCS to be Lab or Jab */
+ repJCh = 0;
+ repLCh = 0;
+ if (pcsor != icxSigJabData)
+ pcsor = icSigLabData;
+
+ if ((invert == 0 && func != icmBwd)
+ || (invert != 0 && func != icmFwd))
+ error("Must use -fb or -fif for grey axis plot");
+ }
+
+ /* Open up the profile for reading */
+ if ((fp = new_icmFileStd_name(prof_name,"r")) == NULL)
+ error ("Can't open file '%s'",prof_name);
+
+ if ((icco = new_icc()) == NULL)
+ error ("Creation of ICC object failed");
+
+ if ((rv = icco->read(icco,fp,0)) != 0)
+ error ("%d, %s",rv,icco->err);
+
+ if (doplot) {
+ if (icco->header->deviceClass != icSigInputClass
+ && icco->header->deviceClass != icSigDisplayClass
+ && icco->header->deviceClass != icSigOutputClass)
+ error("Profile must be a device profile to plot neutral axis");
+ }
+
+ if (verb > 1) {
+ icmFile *op;
+ if ((op = new_icmFileStd_fp(stdout)) == NULL)
+ error ("Can't open stdout");
+ icco->header->dump(icco->header, op, 1);
+ op->del(op);
+ }
+
+ /* Wrap with an expanded icc */
+ if ((xicco = new_xicc(icco)) == NULL)
+ error ("Creation of xicc failed");
+
+ /* Set the default ink limits if not set on command line */
+ icxDefaultLimits(xicco, &ink.tlimit, tlimit, &ink.klimit, klimit);
+
+ if (verb > 1) {
+ if (ink.tlimit >= 0.0)
+ printf("Total ink limit assumed is %3.0f%%\n",100.0 * ink.tlimit);
+ if (ink.klimit >= 0.0)
+ printf("Black ink limit assumed is %3.0f%%\n",100.0 * ink.klimit);
+ }
+
+ ink.KonlyLmin = 0; /* Use normal black as locus Lmin */
+
+ ink.c.Ksmth = ICXINKDEFSMTH; /* Default curve smoothing */
+ ink.c.Kskew = ICXINKDEFSKEW; /* default curve skew */
+ ink.x.Ksmth = ICXINKDEFSMTH;
+ ink.x.Kskew = ICXINKDEFSKEW;
+
+ if (inking == 0) { /* Use minimum */
+ ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
+ ink.c.Kstle = 0.0;
+ ink.c.Kstpo = 0.0;
+ ink.c.Kenpo = 1.0;
+ ink.c.Kenle = 0.0;
+ ink.c.Kshap = 1.0;
+ } else if (inking == 1) { /* Use 0.5 */
+ ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
+ ink.c.Kstle = 0.5;
+ ink.c.Kstpo = 0.0;
+ ink.c.Kenpo = 1.0;
+ ink.c.Kenle = 0.5;
+ ink.c.Kshap = 1.0;
+ } else if (inking == 2) { /* Use maximum */
+ ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
+ ink.c.Kstle = 1.0;
+ ink.c.Kstpo = 0.0;
+ ink.c.Kenpo = 1.0;
+ ink.c.Kenle = 1.0;
+ ink.c.Kshap = 1.0;
+ } else if (inking == 3) { /* Use ramp */
+ ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
+ ink.c.Kstle = 0.0;
+ ink.c.Kstpo = 0.0;
+ ink.c.Kenpo = 1.0;
+ ink.c.Kenle = 1.0;
+ ink.c.Kshap = 1.0;
+ } else if (inking == 4) { /* Use locus */
+ ink.k_rule = icxKlocus;
+ } else if (inking == 5) { /* Use K target */
+ ink.k_rule = icxKvalue;
+ } else if (inking == 6) { /* Use specified curve */
+ ink.k_rule = locus ? icxKluma5 : icxKluma5k; /* Locus or value target */
+ ink.c.Kstle = Kstle;
+ ink.c.Kstpo = Kstpo;
+ ink.c.Kenpo = Kenpo;
+ ink.c.Kenle = Kenle;
+ ink.c.Kshap = Kshap;
+ } else { /* Use dual curves */
+ ink.k_rule = locus ? icxKl5l : icxKl5lk; /* Locus or value target */
+ ink.c.Kstle = Kstle;
+ ink.c.Kstpo = Kstpo;
+ ink.c.Kenpo = Kenpo;
+ ink.c.Kenle = Kenle;
+ ink.c.Kshap = Kshap;
+ ink.x.Kstle = Kstle1;
+ ink.x.Kstpo = Kstpo1;
+ ink.x.Kenpo = Kenpo1;
+ ink.x.Kenle = Kenle1;
+ ink.x.Kshap = Kshap1;
+ }
+
+ /* Setup the viewing conditions */
+ if (xicc_enum_viewcond(xicco, &vc, -1, NULL, 0, NULL) == -999)
+ error ("%d, %s",xicco->errc, xicco->err);
+
+//xicc_dump_viewcond(&vc);
+ if (vc_e != -1)
+ if (xicc_enum_viewcond(xicco, &vc, vc_e, NULL, 0, NULL) == -999)
+ error ("%d, %s",xicco->errc, xicco->err);
+ if (vc_s >= 0)
+ vc.Ev = vc_s;
+ if (vc_wXYZ[1] > 0.0) {
+ /* Normalise it to current media white */
+ vc.Wxyz[0] = vc_wXYZ[0]/vc_wXYZ[1] * vc.Wxyz[1];
+ vc.Wxyz[2] = vc_wXYZ[2]/vc_wXYZ[1] * vc.Wxyz[1];
+ }
+ if (vc_wxy[0] >= 0.0) {
+ double x = vc_wxy[0];
+ double y = vc_wxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
+ double z = 1.0 - x - y;
+ vc.Wxyz[0] = x/y * vc.Wxyz[1];
+ vc.Wxyz[2] = z/y * vc.Wxyz[1];
+ }
+ if (vc_a >= 0.0)
+ vc.La = vc_a;
+ if (vc_b >= 0.0)
+ vc.Yb = vc_b/100.0;
+ if (vc_l >= 0.0)
+ vc.Lv = vc_l;
+ if (vc_f >= 0.0)
+ vc.Yf = vc_f/100.0;
+ if (vc_fXYZ[1] > 0.0) {
+ /* Normalise it to current media white */
+ vc.Fxyz[0] = vc_fXYZ[0]/vc_fXYZ[1] * vc.Fxyz[1];
+ vc.Fxyz[2] = vc_fXYZ[2]/vc_fXYZ[1] * vc.Fxyz[1];
+ }
+ if (vc_fxy[0] >= 0.0) {
+ double x = vc_fxy[0];
+ double y = vc_fxy[1]; /* If Y == 1.0, then X+Y+Z = 1/y */
+ double z = 1.0 - x - y;
+ vc.Fxyz[0] = x/y * vc.Fxyz[1];
+ vc.Fxyz[2] = z/y * vc.Fxyz[1];
+ }
+//xicc_dump_viewcond(&vc);
+
+ /* Get a expanded color conversion object */
+ if ((luo = xicco->get_luobj(xicco, 0
+#ifdef USE_NEARCLIP
+ | ICX_CLIP_NEAREST
+#endif
+ | (intsep ? ICX_INT_SEPARATE : 0)
+ | (merge ? ICX_MERGE_CLUT : 0)
+ | (camclip ? ICX_CAM_CLIP : 0)
+ | ICX_FAST_SETUP
+ , func, intent, pcsor, order, &vc, &ink)) == NULL)
+ error ("%d, %s",xicco->errc, xicco->err);
+
+ /* Get details of conversion (Arguments may be NULL if info not needed) */
+ if (invert)
+ luo->spaces(luo, &outs, &outn, &ins, &inn, &alg, NULL, NULL, NULL);
+ else
+ luo->spaces(luo, &ins, &inn, &outs, &outn, &alg, NULL, NULL, NULL);
+
+ /* If we can do check on clipped values */
+ if (actual != 0) {
+ if (invert == 0) {
+ if (func == icmFwd || func == icmBwd) {
+ if ((aluo = xicco->get_luobj(xicco, ICX_CLIP_NEAREST,
+ func == icmFwd ? icmBwd : icmFwd, intent, pcsor, order, &vc, &ink)) == NULL)
+ error ("%d, %s",xicco->errc, xicco->err);
+ }
+ } else {
+ aluo = luo; /* We can use the same one */
+ }
+ }
+
+ /* More information */
+ if (verb > 1) {
+ int j;
+ double inmin[MAX_CHAN], inmax[MAX_CHAN];
+ double outmin[MAX_CHAN], outmax[MAX_CHAN];
+
+ luo->get_native_ranges(luo, inmin, inmax, outmin,outmax);
+ printf("Internal input value range: ");
+ for (j = 0; j < inn; j++) {
+ if (j > 0)
+ fprintf(stdout," %f..%f",inmin[j], inmax[j]);
+ else
+ fprintf(stdout,"%f..%f",inmin[j], inmax[j]);
+ }
+ printf("\nInternal output value range: ");
+ for (j = 0; j < outn; j++) {
+ if (j > 0)
+ fprintf(stdout," %f..%f",outmin[j], outmax[j]);
+ else
+ fprintf(stdout,"%f..%f",outmin[j], outmax[j]);
+ }
+
+ luo->get_ranges(luo, inmin, inmax, outmin,outmax);
+ printf("\nInput value range: ");
+ for (j = 0; j < inn; j++) {
+ if (j > 0)
+ fprintf(stdout," %f..%f",inmin[j], inmax[j]);
+ else
+ fprintf(stdout,"%f..%f",inmin[j], inmax[j]);
+ }
+ printf("\nOutput value range: ");
+ for (j = 0; j < outn; j++) {
+ if (j > 0)
+ fprintf(stdout," %f..%f",outmin[j], outmax[j]);
+ else
+ fprintf(stdout,"%f..%f",outmin[j], outmax[j]);
+ }
+ printf("\n");
+ }
+
+ if (repYxy) { /* report Yxy rather than XYZ */
+ if (ins == icSigXYZData)
+ ins = icSigYxyData;
+ if (outs == icSigXYZData)
+ outs = icSigYxyData;
+ }
+
+ if (repJCh) { /* report JCh rather than Jab */
+ if (ins == icxSigJabData)
+ ins = icxSigJChData;
+ if (outs == icxSigJabData)
+ outs = icxSigJChData;
+ }
+ if (repLCh) { /* report LCh rather than Lab */
+ if (ins == icSigLabData)
+ ins = icxSigLChData;
+ if (outs == icSigLabData)
+ outs = icxSigLChData;
+ }
+
+#ifdef SPTEST
+ if (sptest) {
+ icxLuLut *clu;
+ double cent[3] = { 50.0, 0.0, 0.0 };
+
+ if (luo->plu->ttype != icmLutType)
+ error("Special test only works on CLUT profiles");
+
+ clu = (icxLuLut *)luo;
+
+ clu->clutTable->comp_gamut(clu->clutTable, cent, NULL, spoutf, clu, spioutf, clu);
+ rspl_gam_plot(clu->clutTable, "sp_test.wrl", sptest-1);
+ exit(0);
+ }
+#endif
+
+ if (doplot) {
+ int i, j;
+ double xx[XRES];
+ double yy[6][XRES];
+ double start[3], end[3];
+
+ /* Plot from white to black by default */
+ luo->efv_wh_bk_points(luo, start, end, NULL);
+
+ if (pstart[0] == -1000.0)
+ icmCpy3(pstart, start);
+
+ if (pend[0] == -1000.0)
+ icmCpy3(pend, end);
+
+ if (verb) {
+ printf("Plotting from white %f %f %f to black %f %f %f\n",
+ pstart[0], pstart[1], pstart[2], pend[0], pend[1], pend[2]);
+ }
+ for (i = 0; i < XRES; i++) {
+ double ival = (double)i/(XRES-1.0);
+
+ /* Input is always Jab or Lab */
+ in[0] = ival * pend[0] + (1.0 - ival) * pstart[0];
+ in[1] = ival * pend[1] + (1.0 - ival) * pstart[1];
+ in[2] = ival * pend[2] + (1.0 - ival) * pstart[2];
+//in[1] = in[2] = 0.0;
+
+ /* Do the conversion */
+ if (invert) {
+ if ((rv = luo->inv_lookup(luo, out, in)) > 1)
+ error ("%d, %s",xicco->errc,xicco->err);
+//printf("~1 %f: %f %f %f -> %f %f %f %f\n", ival, in[0], in[1], in[2], out[0], out[1], out[2], out[3]);
+ } else {
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",xicco->errc,xicco->err);
+ }
+
+ xx[i] = 100.0 * ival;
+ for (j = 0; j < outn; j++)
+ yy[j][i] = 100.0 * out[j];
+ }
+//fflush(stdout);
+
+ /* plot order: Black Red Green Blue Yellow Purple */
+ if (outs == icSigRgbData) {
+ do_plot6(xx, NULL, yy[0], yy[1], yy[2], NULL, NULL, XRES);
+
+ } else if (outs == icSigCmykData) {
+ do_plot6(xx, yy[3], yy[1], NULL, yy[0], yy[2], NULL, XRES);
+
+ } else {
+
+ switch(outn) {
+ case 1:
+ do_plot6(xx, yy[0], NULL, NULL, NULL, NULL, NULL, XRES);
+ break;
+ case 2:
+ do_plot6(xx, yy[0], yy[1], NULL, NULL, NULL, NULL, XRES);
+ break;
+ case 3:
+ do_plot6(xx, yy[0], yy[1], yy[2], NULL, NULL, NULL, XRES);
+ break;
+ case 4:
+ do_plot6(xx, yy[0], yy[1], yy[2], yy[3], NULL, NULL, XRES);
+ break;
+ case 5:
+ do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], NULL, XRES);
+ break;
+ case 6:
+ do_plot6(xx, yy[0], yy[1], yy[2], yy[3], yy[4], yy[5], XRES);
+ break;
+ }
+ }
+
+
+ } else {
+
+ if (slocwarn && outs != icSigXYZData
+ && outs != icSigYxyData
+ && outs != icSigLabData
+ && outs != icxSigLChData) {
+ error("Can't warn if outside spectrum locus unless XYZ like space");
+ }
+
+ /* Process colors to translate */
+ for (;;) {
+ int i,j;
+ char *bp, *nbp;
+ int outsloc = 0;
+
+ /* Read in the next line */
+ if (fgets(buf, 200, stdin) == NULL)
+ break;
+ if (buf[0] == '#') {
+ if (verb > 0)
+ fprintf(stdout,"%s\n",buf);
+ continue;
+ }
+ /* For each input number */
+ for (bp = buf-1, nbp = buf, i = 0; i < MAX_CHAN; i++) {
+ bp = nbp;
+ uout[i] = out[i] = in[i] = uin[i] = strtod(bp, &nbp);
+ if (nbp == bp)
+ break; /* Failed */
+ }
+ if (i == 0)
+ break;
+
+ /* If device data and scale */
+ if(scale > 0.0
+ && ins != icxSigJabData
+ && ins != icxSigJChData
+ && ins != icSigXYZData
+ && ins != icSigLabData
+ && ins != icxSigLChData
+ && ins != icSigLuvData
+ && ins != icSigYCbCrData
+ && ins != icSigYxyData
+ && ins != icSigHsvData
+ && ins != icSigHlsData) {
+ for (i = 0; i < MAX_CHAN; i++) {
+ in[i] /= scale;
+ }
+ }
+
+ if (repXYZ100 && ins == icSigXYZData) {
+ in[0] /= 100.0;
+ in[1] /= 100.0;
+ in[2] /= 100.0;
+ }
+
+ if (repYxy && ins == icSigYxyData) {
+ icmXYZ2Yxy(in, in);
+ }
+
+ /* JCh -> Jab & LCh -> Lab */
+ if ((repJCh && ins == icxSigJChData)
+ || (repLCh && ins == icxSigLChData)) {
+ double C = in[1];
+ double h = in[2];
+ in[1] = C * cos(3.14159265359/180.0 * h);
+ in[2] = C * sin(3.14159265359/180.0 * h);
+ }
+
+ /* Do conversion */
+ if (invert) {
+ for (j = 0; j < MAX_CHAN; j++)
+ out[j] = in[j]; /* Carry any auxiliary value to out for lookup */
+ if ((rv = luo->inv_lookup(luo, out, in)) > 1)
+ error ("%d, %s",xicco->errc,xicco->err);
+ } else {
+ if ((rv = luo->lookup(luo, out, in)) > 1)
+ error ("%d, %s",xicco->errc,xicco->err);
+ }
+
+ if (slocwarn) {
+ double xyz[3];
+
+ if (outs == icSigLabData || outs == icxSigLChData)
+ icmLab2XYZ(&icmD50, out, xyz);
+ else
+ icmCpy3(xyz, out);
+
+ outsloc = icx_outside_spec_locus(xyz, icxOT_CIE_1931_2);
+ }
+
+ /* Copy conversion out value so that we can create user values */
+ for (i = 0; i < MAX_CHAN; i++)
+ uout[i] = out[i];
+
+ if (repXYZ100 && outs == icSigXYZData) {
+ uout[0] *= 100.0;
+ uout[1] *= 100.0;
+ uout[2] *= 100.0;
+ }
+
+ if (repYxy && outs == icSigYxyData) {
+ icmXYZ2Yxy(out, out);
+ }
+
+ /* Jab -> JCh and Lab -> LCh */
+ if ((repJCh && outs == icxSigJChData)
+ || (repLCh && outs == icxSigLChData)) {
+ double a = uout[1];
+ double b = uout[2];
+ uout[1] = sqrt(a * a + b * b);
+ uout[2] = (180.0/3.14159265359) * atan2(b, a);
+ uout[2] = (uout[2] < 0.0) ? uout[2] + 360.0 : uout[2];
+ }
+
+ /* If device data and scale */
+ if(scale > 0.0
+ && outs != icxSigJabData
+ && outs != icxSigJChData
+ && outs != icSigXYZData
+ && outs != icSigLabData
+ && outs != icxSigLChData
+ && outs != icSigLuvData
+ && outs != icSigYCbCrData
+ && outs != icSigYxyData
+ && outs != icSigHsvData
+ && outs != icSigHlsData) {
+ for (i = 0; i < MAX_CHAN; i++) {
+ uout[i] *= scale;
+ }
+ }
+
+ /* Output the results */
+ if (verb > 0) {
+ for (j = 0; j < inn; j++) {
+ if (j > 0)
+ fprintf(stdout," %f",uin[j]);
+ else
+ fprintf(stdout,"%f",uin[j]);
+ }
+ printf(" [%s] -> %s -> ", icx2str(icmColorSpaceSignature, ins),
+ icm2str(icmLuAlg, alg));
+ }
+
+ for (j = 0; j < outn; j++) {
+ if (j > 0)
+ fprintf(stdout," %f",uout[j]);
+ else
+ fprintf(stdout,"%f",uout[j]);
+ }
+ if (verb > 0)
+ printf(" [%s]", icx2str(icmColorSpaceSignature, outs));
+
+ if (verb > 0 && tlimit >= 0) {
+ double tot;
+ for (tot = 0.0, j = 0; j < outn; j++) {
+ tot += out[j];
+ }
+ printf(" Lim %f",tot);
+ }
+ if (outsloc)
+ fprintf(stdout,"(Imaginary)");
+
+ if (verb == 0 || rv == 0)
+ fprintf(stdout,"\n");
+ else {
+ fprintf(stdout," (clip)\n");
+
+ /* This probably isn't right - we need to convert */
+ /* in[] to Lab to Jab if it is not in that space, */
+ /* so we can do a delta E on it. */
+ if (actual && aluo != NULL) {
+ double cin[MAX_CHAN], de;
+ if ((rv = aluo->lookup(aluo, cin, out)) > 1)
+ error ("%d, %s",xicco->errc,xicco->err);
+
+ for (de = 0.0, j = 0; j < inn; j++) {
+ de += (cin[j] - in[j]) * (cin[j] - in[j]);
+ }
+ de = sqrt(de);
+ printf("[Actual ");
+ for (j = 0; j < inn; j++) {
+ if (j > 0)
+ fprintf(stdout," %f",cin[j]);
+ else
+ fprintf(stdout,"%f",cin[j]);
+ }
+ printf(", deltaE %f]\n",de);
+ }
+ }
+ }
+ }
+
+ /* Done with lookup object */
+ if (aluo != NULL && aluo != luo)
+ luo->del(aluo);
+ luo->del(luo);
+
+ xicco->del(xicco); /* Expansion wrapper */
+ icco->del(icco); /* Icc */
+ fp->del(fp);
+
+ return 0;
+}
+