示例#1
0
int
main(int argc, char *argv[]) {
	int fa,nfa;				/* argument we're looking at */
	struct {
		char name[MAXNAMEL+1];	/* Patch filename  */
		int npat;				/* Number of patches */
		pval *pat;				/* patch values */
	} cg[2];					/* Target and current patch file information */
	char dev_name[MAXNAMEL+1];	/* Output device ICC filename for gamut */
	char rd_name[MAXNAMEL+1];	/* Abstract profile ICC to modify */
	char wr_name[MAXNAMEL+1];	/* Modified/created abstract profile ICC */

	int dorel = 0;				/* Do white point relative match */
	int *match;					/* Array mapping first list indexes to corresponding second */
	int fwacomp = 0;			/* FWA compensation on spectral ? */
	int spec = 0;				/* Use spectral data flag */
	icxIllumeType illum = icxIT_D50;	/* Spectral defaults */
	xspect cust_illum;					/* Custom illumination spectrum */
	icxObserverType observ = icxOT_Judd_Voss_2;
	callback cb;				/* Callback support stucture for setting abstract profile */

	icmFile *rd_fp = NULL;		/* Existing abstract profile to modify */
	icc *rd_icc = NULL;

	icmFile *wr_fp;				/* Modified/created abstract profile to write */
	icc *wr_icc;

	int verb = 0;
	int nogamut = 0;					/* Don't impose a gamut limit */
	int docreate = 0;					/* Create an initial abstract correction profile */
	int clutres = DEF_CLUTRES;			/* Output abstract profile clut resolution */
	double damp1 = DEF_DAMP1;			/* Initial damping factor */
	double damp2 = DEF_DAMP2;			/* Subsequent damping factor */
	double smoothf = SMOOTHF;			/* RSPL Smoothing factor */
	double avgdev[MXDO];				/* RSPL Average Deviation */
	double wweight = WWEIGHT;			/* weak default function weight */
	int whitepatch = -1;				/* Index of white patch */
	double merr = 0.0, aerr = 0.0;		/* Stats on color change */
	int i, j, e, n, rv = 0;

	error_program = argv[0];

	if (argc < 6)
		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("Usage requested");

			/* Verbosity */
			else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
				verb = 1;
			}
			/* Create initial abstract correction profile */
			else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') {
				docreate = 1;
			}
			/* Don't impose a gamut limit */
			else if (argv[fa][1] == 'g' || argv[fa][1] == 'G') {
				nogamut = 1;
			}
			/* Override the correction clut resolution */
			else if (argv[fa][1] == 'r') {
				fa = nfa;
				if (na == NULL) usage("Expect argument to -r");
				clutres = atoi(na);
			}
			/* Override the damping factor */
			else if (argv[fa][1] == 'd' || argv[fa][1] == 'D') {
				fa = nfa;
				if (na == NULL) usage("Expect argument to -d");
				damp2 = atof(na);
			}
			/* Aim for white point relative match */
			else if (argv[fa][1] == 'R') {
				dorel = 1;
			}

			/* Spectral Illuminant type */
			else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') {
				fa = nfa;
				if (na == NULL) usage("Expect argument to -i");
				if (strcmp(na, "A") == 0) {
					spec = 1;
					illum = icxIT_A;
				} else if (strcmp(na, "C") == 0) {
					spec = 1;
					illum = icxIT_C;
				} else if (strcmp(na, "D50") == 0) {
					spec = 1;
					illum = icxIT_D50;
				} else if (strcmp(na, "D65") == 0) {
					spec = 1;
					illum = icxIT_D65;
				} else if (strcmp(na, "F5") == 0) {
					spec = 1;
					illum = icxIT_F5;
				} else if (strcmp(na, "F8") == 0) {
					spec = 1;
					illum = icxIT_F8;
				} else if (strcmp(na, "F10") == 0) {
					spec = 1;
					illum = icxIT_F10;
				} else {	/* Assume it's a filename */
					spec = 1;
					illum = icxIT_custom;
					if (read_xspect(&cust_illum, na) != 0)
						usage("Unable to read custom spectrum '%s'",na);
				}
			}

			/* Spectral Observer type */
			else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') {
				fa = nfa;
				if (na == NULL) usage("Expected argument to -o");
				if (strcmp(na, "1931_2") == 0) {			/* Classic 2 degree */
					spec = 1;
					observ = icxOT_CIE_1931_2;
				} else if (strcmp(na, "1964_10") == 0) {	/* Classic 10 degree */
					spec = 1;
					observ = icxOT_CIE_1964_10;
				} else if (strcmp(na, "1955_2") == 0) {		/* Stiles and Burch 1955 2 degree */
					spec = 1;
					observ = icxOT_Stiles_Burch_2;
				} else if (strcmp(na, "1978_2") == 0) {		/* Judd and Voss 1978 2 degree */
					spec = 1;
					observ = icxOT_Judd_Voss_2;
				} else if (strcmp(na, "shaw") == 0) {		/* Shaw and Fairchilds 1997 2 degree */
					spec = 1;
					observ = icxOT_Shaw_Fairchild_2;
				} else
					usage("Unrecogised argument '%s' to -o",na);
			}

			/* FWA compensation */
			else if (argv[fa][1] == 'f' || argv[fa][1] == 'F')
				fwacomp = 1;

			else 
				usage("Unrecognised flag -%c",argv[fa][1]);
		} else
			break;
	}

	/* Grab all the filenames: */

	/* The two CIE value files */
	if (fa >= argc || argv[fa][0] == '-') usage("Expected cietarget file argument");
	strncpy(cg[0].name,argv[fa++],MAXNAMEL); cg[0].name[MAXNAMEL] = '\000';

	if (fa >= argc || argv[fa][0] == '-') usage("Expected ciecurrent file argument");
	strncpy(cg[1].name,argv[fa++],MAXNAMEL); cg[1].name[MAXNAMEL] = '\000';

	/* Optional output device name */
	if (nogamut == 0) {
		if (fa >= argc || argv[fa][0] == '-') usage("Expected outdevicc file argument");
		strncpy(dev_name,argv[fa++],MAXNAMEL); dev_name[MAXNAMEL] = '\000';
	}

	/* Optional input abstract profile name */
	if (docreate == 0) {
		if (fa >= argc || argv[fa][0] == '-') usage("Expected inabs file argument");
		strncpy(rd_name,argv[fa++],MAXNAMEL); rd_name[MAXNAMEL] = '\000';
	}

	/* Output abstract profile name */
	if (fa >= argc || argv[fa][0] == '-') usage("Expected outabs file argument");
	strncpy(wr_name,argv[fa++],MAXNAMEL); wr_name[MAXNAMEL] = '\000';

	/* ======================= */
	/* 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 */
		int sidx;					/* Sample ID index */
		int xix, yix, zix;

		/* Open CIE target values */
		cgf = new_cgats();			/* Create a CGATS structure */
		cgf->add_other(cgf, ""); 	/* Allow any signature file */
	
		if (cgf->read_name(cgf, cg[n].name))
			error("CGATS file '%s' read error : %s",cg[n].name,cgf->err);
	
		if (cgf->ntables < 1)
			error ("Input file '%s' doesn't contain at least one table",cg[n].name);
	
		/* Check if the file is suitable */
		if (!spec
		 && cgf->find_field(cgf, 0, "LAB_L") < 0
		 && cgf->find_field(cgf, 0, "XYZ_X") < 0) {
	
			if (cgf->find_kword(cgf, 0, "SPECTRAL_BANDS") < 0)
				error ("Neither CIE nor spectral data found in file '%s'",cg[n].name);
	
			/* Switch to using spectral information */
			if (verb)
				printf("No CIE data found, switching to spectral with standard observer & D50 for file '%s'\n",cg[n].name);
			spec = 1;
			illum = icxIT_D50;
			observ = icxOT_CIE_1931_2;
		}
		if (spec && cgf->find_kword(cgf, 0, "SPECTRAL_BANDS") < 0)
			error ("No spectral data data found in file '%s' when spectral expected",cg[n].name);
	
		if (!spec && cgf->find_field(cgf, 0, "LAB_L") >= 0)
			isLab = 1;
		
		cg[n].npat = cgf->t[0].nsets;		/* Number of patches */
	
		/* Read all the target patches */
		if (cg[n].npat <= 0)
			error("No sets of data in file '%s'",cg[n].name);
	
		if (verb && n == 0) {
			fprintf(verbo,"No of test patches = %d\n",cg[n].npat);
		}
	
		/* Allocate arrays to hold test patch input and output values */
		if ((cg[n].pat = (pval *)malloc(sizeof(pval) * cg[n].npat)) == NULL)
			error("Malloc failed - pat[]");
	
		/* Read in the CGATs fields */
		if ((sidx = cgf->find_field(cgf, 0, "SAMPLE_ID")) < 0
		 && (sidx = cgf->find_field(cgf, 0, "SampleName")) < 0
		 && (sidx = cgf->find_field(cgf, 0, "Sample_Name")) < 0
		 && (sidx = cgf->find_field(cgf, 0, "SAMPLE_NAME")) < 0
		 && (sidx = cgf->find_field(cgf, 0, "SAMPLE_LOC")) < 0)
			error("Input file '%s' doesn't contain field SAMPLE_ID, SampleName, Sample_Name, SAMPLE_NAME or SAMPLE_LOC",cg[n].name);
		if (cgf->t[0].ftype[sidx] != nqcs_t
		 && cgf->t[0].ftype[sidx] != cs_t)
			error("Sample ID/Name field isn't a quoted or non quoted character string");

		if (spec == 0) { 		/* Using instrument tristimulous value */

			if (isLab) {		/* Expect Lab */
				if ((xix = cgf->find_field(cgf, 0, "LAB_L")) < 0)
					error("Input file '%s' doesn't contain field LAB_L",cg[n].name);
				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",cg[n].name);
				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",cg[n].name);
				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",cg[n].name);
				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",cg[n].name);
				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",cg[n].name);
				if (cgf->t[0].ftype[zix] != r_t)
					error("Field XYZ_Z is wrong type");
			}

			for (i = 0; i < cg[n].npat; i++) {
				strcpy(cg[n].pat[i].sid, (char *)cgf->t[0].fdata[i][sidx]);
				cg[n].pat[i].v[0] = *((double *)cgf->t[0].fdata[i][xix]);
				cg[n].pat[i].v[1] = *((double *)cgf->t[0].fdata[i][yix]);
				cg[n].pat[i].v[2] = *((double *)cgf->t[0].fdata[i][zix]);
				if (!isLab) {
					cg[n].pat[i].v[0] /= 100.0;		/* Normalise XYZ to range 0.0 - 1.0 */
					cg[n].pat[i].v[1] /= 100.0;
					cg[n].pat[i].v[2] /= 100.0;
				}
				if (!isLab) { /* Convert test patch result XYZ to PCS (D50 Lab) */
					icmXYZ2Lab(&icmD50, cg[n].pat[i].v, cg[n].pat[i].v);
				}
			}

		} else { 		/* Using spectral data */
			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 doesn't contain keyword SPECTRAL_BANDS");
			sp.spec_n = atoi(cgf->t[0].kdata[ii]);
			if ((ii = cgf->find_kword(cgf, 0, "SPECTRAL_START_NM")) < 0)
				error ("Input file doesn't contain keyword SPECTRAL_START_NM");
			sp.spec_wl_short = atof(cgf->t[0].kdata[ii]);
			if ((ii = cgf->find_kword(cgf, 0, "SPECTRAL_END_NM")) < 0)
				error ("Input file doesn't contain keyword SPECTRAL_END_NM");
			sp.spec_wl_long = atof(cgf->t[0].kdata[ii]);
			sp.norm = 100.0;

			/* 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 doesn't contain field %s",buf);
			}

			/* Figure out what sort of device it is */
			{
				int ti;
		
				if ((ti = cgf->find_kword(cgf, 0, "DEVICE_CLASS")) < 0)
					error ("Input file '%s' doesn't contain keyword DEVICE_CLASS",cg[n].name);
		
				if (strcmp(cgf->t[0].kdata[ti],"DISPLAY") == 0) {
					illum = icxIT_none;		/* Displays are assumed to be self luminous */
				}
			}

			/* Create a spectral conversion object */
			if ((sp2cie = new_xsp2cie(illum, illum == icxIT_none ? NULL : &cust_illum,
			                          observ, NULL, icSigLabData)) == NULL)
				error("Creation of spectral conversion object failed");

			if (fwacomp) {
				int ti;
				xspect mwsp;			/* Medium spectrum */
				instType itype;			/* Spectral instrument type */
				xspect insp;			/* Instrument illuminant */

				mwsp = sp;		/* Struct copy */

	 			if ((ti = cgf->find_kword(cgf, 0, "TARGET_INSTRUMENT")) < 0)
					error ("Can't find target instrument in '%s' needed for FWA compensation",cg[n].name);

				if ((itype = inst_enum(cgf->t[0].kdata[ti])) == instUnknown)
					error ("Unrecognised target instrument '%s'", cgf->t[0].kdata[ti]);

				if (inst_illuminant(&insp, itype) != 0)
					error ("Instrument doesn't have an FWA illuminent");

				/* Determine a media white spectral reflectance */
				for (j = 0; j < mwsp.spec_n; j++)
					mwsp.spec[j] = 0.0;

				/* Track the maximum reflectance for any band to determine white. */
				/* This might silently fail, if there isn't white in the sampe set. */
				for (i = 0; i < cg[0].npat; i++) {
					for (j = 0; j < mwsp.spec_n; j++) {
						double rv = *((double *)cgf->t[0].fdata[i][spi[j]]);
						if (rv > mwsp.spec[j])
							mwsp.spec[j] = rv;
					}
				}
				if (sp2cie->set_fwa(sp2cie, &insp, &mwsp)) 
					error ("Set FWA on sp2cie failed");
			}

			for (i = 0; i < cg[0].npat; i++) {

				strcpy(cg[n].pat[i].sid, (char *)cgf->t[0].fdata[i][sidx]);

				/* 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]]);
				}

				/* Convert it to CIE space */
				sp2cie->convert(sp2cie, cg[n].pat[i].v, &sp);
			}

			sp2cie->del(sp2cie);		/* Done with this */

		}	/* End of reading in CGATs file */
		cgf->del(cgf);		/* Clean up */
	}

	/* Check that the number of test patches matches */
	if (cg[0].npat != cg[1].npat)
		error("Number of patches between '%s' and '%s' doesn't match",cg[0].name,cg[1].name);
	
	/* Create a list to map the second list (measured) of patches to the first (target) */
	if ((match = (int *)malloc(sizeof(int) * cg[0].npat)) == NULL)
		error("Malloc failed - match[]");
	for (i = 0; i < cg[0].npat; i++) {
		for (j = 0; j < cg[1].npat; j++) {
			if (strcmp(cg[0].pat[i].sid, cg[1].pat[j].sid) == 0)
				break;			/* Found it */
		}
		if (j < cg[1].npat) {
			match[i] = j;
		} else {
			error("Failed to find matching patch to '%s'",cg[0].pat[i].sid);
		}
	}

	/* Try and figure out which is the white patch */
	{
		double hL = -1.0;
		for (i = 0; i < cg[0].npat; i++) {
			if (cg[0].pat[i].v[0] > hL) {
				hL = cg[0].pat[i].v[0];
				whitepatch = i;
			}
		}
	}

	/* If we are aiming for a white point relative match, adjust the */
	/* measured and target values to have a D50 white point */
	if (dorel) {
		for (n = 0; n < 2; n++) {
			int wpix;			/* White patch index */
			double wp_xyz[3];
			icmXYZNumber wp;	/* White value */
			double mat[3][3];	/* Chromatic transform */
			
			if (n == 0)
				wpix = whitepatch;
			else
				wpix = match[whitepatch];
		

			/* Compute a chromatic correction matrix */
			icmLab2XYZ(&icmD50, wp_xyz, cg[n].pat[wpix].v);
			icmAry2XYZ(wp, wp_xyz);

			icmChromAdaptMatrix(ICM_CAM_BRADFORD, icmD50, wp, mat);

			for (i = 0; i < cg[n].npat; i++) {
				icmLab2XYZ(&icmD50, cg[n].pat[i].v, cg[n].pat[i].v);
				icmMulBy3x3(cg[n].pat[i].v, mat, cg[n].pat[i].v);
				icmXYZ2Lab(&icmD50, cg[n].pat[i].v, cg[n].pat[i].v);
//printf("Table %d, patch %d, Lab %f %f %f\n",n,i,cg[n].pat[i].v[0],cg[n].pat[i].v[1],cg[n].pat[i].v[2]);
			}
		}
	}

	/* Compute the delta E's just for information */
	for (i = 0; i < cg[0].npat; i++) {
		double de = icmLabDE(cg[0].pat[i].v, cg[1].pat[match[i]].v);
		cg[0].pat[i].de = de;
		if (de > merr)
			merr = de;
		aerr += de;
	}
	if (cg[0].npat > 0)
		aerr /= (double)cg[0].npat;

	if (verb) {
		fprintf(verbo,"No of correction patches = %d\n",cg[0].npat);
		fprintf(verbo,"Average dE = %f, Maximum dE = %f\n",aerr,merr);
		fprintf(verbo,"White patch assumed to be patch %s\n",cg[0].pat[whitepatch].sid);
	}

	/* ======================= */
	/* Possible limiting gamut */
	if (nogamut == 0) {
		icmFile *dev_fp;
		icc *dev_icc;
		xicc *dev_xicc;
		icxLuBase *dev_luo;
		icxInk ink;							/* Ink parameters */

		/* Open up the device ICC profile, so that we can create a gamut */
		/* and get an absolute PCS->device conversion */
		if ((dev_fp = new_icmFileStd_name(dev_name,"r")) == NULL)
			error ("Can't open file '%s'",dev_name);
	
		if ((dev_icc = new_icc()) == NULL)
			error("Creation of ICC object failed");
	
		/* Read header etc. */
		if ((rv = dev_icc->read(dev_icc,dev_fp,0)) != 0)
			error("Reading profile '%s' failed: %d, %s",dev_name,rv,dev_icc->err);
	
		/* Check that the profile is appropriate */
		if (dev_icc->header->deviceClass != icSigInputClass
		 && dev_icc->header->deviceClass != icSigDisplayClass
		 && dev_icc->header->deviceClass != icSigOutputClass)
			error("Device Profile '%s' isn't a device profile",dev_name);

		ink.tlimit = -1.0;		/* No ink limit by default */
		ink.klimit = -1.0;

		/* Wrap with an expanded icc */
		if ((dev_xicc = new_xicc(dev_icc)) == NULL)
			error ("Creation of xicc failed");

		/* Use a heuristic to guess the ink limit */
		icxGetLimits(dev_xicc, &ink.tlimit, &ink.klimit);
		ink.tlimit += 0.05;		/* allow a slight margine */

		if (verb)
			printf("Estimated Total inklimit is %f%%, Black %f%% \n",100.0 * ink.tlimit,ink.klimit < 0.0 ? 100.0 : 100.0 * ink.klimit);

		/* Get a expanded color conversion object suitable for gamut */
		if ((dev_luo = dev_xicc->get_luobj(dev_xicc, ICX_CLIP_NEAREST, icmFwd,
		     dorel ? icRelativeColorimetric : icAbsoluteColorimetric,
		     icSigLabData, icmLuOrdNorm, NULL, &ink)) == NULL)
			error ("%d, %s",dev_xicc->errc, dev_xicc->err);
	
		/* Creat a gamut surface */
		if ((cb.dev_gam = dev_luo->get_gamut(dev_luo, GAMRES)) == NULL)
			error ("%d, %s",dev_xicc->errc, dev_xicc->err);

		dev_luo->del(dev_luo);
		dev_xicc->del(dev_xicc);
		dev_icc->del(dev_icc);
		dev_fp->del(dev_fp);
	} else {
		cb.dev_gam = NULL;
	}

	/* ======================= */
	/* Open up the existing abstract profile that is to be refined. */
	if (docreate == 0) {
		if ((rd_fp = new_icmFileStd_name(rd_name,"r")) == NULL)
			error ("Can't open file '%s'",rd_name);
	
		if ((rd_icc = new_icc()) == NULL)
			error ("Creation of ICC object failed");
	
		/* Read header etc. */
		if ((rv = rd_icc->read(rd_icc,rd_fp,0)) != 0)
			error ("%d, %s",rv,rd_icc->err);
	
		if (rd_icc->header->deviceClass != icSigAbstractClass)
			error("Input Profile '%s' isn't abstract type",rd_name);

		if ((cb.rd_luo = rd_icc->get_luobj(rd_icc, icmFwd,
		        dorel ? icRelativeColorimetric : icAbsoluteColorimetric,
		        icSigLabData, icmLuOrdNorm)) == NULL)
				error ("%d, %s",rd_icc->errc, rd_icc->err);
	} else {
		cb.rd_luo = NULL;
	}

	/* ======================= */
	/* Create refining rspl */
	{
		cow *rp;		/* rspl setup points */
		int npnts = 0;	/* Total number of test points */
		int gres[MXDI];	/* rspl grid resolution */
		double damp;
		datai mn, mx;

		if ((rp = (cow *)malloc(sizeof(cow) * cg[0].npat)) == NULL)
			error("Malloc failed - rp[]");
		
		/* Create mapping points */
		for (i = 0; i < cg[0].npat; i++) {
			double temp[3];
			double ccor[3], cmag;				/* Current correction vector */
			double ncor[3], nmag;				/* New correction vector */

			/* Input is target [0] */
			for (j = 0; j < 3; j++)
				rp[i].p[j] = cg[0].pat[i].v[j];

			/* Cull out of range points */
			if (rp[i].p[0] < 0.0 || rp[i].p[0] > 100.0
			 || rp[i].p[1] < -127.0 || rp[i].p[1] > 127.0
			 || rp[i].p[2] < -127.0 || rp[i].p[2] > 127.0) {
#ifdef DEBUG1
			printf("Ignoring %f %f %f\n",rp[i].p[0],rp[i].p[1],rp[i].p[2]);
#endif
				continue;
			}
			
#ifdef DEBUG1
			printf("%d: Target        %f %f %f\n",i,rp[i].p[0],rp[i].p[1],rp[i].p[2]);
#endif

			damp = cb.rd_luo != NULL ? damp2 : damp1;
			ccor[0] = ccor[1] = ccor[2] = 0.0;
			cmag = 0.0;

			/* Lookup the current correction applied to the target */
			if (cb.rd_luo != NULL) {		/* Subsequent pass */
				double corval[3];
				cb.rd_luo->lookup(cb.rd_luo, corval, cg[0].pat[i].v);
				icmSub3(ccor, corval, cg[0].pat[i].v);
				cmag = icmNorm3(ccor);
#ifdef DEBUG1
				printf("%d: ccor          %f %f %f, mag %f\n",i, ccor[0],ccor[1],ccor[2],cmag);
#endif
			}

			/* Create a trial 100% full correction point */
			for (j = 0; j < 3; j++)
				rp[i].v[j] = ccor[j] + 2.0 * cg[0].pat[i].v[j] - cg[1].pat[match[i]].v[j];

			/* If a first pass and the target or the correction are out of gamut, */
			/* use a damping factor of 1.0 */
			if (cb.rd_luo == NULL
			 && cb.dev_gam != NULL
			 && cb.dev_gam->nradial(cb.dev_gam, temp, rp[i].p) > 1.0
			 && cb.dev_gam->nradial(cb.dev_gam, temp, rp[i].v) > 1.0) {
				damp = 1.0;
			}

#ifdef DEBUG1
			printf("%d: damp =         %f\n",i, damp);
#endif

			/* Create a new correction that does a damped correction to the current error */
			/* [0] = target, [1] = measured */
			for (j = 0; j < 3; j++)
				ncor[j] = ccor[j] + damp * (cg[0].pat[i].v[j] - cg[1].pat[match[i]].v[j]);
			nmag = icmNorm3(ncor);

#ifdef DEBUG1
			printf("%d: ncor          %f %f %f, mag %f\n",i, ncor[0],ncor[1],ncor[2],nmag);
#endif

			/* If this is not the first pass, limit the new correction */
			/* to be 1 + damp as big as the previous correction */
			if (cb.rd_luo != NULL) {
				if ((nmag/cmag) > (1.0 + damp2)) {
#ifdef DEBUG1
					printf("%d: Limited cor mag from %f to %f\n",i, nmag, (1.0 + damp2) * cmag);
#endif
					icmScale3(ncor, ncor, (1.0 + damp2) * cmag/nmag);
				}
			}

			/* Create correction point */
			for (j = 0; j < 3; j++)
				rp[i].v[j] = cg[0].pat[i].v[j] + ncor[j];

			/* If the target point or corrected point is likely to be outside */
			/* the gamut, limit the magnitude of the correction to be the same */
			/* as the previous correction. */ 
			if (cb.rd_luo != NULL && cb.dev_gam != NULL) {
				if (cb.dev_gam->nradial(cb.dev_gam, temp, rp[i].p) > 1.0
				 || cb.dev_gam->nradial(cb.dev_gam, temp, rp[i].v) > 1.0) {
#ifdef DEBUG1
					printf("%d: Limited cor mag from %f to %f\n",i, nmag, cmag);
#endif
					icmScale3(ncor, ncor, cmag/nmag);
				}
				/* Create correction point again */
				for (j = 0; j < 3; j++)
					rp[i].v[j] = cg[0].pat[i].v[j] + ncor[j];
			}

#ifdef DEBUG1
			printf("%d: Was           %f %f %f\n",i, cg[1].pat[match[i]].v[0], cg[1].pat[match[i]].v[1], cg[1].pat[match[i]].v[2]);
			printf("%d: Correction to %f %f %f\n",i, rp[i].v[0], rp[i].v[1], rp[i].v[2]);
#endif

#ifdef COMPLOOKUP
			/* Remove current correction from new change */
			for (j = 0; j < 3; j++)
				rp[i].v[j] -= ccor[j];
#endif
			/* Set weighting */
			if (i == whitepatch)
				rp[i].w = WHITEWEIGHT;
			else
				rp[i].w = 1.0;
			npnts++;

#ifdef DEBUG3
			{
				char fname[50], tmp[50];
				FILE *lf;
				int mi = match[i];
				double tig, cig, rig;  
				double vv[3], temp[3];
				double del[3], delt;
				double corrdel[3], corrdelt;
				double pcval[3], pcorrdel[3], pcorrdelt;

				for (j = 0;; j++) {
					if (poi[j] == (i+1) || poi[j] < 0)
						break;
				}
				if (poi[j] < 0) {
					continue;
				}

#ifdef COMPLOOKUP
				/* Compute total correction point */
				for (j = 0; j < 3; j++)
					vv[j] = rp[i].v[j] + ccor[j];
#else
				for (j = 0; j < 3; j++)
					vv[j] = rp[i].v[j];
#endif
				sprintf(fname,"patch%04d.log",i+1);
				if ((lf = fopen(fname, "a")) == NULL)
					error("Unable to open debug3 log file '%s'\n",fname);

	 			cig = cb.dev_gam->nradial(cb.dev_gam, temp, cg[1].pat[mi].v) - 1.0;
				if (cig > 0.0)
					sprintf(tmp, " OUT %f",cig);
				else
					sprintf(tmp, "");
				fprintf(lf,"Currently  %f %f %f%s\n", cg[1].pat[mi].v[0], cg[1].pat[mi].v[1], cg[1].pat[mi].v[2], tmp);

	 			tig = cb.dev_gam->nradial(cb.dev_gam, temp, cg[0].pat[i].v) - 1.0;
				if (tig > 0.0)
					sprintf(tmp, " OUT %f",tig);
				else
					sprintf(tmp, "");
				fprintf(lf,"Target     %f %f %f%s\n", cg[0].pat[i].v[0], cg[0].pat[i].v[1], cg[0].pat[i].v[2], tmp);

				icmSub3(del, cg[1].pat[mi].v, cg[0].pat[i].v);
				delt = icmNorm3(del);
				fprintf(lf,"DE         %f %f %f (%f)\n", del[0], del[1], del[2], delt);
				
				rig = cb.dev_gam->nradial(cb.dev_gam, temp, vv) - 1.0;
				if (rig > 0.0)
					sprintf(tmp, " OUT %f",rig);
				else
					sprintf(tmp, "");
				fprintf(lf,"Correction %f %f %f%s\n", vv[0], vv[1], vv[2], tmp);
				icmSub3(corrdel, vv, cg[0].pat[i].v);
				corrdelt = icmNorm3(corrdel);
				fprintf(lf,"CorrDelta  %f %f %f (%f)\n", corrdel[0], corrdel[1], corrdel[2], corrdelt);
				/* Note the previous correction we're compunded with */
				if (cb.rd_luo != NULL) {
					cb.rd_luo->lookup(cb.rd_luo, pcval, cg[0].pat[i].v);
					icmSub3(pcorrdel, pcval, cg[0].pat[i].v);
					pcorrdelt = icmNorm3(pcorrdel);
					fprintf(lf,"PrevCorrDelta %f %f %f (%f)\n", pcorrdel[0], pcorrdel[1], pcorrdel[2], pcorrdelt);
				}
				fprintf(lf,"\n");

				fclose(lf);
			}
#endif /* DEBUG3 */
		}

		/* Create refining rspl */
		mn[0] =   0.0, mn[1] = mn[2] = -128.0;			/* Allow for 16 bit grid range */
		mx[0] = 100.0, mx[1] = mx[2] =  (65535.0 * 255.0)/65280.0 - 128.0;
		cb.verb = verb;
		if ((cb.r = new_rspl(RSPL_NOFLAGS, 3, 3)) == NULL)
			error("new_rspl failed");

		for (e = 0; e < 3; e++)
			gres[e] = clutres;
		for (e = 0; e < 3; e++)
			avgdev[e] = AVGDEV;

		cb.r->fit_rspl_w_df(cb.r,
		           RSPLFLAGS			/* Extra flags */
		           | verb ? RSPL_VERBOSE : 0,
		           rp,					/* Test points */
		           npnts,				/* Number of test points */
		           mn, mx, gres,		/* Low, high, resolution of grid */
		           NULL, NULL,			/* Default data scale */
		           smoothf,				/* Smoothing */
		           avgdev,				/* Average Deviation */
		           NULL,				/* Grid width */
                   wweight,				/* weak default function weight */
				   NULL,				/* No context */
		           wfunc				/* Weak function */
		);
		if (verb) printf("\n");

		/* Report how good the fit is */
		if (verb) {
			co tco;	/* Test point */
			double maxe = -1e6, avge = 0.0;

			for (i = 0; i < npnts; i++) {
				double de;

				icmAry2Ary(tco.p, rp[i].p);
				cb.r->interp(cb.r, &tco);

				de = icmLabDE(tco.v, rp[i].v);
				if (de > maxe)
					maxe = de;
				avge += de;
			}
			avge /= (double)npnts;
			printf("Refining transform has error to defining points avg: %f, max %f\n",avge,maxe);
		}
		free(rp);
	}

	/* ======================= */
	/* Create new abstract ICC profile */
	if ((wr_fp = new_icmFileStd_name(wr_name,"w")) == NULL)
		error ("Can't open file '%s' for writing",wr_name);

	if ((wr_icc = new_icc()) == NULL)
		error ("Creation of write ICC object failed");

	/* Add all the tags required */

	/* The header: */
	{
		icmHeader *wh = wr_icc->header;

		/* Values that must be set before writing */
		wh->deviceClass     = icSigAbstractClass;
    	wh->colorSpace      = icSigLabData;
    	wh->pcs             = icSigLabData;
		if (dorel)
	    	wh->renderingIntent = icRelativeColorimetric;	/* White point relative */
		else
	    	wh->renderingIntent = icAbsoluteColorimetric;	/* Instrument reading based */
	}
	/* Profile Description Tag: */
	{
		icmTextDescription *wo;
		char *dst = "Argyll refine output";
		if ((wo = (icmTextDescription *)wr_icc->add_tag(
		           wr_icc, icSigProfileDescriptionTag,	icSigTextDescriptionType)) == NULL) 
			error("add_tag failed: %d, %s",wr_icc->errc,wr_icc->err);

		wo->size = strlen(dst)+1; 	/* Allocated and used size of desc, inc null */
		wo->allocate((icmBase *)wo);/* Allocate space */
		strcpy(wo->desc, dst);		/* Copy the string in */
	}
	/* Copyright Tag: */
	{
		icmText *wo;
		char *crt = "Copyright the user who created it";
		if ((wo = (icmText *)wr_icc->add_tag(
		           wr_icc, icSigCopyrightTag,	icSigTextType)) == NULL) 
			error("add_tag failed: %d, %s",wr_icc->errc,wr_icc->err);

		wo->size = strlen(crt)+1; 	/* Allocated and used size of text, inc null */
		wo->allocate((icmBase *)wo);/* Allocate space */
		strcpy(wo->data, crt);		/* Copy the text in */
	}
	/* White Point Tag: */
	{
		icmXYZArray *wo;
		/* Note that tag types icSigXYZType and icSigXYZArrayType are identical */
		if ((wo = (icmXYZArray *)wr_icc->add_tag(
		           wr_icc, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL) 
			error("add_tag failed: %d, %s",wr_icc->errc,wr_icc->err);

		wo->size = 1;
		wo->allocate((icmBase *)wo);	/* Allocate space */
		wo->data[0] = icmD50;			/* So absolute/relative rendering is the same */
	}
	/* 16 bit pcs -> pcs lut: */
	{
		icmLut *wo;
		int flags = ICM_CLUT_SET_EXACT;	/* Assume we're setting from RSPL's */

		/* Intent 0 = default/perceptual */
		if ((wo = (icmLut *)wr_icc->add_tag(
		           wr_icc, icSigAToB0Tag,	icSigLut16Type)) == NULL) 
			error("add_tag failed: %d, %s",wr_icc->errc,wr_icc->err);

		wo->inputChan = 3;
		wo->outputChan = 3;
    	wo->clutPoints = clutres;
    	wo->inputEnt = 256;				/* Not actually used */
    	wo->outputEnt = 256;
		wo->allocate((icmBase *)wo);/* Allocate space */

		/* The matrix is only applicable to XYZ input space, */
		/* so it is not used here. */

		/* Use helper function to do the hard work. */
		if (cb.verb) {
			int extra;
			for (cb.total = 1, i = 0; i < 3; i++, cb.total *= wo->clutPoints)
				; 
			/* Add in cell center points */
			for (extra = 1, i = 0; i < wo->inputChan; i++, extra *= (wo->clutPoints-1))
				;
			cb.total += extra;
			cb.count = 0;
			cb.last = -1;
			printf(" 0%%"), fflush(stdout);
		}

#ifdef COMPLOOKUP
		/* Compound with previous correction */
		if (cb.rd_luo != NULL)
			flags = ICM_CLUT_SET_APXLS;	/* Won't be least squares, so do extra sampling */
#endif

		if (wo->set_tables(wo,
				flags,
				&cb,
				icSigLabData, 			/* Input color space */
				icSigLabData, 			/* Output color space */
				NULL,					/* Linear input transform Lab->Lab' (NULL = default) */
				NULL, NULL,				/* Use default Maximum range of Lab' values */
				PCSp_PCSp,				/* Lab' -> Lab' transfer function */
				NULL, NULL,				/* Use default Maximum range of Lab' values */
				NULL					/* Linear output transform Lab'->Lab */
		) != 0)
			error("Setting 16 bit Lab->Lab Lut failed: %d, %s",wr_icc->errc,wr_icc->err);

		if (verb)
			printf("\n");
#ifdef WARN_CLUT_CLIPPING
		if (wr_icc->warnc)
			warning("Values clipped in setting abstract LUT");
#endif /* WARN_CLUT_CLIPPING */
		if (verb)
			printf("Done filling abstract table\n");
	}
	/* Write the file out */
	if ((rv = wr_icc->write(wr_icc,wr_fp,0)) != 0)
		error ("Write file: %d, %s",rv,wr_icc->err);
	
	/* ======================================= */
	
	/* Clean everything up */
	wr_icc->del(wr_icc);
	wr_fp->del(wr_fp);

	if (docreate == 0) {
		cb.rd_luo->del(cb.rd_luo);
		rd_icc->del(rd_icc);
		rd_fp->del(rd_fp);
	}

	if (nogamut == 0) {
		cb.dev_gam->del(cb.dev_gam);
	}

	cb.r->del(cb.r);

	free(match);
	free(cg[0].pat);
	free(cg[1].pat);

	return 0;
}
示例#2
0
int
main(int argc, char *argv[]) {
	int fa,nfa;				/* argument we're looking at */
	char in_name[100];		/* Raster file name */
	char out_name[100];		/* Raster file name */
	int slow = 0;
	int check = 0;
	int i, rv = 0;

	TIFF *rh = NULL, *wh = NULL;
	int x, y, width, height;					/* Size of image */
	uint16 samplesperpixel, bitspersample;
	int no_pmtc;								/* Number of input photometrics */
	uint16 photometric, pmtc[10];				/* Photometrics of input file, and input profile */
	uint16 pconfig;								/* Planar configuration */
	uint16 resunits;
	float resx, resy;
	tdata_t *inbuf, *outbuf, *checkbuf;

	/* IMDI */
	imdi *s = NULL;
	sucntx su;		/* Setup context */
	unsigned char *inp[MAX_CHAN];
	unsigned char *outp[MAX_CHAN];
	int clutres = 33;

	/* Error check */
	int mxerr = 0;
	double avgerr = 0.0;
	double avgcount = 0.0;

	if (argc < 2)
		usage();

	su.verb = 0;
	su.icombine = 0;
	su.ocombine = 0;
	su.link = 0;
	su.in.intent = icmDefaultIntent;
	su.out.intent = icmDefaultIntent;

	/* 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();

			/* Slow, Precise */
			else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') {
				slow = 1;
			}

			/* Combine per channel curves */
			else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') {
				su.icombine = 1;
				su.ocombine = 1;
			}

			/* Check curves */
			else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') {
				check = 1;
			}

			/* Link profiles */
			else if (argv[fa][1] == 'l' || argv[fa][1] == 'L') {
				su.link = 1;
			}

			/* Input profile Intent */
			else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') {
				fa = nfa;
				if (na == NULL) usage();
    			switch (na[0]) {
					case 'p':
					case 'P':
						su.in.intent = icPerceptual;
						break;
					case 'r':
					case 'R':
						su.in.intent = icRelativeColorimetric;
						break;
					case 's':
					case 'S':
						su.in.intent = icSaturation;
						break;
					case 'a':
					case 'A':
						su.in.intent = icAbsoluteColorimetric;
						break;
					default:
						usage();
				}
			}

			/* Output profile Intent */
			else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') {
				fa = nfa;
				if (na == NULL) usage();
    			switch (na[0]) {
					case 'p':
					case 'P':
						su.out.intent = icPerceptual;
						break;
					case 'r':
					case 'R':
						su.out.intent = icRelativeColorimetric;
						break;
					case 's':
					case 'S':
						su.out.intent = icSaturation;
						break;
					case 'a':
					case 'A':
						su.out.intent = icAbsoluteColorimetric;
						break;
					default:
						usage();
				}
			}

			/* Verbosity */
			else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
				su.verb = 1;
			}

			else 
				usage();
		} else
			break;
	}

	if (su.link) {
		if (fa >= argc || argv[fa][0] == '-') usage();
		strcpy(su.in.name,argv[fa++]);

		if (fa >= argc || argv[fa][0] == '-') usage();
		strcpy(su.out.name,argv[fa++]);
	} else {
		if (fa >= argc || argv[fa][0] == '-') usage();
		strcpy(su.dev.name,argv[fa++]);
	}

	if (fa >= argc || argv[fa][0] == '-') usage();
	strcpy(in_name,argv[fa++]);

	if (fa >= argc || argv[fa][0] == '-') usage();
	strcpy(out_name,argv[fa++]);

	/* - - - - - - - - - - - - - - - - */

	if (su.link) {
		icColorSpaceSignature natpcs;

		/* Open up the input device profile for reading */
		if ((su.in.fp = new_icmFileStd_name(su.in.name,"r")) == NULL)
			error ("Can't open file '%s'",su.in.name);
		
		if ((su.in.c = new_icc()) == NULL)
			error ("Creation of Input profile ICC object failed");
	
		/* Read header etc. */
		if ((rv = su.in.c->read(su.in.c,su.in.fp,0)) != 0)
			error ("%d, %s on file '%s'",rv,su.in.c->err,su.in.name);
		su.in.h = su.in.c->header;
	
		/* Check that it is a suitable device input icc */
		if (su.in.h->deviceClass != icSigInputClass
		 && su.in.h->deviceClass != icSigDisplayClass
		 && su.in.h->deviceClass != icSigOutputClass
		 && su.in.h->deviceClass != icSigColorSpaceClass)	/* For sRGB etc. */
			error("Input profile isn't a device profile");
	
		/* Get a conversion object */
		if ((su.in.luo = su.in.c->get_luobj(su.in.c, icmFwd, su.in.intent,
		                                             icSigLabData, icmLuOrdNorm)) == NULL)
			error ("%d, %s for profile '%s'",su.in.c->errc, su.in.c->err, su.in.name);
	
		/* Get details of conversion (Arguments may be NULL if info not needed) */
		su.in.luo->spaces(su.in.luo, &su.ins, &su.id, NULL, NULL, &su.in.alg, NULL, NULL, NULL);

		/* Get native PCS space */
		su.in.luo->lutspaces(su.in.luo, NULL, NULL, NULL, NULL, &natpcs);
	
		if (natpcs == icSigXYZData) {
			su.icombine = 1;			/* XYZ is to non-linear to be a benefit */
		}

		/* Open up the output device profile for reading */
		if ((su.out.fp = new_icmFileStd_name(su.out.name,"r")) == NULL)
			error ("Can't open file '%s'",su.out.name);
		
		if ((su.out.c = new_icc()) == NULL)
			error ("Creation of Output profile ICC object failed");
	
		/* Read header etc. */
		if ((rv = su.out.c->read(su.out.c,su.out.fp,0)) != 0)
			error ("%d, %s on file '%s'",rv,su.out.c->err,su.out.name);
		su.out.h = su.out.c->header;
	
		/* Check that it is a suitable device output icc */
		if (su.out.h->deviceClass != icSigInputClass
		 && su.out.h->deviceClass != icSigDisplayClass
		 && su.out.h->deviceClass != icSigOutputClass
		 && su.out.h->deviceClass != icSigColorSpaceClass)	/* For sRGB etc. */
			error("Output profile isn't a device profile");
	
		/* Get a conversion object */
		if ((su.out.luo = su.out.c->get_luobj(su.out.c, icmBwd, su.out.intent,
		                                             icSigLabData, icmLuOrdNorm)) == NULL)
			error ("%d, %s for profile '%s'",su.out.c->errc, su.out.c->err, su.out.name);
	
		/* Get details of conversion (Arguments may be NULL if info not needed) */
		su.out.luo->spaces(su.out.luo, NULL, NULL, &su.outs, &su.od, &su.out.alg, NULL, NULL, NULL);

		/* Get native PCS space */
		su.out.luo->lutspaces(su.out.luo, NULL, NULL, NULL, NULL, &natpcs);
	
		if (natpcs == icSigXYZData) {
			su.ocombine = 1;			/* XYZ is to non-linear to be a benefit */
		}

		/* See discussion in imdi/imdi_gen.c for ideal numbers */
		/* Use "high quality" resolution numbers */
		switch (su.id) {
			case 0:
				error ("Illegal number of input chanels");
			case 1:
	  		  	clutres = 256;
				break;
			case 2:
	  		  	clutres = 256;
				break;
			case 3:
	  		  	clutres = 33;
				break;
			case 4:
  		  		clutres = 18;
				break;
			case 5:
  		  		clutres = 16;
				break;
			case 6:
  		  		clutres = 9;
				break;
			case 7:
  		  		clutres = 7;
				break;
			case 8:
  		  		clutres = 6;
				break;
			deault: /* > 8 chan */
				clutres = 3;
				break;
		}	

	} else {
		icmLut *lut;						/* ICC LUT table */
		icmLuLut *luluo;					/* LUT lookup object */

		/* Open up the device link profile for reading */
		if ((su.dev.fp = new_icmFileStd_name(su.dev.name,"r")) == NULL)
			error ("Can't open file '%s'",su.dev.name);
	
		if ((su.dev.c = new_icc()) == NULL)
			error ("Creation of ICC object failed");
	
		if ((rv = su.dev.c->read(su.dev.c, su.dev.fp, 0)) != 0)
			error ("%d, %s",rv,su.dev.c->err);
		su.dev.h = su.dev.c->header;

		if (su.verb) {
			icmFile *op;
			if ((op = new_icmFileStd_fp(stdout)) == NULL)
				error ("Can't open stdout");
			su.dev.h->dump(su.dev.h, op, 1);
			op->del(op);
		}

		/* Check that the profile is appropriate */
		if (su.dev.h->deviceClass != icSigLinkClass)
			error("Profile isn't a device link profile");

		/* Get a conversion object */
		if ((su.dev.luo = su.dev.c->get_luobj(su.dev.c, icmFwd, icmDefaultIntent,
		                                             icmSigDefaultData, icmLuOrdNorm)) == NULL)
			error ("%d, %s",su.dev.c->errc, su.dev.c->err);
	
		/* Get details of conversion (Arguments may be NULL if info not needed) */
		su.dev.luo->spaces(su.dev.luo, &su.ins, &su.id, &su.outs, &su.od, &su.dev.alg, NULL, NULL, NULL);

		if (su.dev.alg != icmLutType)
			error ("DeviceLink profile doesn't have Lut !");

		luluo = (icmLuLut *)su.dev.luo;		/* Safe to coerce */
		luluo->get_info(luluo, &lut, NULL, NULL, NULL);	/* Get some details */
		clutres = lut->clutPoints;			/* Desired table resolution */
	}

	/* - - - - - - - - - - - - - - - */
	/* Open up input tiff file ready for reading */
	/* Got arguments, so setup to process the file */
	if ((rh = TIFFOpen(in_name, "r")) == NULL)
		error("error opening read file '%s'",in_name);

	TIFFGetField(rh, TIFFTAG_IMAGEWIDTH,  &width);
	TIFFGetField(rh, TIFFTAG_IMAGELENGTH, &height);

	TIFFGetField(rh, TIFFTAG_BITSPERSAMPLE, &bitspersample);
	if (bitspersample != 8 && bitspersample != 16) {
		error("TIFF Input file must be 8 or 16 bit/channel");
	}

	TIFFGetField(rh, TIFFTAG_PHOTOMETRIC, &photometric);
	if  ((no_pmtc = ColorSpaceSignature2TiffPhotometric(pmtc, su.ins)) == 0)
		error("ICC  input colorspace '%s' can't be handled by a TIFF file!",
		      icm2str(icmColorSpaceSignature, su.ins));
	for (i = 0; i < no_pmtc; i++) {
		if (pmtc[i] == photometric)
			break;				/* Matches */
	}
	if (i >= no_pmtc) {
		switch (no_pmtc) {
			case 1:
				error("ICC input colorspace '%s' doesn't match TIFF photometric '%s'!",
			      icm2str(icmColorSpaceSignature, su.ins), Photometric2str(pmtc[0]));
			case 2:
				error("ICC input colorspace '%s' doesn't match TIFF photometric '%s' or '%s'!",
			      icm2str(icmColorSpaceSignature, su.ins), Photometric2str(pmtc[0]),
			      Photometric2str(pmtc[1]));
			default:
				error("ICC input colorspace '%s' doesn't match TIFF photometric '%s', '%s' or '%s'!",
			      icm2str(icmColorSpaceSignature, su.ins), Photometric2str(pmtc[0]),
			      Photometric2str(pmtc[1]), Photometric2str(pmtc[2]));
		}
	}

	TIFFGetField(rh, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);
	if (su.id != samplesperpixel)
		error ("TIFF Input file has %d input channels mismatched to colorspace '%s'",
		       samplesperpixel, icm2str(icmColorSpaceSignature, su.ins));

	TIFFGetField(rh, TIFFTAG_PLANARCONFIG, &pconfig);
	if (pconfig != PLANARCONFIG_CONTIG)
		error ("TIFF Input file must be planar");

	TIFFGetField(rh, TIFFTAG_RESOLUTIONUNIT, &resunits);
	TIFFGetField(rh, TIFFTAG_XRESOLUTION, &resx);
	TIFFGetField(rh, TIFFTAG_YRESOLUTION, &resy);

	/* - - - - - - - - - - - - - - - */
	if ((wh = TIFFOpen(out_name, "w")) == NULL)
		error("Can\'t create TIFF file '%s'!",out_name);
	
	TIFFSetField(wh, TIFFTAG_IMAGEWIDTH,  width);
	TIFFSetField(wh, TIFFTAG_IMAGELENGTH, height);
	TIFFSetField(wh, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
	TIFFSetField(wh, TIFFTAG_SAMPLESPERPIXEL, su.od);
	TIFFSetField(wh, TIFFTAG_BITSPERSAMPLE, bitspersample);
	TIFFSetField(wh, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
	if  ((no_pmtc = ColorSpaceSignature2TiffPhotometric(pmtc, su.outs)) == 0)
		error("TIFF file can't handle output colorspace '%s'!",
		      icm2str(icmColorSpaceSignature, su.outs));
	TIFFSetField(wh, TIFFTAG_PHOTOMETRIC, pmtc[0]);	/* Use first returned */
	if (pmtc[0] == PHOTOMETRIC_SEPARATED) {
		int iset;
		int inlen;
		char *inames;
		iset = ColorSpaceSignature2TiffInkset(su.outs, &inlen, &inames);
		if (iset != 0xffff && inlen > 0 && inames != NULL) {
			TIFFSetField(wh, TIFFTAG_INKSET, iset);
			if (inames != NULL) {
				TIFFSetField(wh, TIFFTAG_INKNAMES, inlen, inames);
			}
		}
	}
	TIFFSetField(wh, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
	if (resunits) {
		TIFFSetField(wh, TIFFTAG_RESOLUTIONUNIT, resunits);
		TIFFSetField(wh, TIFFTAG_XRESOLUTION, resx);
		TIFFSetField(wh, TIFFTAG_YRESOLUTION, resy);
	}
	TIFFSetField(wh, TIFFTAG_IMAGEDESCRIPTION, "Color corrected by Argyll");

	/* - - - - - - - - - - - - - - - */
	/* Setup the imdi */

	if (!slow) {
		s = new_imdi(
			su.id,			/* Number of input dimensions */
			su.od,			/* Number of output dimensions */
							/* Input pixel representation */
			bitspersample == 8 ? pixint8 : pixint16,
							/* Output pixel representation */
			0x0,			/* Treat every channel as unsigned */
			bitspersample == 8 ? pixint8 : pixint16,
			0x0,			/* Treat every channel as unsigned */
			clutres,		/* Desired table resolution */
			input_curve,	/* Callback functions */
			md_table,
			output_curve,
			(void *)&su		/* Context to callbacks */
		);
	
		if (s == NULL)
			error("new_imdi failed");
	}

	/* - - - - - - - - - - - - - - - */
	/* Process colors to translate */
	/* (Should fix this to process a group of lines at a time ?) */

	inbuf  = _TIFFmalloc(TIFFScanlineSize(rh));
	outbuf = _TIFFmalloc(TIFFScanlineSize(wh));
	if (check)
		checkbuf = _TIFFmalloc(TIFFScanlineSize(wh));

	inp[0] = (unsigned char *)inbuf;
	outp[0] = (unsigned char *)outbuf;

	if (!slow) {		/* Fast */
		for (y = 0; y < height; y++) {

			/* Read in the next line */
			if (TIFFReadScanline(rh, inbuf, y, 0) < 0)
				error ("Failed to read TIFF line %d",y);

			/* Do fast conversion */
			s->interp(s, (void **)outp, (void **)inp, width);
			
			if (check) {
				/* Do floating point conversion */
				for (x = 0; x < width; x++) {
					int i;
					double in[MAX_CHAN], out[MAX_CHAN];
					
					if (bitspersample == 8)
						for (i = 0; i < su.id; i++)
							in[i] = ((unsigned char *)inbuf)[x * su.id + i]/255.0;
					else
						for (i = 0; i < su.id; i++)
							in[i] = ((unsigned short *)inbuf)[x * su.id + i]/65535.0;
					
					if (su.link) {
						if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1)
							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
						if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1)
							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
					} else {
						if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1)
							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
					}
	
					if (bitspersample == 8)
						for (i = 0; i < su.od; i++)
							((unsigned char *)checkbuf)[x * su.od + i] = (int)(out[i] * 255.0 + 0.5);
					else
						for (i = 0; i < su.od; i++)
							((unsigned short *)checkbuf)[x * su.od + i] = (int)(out[i] * 65535.0 + 0.5);
				}
				/* Compute the errors */
				for (x = 0; x < (width * su.od); x++) {
					int err;
					if (bitspersample == 8)
						err = ((unsigned char *)outbuf)[x] - ((unsigned char *)checkbuf)[x];
					else
						err = ((unsigned short *)outbuf)[x] - ((unsigned short *)checkbuf)[x];
					if (err < 0)
						err = -err;
					if (err > mxerr)
						mxerr = err;
					avgerr += (double)err;
					avgcount++;
				}
			}
				
			if (TIFFWriteScanline(wh, outbuf, y, 0) < 0)
				error ("Failed to write TIFF line %d",y);

		}

	} else {	/* Slow but precise */
		if (bitspersample == 8) {
			for (y = 0; y < height; y++) {

				/* Read in the next line */
				if (TIFFReadScanline(rh, inbuf, y, 0) < 0)
					error ("Failed to read TIFF line %d",y);

				/* Do floating point conversion */
				for (x = 0; x < width; x++) {
					int i;
					double in[MAX_CHAN], out[MAX_CHAN];
					
					for (i = 0; i < su.id; i++) {
						in[i] = ((unsigned char *)inbuf)[x * su.id + i]/255.0;
					}
					
					if (su.link) {
						if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1)
							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
						if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1)
							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
					} else {
						if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1)
							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
					}

					for (i = 0; i < su.od; i++) {
						double outi = out[i];
						if (outi < 0.0)			/* Protect against sillies */
							outi = 0.0;
						else if (outi > 1.0)
							outi = 1.0;
						((unsigned char *)outbuf)[x * su.od + i] = (int)(outi * 255.0 + 0.5);
					}
				}
				if (TIFFWriteScanline(wh, outbuf, y, 0) < 0)
					error ("Failed to write TIFF line %d",y);
			}
		} else if (bitspersample == 16) {
			for (y = 0; y < height; y++) {

				/* Read in the next line */
				if (TIFFReadScanline(rh, inbuf, y, 0) < 0)
					error ("Failed to read TIFF line %d",y);

				/* Do floating point conversion */
				for (x = 0; x < width; x++) {
					int i;
					double in[MAX_CHAN], out[MAX_CHAN];
					
					for (i = 0; i < su.id; i++) {
						in[i] = ((unsigned short *)inbuf)[x * su.id + i]/65535.0;
					}
					
					if (su.link) {
						if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1)
							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
						if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1)
							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
					} else {
						if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1)
							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
					}

					for (i = 0; i < su.od; i++) {
						double outi = out[i];
						if (outi < 0.0)			/* Protect against sillies */
							outi = 0.0;
						else if (outi > 1.0)
							outi = 1.0;
						((unsigned short *)outbuf)[x * su.od + i] = (int)(outi * 65535.0 + 0.5);
					}
				}
				if (TIFFWriteScanline(wh, outbuf, y, 0) < 0)
					error ("Failed to write TIFF line %d",y);
			}
		}
	}

	if (check) {
		printf("Worst error = %d bits, average error = %f bits\n", mxerr, avgerr/avgcount);
		if (bitspersample == 8)
			printf("Worst error = %f%%, average error = %f%%\n",
			       mxerr/2.55, avgerr/(2.55 * avgcount));
		else
			printf("Worst error = %f%%, average error = %f%%\n",
			       mxerr/655.35, avgerr/(655.35 * avgcount));
	}

	/* Done with lookup object */
	if (s != NULL)
		s->done(s);

	if (su.link) {
		su.in.luo->del(su.in.luo);
		su.in.c->del(su.in.c);
		su.in.fp->del(su.in.fp);
		su.out.luo->del(su.out.luo);
		su.out.c->del(su.out.c);
		su.out.fp->del(su.out.fp);
	} else {
		su.dev.luo->del(su.dev.luo);
		su.dev.c->del(su.dev.c);
		su.dev.fp->del(su.dev.fp);
	}

	_TIFFfree(inbuf);
	_TIFFfree(outbuf);
	if (check)
		_TIFFfree(checkbuf);

	TIFFClose(rh);		/* Close Input file */
	TIFFClose(wh);		/* Close Output file */

	return 0;
}
示例#3
0
int
main(int argc, char *argv[]) {
	int fa,nfa;				/* argument we're looking at */
	char prof_name[100];
	char *xl, out_name[100];
	icmFile *fp;
	icc *icco;
	xicc *xicco;
	gamut *gam;
	int verb = 0;
	int rv = 0;
	int vrml = 0;
	int doaxes = 1;
	int docusps = 0;
	double gamres = GAMRES;		/* Surface resolution */
	int special = 0;			/* Special surface plot */
	int fl = 0;					/* luobj flags */
	icxInk ink;					/* Ink parameters */
	int tlimit = -1;			/* Total ink limit as a % */
	int klimit = -1;			/* Black ink limit as a % */
	icxViewCond vc;				/* Viewing Condition for CIECAM */
	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_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 */

	icxLuBase *luo;

	/* Lookup parameters */
	icmLookupFunc     func   = icmFwd;				/* Default */
	icRenderingIntent intent = -1;					/* Default */
	icColorSpaceSignature pcsor = icSigLabData;		/* Default */
	icmLookupOrder    order  = icmLuOrdNorm;		/* Default */
	
	error_program = argv[0];

	if (argc < 2)
		usage("Too few parameters");

	/* 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(NULL);

			/* function */
			else if (argv[fa][1] == 'f' || 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;
					default:
						usage("Unrecognised parameter after flag -f");
				}
			}

			/* Intent */
			else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') {
				fa = nfa;
				if (na == NULL) usage("No parameter after flag -i");
    			switch (na[0]) {
					case 'd':
						intent = icmDefaultIntent;
						break;
					case 'a':
						intent = icAbsoluteColorimetric;
						break;
					case 'p':
						intent = icPerceptual;
						break;
					case 'r':
						intent = icRelativeColorimetric;
						break;
					case 's':
						intent = icSaturation;
						break;
					/* Argyll special intents to check spaces underlying */
					/* icxPerceptualAppearance & icxSaturationAppearance */
					case 'P':
						intent = icmAbsolutePerceptual;
						break;
					case 'S':
						intent = icmAbsoluteSaturation;
						break;
					default:
						usage("Unrecognised parameter after flag -i");
				}
			}

			/* Search order */
			else if (argv[fa][1] == 'o' || 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("Unrecognised parameter after flag -o");
				}
			}

			/* PCS override */
			else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') {
				fa = nfa;
				if (na == NULL) usage("No parameter after flag -p");
    			switch (na[0]) {
					case 'l':
						pcsor = icSigLabData;
						break;
					case 'j':
						pcsor = icxSigJabData;
						break;
					default:
						usage("Unrecognised parameter after flag -p");
				}
			}

			/* Verbosity */
			else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
				verb = 1;
			}
			/* VRML output */
			else if (argv[fa][1] == 'w' || argv[fa][1] == 'W') {
				vrml = 1;
			}
			/* No axis output in vrml */
			else if (argv[fa][1] == 'n' || argv[fa][1] == 'N') {
				doaxes = 0;
			}
			/* Do cusp markers in vrml */
			else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') {
				docusps = 1;
			}
			/* Special */
			else if (argv[fa][1] == 's' || argv[fa][1] == 'S') {
				special = 1;
			}
			/* Ink limit */
			else if (argv[fa][1] == 'l') {
				fa = nfa;
				if (na == NULL) usage("No parameter after flag -l");
				tlimit = atoi(na);
			}

			else if (argv[fa][1] == 'L') {
				fa = nfa;
				if (na == NULL) usage("No parameter after flag -L");
				klimit = atoi(na);
			}


			/* Surface Detail */
			else if (argv[fa][1] == 'd' || argv[fa][1] == 'D') {
				fa = nfa;
				if (na == NULL) usage("No parameter after flag -d");
				gamres = atof(na);
			}

			/* Viewing conditions */
			else if (argv[fa][1] == 'c' || 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] == '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] == '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 (intent == -1) {
		if (pcsor == icxSigJabData)
			intent = icRelativeColorimetric;	/* Default to icxAppearance */
		else
			intent = icAbsoluteColorimetric;	/* Default to icAbsoluteColorimetric */
	}

	if (fa >= argc || argv[fa][0] == '-') usage("Expected profile name");
	strcpy(prof_name,argv[fa]);

	/* 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 (verb) {
		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 ink limits */
	icxDefaultLimits(xicco, &ink.tlimit, tlimit/100.0, &ink.klimit, klimit/100.0);

	if (verb) {
		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);
	}

	/* Setup a safe ink generation (not used) */
	ink.KonlyLmin = 0;		/* Use normal black Lmin for locus */
	ink.k_rule = icxKluma5k;
	ink.c.Ksmth = ICXINKDEFSMTH;	/* Default smoothing */
	ink.c.Kskew = ICXINKDEFSKEW;	/* default curve skew */
	ink.c.Kstle = 0.0;		/* Min K at white end */
	ink.c.Kstpo = 0.0;		/* Start of transition is at white */
	ink.c.Kenle = 1.0;		/* Max K at black end */
	ink.c.Kenpo = 1.0;		/* End transition at black */
	ink.c.Kshap = 1.0;		/* Linear transition */

	/* Setup the default viewing conditions */
	if (xicc_enum_viewcond(xicco, &vc, -1, NULL, 0, NULL) == -2)
		error ("%d, %s",xicco->errc, xicco->err);

	if (vc_e != -1)
		if (xicc_enum_viewcond(xicco, &vc, vc_e, NULL, 0, NULL) == -2)
			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_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];
	}

	fl |= ICX_CLIP_NEAREST;		/* Don't setup rev uncessarily */

#ifdef USE_CAM_CLIP_OPT
	 fl |= ICX_CAM_CLIP;
#endif

#ifdef NEVER
	printf("~1 output space flags = 0x%x\n",fl);
	printf("~1 output space intent = %s\n",icx2str(icmRenderingIntent,intent));
	printf("~1 output space pcs = %s\n",icx2str(icmColorSpaceSignature,pcsor));
	printf("~1 output space viewing conditions =\n"); xicc_dump_viewcond(&vc);
	printf("~1 output space inking =\n"); xicc_dump_inking(&ink);
#endif

	strcpy(out_name, prof_name);
	if ((xl = strrchr(out_name, '.')) == NULL)	/* Figure where extention is */
		xl = out_name + strlen(out_name);

	strcpy(xl,".gam");

	/* Get a expanded color conversion object */
	if ((luo = xicco->get_luobj(xicco, fl, func, intent, pcsor, order, &vc, &ink)) == NULL)
		error ("%d, %s",xicco->errc, xicco->err);

	if (special) {
		if (func != icmFwd)
			error("Must be forward direction for special plot");
		strcpy(xl,".wrl");
		diag_gamut(luo, gamres, doaxes, tlimit/100.0, klimit/100.0, out_name); 
	} else {
		/* Creat a gamut surface */
		if ((gam = luo->get_gamut(luo, gamres)) == NULL)
			error ("%d, %s",xicco->errc, xicco->err);

		if (gam->write_gam(gam,out_name))
			error ("write gamut failed on '%s'",out_name);

		if (vrml) {
			strcpy(xl,".wrl");
			if (gam->write_vrml(gam,out_name, doaxes, docusps))
				error ("write vrml failed on '%s'",out_name);
		}

		if (verb) {
			printf("Total volume of gamut is %f cubic colorspace units\n",gam->volume(gam));
		}
		gam->del(gam);
	}

	luo->del(luo);			/* Done with lookup object */

	xicco->del(xicco);		/* Expansion wrapper */
	icco->del(icco);		/* Icc */
	fp->del(fp);


	return 0;
}
示例#4
0
int
main(
int argc,
char *argv[]
) {
	int fa,nfa;
	char out_name[1000];
	icmFile *wr_fp;
	icc *wr_icco;
	int rv = 0;
	int verb = 0;

	if (argc < 2)
		usage();

	/* 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();

			/* Verbosity */
			else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
				verb = 1;
			}

			else 
				usage();
		}
		else
			break;
	}

	if (fa >= argc || argv[fa][0] == '-') usage();
	strcpy(out_name,argv[fa]);

	/* ---------------------------------------- */
	/* Create a matrix/shaper based XYZ profile */
	/* ---------------------------------------- */

	/* Open up the file for writing */
	if ((wr_fp = new_icmFileStd_name(out_name,"w")) == NULL)
		error ("Write: Can't open file '%s'",out_name);

	if ((wr_icco = new_icc()) == NULL)
		error ("Write: Creation of ICC object failed");

	/* Add all the tags required */

	/* The header: */
	{
		icmHeader *wh = wr_icco->header;

		/* Values that must be set before writing */
		wh->deviceClass     = icSigDisplayClass;
    	wh->colorSpace      = icSigRgbData;			/* It's RGB space */
    	wh->pcs             = icSigXYZData;
    	wh->renderingIntent = icPerceptual;

		/* Values that should be set before writing */
		wh->manufacturer = str2tag("????");
    	wh->model        = str2tag("????");
	}
	/* Profile Description Tag: */
	{
		icmTextDescription *wo;
		char *dst;

		dst = "sRGB like Matrix Display profile";
		if ((wo = (icmTextDescription *)wr_icco->add_tag(
		           wr_icco, icSigProfileDescriptionTag,	icSigTextDescriptionType)) == NULL) 
			error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);

		wo->size = strlen(dst)+1; 	/* Allocated and used size of desc, inc null */
		wo->scCode = 0;
		wo->scSize = strlen(dst)+1;
		if (wo->scSize > 67)
			error("Description scriptCode string longer than 67");
		wo->allocate((icmBase *)wo);/* Allocate space */
		strcpy(wo->desc, dst);		/* Copy the string in */
		strcpy((char *)wo->scDesc, dst);	/* Copy the string in */
	}
	/* Copyright Tag: */
	{
		icmText *wo;
		char *crt = "Copyright tag goes here";
		if ((wo = (icmText *)wr_icco->add_tag(
		           wr_icco, icSigCopyrightTag,	icSigTextType)) == NULL) 
			error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);

		wo->size = strlen(crt)+1; 	/* Allocated and used size of text, inc null */
		wo->allocate((icmBase *)wo);/* Allocate space */
		strcpy(wo->data, crt);		/* Copy the text in */
	}

	/* Could add other relevant tags here, such as:

	   Device Manufacturers Description Tag
	   Device Model Description Tag
	   Device technology Tag
	   Viewing conditions Description Tag
	   Viewing conditions
	   Display Luminance Tag
	   Measurement information Tag

	   etc.
	 */

	/* Setup the primaries */
	{
		/* Compute primaries as XYZ */
		double wrgb[4][3] = {		/* Primaries in Yxy from the standard */
			{ 1.0, 0.3127, 0.3290 },	/* White */
			{ 1.0, 0.6400, 0.3300 },	/* Red */
			{ 1.0, 0.3000, 0.6000 },	/* Green */
			{ 1.0, 0.1500, 0.0600 }		/* Blue */
		};
		double mat[3][3];
		int i;

		/* Convert yxy to normalised 3x3 matrix */
		icmRGBYxyprim2matrix(wrgb[1], wrgb[2], wrgb[3], wrgb[0], mat, wrgb[0]);
		icmTranspose3x3(mat, mat);

#ifdef NEVER		/* Dump XYZ */
		printf("sRGB: XYZ\n");
		printf("{ %f, %f, %f }, /* Red */\n"
		       "{ %f, %f, %f }, /* Green */\n"
		       "{ %f, %f, %f }, /* Blue */\n"
		       "{ %f, %f, %f }  /* White */\n",
		mat[0][0], mat[0][1], mat[0][2],
		mat[1][0], mat[1][1], mat[1][2],
		mat[2][0], mat[2][1], mat[2][2],
		wrgb[0][0], wrgb[0][1], wrgb[0][2]);
#endif

#ifdef NEVER		/* Dump xyz */
{
	double mat2[4][3];
	double tt;

	for (i = 0; i < 3; i++) {
	
		tt = mat[i][0] + mat[i][1] + mat[i][2];
		mat2[i][0] = mat[i][0] / tt;
		mat2[i][1] = mat[i][1] / tt;
		mat2[i][2] = mat[i][2] / tt;
	}
	tt = wrgb[0][0] + wrgb[0][1] + wrgb[0][2];
	mat2[i][0] = wrgb[0][0] / tt;
	mat2[i][1] = wrgb[0][1] / tt;
	mat2[i][2] = wrgb[0][2] / tt;

	printf("sRGB: xyz\n");
	printf("{ %f, %f, %f },		/* Red */\n"
	       "{ %f, %f, %f },		/* Green */\n"
	       "{ %f, %f, %f },		/* Blue */\n"
	       "{ %f, %f, %f }		/* White */\n",
	mat2[0][0], mat2[0][1], mat2[0][2],
	mat2[1][0], mat2[1][1], mat2[1][2],
	mat2[2][0], mat2[2][1], mat2[2][2],
	mat2[3][0], mat2[3][1], mat2[3][2]);
}
#endif

		/* White Point Tag: */
		{
			icmXYZArray *wo;

			if ((wo = (icmXYZArray *)wr_icco->add_tag(
			           wr_icco, icSigMediaWhitePointTag, icSigXYZArrayType)) == NULL) 
				error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);

			wo->size = 1;
			wo->allocate((icmBase *)wo);	/* Allocate space */
			wo->data[0].X = wrgb[0][0];
			wo->data[0].Y = wrgb[0][1];
			wo->data[0].Z = wrgb[0][2]; 
		}
		/* Black Point Tag: */
		{
			icmXYZArray *wo;

			if ((wo = (icmXYZArray *)wr_icco->add_tag(
			           wr_icco, icSigMediaBlackPointTag, icSigXYZArrayType)) == NULL) 
				error("add_tag failed: %d, %s",wr_icco->errc,wr_icco->err);

			wo->size = 1;
			wo->allocate((icmBase *)wo);	/* Allocate space */
			wo->data[0].X = 0.00;
			wo->data[0].Y = 0.00;
			wo->data[0].Z = 0.00;
		}
		/* Red, Green and Blue Colorant Tags: */
		{
			icmXYZNumber white;
			icmXYZArray *wor, *wog, *wob;
			double fromAbs[3][3];
			double d50m[3][3];

			/* Convert to D50 adapated */
			icmAry2XYZ(white, wrgb[0]);
			wr_icco->chromAdaptMatrix(wr_icco, ICM_CAM_NONE, icmD50, white, fromAbs);
			icmMulBy3x3(d50m[0], fromAbs, mat[0]);
			icmMulBy3x3(d50m[1], fromAbs, mat[1]);
			icmMulBy3x3(d50m[2], fromAbs, mat[2]);

			/* Make sure rounding doesn't wreck white point */
			quantizeRGBprimsS15Fixed16(d50m);

			if ((wor = (icmXYZArray *)wr_icco->add_tag(
			           wr_icco, icSigRedColorantTag, icSigXYZArrayType)) == NULL) 
					error("add_tag failed: %d, %s",rv,wr_icco->err);
			if ((wog = (icmXYZArray *)wr_icco->add_tag(
			           wr_icco, icSigGreenColorantTag, icSigXYZArrayType)) == NULL) 
				error("add_tag failed: %d, %s",rv,wr_icco->err);
			if ((wob = (icmXYZArray *)wr_icco->add_tag(
			           wr_icco, icSigBlueColorantTag, icSigXYZArrayType)) == NULL) 
				error("add_tag failed: %d, %s",rv,wr_icco->err);

			wor->size = wog->size = wob->size = 1;
			wor->allocate((icmBase *)wor);	/* Allocate space */
			wog->allocate((icmBase *)wog);
			wob->allocate((icmBase *)wob);
			wor->data[0].X = d50m[0][0]; wor->data[0].Y = d50m[0][1]; wor->data[0].Z = d50m[0][2];
			wog->data[0].X = d50m[1][0]; wog->data[0].Y = d50m[1][1]; wog->data[0].Z = d50m[1][2];
			wob->data[0].X = d50m[2][0]; wob->data[0].Y = d50m[2][1]; wob->data[0].Z = d50m[2][2];
		}
	}
	/* Red, Green and Blue Gamma Curve Tags: */
	{
		icmCurve *wor, *wog, *wob;
		int i;

		if ((wor = (icmCurve *)wr_icco->add_tag(
		           wr_icco, icSigRedTRCTag, icSigCurveType)) == NULL) 
			error("add_tag failed: %d, %s",rv,wr_icco->err);
		wor->flag = icmCurveSpec;
		wor->size = 1024;
		wor->allocate((icmBase *)wor);	/* Allocate space */
		for (i = 0; i < wor->size; i++)
			wor->data[i] = gdv2dv(i/(wor->size-1.0));

		/* Link other channels to the red */
		if ((wog = (icmCurve *)wr_icco->link_tag(
		           wr_icco, icSigGreenTRCTag, icSigRedTRCTag)) == NULL) 
			error("link_tag failed: %d, %s",rv,wr_icco->err);
		if ((wob = (icmCurve *)wr_icco->link_tag(
		           wr_icco, icSigBlueTRCTag, icSigRedTRCTag)) == NULL) 
			error("link_tag failed: %d, %s",rv,wr_icco->err);
	}

	/* Write the file out */
	if ((rv = wr_icco->write(wr_icco,wr_fp,0)) != 0)
		error ("Write file: %d, %s",rv,wr_icco->err);
	
	wr_icco->del(wr_icco);
	wr_fp->del(wr_fp);

	return 0;
}
示例#5
0
int
gx_load_icc_profile(gs_cie_icc *picc_info)
{
    stream *        instrp = picc_info->instrp;
    icc *           picc;
    icmLuBase * plu = NULL;
    icmFile *pfile = NULL;

#if   SAVEICCPROFILE

    unsigned int num_bytes;
    unsigned char *iccbuffer;
    FILE *fid;

#endif

    /* verify that the file is legitimate */
    if (picc_info->file_id != (instrp->read_id | instrp->write_id))
	return_error(gs_error_ioerror);
    /*
     * Load the top-level ICC profile.
     *
     * If an ICC profile fails to load, generate an error.
     *
     * Testing demonstrates, however, Acrobat Reader silently
     * ignores the error and uses the alternate color space.
     * This behaviour is implemented by catching the error using
     * a stopped context from within the interpreter (gs_icc.ps).
     *
     * Failure to allocate the top-level profile object is considered
     * a limitcheck rather than a VMerror, as profile data structures
     * are stored in "foreign" memory.
     */
    if ((picc = new_icc()) == NULL)
	return_error(gs_error_limitcheck);
    {
	icProfileClassSignature profile_class;
	icColorSpaceSignature   cspace_type;
	gs_vector3 *            ppt;

	pfile = gx_wrap_icc_stream (instrp);

	if ((picc->read(picc, pfile, 0)) != 0)
	    goto return_rangecheck;

#if SAVEICCPROFILE

        num_bytes = picc->header->size;
        iccbuffer = (unsigned char *) malloc(num_bytes);
        pfile->seek(pfile,0);
        pfile->read(pfile,iccbuffer,1,num_bytes);
        fid = fopen("DumpedICC.icm","wb");
        fwrite(iccbuffer,sizeof(unsigned char),num_bytes,fid);
        fclose(fid);
        free(iccbuffer);

#endif


	/* verify the profile type */
	profile_class = picc->header->deviceClass;
	if ( profile_class != icSigInputClass     &&
	     profile_class != icSigDisplayClass   &&
	     profile_class != icSigOutputClass    &&
	     profile_class != icSigColorSpaceClass  )
	    goto return_rangecheck;

	/* verify the profile connection space */
	cspace_type = picc->header->pcs;
	if (cspace_type == icSigLabData)
	    picc_info->pcs_is_cielab = true;
	else if (cspace_type == icSigXYZData)
	    picc_info->pcs_is_cielab = false;
	else
	    goto return_rangecheck;

	/* verify the source color space */
	cspace_type = picc->header->colorSpace;
	if (cspace_type == icSigCmykData) {
	    if (picc_info->num_components != 4)
		goto return_rangecheck;
	} else if ( cspace_type == icSigRgbData ||
		    cspace_type == icSigLabData   ) {
	    if (picc_info->num_components != 3)
		goto return_rangecheck;
	} else if (cspace_type == icSigGrayData) {
	    if (picc_info->num_components != 1)
		goto return_rangecheck;
	}

	/*
	 * Fetch the lookup object.
	 *
	 * PostScript and PDF deal with rendering intent as strictly a
	 * rendering dictionary facility. ICC profiles allow a rendering
	 * intent to be specified for both the input (device ==> pcs) and
	 * output (pcs ==> device) operations. Hence, when using ICCBased
	 * color spaces with PDF, no clue is provided as to which source
	 * mapping to select.
	 *
	 * In the absence of other information, there are two possible
	 * selections. If our understanding is correct, when relative
	 * colorimetry is specified, the icclib code will map source
	 * color values to XYZ or L*a*b* values such that the relationship
	 * of the source color, relative to the source white and black
	 * points, will be the same as the output colors and the
	 * profile connection space illuminant (currently always D50)
	 * and pure black ([0, 0, 0]). In this case, the white and black
	 * points that should be listed in the color space are the
	 * profile connection space illuminant (D50) and pure black.
	 *
	 * If absolute colorimetry is employed, the XYZ or L*a*b* values
	 * generated will be absolute in the chromatic sense (they are
	 * not literally "absolute", as we still must have overall
	 * intensity information in order to determine weighted spectral
	 * power levels). To achieve relative colorimetry for the output,
	 * these colors must be evaluated relative to the source white
	 * and black points. Hence, in this case, the appropriate white
	 * and black points to list in the color space are the source
	 * white and black points provided in the profile tag array.
	 *
	 * In this implementation, we will always request relative
	 * colorimetry from the icclib, and so will use the profile
	 * connection space illuminant and pure black as the white and
	 * black points of the color space. This approach is somewhat
	 * simpler, as it allows the color space white point to also
	 * be used for L*a*b* to XYZ conversion (otherwise we would
	 * need to store the profile connection space illuminant
	 * separately for that purpose). The approach does reduce to
	 * to some extent the range of mappings that can be achieved
	 * via the color rendering dictionary, but for now we believe
	 * this loss is not significant.
	 *
	 * For reasons that are not clear to us, the icclib code does
	 * not support relative colorimetry for all color profiles. For
	 * this reason, we specify icmDefaultIntent rather than
	 * icRelativeColormetric.
	 *
	 * NB: We are not color experts; our understanding of this area
	 *     may well be incorrect.
	 */
	plu = picc->get_luobj( picc,
			       icmFwd,
			       icmDefaultIntent,
			       0, /* PCS override */
			       icmLuOrdNorm );
	if (plu == NULL)
	    goto return_rangecheck;

	/*
	 * Get the appropriate white and black points. See the note on
	 * rendering intent above for a discussion of why we are using
	 * the profile space illuminant and pure black. (Pure black need
	 * not be set explicitly, as it is the default.)
	 */
	ppt = &picc_info->common.points.WhitePoint;
	ppt->u = picc->header->illuminant.X;
	ppt->v = picc->header->illuminant.Y;
	ppt->w = picc->header->illuminant.Z;

	picc_info->picc = picc;
	picc_info->plu = plu;
	picc_info->pfile = pfile;
    }

    return 0;

 return_rangecheck:
    if (plu != NULL)
	plu->del(plu);
    if (picc != NULL)
	picc->del(picc);
    if (pfile != NULL)
	pfile->del(pfile);
    return_error(gs_error_rangecheck);
}
示例#6
0
int
main(
	int argc,
	char *argv[]
) {
	int fa,nfa;				/* argument we're looking at */
	int verb = 0;
	int cchan = -1;			/* default all */
	int dovrml = 0;
	int doaxes = 0;
	char in_name[500];
	char out_name[500], *xl;
	icmFile *rd_fp;
	icc *wr_icco, *rd_icco;		/* Keep object separate */
	int rv = 0;

	/* Check variables */
	icmLuBase *luo;
	icmLuLut *luluto;	/* Lookup xLut type object */
	int gres;			/* Grid resolution */
	icColorSpaceSignature ins, outs;	/* Type of input and output spaces */
	int inn;							/* Number of input chanels */
	icmLuAlgType alg;
	FILE *wrl;
	int dx[4];			/* Device index mapping */
	int chan, cs, ce;
	
	if (argc < 2)
		usage();

	/* 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 */
					}
				}
			}

			/* Verbosity */
			if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
				verb = 1;
			}
			/* VRML */
			else if (argv[fa][1] == 'w' || argv[fa][1] == 'W') {
				dovrml = 1;
			}
			/* Cyan */
			else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') {
				cchan = 0;
			}
			/* Magenta */
			else if (argv[fa][1] == 'm' || argv[fa][1] == 'M') {
				cchan = 1;
			}
			/* Yellow */
			else if (argv[fa][1] == 'y' || argv[fa][1] == 'Y') {
				cchan = 2;
			}
			/* Black */
			else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') {
				cchan = 3;
			}
			else if (argv[fa][1] == '?')
				usage();
			else 
				usage();
		}
		else
			break;
	}

	if (fa >= argc || argv[fa][0] == '-') usage();
	strcpy(in_name,argv[fa]);

	strcpy(out_name, in_name);
	if ((xl = strrchr(out_name, '.')) == NULL)	/* Figure where extention is */
		xl = out_name + strlen(out_name);
	strcpy(xl,".wrl");

	/* Open up the file for reading */
	if ((rd_fp = new_icmFileStd_name(in_name,"r")) == NULL)
		error ("Read: Can't open file '%s'",in_name);

	if ((rd_icco = new_icc()) == NULL)
		error ("Read: Creation of ICC object failed");

	/* Read the header and tag list */
	if ((rv = rd_icco->read(rd_icco,rd_fp,0)) != 0)
		error ("Read: %d, %s",rv,rd_icco->err);

	/* Get a Device to PCS conversion object */
	if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icRelativeColorimetric, icSigLabData, icmLuOrdNorm)) == NULL) {
		if ((luo = rd_icco->get_luobj(rd_icco, icmFwd, icmDefaultIntent, icSigLabData, icmLuOrdNorm)) == NULL)
			error ("%d, %s",rd_icco->errc, rd_icco->err);
	}
	/* Get details of conversion */
	luo->spaces(luo, &ins, &inn, &outs, NULL, &alg, NULL, NULL, NULL);

	if (alg != icmLutType) {
		error("Expecting Lut based profile");
	}

	if (ins != icSigCmykData) {
		error("Expecting CMYK device");
	}

	if (outs != icSigLabData) {
		error("Expecting Lab PCS");
	}

	luluto = (icmLuLut *)luo;	/* Lookup xLut type object */

	gres = luluto->lut->clutPoints;
	if (gres > MGR) {
		error("Can't handle grid resolution greater than %d\n",MGR);
	}

	if (dovrml) {
		wrl = start_vrml(out_name, doaxes);
		start_line_set(wrl);
	}

	/* For all the device chanels chosen */
	if (cchan < 0) {
		cs = 0;
		ce = inn;
	} else {
		cs = cchan;
		ce = cs + 1;
	}
	for (chan = cs; chan < ce; chan++) {

		/* Check the monotonicity of the output for a given device input */
		int co[4];
		if (chan == 0) {
			dx[0] = 1;
			dx[1] = 2;
			dx[2] = 3;
			dx[3] = 0;		/* Cyan is variable */
		} else if (chan == 1) {
			dx[0] = 0;
			dx[1] = 2;
			dx[2] = 3;
			dx[3] = 1;		/* Magenta is variable */
		} else if (chan == 2) {
			dx[0] = 0;
			dx[1] = 1;
			dx[2] = 3;
			dx[3] = 2;		/* Yellow is variable */
		} else if (chan == 3) {
			dx[0] = 0;
			dx[1] = 1;
			dx[2] = 2;
			dx[3] = 3;		/* Black is variable */
		}

		/* Itterate throught the CMY clut grid points */
		for (co[0] = 0; co[0] < gres; co[0]++) {
			for (co[1] = 0; co[1] < gres; co[1]++) {
				for (co[2] = 0; co[2] < gres; co[2]++) {
					int j, k, ck, nm;
					double dev[MGR][4];
					double pcs[MGR][3];
					double apcs[3], ss;

					/* Run up the variable axis */
					for (ck = 0; ck < gres; ck++) {

						dev[ck][dx[0]] = co[0]/(gres-1.0);
						dev[ck][dx[1]] = co[1]/(gres-1.0);
						dev[ck][dx[2]] = co[2]/(gres-1.0);
						dev[ck][dx[3]] = ck/(gres-1.0);

						/* Device to PCS */
						if ((rv = luluto->clut(luluto, pcs[ck], dev[ck])) > 1)
							error ("%d, %s",rd_icco->errc,rd_icco->err);

//						if (dovrml)
//							add_vertex(wrl, pcs[ck]);
					}

					/* Compute average vector direction */
					for (ss = 0.0, k = 0; k < 3; k++) {
						double tt;
						tt = pcs[gres-1][k] - pcs[0][k];
						ss += tt * tt;
						apcs[k] = tt;
					}
					for (k = 0; k < 3; k++)
						apcs[k] /= ss;

					/* Now compute the dot product for each vector, */
					/* and check for reversals. */
					j = 0;
//printf("Checking          CMYK %f %f %f %f Lab %f %f %f\n",
//       dev[j][0], dev[j][1], dev[j][2], dev[j][3],
//       pcs[j][0], pcs[j][1], pcs[j][2]);
					for (nm = 0, j = 1; j < gres; j++) {
						for (ss = 0.0, k = 0; k < 3; k++)	/* Dot product */
							ss += (pcs[j][k] - pcs[j-1][k]) * apcs[k];

//printf("Checking %f CMYK %f %f %f %f Lab %f %f %f\n",
//       ss, dev[j][0], dev[j][1], dev[j][2], dev[j][3],
//       pcs[j][0], pcs[j][1], pcs[j][2]);

						if (ss <= 0.0) {
							nm = 1;
							printf("NonMon %f at CMYK %f %f %f %f Lab %f %f %f\n",
							       ss, dev[j][0], dev[j][1], dev[j][2], dev[j][3],
							       pcs[j][0], pcs[j][1], pcs[j][2]);
						}
					}
//printf("\n");

					/* Display just the non mono threads */
					if (nm && dovrml) {
						for (j = 0; j < gres; j++)
							add_vertex(wrl, pcs[j]);
					}
					if (verb) {
						printf("."); fflush(stdout);
					}
				}
			}
		}
	}

	if (dovrml) {
		make_lines(wrl, gres);
		end_vrml(wrl);
	}

	/* Done with lookup object */
	luo->del(luo);

	rd_icco->del(rd_icco);
	rd_fp->del(rd_fp);

	return 0;
}