Ejemplo n.º 1
0
int
main(int argc, char *argv[]) {
	int fa,nfa;					/* argument we're looking at */
	char prof_name[MAXNAMEL+1] = { '\000' };	/* ICC profile name, "" if none */
	char in_name[MAXNAMEL+1];			/* TIFF input file */
	char *xl = NULL, out_name[MAXNAMEL+4+1] = "locus.ts";	/* locus output file */
	int verb = 0;
	int dovrml = 0;
	int doaxes = 1;
	int usevec = 0;
	double vec[3];
	int rv = 0;

	icc *icco = NULL;
	xicc *xicco = NULL;
	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 = NULL;					/* Generic lookup object */
	icColorSpaceSignature ins = icSigLabData, outs;	/* Type of input and output spaces */
	int inn, outn;						/* Number of components */
	icmLuAlgType alg;					/* Type of lookup algorithm */
	icmLookupFunc     func   = icmFwd;				/* Must be */
	icRenderingIntent intent = -1;					/* Default */
	icColorSpaceSignature pcsor = icSigLabData;		/* Default */
	icmLookupOrder    order  = icmLuOrdNorm;		/* Default */

	TIFF *rh = NULL;
	int x, y, width, height;					/* Size of image */
	uint16 samplesperpixel, bitspersample;
	uint16 pconfig, photometric, pmtc;
	uint16 resunits;
	float resx, resy;
	tdata_t *inbuf;
	void (*cvt)(double *out, double *in);		/* TIFF conversion function, NULL if none */
	icColorSpaceSignature tcs;					/* TIFF colorspace */
	uint16 extrasamples;						/* Extra "alpha" samples */
	uint16 *extrainfo;							/* Info about extra samples */
	int sign_mask;								/* Handling of encoding sign */

	int i, j;
	int nipoints = 0;					/* Number of raster sample points */ 
	co *inp = NULL;						/* Input point values */
	double tdel = 0.0;					/* Total delta along locus */
	rspl *rr = NULL;
	int nopoints = 0;					/* Number of raster sample points */ 
	co *outp = NULL;

	error_program = argv[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') {
				verb = 1;
			}

			/* Intent */
			else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') {
				fa = nfa;
				if (na == NULL) usage();
    			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();
				}
			}

			/* Search order */
			else if (argv[fa][1] == 'o') {
				fa = nfa;
				if (na == NULL) usage();
    			switch (na[0]) {
					case 'n':
					case 'N':
						order = icmLuOrdNorm;
						break;
					case 'r':
					case 'R':
						order = icmLuOrdRev;
						break;
					default:
						usage();
				}
			}

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

			/* Viewing conditions */
			else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') {
				fa = nfa;
				if (na == NULL) usage();

				/* Switch to Jab automatically */
				pcsor = icxSigJabData;

				/* Set the viewing conditions */
				if (na[1] != ':') {
					if ((vc_e = xicc_enum_viewcond(NULL, NULL, -2, na, 1, NULL)) == -999)
						usage();
				} else if (na[0] == 's' || na[0] == 'S') {
					if (na[1] != ':')
						usage();
					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();
				} 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();
				} else if (na[0] == 'a' || na[0] == 'A') {
					if (na[1] != ':')
						usage();
					vc_a = atof(na+2);
				} else if (na[0] == 'b' || na[0] == 'B') {
					if (na[1] != ':')
						usage();
					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();
				} else
					usage();
			}

			/* VRML output */
			else if (argv[fa][1] == 'w' || argv[fa][1] == 'W') {
				dovrml = 1;
			}
			/* No axis output */
			else if (argv[fa][1] == 'n' || argv[fa][1] == 'N') {
				doaxes = 0;
			}
			/* Vector direction for span */
			else if (argv[fa][1] == 'V') {
				usevec = 1;
				if (na == NULL) usage();
				fa = nfa;
				if (sscanf(na, " %lf , %lf , %lf ",&vec[0], &vec[1], &vec[2]) != 3)
						usage();
			}
			/* Output file name */
			else if (argv[fa][1] == 'O') {
				fa = nfa;
				if (na == NULL) usage();
				strncpy(out_name,na,MAXNAMEL); out_name[MAXNAMEL] = '\000';
			}

			else 
				usage();
		} else
			break;
	}

	if (fa >= argc || argv[fa][0] == '-') usage();
	if (fa < (argc-1))
		strncpy(prof_name,argv[fa++],MAXNAMEL); prof_name[MAXNAMEL] = '\000';

	if (fa >= argc || argv[fa][0] == '-') usage();
	strncpy(in_name,argv[fa],MAXNAMEL); in_name[MAXNAMEL] = '\000';

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

	if (verb) {
		printf("Profile     = '%s'\n",prof_name);
		printf("Input TIFF  = '%s'\n",in_name);
		printf("Output file = '%s'\n",out_name);
	}

	if (intent == -1) {
		if (pcsor == icxSigJabData)
			intent = icRelativeColorimetric;	/* Default to icxAppearance */
		else
			intent = icAbsoluteColorimetric;	/* Default to icAbsoluteColorimetric */
	}

	/* - - - - - - - - - - - - - - - - */
	/* If we were provided an ICC profile to use */
	if (prof_name[0] != '\000') {

		/* Open up the profile or TIFF embedded profile for reading */
		if ((icco = read_embedded_icc(prof_name)) == NULL)
			error ("Can't open profile in file '%s'",prof_name);
	
		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);
		}
	
		/* Check that the profile is appropriate */
		if (icco->header->deviceClass != icSigInputClass
		 && icco->header->deviceClass != icSigDisplayClass
		 && icco->header->deviceClass != icSigOutputClass
		 && icco->header->deviceClass != icSigColorSpaceClass)
			error("Profile type isn't device or colorspace");
	
		/* Wrap with an expanded icc */
		if ((xicco = new_xicc(icco)) == NULL)
			error ("Creation of xicc failed");
	
		/* Setup the default viewing conditions */
		if (xicc_enum_viewcond(xicco, &vc, -1, NULL, 0, NULL) == -999)
			error ("%d, %s",xicco->errc, xicco->err);
	
		if (vc_e != -1)
			if (xicc_enum_viewcond(xicco, &vc, vc_e, NULL, 0, NULL) == -999)
				error ("%d, %s",xicco->errc, xicco->err);
		if (vc_s >= 0)
			vc.Ev = vc_s;
		if (vc_wXYZ[1] > 0.0) {
			/* Normalise it to current media white */
			vc.Wxyz[0] = vc_wXYZ[0]/vc_wXYZ[1] * vc.Wxyz[1];
			vc.Wxyz[2] = vc_wXYZ[2]/vc_wXYZ[1] * vc.Wxyz[1];
		} 
		if (vc_wxy[0] >= 0.0) {
			double x = vc_wxy[0];
			double y = vc_wxy[1];	/* If Y == 1.0, then X+Y+Z = 1/y */
			double z = 1.0 - x - y;
			vc.Wxyz[0] = x/y * vc.Wxyz[1];
			vc.Wxyz[2] = z/y * vc.Wxyz[1];
		}
		if (vc_a >= 0.0)
			vc.La = vc_a;
		if (vc_b >= 0.0)
			vc.Yb = vc_b/100.0;
		if (vc_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];
		}
	
		/* Get a expanded color conversion object */
		if ((luo = xicco->get_luobj(xicco, ICX_CLIP_NEAREST
		           , func, intent, pcsor, order, &vc, NULL)) == NULL)
			error ("%d, %s",xicco->errc, xicco->err);
	
		luo->spaces(luo, &ins, &inn, &outs, &outn, &alg, NULL, NULL, NULL);
	
	}

	/* Establish the PCS range if we are filtering */
	{
		double pcsmin[3], pcsmax[3];		/* PCS range for filter stats array */
	
		if (luo) {
			gamut *csgam;

			if ((csgam = luo->get_gamut(luo, 20.0)) == NULL)
				error("Getting the gamut of the source colorspace failed");
			
			csgam->getrange(csgam, pcsmin, pcsmax);
			csgam->del(csgam);
		} else {
			pcsmin[0] = 0.0;
			pcsmax[0] = 100.0;
			pcsmin[1] = -128.0;
			pcsmax[1] = 128.0;
			pcsmin[2] = -128.0;
			pcsmax[2] = 128.0;
		}

		if (verb)
			printf("PCS range = %f..%f, %f..%f. %f..%f\n\n", pcsmin[0], pcsmax[0], pcsmin[1], pcsmax[1], pcsmin[2], pcsmax[2]);

		/* Allocate and initialize the filter */
		set_fminmax(pcsmin, pcsmax);
	}

	/* - - - - - - - - - - - - - - - */
	/* 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_SAMPLESPERPIXEL, &samplesperpixel);
	TIFFGetField(rh, TIFFTAG_BITSPERSAMPLE, &bitspersample);
	if (bitspersample != 8 && bitspersample != 16)
		error("TIFF Input file must be 8 bit/channel");

	TIFFGetFieldDefaulted(rh, TIFFTAG_EXTRASAMPLES, &extrasamples, &extrainfo);
	TIFFGetField(rh, TIFFTAG_PHOTOMETRIC, &photometric);

	if (inn != (samplesperpixel-extrasamples))
		error ("TIFF Input file has %d input chanels mismatched to colorspace '%s'",
		       samplesperpixel, icm2str(icmColorSpaceSignature, ins));

	if ((tcs = TiffPhotometric2ColorSpaceSignature(&cvt, &sign_mask, photometric,
	                                     bitspersample, samplesperpixel, extrasamples)) == 0)
		error("Can't handle TIFF file photometric %s", Photometric2str(photometric));

	if (tcs != ins) {
		if (luo != NULL)
			error("TIFF photometric '%s' doesn't match ICC input colorspace '%s' !",
			      Photometric2str(photometric), icm2str(icmColorSpaceSignature,ins));
		else
			error("No profile provided and TIFF photometric '%s' isn't Lab !",
			      Photometric2str(photometric));
	}

	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 (verb) {
		printf("Input TIFF file '%s'\n",in_name);
		printf("TIFF file colorspace is %s\n",icm2str(icmColorSpaceSignature,tcs));
		printf("TIFF file photometric is %s\n",Photometric2str(photometric));
		printf("\n");
	}

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

	nipoints = width * height;

//	if ((inp = malloc(sizeof(co) * nipoints)) == NULL)
//		error("Unable to allocate co array");

	inbuf  = _TIFFmalloc(TIFFScanlineSize(rh));

	for (i = 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 e;
			double in[MAX_CHAN], out[MAX_CHAN];
			
			if (bitspersample == 8) {
				for (e = 0; e < samplesperpixel; e++) {
					int v = ((unsigned char *)inbuf)[x * samplesperpixel + e];
					if (sign_mask & (1 << i))		/* Treat input as signed */
						v = (v & 0x80) ? v - 0x80 : v + 0x80;
					in[e] = v/255.0;
				}
			} else {
				for (e = 0; e < samplesperpixel; e++) {
					int v = ((unsigned short *)inbuf)[x * samplesperpixel + e];
					if (sign_mask & (1 << i))		/* Treat input as signed */
						v = (v & 0x8000) ? v - 0x8000 : v + 0x8000;
					in[e] = v/65535.0;
				}
			}
			if (cvt != NULL) {	/* Undo TIFF encoding */
				cvt(in, in);
			}
			if (luo != NULL) {
				if ((rv = luo->lookup(luo, out, in)) > 1)
					error ("%d, %s",icco->errc,icco->err);
				
				if (outs == icSigXYZData)	/* Convert to Lab */
					icmXYZ2Lab(&icco->header->illuminant, out, out);
			} else {
				for (e = 0; e < samplesperpixel; e++)
					out[e] = in[e];
			}

//printf("~1 %f %f %f -> %f %f %f\n", in[0], in[1], in[2], out[0], out[1], out[2]);

			add_fpixel(out);

#ifdef NEVER
	 		/* Store PCS value in array */
			inp[i].v[0] = out[0];
			inp[i].v[1] = out[1];
			inp[i].v[2] = out[2];
			i++;
#endif
		}
	}

	_TIFFfree(inbuf);

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

	/* Done with lookup object */
	if (luo != NULL) {
		luo->del(luo);
		xicco->del(xicco);		/* Expansion wrapper */
		icco->del(icco);		/* Icc */
	}


	nipoints = flush_filter(verb, 80.0);

	if ((inp = malloc(sizeof(co) * nipoints)) == NULL)
		error("Unable to allocate co array");

	get_filter(inp);

printf("~1 There are %d points\n",nipoints);
//for (i = 0; i < nipoints; i++)
//printf("~1 point %d = %f %f %f\n", i, inp[i].v[0], inp[i].v[1], inp[i].v[2]);

	del_filter();

	/* Create the locus */
	{
		double s0[3], s1[3];
		double t0[3], t1[3];
		double mm[3][4];
		double im[3][4];
		int gres[MXDI] = { 256 } ;

		if (usevec) {
			double max = -1e6; 
			double min = 1e6; 
			double dist;
					
			icmScale3(vec, vec, 1.0/icmNorm3(vec));

			/* Locate the two furthest distant points measured along the vector */
			for (i = 0; i < nipoints; i++) {
				double tt;
				tt = icmDot3(vec, inp[i].v);
				if (tt > max) {
					max = tt;
					icmAry2Ary(s1, inp[i].v);
				}
				if (tt < min) {
					min = tt;
					icmAry2Ary(s0, inp[i].v);
				}
			}
			dist = icmNorm33sq(s0, s1);

printf("~1 most distant in vector %f %f %f = %f %f %f -> %f %f %f dist %f\n",
vec[0], vec[1], vec[2], s0[0], s0[1], s0[2], s1[0], s1[1], s1[2], sqrt(dist));

			t0[0] = 0.0;
			t0[1] = 0.0;
			t0[2] = 0.0;
			t1[0] = sqrt(dist);
			t1[1] = 0.0;
			t1[2] = 0.0;

		} else {
			double dist = 0.0;

			/* Locate the two furthest distant points (brute force) */
			for (i = 0; i < (nipoints-1); i++) {
				for (j = i+1; j < nipoints; j++) {
					double tt;
					if ((tt = icmNorm33sq(inp[i].v, inp[j].v)) > dist) {
						dist = tt;
						icmAry2Ary(s0, inp[i].v);
						icmAry2Ary(s1, inp[j].v);
					}
				}
			}
printf("~1 most distant = %f %f %f -> %f %f %f dist %f\n",
s0[0], s0[1], s0[2], s1[0], s1[1], s1[2], sqrt(dist));

			t0[0] = 0.0;
			t0[1] = 0.0;
			t0[2] = 0.0;
			t1[0] = sqrt(dist);
			t1[1] = 0.0;
			t1[2] = 0.0;
		}

		/* Transform our direction vector to the L* axis, and create inverse too */
		icmVecRotMat(mm, s1, s0, t1, t0);
		icmVecRotMat(im, t1, t0, s1, s0);

		/* Setup for rspl to create smoothed locus */
		for (i = 0; i < nipoints; i++) {
			icmMul3By3x4(inp[i].v, mm, inp[i].v);
			inp[i].p[0] = inp[i].v[0];
			inp[i].v[0] = inp[i].v[1];
			inp[i].v[1] = inp[i].v[2];
//printf("~1 point %d = %f -> %f %f\n", i, inp[i].p[0], inp[i].v[0], inp[i].v[1]);
		}

		/* Create rspl */
		if ((rr = new_rspl(RSPL_NOFLAGS, 1, 2)) == NULL)
			error("Creating rspl failed");

		rr->fit_rspl(rr, RSPL_NOFLAGS,inp, nipoints, NULL, NULL, gres, NULL, NULL, 5.0, NULL, NULL);       
#ifdef DEBUG_PLOT
		{
#define	XRES 100
			double xx[XRES];
			double y1[XRES];
			double y2[XRES];

			for (i = 0; i < XRES; i++) {
				co pp;
				double x;
				x = i/(double)(XRES-1);
				xx[i] = x * (t1[0] - t0[0]);
				pp.p[0] = xx[i];
				rr->interp(rr, &pp);
				y1[i] = pp.v[0];
				y2[i] = pp.v[1];
			}
			do_plot(xx,y1,y2,NULL,XRES);
		}
#endif /* DEBUG_PLOT */

		free(inp);

		nopoints = t1[0] / DE_SPACE;
		if (nopoints < 2)
			nopoints = 2;

		/* Create the output points */
		if ((outp = malloc(sizeof(co) * nopoints)) == NULL)
			error("Unable to allocate co array");

		/* Setup initial division of locus */
		for (i = 0; i < nopoints; i++) {
			double xx;

			xx = i/(double)(nopoints-1);
			xx *= (t1[0] - t0[0]);
			
			outp[i].p[0] = xx;
//printf("~1 div %d = %f\n",i,outp[i].p[0]);
		}
		for (i = 0; i < (nopoints-1); i++) {
			outp[i].p[1] = outp[i+1].p[0] - outp[i].p[0];
//printf("~1 del div  %d = %f\n",i,outp[i].p[1]);
		}

		/* Itterate until the delta between samples is even */
		for (j = 0; j < 10; j++) {
			double alen, minl, maxl;
			double tdiv;
			
			alen = 0.0;
			minl = 1e38;
			maxl = -1.0;
			for (i = 0; i < nopoints; i++) {
				rr->interp(rr, &outp[i]);
				outp[i].v[2] = outp[i].v[1];
				outp[i].v[1] = outp[i].v[0];
				outp[i].v[0] = outp[i].p[0];
				icmMul3By3x4(outp[i].v, im, outp[i].v);

//printf("~1 locus pnt %d = %f %f %f\n", i,outp[i].v[0],outp[i].v[1],outp[i].v[1]);

				if (i > 0) {
					double tt[3], len;
					icmSub3(tt, outp[i].v, outp[i-1].v);
					len = icmNorm3(tt);
					outp[i-1].p[2] = len;
					if (len > maxl)
						maxl = len;
					if (len < minl)
						minl = len;
					alen += len;
				}
			}
			alen /= (nopoints-1.0);
printf("~1 itter %d, alen = %f, minl = %f, maxl = %f\n",j,alen,minl,maxl);

			/* Adjust spacing */
			tdiv = 0.0;
			for (i = 0; i < (nopoints-1); i++) {
				outp[i].p[1] *= pow(alen/outp[i].p[2], 1.0);
				tdiv += outp[i].p[1];
			}
//printf("~1 tdiv = %f\n",tdiv);
			for (i = 0; i < (nopoints-1); i++) {
				outp[i].p[1] *= (t1[0] - t0[0])/tdiv;
//printf("~1 del div %d = %f\n",i,outp[i].p[1]);
			}
			tdiv = 0.0;
			for (i = 0; i < (nopoints-1); i++) {
				tdiv += outp[i].p[1];
			}
//printf("~1 tdiv now = %f\n",tdiv);
			for (i = 1; i < nopoints; i++) {
				outp[i].p[0] = outp[i-1].p[0] + outp[i-1].p[1];
//printf("~1 div %d = %f\n",i,outp[i].p[0]);
			}
		}

		/* Write the CGATS file */
		{
			time_t clk = time(0);
			struct tm *tsp = localtime(&clk);
			char *atm = asctime(tsp); /* Ascii time */
			cgats *pp;

			pp = new_cgats();	/* Create a CGATS structure */
			pp->add_other(pp, "TS"); 	/* Test Set */

			pp->add_table(pp, tt_other, 0);	/* Add the first table for target points */
			pp->add_kword(pp, 0, "DESCRIPTOR", "Argyll Test Point set",NULL);
			pp->add_kword(pp, 0, "ORIGINATOR", "Argyll tiffgmts", NULL);
			atm[strlen(atm)-1] = '\000';	/* Remove \n from end */
			pp->add_kword(pp, 0, "CREATED",atm, NULL);

			pp->add_field(pp, 0, "SAMPLE_ID", cs_t);
			pp->add_field(pp, 0, "LAB_L", r_t);
			pp->add_field(pp, 0, "LAB_A", r_t);
			pp->add_field(pp, 0, "LAB_B", r_t);

			for (i = 0; i < nopoints; i++) {
				char buf[100];
				cgats_set_elem ary[1 + 3];

				sprintf(buf,"%d",i+1);
				ary[0].c = buf;
				ary[1 + 0].d = outp[i].v[0];
				ary[1 + 1].d = outp[i].v[1];
				ary[1 + 2].d = outp[i].v[2];
		
				pp->add_setarr(pp, 0, ary);
			}

			if (pp->write_name(pp, out_name))
			    error("Write error : %s",pp->err);
		}

		/* Create the VRML file */
		if (dovrml) {
			vrml *vv;
			
			strcpy(xl,".wrl");
			printf("Output vrml file '%s'\n",out_name);
			if ((vv = new_vrml(out_name, doaxes)) == NULL)
				error ("Creating VRML object failed");

#ifdef NEVER
			vv->start_line_set(vv);
			for (i = 0; i < nopoints; i++) {
				vv->add_vertex(vv, outp[i].v);
			}
			vv->make_lines(vv, nopoints);
#else
			for (i = 1; i < nopoints; i++) {
				vv->add_cone(vv, outp[i-1].v, outp[i].v, NULL, 0.5);
			}
#endif

			vv->del(vv);
		}
		free(outp);
	}

	rr->del(rr);

	return 0;
}
Ejemplo n.º 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;
}