From 22f703cab05b7cd368f4de9e03991b7664dc5022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Mon, 1 Sep 2014 13:56:46 +0200 Subject: Initial import of argyll version 1.5.1-8 --- spectro/ccxxmake.c | 1446 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1446 insertions(+) create mode 100644 spectro/ccxxmake.c (limited to 'spectro/ccxxmake.c') diff --git a/spectro/ccxxmake.c b/spectro/ccxxmake.c new file mode 100644 index 0000000..737600c --- /dev/null +++ b/spectro/ccxxmake.c @@ -0,0 +1,1446 @@ + +/* Colorimeter Correction Matrix and */ +/* Colorimeter Calibration Spectral Sample creation utility */ + +/* + * Argyll Color Correction System + * Author: Graeme W. Gill + * Date: 19/8/2010 + * + * Copyright 2010, 2011, 2013 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. + */ + +/* This program uses display measurements from a colorimeter and */ +/* a spectrometer to create a correction matrix for a particular */ +/* colorimeter/display combination,. */ +/* or */ +/* It uses display measurements from a spectrometer to create */ +/* calibration samples that can be used with a Colorimeter that */ +/* knowns its own spectral sensitivity curves (ie. X-Rite i1d3, Spyder 4). */ + +/* Based on spotread.c, illumread.c, dispcal.c */ + +/* + TTBD: + + Should add an option to set a UI_SELECTORS value. + + If any spectrometer gets a display type function (ie. refresh/non-refresh) + then it becomes difficult to know what to do with the -y option :- + + * Ignore the problem - don't set -y option on spectrometers. + Error shouldn't be significant for ref/nonref ? + + * Force the colorimeter to go first, record the ref/nonref state and + set in the spectrometer ? Make .ti3 file order the same for consistency ? + + Would be nice to have a veryify option that produces + a fit report of a matrix vs. the input files. + + Would be nice to have the option of procssing a Spyder 3 correction.txt file. + (See post from umberto.guidali@tiscali.it) + + Would be nice to be able to use an i1D3 to correct other instruments, + or an i1D3 created .ti3 as the reference. + + Would be nice to have an option of providing two ICC profiles, + instead of using .ti3 files (?? How well would it work though ?) + */ + + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include "copyright.h" +#include "aconfig.h" +#include "numlib.h" +#include "cgats.h" +#include "xspect.h" +#include "insttypes.h" +#include "conv.h" +#include "icoms.h" +#include "inst.h" +#include "dispwin.h" +#include "webwin.h" +#include "dispsup.h" +#include "ccss.h" +#include "ccmx.h" +#include "instappsup.h" +#include "spyd2setup.h" + +#if defined (NT) +#include +#endif + +#define DEFAULT_MSTEPS 1 +#undef SHOW_WINDOW_ONFAKE /* Display a test window up for a fake device */ +#define COMPORT 1 /* Default com port 1..4 */ + + +#if defined(__APPLE__) && defined(__POWERPC__) +/* Workaround for a ppc gcc 3.3 optimiser bug... */ +static int gcc_bug_fix(int i) { + static int nn; + nn += i; + return nn; +} +#endif /* APPLE */ + +/* Invoke with -dfake for testing with a fake device. */ +/* Invoke with -dFAKE for automatic creation of test matrix. */ +/* Will use a fake.icm/.icc profile if present, or a built in fake */ +/* device behaviour if not. */ + +void +usage(char *diag, ...) { + disppath **dp; + icompaths *icmps = new_icompaths(0); + inst2_capability cap = 0; + + fprintf(stderr,"Create CCMX or CCSS, Version %s\n",ARGYLL_VERSION_STR); + fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n"); + if (setup_spyd2() == 2) + fprintf(stderr,"WARNING: This file contains a proprietary firmware image, and may not be freely distributed !\n"); + if (diag != NULL) { + va_list args; + fprintf(stderr,"Diagnostic: "); + va_start(args, diag); + vfprintf(stderr, diag, args); + va_end(args); + fprintf(stderr,"\n"); + } + fprintf(stderr,"usage: ccmxmake [-options] output.ccmx\n"); + fprintf(stderr," -v Verbose mode\n"); + fprintf(stderr," -S Create CCSS rather than CCMX\n"); + fprintf(stderr," -f file1.ti3[,file2.ti3] Create from one or two .ti3 files rather than measure.\n"); +#if defined(UNIX_X11) + fprintf(stderr," -display displayname Choose X11 display name\n"); + fprintf(stderr," -d n[,m] Choose the display n from the following list (default 1)\n"); + fprintf(stderr," Optionally choose different display m for VideoLUT access\n"); +#else + fprintf(stderr," -d n Choose the display from the following list (default 1)\n"); +#endif + dp = get_displays(); + if (dp == NULL || dp[0] == NULL) + fprintf(stderr," ** No displays found **\n"); + else { + int i; + for (i = 0; ; i++) { + if (dp[i] == NULL) + break; + fprintf(stderr," %d name = '%s'\n",i+1,dp[i]->name); + fprintf(stderr," %d = '%s'\n",i+1,dp[i]->description); + } + } + free_disppaths(dp); + fprintf(stderr," -dweb[:port] Display via a web server at port (default 8080)\n"); +// fprintf(stderr," -d fake Use a fake display device for testing, fake%s if present\n",ICC_FILE_EXT); + fprintf(stderr," -p Use telephoto mode (ie. for a projector) (if available)\n"); + cap = inst_show_disptype_options(stderr, " -y c|l ", icmps, 1); + fprintf(stderr," -P ho,vo,ss[,vs] Position test window and scale it\n"); + fprintf(stderr," ho,vi: 0.0 = left/top, 0.5 = center, 1.0 = right/bottom etc.\n"); + fprintf(stderr," ss: 0.5 = half, 1.0 = normal, 2.0 = double etc.\n"); + fprintf(stderr," -F Fill whole screen with black background\n"); +#if defined(UNIX_X11) + fprintf(stderr," -n Don't set override redirect on test window\n"); +#endif + fprintf(stderr," -N Disable initial calibration of instrument if possible\n"); + fprintf(stderr," -H Use high resolution spectrum mode (if available)\n"); +// fprintf(stderr," -V Use adaptive measurement mode (if available)\n"); + fprintf(stderr," -C \"command\" Invoke shell \"command\" each time a color is set\n"); + fprintf(stderr," -o observ Choose CIE Observer for CCMX spectrometer data:\n"); + fprintf(stderr," 1931_2 (def), 1964_10, S&B 1955_2, shaw, J&V 1978_2\n"); + fprintf(stderr," -s steps Override default patch sequence combination steps (default %d)\n",DEFAULT_MSTEPS); + fprintf(stderr," -W n|h|x Override serial port flow control: n = none, h = HW, x = Xon/Xoff\n"); + fprintf(stderr," -D [level] Print debug diagnostics to stderr\n"); + fprintf(stderr," -E desciption Override the default overall description\n"); + fprintf(stderr," -I displayname Set display make and model description\n"); + fprintf(stderr," -T displaytech Set display technology description (ie. CRT, LCD etc.)\n"); + fprintf(stderr," -U c Set UI selection character(s)\n"); + fprintf(stderr," -Y r|n Set or override refresh/non-refresh display type\n"); + fprintf(stderr," -Y A Use non-adaptive integration time mode (if available).\n"); + fprintf(stderr," correction.ccmx | calibration.ccss\n"); + fprintf(stderr," File to save result to\n"); + if (icmps != NULL) + icmps->del(icmps); + exit(1); +} + +typedef double ary3[3]; + +int main(int argc, char *argv[]) +{ + int i,j; + int fa, nfa, mfa; /* current argument we're looking at */ + disppath *disp = NULL; /* Display being used */ + double hpatscale = 1.0, vpatscale = 1.0; /* scale factor for test patch size */ + double ho = 0.0, vo = 0.0; /* Test window offsets, -1.0 to 1.0 */ + int blackbg = 0; /* NZ if whole screen should be filled with black */ + int verb = 0; + int debug = 0; + int doccss = 0; /* Create CCSS rather than CCMX */ + int fake = 0; /* Use the fake device for testing, 2 for auto */ + int faketoggle = 0; /* Toggle fake between "colorimeter" and "spectro" */ + int fakeseq = 0; /* Fake auto CCMX sequence */ + int spec = 0; /* Need spectral data to implement option */ + icxObserverType observ = icxOT_CIE_1931_2; + int override = 1; /* Override redirect on X11 */ + icompaths *icmps = NULL; /* Ports to choose from */ + int comno = COMPORT; /* COM port used */ + flow_control fc = fc_nc; /* Default flow control */ + int highres = 0; /* High res mode if available */ + int dtype = 0; /* Display kind, 0 = default, 1 = CRT, 2 = LCD */ + int refrmode = -1; /* Refresh mode */ + int cbid = 0; /* Calibration base display mode ID */ + int nadaptive = 0; /* Use non-adaptive mode if available */ + int tele = 0; /* NZ if telephoto mode */ + int noinitcal = 0; /* Disable initial calibration */ + int webdisp = 0; /* NZ for web display, == port number */ + char *ccallout = NULL; /* Change color Shell callout */ + int msteps = DEFAULT_MSTEPS; /* Patch surface size */ + int npat = 0; /* Number of patches/colors */ + ary3 *refs = NULL; /* Reference XYZ values */ + int gotref = 0; + char *refname = NULL; /* Name of reference instrument */ + ary3 *cols = NULL; /* Colorimeter XYZ values */ + int gotcol = 0; + char *colname = NULL; /* Name of colorimeter instrument */ + col *rdcols = NULL; /* Internal storage of all the patch colors */ + int saved = 0; /* Saved result */ + char innames[2][MAXNAMEL+1] = { "\000", "\000" }; /* .ti3 input names */ + char outname[MAXNAMEL+1] = "\000"; /* ccmx output file name */ + char *description = NULL; /* Given overall description */ + char *displayname = NULL; /* Given display name */ + char *displaytech = NULL; /* Given display technology */ + char *uisel = NULL; /* UI selection letters */ + int rv; + + set_exe_path(argv[0]); /* Set global exe_path and error_program */ + check_if_not_interactive(); + setup_spyd2(); /* Load firware if available */ + + /* Process the arguments */ + mfa = 0; /* Minimum final 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+mfa) < argc) { + if (argv[fa+1][0] != '-') { + nfa = fa + 1; + na = argv[nfa]; /* next is seperate non-flag argument */ + } + } + } + + if (argv[fa][1] == '?') { + usage("Usage requested"); + + } else if (argv[fa][1] == 'v') { + verb = 1; + g_log->verb = verb; + + } else if (argv[fa][1] == 'S') { + doccss = 1; + + } else if (argv[fa][1] == 'f') { + char *cna, *f1 = NULL; + fa = nfa; + if (na == NULL) usage("Expect argument to input file flag -f"); + + if ((cna = strdup(na)) == NULL) + error("Malloc failed"); + + /* If got just one file - enough for CCSS */ + if ((f1 = strchr(cna, ',')) == NULL) { + strncpy(innames[0],cna,MAXNAMEL-1); innames[0][MAXNAMEL-1] = '\000'; + free(cna); + + /* Got two files - needed for CCMX */ + } else { + *f1++ = '\000'; + strncpy(innames[0],cna,MAXNAMEL-1); innames[0][MAXNAMEL-1] = '\000'; + strncpy(innames[1],f1,MAXNAMEL-1); innames[1][MAXNAMEL-1] = '\000'; + free(cna); + } + + /* Display number */ + } else if (argv[fa][1] == 'd') { + if (strncmp(na,"web",3) == 0 + || strncmp(na,"WEB",3) == 0) { + webdisp = 8080; + if (na[3] == ':') { + webdisp = atoi(na+4); + if (webdisp == 0 || webdisp > 65535) + usage("Web port number must be in range 1..65535"); + } + fa = nfa; + } else { +#if defined(UNIX_X11) + int ix, iv; + + if (strcmp(&argv[fa][2], "isplay") == 0 || strcmp(&argv[fa][2], "ISPLAY") == 0) { + if (++fa >= argc || argv[fa][0] == '-') usage("Parameter expected following -display"); + setenv("DISPLAY", argv[fa], 1); + } else { + if (na == NULL) usage("Parameter expected following -d"); + fa = nfa; + if (strcmp(na,"fake") == 0 || strcmp(na,"FAKE") == 0) { + fake = 1; + if (strcmp(na,"FAKE") == 0) + fakeseq = 1; + } else { + if (sscanf(na, "%d,%d",&ix,&iv) != 2) { + ix = atoi(na); + iv = 0; + } + if (disp != NULL) + free_a_disppath(disp); + if ((disp = get_a_display(ix-1)) == NULL) + usage("-d parameter %d out of range",ix); + if (iv > 0) + disp->rscreen = iv-1; + } + } +#else + int ix; + if (na == NULL) usage("Parameter expected following -d"); + fa = nfa; + if (strcmp(na,"fake") == 0 || strcmp(na,"FAKE") == 0) { + fake = 1; + if (strcmp(na,"FAKE") == 0) + fakeseq = 1; + } else { + ix = atoi(na); + if (disp != NULL) + free_a_disppath(disp); + if ((disp = get_a_display(ix-1)) == NULL) + usage("-d parameter %d out of range",ix); + } +#endif + } +#if defined(UNIX_X11) + } else if (argv[fa][1] == 'n') { + override = 0; +#endif /* UNIX */ + + /* COM port */ + } else if (argv[fa][1] == 'c') { + fa = nfa; + if (na == NULL) usage("Paramater expected following -c"); + comno = atoi(na); + if (comno < 1 || comno > 40) usage("-c parameter %d out of range",comno); + + /* Telephoto */ + } else if (argv[fa][1] == 'p') { + tele = 1; + + /* Display type */ + } else if (argv[fa][1] == 'y') { + fa = nfa; + if (na == NULL) usage("Parameter expected after -y"); + dtype = na[0]; + + /* For ccss, set a default */ + if (na[0] == 'r') { + refrmode = 1; + } else if (na[0] == 'n') { + refrmode = 0; + } + + /* Test patch offset and size */ + } else if (argv[fa][1] == 'P') { + fa = nfa; + if (na == NULL) usage("Parameter expected after -P"); + if (sscanf(na, " %lf,%lf,%lf,%lf ", &ho, &vo, &hpatscale, &vpatscale) == 4) { + ; + } else if (sscanf(na, " %lf,%lf,%lf ", &ho, &vo, &hpatscale) == 3) { + vpatscale = hpatscale; + } else { + usage("-P parameter '%s' not recognised",na); + } + if (ho < 0.0 || ho > 1.0 + || vo < 0.0 || vo > 1.0 + || hpatscale <= 0.0 || hpatscale > 50.0 + || vpatscale <= 0.0 || vpatscale > 50.0) + usage("-P parameters %f %f %f %f out of range",ho,vo,hpatscale,vpatscale); + ho = 2.0 * ho - 1.0; + vo = 2.0 * vo - 1.0; + + /* Black background */ + } else if (argv[fa][1] == 'F') { + blackbg = 1; + + /* No initial calibration */ + } else if (argv[fa][1] == 'N') { + noinitcal = 1; + + /* High res spectral mode */ + } else if (argv[fa][1] == 'H') { + highres = 1; + + /* Adaptive mode - now default, so flag is deprecated */ + } else if (argv[fa][1] == 'V') { + warning("dispcal -V flag is deprecated"); + + /* Spectral Observer type (only relevant for CCMX) */ + } else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') { + fa = nfa; + if (na == NULL) usage("Parameter expecte after -o"); + if (strcmp(na, "1931_2") == 0) { /* Classic 2 degree */ + spec = 2; + observ = icxOT_CIE_1931_2; + } else if (strcmp(na, "1964_10") == 0) { /* Classic 10 degree */ + spec = 2; + observ = icxOT_CIE_1964_10; + } else if (strcmp(na, "1955_2") == 0) { /* Stiles and Burch 1955 2 degree */ + spec = 2; + observ = icxOT_Stiles_Burch_2; + } else if (strcmp(na, "1978_2") == 0) { /* Judd and Voss 1978 2 degree */ + spec = 2; + observ = icxOT_Judd_Voss_2; + } else if (strcmp(na, "shaw") == 0) { /* Shaw and Fairchilds 1997 2 degree */ + spec = 2; + observ = icxOT_Shaw_Fairchild_2; + } else + usage("-o parameter '%s' not recognised",na); + + } else if (argv[fa][1] == 's') { + fa = nfa; + if (na == NULL) usage("Parameter expecte after -s"); + msteps = atoi(na); + if (msteps < 1 || msteps > 16) + usage("-s parameter value %d is outside the range 1 to 16",msteps); + + /* Change color callout */ + } else if (argv[fa][1] == 'C') { + fa = nfa; + if (na == NULL) usage("Parameter expected after -C"); + ccallout = na; + + /* Serial port flow control */ + } else if (argv[fa][1] == 'W') { + fa = nfa; + if (na == NULL) usage("Paramater expected following -W"); + if (na[0] == 'n' || na[0] == 'N') + fc = fc_none; + else if (na[0] == 'h' || na[0] == 'H') + fc = fc_Hardware; + else if (na[0] == 'x' || na[0] == 'X') + fc = fc_XonXOff; + else + usage("-W parameter '%c' not recognised",na[0]); + + } else if (argv[fa][1] == 'D') { + debug = 1; + if (na != NULL && na[0] >= '0' && na[0] <= '9') { + debug = atoi(na); + fa = nfa; + } + g_log->debug = debug; + + } else if (argv[fa][1] == 'I') { + fa = nfa; + if (na == NULL) usage("Expect argument to display description flag -I"); + displayname = strdup(na); + + } else if (argv[fa][1] == 'T') { + fa = nfa; + if (na == NULL) usage("Expect argument to display technology flag -T"); + displaytech = strdup(na); + + /* Copyright string */ + } else if (argv[fa][1] == 'E') { + fa = nfa; + if (na == NULL) usage("Expect argument to overall description flag -E"); + description = strdup(na); + + /* Extra flags */ + } else if (argv[fa][1] == 'Y') { + if (na == NULL) + usage("Flag '-Y' expects extra flag"); + + if (na[0] == 'A') { + nadaptive = 1; + } else if (na[0] == 'r') { + refrmode = 1; + } else if (na[0] == 'n') { + refrmode = 0; + } else { + usage("Flag '-Z %c' not recognised",na[0]); + } + + /* UI selection character */ + } else if (argv[fa][1] == 'U') { + fa = nfa; + if (na == NULL || na[0] == '\000') usage("Expect argument to flag -U"); + uisel = na; + for (i = 0; uisel[i] != '\000'; i++) { + if (!( (uisel[i] >= '0' && uisel[i] <= '9') + || (uisel[i] >= 'A' && uisel[i] <= 'Z') + || (uisel[i] >= 'a' && uisel[i] <= 'z'))) { + usage("-U character(s) must be 0-9,A-Z,a-z"); + } + } + + } else + usage("Flag '-%c' not recognised",argv[fa][1]); + } + else + break; + } + + /* Get the output ccmx file name argument */ + if (fa >= argc) + usage("Output filname expected"); + + strncpy(outname,argv[fa++],MAXNAMEL-1); outname[MAXNAMEL-1] = '\000'; + + if (fakeseq && doccss) + error("Fake CCSS test not implemeted"); + + printf("\n"); + + if (displayname == NULL && displaytech == NULL) + error("Either the display description (-I) or technology (-T) needs to be set"); + + /* CCSS: See if we're working from a .ti3 file */ + if (doccss && innames[0][0] != '\000') { + cgats *cgf = NULL; /* cgats file data */ + int sidx; /* Sample ID index */ + int ii, ti; + char buf[100]; + int spi[XSPECT_MAX_BANDS]; /* CGATS indexes for each wavelength */ + xspect sp, *samples = NULL; + ccss *cc; + double bigv = -1e60; + + /* Open spectral values file */ + cgf = new_cgats(); /* Create a CGATS structure */ + cgf->add_other(cgf, ""); /* Allow any signature file */ + + if (cgf->read_name(cgf, innames[0])) + error("CGATS file '%s' read error : %s",innames[0],cgf->err); + + if (cgf->ntables < 1) + error ("Input file '%s' doesn't contain at least one table",innames[0]); + + if ((npat = cgf->t[0].nsets) <= 0) + error("No sets of data in file '%s'",innames[0]); + + if ((samples = (xspect *)malloc(npat * sizeof(xspect))) == NULL) + error("malloc failed"); + + if ((ii = cgf->find_kword(cgf, 0, "TARGET_INSTRUMENT")) < 0) + error ("Can't find keyword TARGET_INSTRUMENT in '%s'",innames[0]); + + if ((ti = cgf->find_kword(cgf, 0, "DISPLAY_TYPE_REFRESH")) >= 0) { + if (stricmp(cgf->t[0].kdata[ti], "YES") == 0) + refrmode = 1; + else if (stricmp(cgf->t[0].kdata[ti], "NO") == 0) + refrmode = 0; + } + + if ((ii = cgf->find_kword(cgf, 0, "SPECTRAL_BANDS")) < 0) + error ("Input file '%s' doesn't contain keyword SPECTRAL_BANDS",innames[0]); + sp.spec_n = atoi(cgf->t[0].kdata[ii]); + if ((ii = cgf->find_kword(cgf, 0, "SPECTRAL_START_NM")) < 0) + error ("Input file '%s' doesn't contain keyword SPECTRAL_START_NM",innames[0]); + sp.spec_wl_short = atof(cgf->t[0].kdata[ii]); + if ((ii = cgf->find_kword(cgf, 0, "SPECTRAL_END_NM")) < 0) + error ("Input file '%s' doesn't contain keyword SPECTRAL_END_NM",innames[0]); + sp.spec_wl_long = atof(cgf->t[0].kdata[ii]); + sp.norm = 1.0; /* We assume emssive */ + + /* Find the fields for spectral values */ + for (j = 0; j < sp.spec_n; j++) { + int nm; + + /* Compute nearest integer wavelength */ + nm = (int)(sp.spec_wl_short + ((double)j/(sp.spec_n-1.0)) + * (sp.spec_wl_long - sp.spec_wl_short) + 0.5); + + sprintf(buf,"SPEC_%03d",nm); + + if ((spi[j] = cgf->find_field(cgf, 0, buf)) < 0) + error("Input file '%s' doesn't contain field %s",innames[0],buf); + } + + /* Transfer all the spectral values */ + for (i = 0; i < npat; i++) { + + XSPECT_COPY_INFO(&samples[i], &sp); + + for (j = 0; j < sp.spec_n; j++) { + samples[i].spec[j] = *((double *)cgf->t[0].fdata[i][spi[j]]); + } + } + cgf->del(cgf); /* Clean up */ + cgf = NULL; + + if (description == NULL) { + char *disp = displaytech != NULL ? displaytech : displayname; + char *tt = "CCSS for "; + if ((description = malloc(strlen(disp) + strlen(tt) + 1)) == NULL) + error("Malloc failed"); + strcpy(description, tt); + strcat(description, disp); + } + + /* See what the highest value is */ + for (i = 0; i < npat; i++) { /* For all grid points */ + + for (j = 0; j < samples[i].spec_n; j++) { + if (samples[i].spec[j] > bigv) + bigv = samples[i].spec[j]; + } + } + + /* Normalize the values */ + for (i = 0; i < npat; i++) { /* For all grid points */ + double scale = 100.0; + + for (j = 0; j < samples[i].spec_n; j++) + samples[i].spec[j] *= scale / bigv; + } + + if (refrmode < 0) + error("The display refresh mode is not known - use the -Y flag"); + + if ((cc = new_ccss()) == NULL) + error("new_ccss() failed"); + + if (cc->set_ccss(cc, "Argyll ccxxmake", NULL, description, displayname, + displaytech, refrmode, uisel, refname, samples, npat)) { + error("set_ccss failed with '%s'\n",cc->err); + } + if(cc->write_ccss(cc, outname)) + printf("\nWriting CCXX file '%s' failed\n",outname); + else + printf("\nWriting CCXX file '%s' succeeded\n",outname); + cc->del(cc); + free(samples); + +#ifdef DEBUG + printf("About to exit\n"); +#endif + return 0; + } + + /* CCMX: See if we're working from two files */ + if (!doccss && innames[0][0] != '\000') { + int n; + char *oname = NULL; /* Observer name */ + ccmx *cc; + + if (innames[1][0] == '\000') { + error("Need two .ti3 files to create CCMX"); + } + + /* Open up each CIE file in turn, target then measured, */ + /* and read in the CIE values. */ + for (n = 0; n < 2; n++) { + cgats *cgf = NULL; /* cgats file data */ + int isLab = 0; /* 0 if file CIE is XYZ, 1 if is Lab */ + double wxyz[3], scale = 1.0;/* Scale factor back to absolute */ + int sidx; /* Sample ID index */ + int xix, yix, zix; + ary3 *current = NULL; /* Current value array */ + int ii, ti; + int instspec = 0; /* File is spectrale */ + + /* Open CIE target values */ + cgf = new_cgats(); /* Create a CGATS structure */ + cgf->add_other(cgf, ""); /* Allow any signature file */ + + if (cgf->read_name(cgf, innames[n])) + error("CGATS file '%s' read error : %s",innames[n],cgf->err); + + if (cgf->ntables < 1) + error ("Input file '%s' doesn't contain at least one table",innames[n]); + + /* Check if the file is suitable */ + if (cgf->find_field(cgf, 0, "LAB_L") < 0 + && cgf->find_field(cgf, 0, "XYZ_X") < 0) { + + error ("No CIE data found in file '%s'",innames[n]); + } + + if (cgf->find_field(cgf, 0, "LAB_L") >= 0) + isLab = 1; + + if (cols == NULL) { + if ((npat = cgf->t[0].nsets) <= 0) + error("No sets of data in file '%s'",innames[n]); + + if ((refs = (ary3 *)malloc(npat * sizeof(ary3))) == NULL) + error("malloc failed"); + if ((cols = (ary3 *)malloc(npat * sizeof(ary3))) == NULL) + error("malloc failed"); + + } else { + if (npat != cgf->t[0].nsets) + error ("Number of sets %d in file '%s' doesn't match other file %d",cgf->t[0].nsets,innames[n],npat); + } + + if ((ii = cgf->find_kword(cgf, 0, "TARGET_INSTRUMENT")) < 0) + error ("Can't find keyword TARGET_INSTRUMENT in '%s'",innames[n]); + + if ((ti = cgf->find_kword(cgf, 0, "INSTRUMENT_TYPE_SPECTRAL")) < 0) + error ("Can't find keyword INSTRUMENT_TYPE_SPECTRAL in '%s'",innames[n]); + + if (strcmp(cgf->t[0].kdata[ti],"YES") == 0) { + instspec = 1; /* Currently is a spectral file */ + if (gotref) + error("Found two spectral files - expect one colorimtric file"); + current = refs; + refname = strdup(cgf->t[0].kdata[ii]); + gotref = 1; + + } else if (strcmp(cgf->t[0].kdata[ti],"NO") == 0) { + instspec = 0; /* Currently is not spectral file */ + if (gotcol) { + /* Copy what we though was cols to refs */ + refname = colname; + for (i = 0; i < npat; i++) { + refs[i][0] = cols[i][0]; + refs[i][1] = cols[i][1]; + refs[i][2] = cols[i][2]; + } + gotref = 1; + warning("Got two colorimetric files - assuming '%s' is the refrence",innames[0]); + refrmode = -1; + cbid = 0; + + if (spec) { + error("Spectral reference is required to use non-standard observer"); + } + } + if ((ti = cgf->find_kword(cgf, 0, "DISPLAY_TYPE_REFRESH")) >= 0) { + if (stricmp(cgf->t[0].kdata[ti], "YES") == 0) + refrmode = 1; + else if (stricmp(cgf->t[0].kdata[ti], "NO") == 0) + refrmode = 0; + } + if ((ti = cgf->find_kword(cgf, 0, "DISPLAY_TYPE_BASE_ID")) >= 0) { + cbid = atoi(cgf->t[0].kdata[ti]); + } else { + cbid = 0; + } + current = cols; + colname = strdup(cgf->t[0].kdata[ii]); + gotcol = 1; + } else { + error ("Unknown INSTRUMENT_TYPE_SPECTRAL value '%s'",cgf->t[0].kdata[ti]); + } + + if ((ti = cgf->find_kword(cgf, 0, "NORMALIZED_TO_Y_100")) < 0 + || strcmp(cgf->t[0].kdata[ti],"NO") == 0) { + scale = 1.0; /* Leave absolute */ + } else { + if ((ti = cgf->find_kword(cgf, 0, "LUMINANCE_XYZ_CDM2")) < 0) + error ("Can't find keyword LUMINANCE_XYZ_CDM2 in '%s'",innames[n]); + if (sscanf(cgf->t[0].kdata[ti],"%lf %lf %lf", &wxyz[0], &wxyz[1], &wxyz[2]) != 3) + error ("Unable to parse LUMINANCE_XYZ_CDM2 in '%s'",innames[n]); + scale = wxyz[1]/100.0; /* Convert from Y = 100 normalise back to absolute */ + } + + if (instspec && spec) { + int ii; + xspect sp; + char buf[100]; + int spi[XSPECT_MAX_BANDS]; /* CGATS indexes for each wavelength */ + xsp2cie *sp2cie; /* Spectral conversion object */ + + if ((ii = cgf->find_kword(cgf, 0, "SPECTRAL_BANDS")) < 0) + error ("Input file '%s' doesn't contain keyword SPECTRAL_BANDS",innames[n]); + sp.spec_n = atoi(cgf->t[0].kdata[ii]); + if ((ii = cgf->find_kword(cgf, 0, "SPECTRAL_START_NM")) < 0) + error ("Input file '%s' doesn't contain keyword SPECTRAL_START_NM",innames[n]); + sp.spec_wl_short = atof(cgf->t[0].kdata[ii]); + if ((ii = cgf->find_kword(cgf, 0, "SPECTRAL_END_NM")) < 0) + error ("Input file '%s' doesn't contain keyword SPECTRAL_END_NM",innames[n]); + sp.spec_wl_long = atof(cgf->t[0].kdata[ii]); + sp.norm = 1.0; /* We assume emssive */ + + /* Find the fields for spectral values */ + for (j = 0; j < sp.spec_n; j++) { + int nm; + + /* Compute nearest integer wavelength */ + nm = (int)(sp.spec_wl_short + ((double)j/(sp.spec_n-1.0)) + * (sp.spec_wl_long - sp.spec_wl_short) + 0.5); + + sprintf(buf,"SPEC_%03d",nm); + + if ((spi[j] = cgf->find_field(cgf, 0, buf)) < 0) + error("Input file '%s' doesn't contain field %s",innames[n],buf); + } + + /* Create a spectral conversion object */ + if ((sp2cie = new_xsp2cie(icxIT_none, NULL, observ, NULL, icSigXYZData, icxClamp)) == NULL) + error("Creation of spectral conversion object failed"); + + for (i = 0; i < npat; i++) { + + /* Read the spectral values for this patch */ + for (j = 0; j < sp.spec_n; j++) { + sp.spec[j] = *((double *)cgf->t[0].fdata[i][spi[j]]) * scale; + } + + /* Convert it to CIE space */ + sp2cie->convert(sp2cie, current[i], &sp); + } + sp2cie->del(sp2cie); /* Done with this */ + + /* Colorimetric file - assume it's the target */ + } else { + + if (isLab) { /* Expect Lab */ + if ((xix = cgf->find_field(cgf, 0, "LAB_L")) < 0) + error("Input file '%s' doesn't contain field LAB_L",innames[n]); + if (cgf->t[0].ftype[xix] != r_t) + error("Field LAB_L is wrong type"); + if ((yix = cgf->find_field(cgf, 0, "LAB_A")) < 0) + error("Input file '%s' doesn't contain field LAB_A",innames[n]); + if (cgf->t[0].ftype[yix] != r_t) + error("Field LAB_A is wrong type"); + if ((zix = cgf->find_field(cgf, 0, "LAB_B")) < 0) + error("Input file '%s' doesn't contain field LAB_B",innames[n]); + if (cgf->t[0].ftype[zix] != r_t) + error("Field LAB_B is wrong type"); + + } else { /* Expect XYZ */ + if ((xix = cgf->find_field(cgf, 0, "XYZ_X")) < 0) + error("Input file '%s' doesn't contain field XYZ_X",innames[n]); + if (cgf->t[0].ftype[xix] != r_t) + error("Field XYZ_X is wrong type"); + if ((yix = cgf->find_field(cgf, 0, "XYZ_Y")) < 0) + error("Input file '%s' doesn't contain field XYZ_Y",innames[n]); + if (cgf->t[0].ftype[yix] != r_t) + error("Field XYZ_Y is wrong type"); + if ((zix = cgf->find_field(cgf, 0, "XYZ_Z")) < 0) + error("Input file '%s' doesn't contain field XYZ_Z",innames[n]); + if (cgf->t[0].ftype[zix] != r_t) + error("Field XYZ_Z is wrong type"); + } + + for (i = 0; i < npat; i++) { + current[i][0] = *((double *)cgf->t[0].fdata[i][xix]); + current[i][1] = *((double *)cgf->t[0].fdata[i][yix]); + current[i][2] = *((double *)cgf->t[0].fdata[i][zix]); + if (isLab) { /* Convert test patch Lab to XYZ scale 100 */ + icmLab2XYZ(&icmD50_100, current[i], current[i]); + } + /* Rescale to absolute if needed */ + current[i][0] *= scale; + current[i][1] *= scale; + current[i][2] *= scale; + } + } + cgf->del(cgf); /* Clean up */ + cgf = NULL; + } + + if (spec != 0 && observ != icxOT_CIE_1931_2) + oname = standardObserverDescription(observ); + + if (oname != NULL) { + char *tt = colname; + + if ((colname = malloc(strlen(tt) + strlen(oname) + 3)) == NULL) + error("Malloc failed"); + strcpy(colname, tt); + strcat(colname, " ("); + strcat(colname, oname); + strcat(colname, ")"); + } + if (description == NULL) { + char *disp = displaytech != NULL ? displaytech : displayname; + if ((description = malloc(strlen(colname) + strlen(disp) + 4)) == NULL) + error("Malloc failed"); + strcpy(description, colname); + strcat(description, " & "); + strcat(description, disp); + } + + if (refrmode < 0) + error("The display refresh mode is not known - use the -Y flag"); + + if (cbid == 0) + error("The calibration base display mode not specified in the .ti3 file"); + + if ((cc = new_ccmx()) == NULL) + error("new_ccmx() failed"); + + if (cc->create_ccmx(cc, description, colname, displayname, displaytech, + refrmode, cbid, uisel, refname, npat, refs, cols)) { + error("create_ccmx failed with '%s'\n",cc->err); + } + if (verb) { + printf("Fit error is max %f, avg %f DE94\n",cc->mx_err,cc->av_err); + printf("Correction matrix is:\n"); + printf(" %f %f %f\n", cc->matrix[0][0], cc->matrix[0][1], cc->matrix[0][2]); + printf(" %f %f %f\n", cc->matrix[1][0], cc->matrix[1][1], cc->matrix[1][2]); + printf(" %f %f %f\n", cc->matrix[2][0], cc->matrix[2][1], cc->matrix[2][2]); + } + + if(cc->write_ccmx(cc, outname)) + printf("\nWriting CCMX file '%s' failed\n",outname); + else + printf("\nWriting CCMX file '%s' succeeded\n",outname); + cc->del(cc); + + /* Do interactive measurements */ + } else { + + /* No explicit display has been set */ + if ( +#ifndef SHOW_WINDOW_ONFAKE + !fake && +#endif + webdisp == 0 && disp == NULL) { + int ix = 0; +#if defined(UNIX_X11) + char *dn, *pp; + + if ((dn = getenv("DISPLAY")) != NULL) { + if ((pp = strrchr(dn, ':')) != NULL) { + if ((pp = strchr(pp, '.')) != NULL) { + if (pp[1] != '\000') + ix = atoi(pp+1); + } + } + } +#endif + if ((disp = get_a_display(ix)) == NULL) + error("Unable to open the default display"); + + if (displayname == NULL && (displayname = strdup(disp->description)) == NULL) + error("Malloc failed"); + + printf("Display description is '%s'\n",displayname); + } + if (fake) { + displayname = strdup("fake display"); + } + + /* Create grid of device test values */ + { + int j; + int gc[3]; /* Grid coordinate */ + + if (msteps == 1) + npat = 4; + else + npat = msteps * msteps * msteps; + + if ((rdcols = (col *)malloc(npat * sizeof(col))) == NULL) { + error("malloc failed"); + } + if ((refs = (ary3 *)malloc(npat * sizeof(ary3))) == NULL) { + free(rdcols); + error("malloc failed"); + } + if ((cols = (ary3 *)malloc(npat * sizeof(ary3))) == NULL) { + free(rdcols); + free(refs); + error("malloc failed"); + } + + /* RGBW */ + if (msteps == 1) { + npat = 0; + rdcols[npat].r = 1.0; + rdcols[npat].g = 0.0; + rdcols[npat].b = 0.0; + npat++; + rdcols[npat].r = 0.0; + rdcols[npat].g = 1.0; + rdcols[npat].b = 0.0; + npat++; + rdcols[npat].r = 0.0; + rdcols[npat].g = 0.0; + rdcols[npat].b = 1.0; + npat++; + rdcols[npat].r = 1.0; + rdcols[npat].g = 1.0; + rdcols[npat].b = 1.0; + npat++; +#ifdef DEBUG + for (j = 0; j < 4; j++) + printf("Dev val %f %f %f\n",rdcols[j].r,rdcols[j].g,rdcols[j].b); +#endif + } else { + for (j = 0; j < 3; j++) + gc[j] = 0; /* init coords */ + + for (npat = 0; ;) { /* For all grid points */ + + /* Just colors with at least one channel at 100% */ + if (gc[0] == (msteps-1) + || gc[1] == (msteps-1) + || gc[2] == (msteps-1)) + { + + rdcols[npat].r = (double)gc[0]/(msteps-1); + rdcols[npat].g = (double)gc[1]/(msteps-1); + rdcols[npat].b = (double)gc[2]/(msteps-1); +#ifdef DEBUG + printf("Dev val %f %f %f\n",rdcols[npat].r,rdcols[npat].g,rdcols[npat].b); +#endif + npat++; + } + + /* Increment grid index and position */ + for (j = 0; j < 3; j++) { + gc[j]++; + if (gc[j] < msteps) + break; /* No carry */ + gc[j] = 0; + } + if (j >= 3) + break; /* Done grid */ + } + } + if (verb) + printf("Total test patches = %d\n",npat); + } + + /* Until the measurements are done, or we give up */ + for (;;) { + int c; + + /* Print the menue of adjustments */ + printf("\n"); + if (gotref) + printf("[Got spectrometer readings]\n"); + if (gotcol) + printf("[Got colorimeter readings]\n"); + printf("Press 1 .. 4:\n"); + { + printf("1) Select an instrument, Currently %d (", comno); + if (icmps == NULL) + icmps = new_icompaths(g_log); + else + icmps->refresh(icmps); + if (icmps != NULL) { + icompath **paths; + if ((paths = icmps->paths) != NULL) { + int i; + for (i = 0; ; i++) { + if (paths[i] == NULL) + break; + if ((i+1) == comno) { + printf(" '%s'",paths[i]->name); + break; + } + } + } + } + printf(")\n"); + } + if (doccss) + printf("2) Measure test patches with current (spectrometer) instrument\n"); + else + printf("2) Measure test patches with current instrument\n"); + + if (doccss) { + if (gotref) + printf("3) Save Colorimeter Calibration Spectral Set\n"); + else + printf("3) [ Save Colorimeter Calibration Spectral Set ]\n"); + + } else { + if (gotref && gotcol) + printf("3) Compute Colorimeter Correction Matrix & save it\n"); + else + printf("3) [ Compute Colorimeter Correction Matrix & save it ]\n"); + } + printf("4) Exit\n"); + + if (fakeseq == 0) { + empty_con_chars(); + c = next_con_char(); + } else { + switch (fakeseq) { + case 1: + c = '2'; + fakeseq = 2; + break; + case 2: + c = '2'; + fakeseq = 3; + break; + case 3: + c = '3'; + fakeseq = 4; + break; + default: + c = '4'; + break; + } + } + printf("'%c'\n",c); + + + /* Deal with selecting the instrument */ + if (c == '1') { + if (icmps == NULL) + icmps = new_icompaths(g_log); + else + icmps->refresh(icmps); + if (icmps != NULL) { + icompath **paths; + if ((paths = icmps->paths) != NULL) { + int i; + for (i = 0; ; i++) { + if (paths[i] == NULL) + break; + if (paths[i]->itype == instSpyder2 && setup_spyd2() == 0) + fprintf(stderr," %d = '%s' !! Disabled - no firmware !!\n",i+1,paths[i]->name); + else + fprintf(stderr," %d = '%s'\n",i+1,paths[i]->name); + } + printf("Select device 1 - %d: \n",i); + empty_con_chars(); + c = next_con_char(); + + if (c < '1' || c > ('0' + i)) { + printf("'%c' is out of range - ignored !\n",c); + } else { + comno = c - '0'; + } + + } else { + fprintf(stderr,"No ports to select from!\n"); + } + } + continue; + } + + /* Deal with doing a measurement */ + if (c == '2') { + int errc; /* Return value from new_disprd() */ + disprd *dr; /* Display patch read object */ + inst *it; /* Instrument */ + inst_mode cap = inst_mode_none; /* Instrument mode capabilities */ + inst2_capability cap2 = inst2_none; /* Instrument capabilities 2 */ + inst3_capability cap3 = inst3_none; /* Instrument capabilities 3 */ + + if (fake) + comno = -99; + if (icmps == NULL) + icmps = new_icompaths(g_log); + + /* Should we use current cal rather than native ??? */ + if ((dr = new_disprd(&errc, icmps->get_path(icmps, comno), + fc, dtype, 1, tele, nadaptive, + noinitcal, highres, 2, NULL, NULL, 0, 0, disp, blackbg, + override, webdisp, ccallout, NULL, + 100.0 * hpatscale, 100.0 * vpatscale, ho, vo, + NULL, NULL, 0, 2, icxOT_default, NULL, + 0, 0, "fake" ICC_FILE_EXT, g_log)) == NULL) + error("new_disprd failed with '%s'\n",disprd_err(errc)); + + it = dr->it; + + if (fake) { + if (faketoggle) + cap = inst_mode_spectral; + else + cap = inst_mode_colorimeter; + cap2 = inst2_none; + cap3 = inst3_none; + refrmode = 0; + cbid = 1; + } else { + it->capabilities(it, &cap, &cap2, &cap3); + if (!IMODETST(cap, inst_mode_spectral)) { + dr->get_disptype(dr, &refrmode, &cbid); /* Get the display type info */ + } + } + + if (doccss && !IMODETST(cap, inst_mode_spectral)) { + printf("You have to use a spectrometer to create a CCSS!\n"); + continue; + } + + if (faketoggle) + dr->fake2 = 1; + else + dr->fake2 = -1; + + /* Test the CRT with all of the test points */ + if ((rv = dr->read(dr, rdcols, npat, 1, npat, 1, 0, instClamp)) != 0) { + dr->del(dr); + error("disprd returned error code %d\n",rv); + } + + if (doccss) { /* We'll use the rdcols values */ + gotref = 1; + } else { + if (IMODETST(cap, inst_mode_spectral)) { + xsp2cie *sp2cie = NULL; + + if (spec) { + /* Create a spectral conversion object */ + if ((sp2cie = new_xsp2cie(icxIT_none, NULL, observ, NULL, icSigXYZData, icxClamp)) == NULL) + error("Creation of spectral conversion object failed"); + } + for (i = 0; i < npat; i++) { /* For all grid points */ + if (spec) { + if (rdcols[i].sp.spec_n <= 0) + error("Didn't get spectral value"); + sp2cie->convert(sp2cie, refs[i], &rdcols[i].sp); + } else { + if (rdcols[i].XYZ_v == 0) + error("Didn't get XYZ value"); + refs[i][0] = rdcols[i].XYZ[0]; + refs[i][1] = rdcols[i].XYZ[1]; + refs[i][2] = rdcols[i].XYZ[2]; + } + } + if (fake) + refname = "fake spectrometer"; + else + refname = inst_name(it->itype); + gotref = 1; + if (sp2cie != NULL) + sp2cie->del(sp2cie); + } else if (IMODETST(cap, inst_mode_colorimeter)) { + for (i = 0; i < npat; i++) { /* For all grid points */ + if (rdcols[i].XYZ_v == 0) + error("Didn't get XYZ value"); + cols[i][0] = rdcols[i].XYZ[0]; + cols[i][1] = rdcols[i].XYZ[1]; + cols[i][2] = rdcols[i].XYZ[2]; + } + if (fake) + colname = "fake colorimeter"; + else + colname = inst_name(it->itype); + gotcol = 1; + } + } + dr->del(dr); + + faketoggle ^= 1; + + } /* End of take a measurement */ + + if (c == '3') { /* Compute result and save */ + /* Save the CCSS */ + if (doccss) { + ccss *cc; + xspect *samples = NULL; + double bigv = -1e60; + + if (!gotref) { + printf("You have to read the spectrometer values first!\n"); + continue; + } + + if (description == NULL) { + char *disp = displaytech != NULL ? displaytech : displayname; + char *tt = "CCSS for "; + if ((description = malloc(strlen(disp) + strlen(tt) + 1)) == NULL) + error("Malloc failed"); + strcpy(description, tt); + strcat(description, disp); + } + + if ((samples = (xspect *)malloc(sizeof(xspect) * npat)) == NULL) + error("Malloc failed"); + + /* See what the highest value is */ + for (i = 0; i < npat; i++) { /* For all grid points */ + if (rdcols[i].sp.spec_n <= 0) + error("Didn't get spectral values"); + for (j = 0; j < rdcols[i].sp.spec_n; j++) { + if (rdcols[i].sp.spec[j] > bigv) + bigv = rdcols[i].sp.spec[j]; + } + } + + /* Copy all the values and normalize them */ + for (i = 0; i < npat; i++) { /* For all grid points */ + double scale = 100.0; + + samples[i] = rdcols[i].sp; /* Structure copy */ + for (j = 0; j < rdcols[i].sp.spec_n; j++) + samples[i].spec[j] *= scale / bigv; + } + + if (refrmode < 0) + warning("No refresh mode specified! Assuming non-refresh !"); + + if ((cc = new_ccss()) == NULL) + error("new_ccss() failed"); + + if (cc->set_ccss(cc, "Argyll ccxxmake", NULL, description, displayname, + displaytech, refrmode, NULL, refname, samples, npat)) { + error("set_ccss failed with '%s'\n",cc->err); + } + if(cc->write_ccss(cc, outname)) + printf("\nWriting CCSS file '%s' failed\n",outname); + else + printf("\nWriting CCSS file '%s' succeeded\n",outname); + cc->del(cc); + free(samples); + saved = 1; + + /* Compute and save CCMX */ + } else { + char *oname = NULL; /* Observer desciption */ + ccmx *cc; + + if (!gotref) { + printf("You have to read the spectrometer values first!\n"); + continue; + } + if (!gotcol) { + printf("You have to read the colorimeter values first!\n"); + continue; + } + + if (spec != 0 && observ != icxOT_CIE_1931_2) + oname = standardObserverDescription(observ); + + if (oname != NULL) { /* Incorporate observer name in colname */ + char *tt = colname; + + if ((colname = malloc(strlen(tt) + strlen(oname) + 3)) == NULL) + error("Malloc failed"); + strcpy(colname, tt); + strcat(colname, " ("); + strcat(colname, oname); + strcat(colname, ")"); + } + if (description == NULL) { + if ((description = malloc(strlen(colname) + strlen(displayname) + 4)) == NULL) + error("Malloc failed"); + strcpy(description, colname); + strcat(description, " & "); + strcat(description, displayname); + } + + if (refrmode < 0) + error("Internal error - the instrument did not return a refmode"); + if (cbid == 0) + error("Internal error - the instrument did not return a cbid"); + + if ((cc = new_ccmx()) == NULL) + error("new_ccmx() failed"); + + if (cc->create_ccmx(cc, description, colname, displayname, displaytech, + refrmode, cbid, uisel, refname, npat, refs, cols)) { + error("create_ccmx failed with '%s'\n",cc->err); + } + if (verb) { + printf("Fit error is avg %f, max %f DE94\n",cc->av_err,cc->mx_err); + printf("Correction matrix is:\n"); + printf(" %f %f %f\n", cc->matrix[0][0], cc->matrix[0][1], cc->matrix[0][2]); + printf(" %f %f %f\n", cc->matrix[1][0], cc->matrix[1][1], cc->matrix[1][2]); + printf(" %f %f %f\n", cc->matrix[2][0], cc->matrix[2][1], cc->matrix[2][2]); + } + + if(cc->write_ccmx(cc, outname)) + printf("\nWriting CCMX file '%s' failed\n",outname); + else + printf("\nWriting CCMX file '%s' succeeded\n",outname); + cc->del(cc); + saved = 1; + } + } + + if (c == '4' || c == 0x3) { /* Exit */ + if (!saved) { + printf("Not saved yet, are you sure ? (y/n): "); fflush(stdout); + empty_con_chars(); + c = next_con_char(); + printf("\n"); + if (c != 'y' && c != 'Y') + continue; + } + break; + } + + } /* Next command */ + + free(displayname); + if (icmps != NULL) + icmps->del(icmps); + } + +#ifdef DEBUG + /* Do a CCMX verification */ + if (!doccss) { + ccmx *cc; + double av_err, mx_err; + int wix; + double maxy = -1e6; + icmXYZNumber wh; + + for (i = 0; i < npat; i++) { + if (refs[i][1] > maxy) { + maxy = refs[i][1]; + wix = i; + } + } + wh.X = refs[wix][0]; + wh.Y = refs[wix][1]; + wh.Z = refs[wix][2]; + + if ((cc = new_ccmx()) == NULL) + error("new_ccmx() failed"); + if(cc->read_ccmx(cc, outname)) + printf("Reading file '%s' failed\n",outname); + + av_err = mx_err = 0.0; + for (i = 0; i < npat; i++) { + double txyz[3], tlab[3], xyz[3], _xyz[3], lab[3], de; + icmCpy3(txyz, refs[i]); + icmXYZ2Lab(&wh, tlab, txyz); + icmCpy3(xyz, cols[i]); + cc->xform(cc,_xyz, xyz); + icmXYZ2Lab(&wh, lab, _xyz); + de = icmCIE94(tlab, lab); + av_err += de; + if (de > mx_err) + mx_err = de; + printf("%d: txyz %f %f %f\n",i,txyz[0], txyz[1], txyz[2]); + printf("%d: xyz %f %f %f, _xyz %f %f %f\n",i,xyz[0], xyz[1], xyz[2], _xyz[0], _xyz[1], _xyz[2]); + printf("%d: tlab %f %f %f, lab %f %f %f\n",i,tlab[0], tlab[1], tlab[2], lab[0], lab[1], lab[2]); + printf("%d: de %f\n",i,de); + } + av_err /= npat; + printf("Avg = %f, max = %f\n",av_err,mx_err); + cc->del(cc); + } +#endif + +#ifdef DEBUG + printf("About to exit\n"); +#endif + + return 0; +} + + + + -- cgit v1.2.3