1 
2 /*
3  * Color Correct a TIFF file, using an ICC Device link profile.
4  *
5  * Author:  Graeme W. Gill
6  * Date:    00/3/8
7  * Version: 1.30
8  *
9  * Copyright 2000 - 2004 Graeme W. Gill
10  * All rights reserved.
11  *
12  * This material is licenced under the GNU GENERAL PUBLIC LICENCE :-
13  * see the Licence.txt file for licencing details.
14  */
15 
16 /*
17  * Thanks to Neil Okamoto for the 16 bit TIFF mods.
18  */
19 
20 /* TTBD:
21  */
22 
23 /*
24 	This program is a framework that exercises the
25 	IMDI code, as well as a demonstration of simple
26     profile linking.  It can also do the conversion using the
27     floating point code in ICCLIB as a reference.
28 
29  */
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <math.h>
37 #include "copyright.h"
38 #include "config.h"
39 #include "tiffio.h"
40 #include "icc.h"
41 #include "imdi.h"
42 
43 #undef TREAT_CMY_AS_RGB
44 
45 void error(char *fmt, ...), warning(char *fmt, ...);
46 
usage(void)47 void usage(void) {
48 	fprintf(stderr,"Color Correct a TIFF file using an ICC device link profile, V%s\n",ARGYLL_VERSION_STR);
49 	fprintf(stderr,"Author: Graeme W. Gill, licensed under the GPL\n");
50 	fprintf(stderr,"usage: cctiff [-options] devlinkprofile.icm infile.tif outfile.tif\n");
51 	fprintf(stderr,"usage: cctiff [-options] -l inprofile.icm outprofile.icm infile.tif outfile.tif\n");
52 	fprintf(stderr," -v            Verbose\n");
53 	fprintf(stderr," -c            Combine linearisation curves into one transform\n");
54 	fprintf(stderr," -p            Use slow precise correction\n");
55 	fprintf(stderr," -k            Check fast result against precise, and report\n");
56 	fprintf(stderr," -l            Link input and output profiles\n");
57 	fprintf(stderr,"  -i in_intent  p = perceptual, r = relative colorimetric,\n");
58 	fprintf(stderr,"                s = saturation, a = absolute colorimetric\n");
59 	fprintf(stderr,"  -o out_intent p = perceptual, r = relative colorimetric,\n");
60 	fprintf(stderr,"                s = saturation, a = absolute colorimetric\n");
61 	exit(1);
62 }
63 
64 /* Convert an ICC colorspace to the corresponding possible TIFF Photometric tags. */
65 /* Return the number of matching tags, and 0 if there is no corresponding tag. */
66 int
ColorSpaceSignature2TiffPhotometric(uint16 tags[10],icColorSpaceSignature cspace)67 ColorSpaceSignature2TiffPhotometric(
68 uint16 tags[10],					/* Pointer to return array, up to 10 */
69 icColorSpaceSignature cspace	/* Input ICC colorspace */
70 ) {
71 	switch(cspace) {
72 		case icSigGrayData:
73 			tags[0] = PHOTOMETRIC_MINISBLACK;
74 			return 1;
75 		case icSigRgbData:
76 #ifdef TREAT_CMY_AS_RGB
77 		case icSigCmyData:
78 #endif
79 			tags[0] = PHOTOMETRIC_RGB;
80 			return 1;
81 #ifndef TREAT_CMY_AS_RGB
82 		case icSigCmyData:
83 #endif
84 		case icSigCmykData:
85 			tags[0] = PHOTOMETRIC_SEPARATED;
86 			return 1;
87 		case icSigYCbCrData:
88 			tags[0] = PHOTOMETRIC_YCBCR;
89 			return 1;
90 		case icSigLabData:
91 			tags[0] = PHOTOMETRIC_CIELAB;
92 #ifdef PHOTOMETRIC_ICCLAB
93 			tags[1] = PHOTOMETRIC_ICCLAB;
94 			tags[2] = PHOTOMETRIC_ITULAB;
95 #endif
96 			return 3;
97 
98 		case icSigXYZData:
99 		case icSigLuvData:
100 		case icSigYxyData:
101 		case icSigHsvData:
102 		case icSigHlsData:
103 			return 0;
104 
105 		case icSig2colorData:
106 			tags[0] = PHOTOMETRIC_SEPARATED;
107 			tags[1] = 2;			/* Cheat */
108 			return 1;
109 
110 		case icSig3colorData:
111 			tags[0] = PHOTOMETRIC_SEPARATED;
112 			tags[1] = 3;			/* Cheat */
113 			return 1;
114 
115 		case icSig4colorData:
116 			tags[0] = PHOTOMETRIC_SEPARATED;
117 			tags[1] = 4;			/* Cheat */
118 			return 1;
119 
120 		case icSig5colorData:
121 		case icSigMch5Data:
122 			tags[0] = PHOTOMETRIC_SEPARATED;
123 			tags[1] = 5;			/* Cheat */
124 			return 1;
125 
126 		case icSig6colorData:
127 		case icSigMch6Data:
128 			tags[0] = PHOTOMETRIC_SEPARATED;
129 			tags[1] = 6;			/* Cheat */
130 			return 1;
131 
132 		case icSig7colorData:
133 		case icSigMch7Data:
134 			tags[0] = PHOTOMETRIC_SEPARATED;
135 			tags[1] = 7;			/* Cheat */
136 			return 1;
137 
138 		case icSig8colorData:
139 		case icSigMch8Data:
140 			tags[0] = PHOTOMETRIC_SEPARATED;
141 			tags[1] = 8;			/* Cheat */
142 			return 1;
143 
144 		case icSig9colorData:
145 			tags[0] = PHOTOMETRIC_SEPARATED;
146 			tags[1] = 9;			/* Cheat */
147 			return 1;
148 
149 		case icSig10colorData:
150 			tags[0] = PHOTOMETRIC_SEPARATED;
151 			tags[1] = 10;			/* Cheat */
152 			return 1;
153 
154 		case icSig11colorData:
155 			tags[0] = PHOTOMETRIC_SEPARATED;
156 			tags[1] = 11;			/* Cheat */
157 			return 1;
158 
159 		case icSig12colorData:
160 			tags[0] = PHOTOMETRIC_SEPARATED;
161 			tags[1] = 12;			/* Cheat */
162 			return 1;
163 
164 		case icSig13colorData:
165 			tags[0] = PHOTOMETRIC_SEPARATED;
166 			tags[1] = 13;			/* Cheat */
167 			return 1;
168 
169 		case icSig14colorData:
170 			tags[0] = PHOTOMETRIC_SEPARATED;
171 			tags[1] = 14;			/* Cheat */
172 			return 1;
173 
174 		case icSig15colorData:
175 			tags[0] = PHOTOMETRIC_SEPARATED;
176 			tags[1] = 15;			/* Cheat */
177 			return 1;
178 
179 		default:
180 			return 0;
181 	}
182 	return 0;
183 }
184 
185 
186 /* Compute the length of a double nul terminated string, including */
187 /* the nuls. */
zzstrlen(char * s)188 static int zzstrlen(char *s) {
189 	int i;
190 	for (i = 0;; i++) {
191 		if (s[i] == '\000' && s[i+1] == '\000')
192 			return i+2;
193 	}
194 	return 0;
195 }
196 
197 /* Convert an ICC colorspace to the corresponding TIFF Inkset tag */
198 /* return 0xffff if not possible or applicable. */
199 
200 int
ColorSpaceSignature2TiffInkset(icColorSpaceSignature cspace,int * len,char ** inknames)201 ColorSpaceSignature2TiffInkset(
202 icColorSpaceSignature cspace,
203 int *len,						/* Return length of ASCII inknames */
204 char **inknames					/* Return ASCII inknames if non NULL */
205 ) {
206 	switch(cspace) {
207 		case icSigCmyData:
208 			return 0xffff;	/* ~~9999 */
209 			if (inknames != NULL) {
210 				*inknames = "cyan\000magenta\000yellow\000\000";
211 				*len = zzstrlen(*inknames);
212 			}
213 			return 0;				/* Not CMYK */
214 		case icSigCmykData:
215 			if (inknames != NULL) {
216 				*inknames = NULL;	/* No inknames */
217 				*len = 0;
218 			}
219 			return INKSET_CMYK;
220 
221 		case icSigGrayData:
222 		case icSigRgbData:
223 		case icSigYCbCrData:
224 		case icSigLabData:
225 		case icSigXYZData:
226 		case icSigLuvData:
227 		case icSigYxyData:
228 		case icSigHsvData:
229 		case icSigHlsData:
230 		case icSig2colorData:
231 		case icSig3colorData:
232 		case icSig4colorData:
233 		case icSig5colorData:
234 		case icSigMch5Data:
235 			return 0xffff;
236 
237 		case icSig6colorData:
238 		case icSigMch6Data:
239 				/* This is a cheat and a hack. Should really make sure that icclink */
240 				/* transfers the right information from the destination */
241 				/* profile, and then copies it to the device profile, */
242 				/* allowing cctiff to read it. */
243 			if (inknames != NULL) {
244 				*inknames = "cyan\000magenta\000yellow\000black\000orange\000green\000\000";
245 				*len = zzstrlen(*inknames);
246 			}
247 			return 0;				/* Not CMYK */
248 
249 		case icSig7colorData:
250 		case icSigMch7Data:
251 			return 0xffff;
252 
253 		case icSig8colorData:
254 		case icSigMch8Data:
255 				/* This is a cheat and a hack. Should really make sure that icclink */
256 				/* transfers the right information from the destination */
257 				/* profile, and then copies it to the device profile, */
258 				/* allowing cctiff to read it. */
259 			if (inknames != NULL) {
260 				*inknames = "cyan\000magenta\000yellow\000black\000orange\000green\000lightcyan\000lightmagenta\000\000";
261 				*len = zzstrlen(*inknames);
262 			}
263 			return 0;				/* Not CMYK */
264 		case icSig9colorData:
265 		case icSig10colorData:
266 		case icSig11colorData:
267 		case icSig12colorData:
268 		case icSig13colorData:
269 		case icSig14colorData:
270 		case icSig15colorData:
271 		default:
272 			return 0xffff;
273 	}
274 	return 0xffff;
275 }
276 
277 char *
Photometric2str(int pmtc)278 Photometric2str(
279 int pmtc
280 ) {
281 	static char buf[80];
282 	switch (pmtc) {
283 		case PHOTOMETRIC_MINISWHITE:
284 			return "Subtractive Gray";
285 		case PHOTOMETRIC_MINISBLACK:
286 			return "Additive Gray";
287 		case PHOTOMETRIC_RGB:
288 			return "RGB";
289 		case PHOTOMETRIC_PALETTE:
290 			return "Indexed";
291 		case PHOTOMETRIC_MASK:
292 			return "Transparency Mask";
293 		case PHOTOMETRIC_SEPARATED:
294 			return "Separated";
295 		case PHOTOMETRIC_YCBCR:
296 			return "YCbCr";
297 		case PHOTOMETRIC_CIELAB:
298 			return "CIELab";
299 #ifdef PHOTOMETRIC_ICCLAB
300 		case PHOTOMETRIC_ICCLAB:
301 			return "ICCLab";
302 		case PHOTOMETRIC_ITULAB:
303 			return "ITULab";
304 #endif
305 		case PHOTOMETRIC_LOGL:
306 			return "CIELog2L";
307 		case PHOTOMETRIC_LOGLUV:
308 			return "CIELog2Luv";
309 	}
310 	sprintf(buf,"Unknonw Tag %d",pmtc);
311 	return buf;
312 }
313 
314 /* Callbacks used to initialise imdi */
315 
316 /* Information needed from a profile */
317 struct _profinfo {
318 	char name[100];
319 	icmFile *fp;
320 	icc *c;
321 	icmHeader *h;
322 	icRenderingIntent intent;
323 	icmLuBase *luo;				/* Base Lookup type object */
324 	icmLuAlgType alg;			/* Type of lookup algorithm */
325 	int chan;					/* Device channels */
326 }; typedef struct _profinfo profinfo;
327 
328 /* Context for imdi setup callbacks */
329 typedef struct {
330 	/* Overall parameters */
331 	int verb;			/* Non-zero if verbose */
332 	icColorSpaceSignature ins, outs;	/* Input/Output spaces */
333 	int id, od;			/* Input/Output dimensions */
334 	int icombine;		/* Non-zero if input curves are to be combined */
335 	int ocombine;		/* Non-zero if output curves are to be combined */
336 	int link;			/* Non-zero if input and output profiles are to be linked */
337 
338 	profinfo dev;		/* Device link profile */
339 	profinfo in;		/* Device to PCS profile */
340 	profinfo out;		/* PCS to Device profile */
341 } sucntx;
342 
343 /* Input curve function */
input_curve(void * cntx,int ch,double in_val)344 double input_curve(
345 	void *cntx,
346 	int ch,
347 	double in_val
348 ) {
349 	sucntx *rx = (sucntx *)cntx;
350 	int i;
351 	double vals[MAX_CHAN];
352 
353 	if (rx->icombine)
354 		return in_val;
355 
356 	if (rx->link) {
357 
358 		for (i = 0; i < rx->id; i++)
359 			vals[i] = 0.0;
360 		vals[ch] = in_val;
361 
362 		switch(rx->in.alg) {
363 		    case icmMonoFwdType: {
364 				icmLuMono *lu = (icmLuMono *)rx->in.luo;	/* Safe to coerce */
365 				lu->fwd_curve(lu, vals, vals);
366 				break;
367 			}
368 		    case icmMatrixFwdType: {
369 				icmLuMatrix *lu = (icmLuMatrix *)rx->in.luo;	/* Safe to coerce */
370 				lu->fwd_curve(lu, vals, vals);
371 				break;
372 			}
373 		    case icmLutType: {
374 				icmLuLut *lu = (icmLuLut *)rx->in.luo;	/* Safe to coerce */
375 				/* Since not PCS, in_abs and matrix cannot be valid, */
376 				/* so input curve on own is ok to use. */
377 				lu->input(lu, vals, vals);
378 				break;
379 			}
380 			default:
381 				error("Unexpected algorithm type in input curve");
382 		}
383 	} else {
384 		icmLuLut *lu = (icmLuLut *)rx->dev.luo;		/* Safe to coerce */
385 
386 		for (i = 0; i < rx->id; i++)
387 			vals[i] = 0.0;
388 		vals[ch] = in_val;
389 
390 		/* Since input not PCS, in_abs and matrix cannot be valid, */
391 		/* so input curve on own is ok to use. */
392 		lu->input(lu, vals, vals);
393 
394 	}
395 	return vals[ch];
396 }
397 
398 /* Multi-dim table function */
md_table(void * cntx,double * out_vals,double * in_vals)399 void md_table(
400 void *cntx,
401 double *out_vals,
402 double *in_vals
403 ) {
404 	sucntx *rx = (sucntx *)cntx;
405 	double pcsv[3];
406 	int i;
407 
408 	if (rx->link) {
409 		double vals[MAX_CHAN];
410 
411 		switch(rx->in.alg) {
412 		    case icmMonoFwdType: {
413 				icmLuMono *lu = (icmLuMono *)rx->in.luo;	/* Safe to coerce */
414 				if (rx->icombine) {
415 					lu->fwd_curve(lu, vals, in_vals);
416 					lu->fwd_map(lu, pcsv, vals);
417 				} else {
418 					lu->fwd_map(lu, pcsv, in_vals);
419 				}
420 				lu->fwd_abs(lu, pcsv, pcsv);
421 				break;
422 			}
423 		    case icmMatrixFwdType: {
424 				icmLuMatrix *lu = (icmLuMatrix *)rx->in.luo;	/* Safe to coerce */
425 				if (rx->icombine) {
426 					lu->fwd_curve(lu, vals, in_vals);
427 					lu->fwd_matrix(lu, pcsv, vals);
428 				} else {
429 					lu->fwd_matrix(lu, pcsv, in_vals);
430 				}
431 				lu->fwd_abs(lu, pcsv, pcsv);
432 				break;
433 			}
434 		    case icmLutType: {
435 				icmLuLut *lu = (icmLuLut *)rx->in.luo;	/* Safe to coerce */
436 				if (rx->icombine) {
437 					/* Since not PCS, in_abs and matrix cannot be valid, */
438 					/* so input curve on own is ok to use. */
439 					lu->input(lu, vals, in_vals);
440 					lu->clut(lu, pcsv, vals);
441 				} else {
442 					lu->clut(lu, pcsv, in_vals);
443 				}
444 				lu->output(lu, pcsv, pcsv);
445 				lu->out_abs(lu, pcsv, pcsv);
446 				break;
447 			}
448 			default:
449 				error("Unexpected algorithm type in clut lookup");
450 		}
451 
452 		switch(rx->out.alg) {
453 		    case icmMonoBwdType: {
454 				icmLuMono *lu = (icmLuMono *)rx->out.luo;	/* Safe to coerce */
455 				lu->bwd_abs(lu, pcsv, pcsv);
456 				lu->bwd_map(lu, out_vals, pcsv);
457 				if (rx->ocombine) {
458 					lu->bwd_curve(lu, out_vals, out_vals);
459 				}
460 				break;
461 			}
462 		    case icmMatrixBwdType: {
463 				icmLuMatrix *lu = (icmLuMatrix *)rx->out.luo;	/* Safe to coerce */
464 				lu->bwd_abs(lu, pcsv, pcsv);
465 				lu->bwd_matrix(lu, out_vals, pcsv);
466 				if (rx->ocombine) {
467 					lu->bwd_curve(lu, out_vals, out_vals);
468 				}
469 				break;
470 			}
471 		    case icmLutType: {
472 				icmLuLut *lu = (icmLuLut *)rx->out.luo;	/* Safe to coerce */
473 				lu->in_abs(lu, pcsv, pcsv);
474 				lu->matrix(lu, pcsv, pcsv);
475 				lu->input(lu, pcsv, pcsv);
476 				lu->clut(lu, out_vals, pcsv);
477 				if (rx->ocombine) {
478 					lu->output(lu, out_vals, out_vals);
479 					/* Since not PCS, out_abs is never used */
480 				}
481 				break;
482 			}
483 
484 			default:
485 				error("Unexpected algorithm type in clut lookup");
486 		}
487 	} else {
488 		icmLuLut *lu = (icmLuLut *)rx->dev.luo;		/* Safe to coerce */
489 
490 		if (rx->icombine && rx->ocombine) {
491 			lu->lookup((icmLuBase *)lu, out_vals, in_vals);		/* Do everything here */
492 		} else {
493 			lu->clut(lu, out_vals, in_vals);
494 		}
495 	}
496 }
497 
498 /* Output curve function */
output_curve(void * cntx,int ch,double in_val)499 double output_curve(
500 void *cntx,
501 int ch,
502 double in_val
503 ) {
504 	sucntx *rx = (sucntx *)cntx;
505 	int i;
506 	double vals[MAX_CHAN];
507 
508 	if (rx->ocombine)
509 		return in_val;
510 
511 	if (rx->link) {
512 		for (i = 0; i < rx->od; i++)
513 			vals[i] = 0.0;
514 		vals[ch] = in_val;
515 
516 		switch(rx->out.alg) {
517 		    case icmMonoBwdType: {
518 				icmLuMono *lu = (icmLuMono *)rx->out.luo;	/* Safe to coerce */
519 				lu->bwd_curve(lu, vals, vals);
520 				break;
521 			}
522 		    case icmMatrixBwdType: {
523 				icmLuMatrix *lu = (icmLuMatrix *)rx->out.luo;	/* Safe to coerce */
524 				lu->bwd_curve(lu, vals, vals);
525 				break;
526 			}
527 		    case icmLutType: {
528 				icmLuLut *lu = (icmLuLut *)rx->out.luo;	/* Safe to coerce */
529 				lu->output(lu, vals, vals);
530 				/* Since not PCS, out_abs is never used */
531 				break;
532 			}
533 			default:
534 				error("Unexpected algorithm type in devop_devo()");
535 		}
536 
537 	} else {
538 		icmLuLut *lu = (icmLuLut *)rx->dev.luo;		/* Safe to coerce */
539 
540 		for (i = 0; i < rx->od; i++)
541 			vals[i] = 0.0;
542 		vals[ch] = in_val;
543 
544 		/* Since output not PCS, out_abs cannot be valid, */
545 		lu->output(lu, vals, vals);
546 
547 	}
548 	return vals[ch];
549 }
550 
551 
552 int
main(int argc,char * argv[])553 main(int argc, char *argv[]) {
554 	int fa,nfa;				/* argument we're looking at */
555 	char in_name[100];		/* Raster file name */
556 	char out_name[100];		/* Raster file name */
557 	int slow = 0;
558 	int check = 0;
559 	int i, rv = 0;
560 
561 	TIFF *rh = NULL, *wh = NULL;
562 	int x, y, width, height;					/* Size of image */
563 	uint16 samplesperpixel, bitspersample;
564 	int no_pmtc;								/* Number of input photometrics */
565 	uint16 photometric, pmtc[10];				/* Photometrics of input file, and input profile */
566 	uint16 pconfig;								/* Planar configuration */
567 	uint16 resunits;
568 	float resx, resy;
569 	tdata_t *inbuf, *outbuf, *checkbuf;
570 
571 	/* IMDI */
572 	imdi *s = NULL;
573 	sucntx su;		/* Setup context */
574 	unsigned char *inp[MAX_CHAN];
575 	unsigned char *outp[MAX_CHAN];
576 	int clutres = 33;
577 
578 	/* Error check */
579 	int mxerr = 0;
580 	double avgerr = 0.0;
581 	double avgcount = 0.0;
582 
583 	if (argc < 2)
584 		usage();
585 
586 	su.verb = 0;
587 	su.icombine = 0;
588 	su.ocombine = 0;
589 	su.link = 0;
590 	su.in.intent = icmDefaultIntent;
591 	su.out.intent = icmDefaultIntent;
592 
593 	/* Process the arguments */
594 	for(fa = 1;fa < argc;fa++) {
595 		nfa = fa;					/* skip to nfa if next argument is used */
596 		if (argv[fa][0] == '-')	{	/* Look for any flags */
597 			char *na = NULL;		/* next argument after flag, null if none */
598 
599 			if (argv[fa][2] != '\000')
600 				na = &argv[fa][2];		/* next is directly after flag */
601 			else {
602 				if ((fa+1) < argc) {
603 					if (argv[fa+1][0] != '-') {
604 						nfa = fa + 1;
605 						na = argv[nfa];		/* next is seperate non-flag argument */
606 					}
607 				}
608 			}
609 
610 			if (argv[fa][1] == '?')
611 				usage();
612 
613 			/* Slow, Precise */
614 			else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') {
615 				slow = 1;
616 			}
617 
618 			/* Combine per channel curves */
619 			else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') {
620 				su.icombine = 1;
621 				su.ocombine = 1;
622 			}
623 
624 			/* Check curves */
625 			else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') {
626 				check = 1;
627 			}
628 
629 			/* Link profiles */
630 			else if (argv[fa][1] == 'l' || argv[fa][1] == 'L') {
631 				su.link = 1;
632 			}
633 
634 			/* Input profile Intent */
635 			else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') {
636 				fa = nfa;
637 				if (na == NULL) usage();
638     			switch (na[0]) {
639 					case 'p':
640 					case 'P':
641 						su.in.intent = icPerceptual;
642 						break;
643 					case 'r':
644 					case 'R':
645 						su.in.intent = icRelativeColorimetric;
646 						break;
647 					case 's':
648 					case 'S':
649 						su.in.intent = icSaturation;
650 						break;
651 					case 'a':
652 					case 'A':
653 						su.in.intent = icAbsoluteColorimetric;
654 						break;
655 					default:
656 						usage();
657 				}
658 			}
659 
660 			/* Output profile Intent */
661 			else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') {
662 				fa = nfa;
663 				if (na == NULL) usage();
664     			switch (na[0]) {
665 					case 'p':
666 					case 'P':
667 						su.out.intent = icPerceptual;
668 						break;
669 					case 'r':
670 					case 'R':
671 						su.out.intent = icRelativeColorimetric;
672 						break;
673 					case 's':
674 					case 'S':
675 						su.out.intent = icSaturation;
676 						break;
677 					case 'a':
678 					case 'A':
679 						su.out.intent = icAbsoluteColorimetric;
680 						break;
681 					default:
682 						usage();
683 				}
684 			}
685 
686 			/* Verbosity */
687 			else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
688 				su.verb = 1;
689 			}
690 
691 			else
692 				usage();
693 		} else
694 			break;
695 	}
696 
697 	if (su.link) {
698 		if (fa >= argc || argv[fa][0] == '-') usage();
699 		strcpy(su.in.name,argv[fa++]);
700 
701 		if (fa >= argc || argv[fa][0] == '-') usage();
702 		strcpy(su.out.name,argv[fa++]);
703 	} else {
704 		if (fa >= argc || argv[fa][0] == '-') usage();
705 		strcpy(su.dev.name,argv[fa++]);
706 	}
707 
708 	if (fa >= argc || argv[fa][0] == '-') usage();
709 	strcpy(in_name,argv[fa++]);
710 
711 	if (fa >= argc || argv[fa][0] == '-') usage();
712 	strcpy(out_name,argv[fa++]);
713 
714 	/* - - - - - - - - - - - - - - - - */
715 
716 	if (su.link) {
717 		icColorSpaceSignature natpcs;
718 
719 		/* Open up the input device profile for reading */
720 		if ((su.in.fp = new_icmFileStd_name(su.in.name,"r")) == NULL)
721 			error ("Can't open file '%s'",su.in.name);
722 
723 		if ((su.in.c = new_icc()) == NULL)
724 			error ("Creation of Input profile ICC object failed");
725 
726 		/* Read header etc. */
727 		if ((rv = su.in.c->read(su.in.c,su.in.fp,0)) != 0)
728 			error ("%d, %s on file '%s'",rv,su.in.c->err,su.in.name);
729 		su.in.h = su.in.c->header;
730 
731 		/* Check that it is a suitable device input icc */
732 		if (su.in.h->deviceClass != icSigInputClass
733 		 && su.in.h->deviceClass != icSigDisplayClass
734 		 && su.in.h->deviceClass != icSigOutputClass
735 		 && su.in.h->deviceClass != icSigColorSpaceClass)	/* For sRGB etc. */
736 			error("Input profile isn't a device profile");
737 
738 		/* Get a conversion object */
739 		if ((su.in.luo = su.in.c->get_luobj(su.in.c, icmFwd, su.in.intent,
740 		                                             icSigLabData, icmLuOrdNorm)) == NULL)
741 			error ("%d, %s for profile '%s'",su.in.c->errc, su.in.c->err, su.in.name);
742 
743 		/* Get details of conversion (Arguments may be NULL if info not needed) */
744 		su.in.luo->spaces(su.in.luo, &su.ins, &su.id, NULL, NULL, &su.in.alg, NULL, NULL, NULL);
745 
746 		/* Get native PCS space */
747 		su.in.luo->lutspaces(su.in.luo, NULL, NULL, NULL, NULL, &natpcs);
748 
749 		if (natpcs == icSigXYZData) {
750 			su.icombine = 1;			/* XYZ is to non-linear to be a benefit */
751 		}
752 
753 		/* Open up the output device profile for reading */
754 		if ((su.out.fp = new_icmFileStd_name(su.out.name,"r")) == NULL)
755 			error ("Can't open file '%s'",su.out.name);
756 
757 		if ((su.out.c = new_icc()) == NULL)
758 			error ("Creation of Output profile ICC object failed");
759 
760 		/* Read header etc. */
761 		if ((rv = su.out.c->read(su.out.c,su.out.fp,0)) != 0)
762 			error ("%d, %s on file '%s'",rv,su.out.c->err,su.out.name);
763 		su.out.h = su.out.c->header;
764 
765 		/* Check that it is a suitable device output icc */
766 		if (su.out.h->deviceClass != icSigInputClass
767 		 && su.out.h->deviceClass != icSigDisplayClass
768 		 && su.out.h->deviceClass != icSigOutputClass
769 		 && su.out.h->deviceClass != icSigColorSpaceClass)	/* For sRGB etc. */
770 			error("Output profile isn't a device profile");
771 
772 		/* Get a conversion object */
773 		if ((su.out.luo = su.out.c->get_luobj(su.out.c, icmBwd, su.out.intent,
774 		                                             icSigLabData, icmLuOrdNorm)) == NULL)
775 			error ("%d, %s for profile '%s'",su.out.c->errc, su.out.c->err, su.out.name);
776 
777 		/* Get details of conversion (Arguments may be NULL if info not needed) */
778 		su.out.luo->spaces(su.out.luo, NULL, NULL, &su.outs, &su.od, &su.out.alg, NULL, NULL, NULL);
779 
780 		/* Get native PCS space */
781 		su.out.luo->lutspaces(su.out.luo, NULL, NULL, NULL, NULL, &natpcs);
782 
783 		if (natpcs == icSigXYZData) {
784 			su.ocombine = 1;			/* XYZ is to non-linear to be a benefit */
785 		}
786 
787 		/* See discussion in imdi/imdi_gen.c for ideal numbers */
788 		/* Use "high quality" resolution numbers */
789 		switch (su.id) {
790 			case 0:
791 				error ("Illegal number of input chanels");
792 			case 1:
793 	  		  	clutres = 256;
794 				break;
795 			case 2:
796 	  		  	clutres = 256;
797 				break;
798 			case 3:
799 	  		  	clutres = 33;
800 				break;
801 			case 4:
802   		  		clutres = 18;
803 				break;
804 			case 5:
805   		  		clutres = 16;
806 				break;
807 			case 6:
808   		  		clutres = 9;
809 				break;
810 			case 7:
811   		  		clutres = 7;
812 				break;
813 			case 8:
814   		  		clutres = 6;
815 				break;
816 			deault: /* > 8 chan */
817 				clutres = 3;
818 				break;
819 		}
820 
821 	} else {
822 		icmLut *lut;						/* ICC LUT table */
823 		icmLuLut *luluo;					/* LUT lookup object */
824 
825 		/* Open up the device link profile for reading */
826 		if ((su.dev.fp = new_icmFileStd_name(su.dev.name,"r")) == NULL)
827 			error ("Can't open file '%s'",su.dev.name);
828 
829 		if ((su.dev.c = new_icc()) == NULL)
830 			error ("Creation of ICC object failed");
831 
832 		if ((rv = su.dev.c->read(su.dev.c, su.dev.fp, 0)) != 0)
833 			error ("%d, %s",rv,su.dev.c->err);
834 		su.dev.h = su.dev.c->header;
835 
836 		if (su.verb) {
837 			icmFile *op;
838 			if ((op = new_icmFileStd_fp(stdout)) == NULL)
839 				error ("Can't open stdout");
840 			su.dev.h->dump(su.dev.h, op, 1);
841 			op->del(op);
842 		}
843 
844 		/* Check that the profile is appropriate */
845 		if (su.dev.h->deviceClass != icSigLinkClass)
846 			error("Profile isn't a device link profile");
847 
848 		/* Get a conversion object */
849 		if ((su.dev.luo = su.dev.c->get_luobj(su.dev.c, icmFwd, icmDefaultIntent,
850 		                                             icmSigDefaultData, icmLuOrdNorm)) == NULL)
851 			error ("%d, %s",su.dev.c->errc, su.dev.c->err);
852 
853 		/* Get details of conversion (Arguments may be NULL if info not needed) */
854 		su.dev.luo->spaces(su.dev.luo, &su.ins, &su.id, &su.outs, &su.od, &su.dev.alg, NULL, NULL, NULL);
855 
856 		if (su.dev.alg != icmLutType)
857 			error ("DeviceLink profile doesn't have Lut !");
858 
859 		luluo = (icmLuLut *)su.dev.luo;		/* Safe to coerce */
860 		luluo->get_info(luluo, &lut, NULL, NULL, NULL);	/* Get some details */
861 		clutres = lut->clutPoints;			/* Desired table resolution */
862 	}
863 
864 	/* - - - - - - - - - - - - - - - */
865 	/* Open up input tiff file ready for reading */
866 	/* Got arguments, so setup to process the file */
867 	if ((rh = TIFFOpen(in_name, "r")) == NULL)
868 		error("error opening read file '%s'",in_name);
869 
870 	TIFFGetField(rh, TIFFTAG_IMAGEWIDTH,  &width);
871 	TIFFGetField(rh, TIFFTAG_IMAGELENGTH, &height);
872 
873 	TIFFGetField(rh, TIFFTAG_BITSPERSAMPLE, &bitspersample);
874 	if (bitspersample != 8 && bitspersample != 16) {
875 		error("TIFF Input file must be 8 or 16 bit/channel");
876 	}
877 
878 	TIFFGetField(rh, TIFFTAG_PHOTOMETRIC, &photometric);
879 	if  ((no_pmtc = ColorSpaceSignature2TiffPhotometric(pmtc, su.ins)) == 0)
880 		error("ICC  input colorspace '%s' can't be handled by a TIFF file!",
881 		      icm2str(icmColorSpaceSignature, su.ins));
882 	for (i = 0; i < no_pmtc; i++) {
883 		if (pmtc[i] == photometric)
884 			break;				/* Matches */
885 	}
886 	if (i >= no_pmtc) {
887 		switch (no_pmtc) {
888 			case 1:
889 				error("ICC input colorspace '%s' doesn't match TIFF photometric '%s'!",
890 			      icm2str(icmColorSpaceSignature, su.ins), Photometric2str(pmtc[0]));
891 			case 2:
892 				error("ICC input colorspace '%s' doesn't match TIFF photometric '%s' or '%s'!",
893 			      icm2str(icmColorSpaceSignature, su.ins), Photometric2str(pmtc[0]),
894 			      Photometric2str(pmtc[1]));
895 			default:
896 				error("ICC input colorspace '%s' doesn't match TIFF photometric '%s', '%s' or '%s'!",
897 			      icm2str(icmColorSpaceSignature, su.ins), Photometric2str(pmtc[0]),
898 			      Photometric2str(pmtc[1]), Photometric2str(pmtc[2]));
899 		}
900 	}
901 
902 	TIFFGetField(rh, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);
903 	if (su.id != samplesperpixel)
904 		error ("TIFF Input file has %d input channels mismatched to colorspace '%s'",
905 		       samplesperpixel, icm2str(icmColorSpaceSignature, su.ins));
906 
907 	TIFFGetField(rh, TIFFTAG_PLANARCONFIG, &pconfig);
908 	if (pconfig != PLANARCONFIG_CONTIG)
909 		error ("TIFF Input file must be planar");
910 
911 	TIFFGetField(rh, TIFFTAG_RESOLUTIONUNIT, &resunits);
912 	TIFFGetField(rh, TIFFTAG_XRESOLUTION, &resx);
913 	TIFFGetField(rh, TIFFTAG_YRESOLUTION, &resy);
914 
915 	/* - - - - - - - - - - - - - - - */
916 	if ((wh = TIFFOpen(out_name, "w")) == NULL)
917 		error("Can\'t create TIFF file '%s'!",out_name);
918 
919 	TIFFSetField(wh, TIFFTAG_IMAGEWIDTH,  width);
920 	TIFFSetField(wh, TIFFTAG_IMAGELENGTH, height);
921 	TIFFSetField(wh, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
922 	TIFFSetField(wh, TIFFTAG_SAMPLESPERPIXEL, su.od);
923 	TIFFSetField(wh, TIFFTAG_BITSPERSAMPLE, bitspersample);
924 	TIFFSetField(wh, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
925 	if  ((no_pmtc = ColorSpaceSignature2TiffPhotometric(pmtc, su.outs)) == 0)
926 		error("TIFF file can't handle output colorspace '%s'!",
927 		      icm2str(icmColorSpaceSignature, su.outs));
928 	TIFFSetField(wh, TIFFTAG_PHOTOMETRIC, pmtc[0]);	/* Use first returned */
929 	if (pmtc[0] == PHOTOMETRIC_SEPARATED) {
930 		int iset;
931 		int inlen;
932 		char *inames;
933 		iset = ColorSpaceSignature2TiffInkset(su.outs, &inlen, &inames);
934 		if (iset != 0xffff && inlen > 0 && inames != NULL) {
935 			TIFFSetField(wh, TIFFTAG_INKSET, iset);
936 			if (inames != NULL) {
937 				TIFFSetField(wh, TIFFTAG_INKNAMES, inlen, inames);
938 			}
939 		}
940 	}
941 	TIFFSetField(wh, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
942 	if (resunits) {
943 		TIFFSetField(wh, TIFFTAG_RESOLUTIONUNIT, resunits);
944 		TIFFSetField(wh, TIFFTAG_XRESOLUTION, resx);
945 		TIFFSetField(wh, TIFFTAG_YRESOLUTION, resy);
946 	}
947 	TIFFSetField(wh, TIFFTAG_IMAGEDESCRIPTION, "Color corrected by Argyll");
948 
949 	/* - - - - - - - - - - - - - - - */
950 	/* Setup the imdi */
951 
952 	if (!slow) {
953 		s = new_imdi(
954 			su.id,			/* Number of input dimensions */
955 			su.od,			/* Number of output dimensions */
956 							/* Input pixel representation */
957 			bitspersample == 8 ? pixint8 : pixint16,
958 							/* Output pixel representation */
959 			0x0,			/* Treat every channel as unsigned */
960 			bitspersample == 8 ? pixint8 : pixint16,
961 			0x0,			/* Treat every channel as unsigned */
962 			clutres,		/* Desired table resolution */
963 			input_curve,	/* Callback functions */
964 			md_table,
965 			output_curve,
966 			(void *)&su		/* Context to callbacks */
967 		);
968 
969 		if (s == NULL)
970 			error("new_imdi failed");
971 	}
972 
973 	/* - - - - - - - - - - - - - - - */
974 	/* Process colors to translate */
975 	/* (Should fix this to process a group of lines at a time ?) */
976 
977 	inbuf  = _TIFFmalloc(TIFFScanlineSize(rh));
978 	outbuf = _TIFFmalloc(TIFFScanlineSize(wh));
979 	if (check)
980 		checkbuf = _TIFFmalloc(TIFFScanlineSize(wh));
981 
982 	inp[0] = (unsigned char *)inbuf;
983 	outp[0] = (unsigned char *)outbuf;
984 
985 	if (!slow) {		/* Fast */
986 		for (y = 0; y < height; y++) {
987 
988 			/* Read in the next line */
989 			if (TIFFReadScanline(rh, inbuf, y, 0) < 0)
990 				error ("Failed to read TIFF line %d",y);
991 
992 			/* Do fast conversion */
993 			s->interp(s, (void **)outp, (void **)inp, width);
994 
995 			if (check) {
996 				/* Do floating point conversion */
997 				for (x = 0; x < width; x++) {
998 					int i;
999 					double in[MAX_CHAN], out[MAX_CHAN];
1000 
1001 					if (bitspersample == 8)
1002 						for (i = 0; i < su.id; i++)
1003 							in[i] = ((unsigned char *)inbuf)[x * su.id + i]/255.0;
1004 					else
1005 						for (i = 0; i < su.id; i++)
1006 							in[i] = ((unsigned short *)inbuf)[x * su.id + i]/65535.0;
1007 
1008 					if (su.link) {
1009 						if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1)
1010 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
1011 						if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1)
1012 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
1013 					} else {
1014 						if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1)
1015 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
1016 					}
1017 
1018 					if (bitspersample == 8)
1019 						for (i = 0; i < su.od; i++)
1020 							((unsigned char *)checkbuf)[x * su.od + i] = (int)(out[i] * 255.0 + 0.5);
1021 					else
1022 						for (i = 0; i < su.od; i++)
1023 							((unsigned short *)checkbuf)[x * su.od + i] = (int)(out[i] * 65535.0 + 0.5);
1024 				}
1025 				/* Compute the errors */
1026 				for (x = 0; x < (width * su.od); x++) {
1027 					int err;
1028 					if (bitspersample == 8)
1029 						err = ((unsigned char *)outbuf)[x] - ((unsigned char *)checkbuf)[x];
1030 					else
1031 						err = ((unsigned short *)outbuf)[x] - ((unsigned short *)checkbuf)[x];
1032 					if (err < 0)
1033 						err = -err;
1034 					if (err > mxerr)
1035 						mxerr = err;
1036 					avgerr += (double)err;
1037 					avgcount++;
1038 				}
1039 			}
1040 
1041 			if (TIFFWriteScanline(wh, outbuf, y, 0) < 0)
1042 				error ("Failed to write TIFF line %d",y);
1043 
1044 		}
1045 
1046 	} else {	/* Slow but precise */
1047 		if (bitspersample == 8) {
1048 			for (y = 0; y < height; y++) {
1049 
1050 				/* Read in the next line */
1051 				if (TIFFReadScanline(rh, inbuf, y, 0) < 0)
1052 					error ("Failed to read TIFF line %d",y);
1053 
1054 				/* Do floating point conversion */
1055 				for (x = 0; x < width; x++) {
1056 					int i;
1057 					double in[MAX_CHAN], out[MAX_CHAN];
1058 
1059 					for (i = 0; i < su.id; i++) {
1060 						in[i] = ((unsigned char *)inbuf)[x * su.id + i]/255.0;
1061 					}
1062 
1063 					if (su.link) {
1064 						if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1)
1065 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
1066 						if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1)
1067 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
1068 					} else {
1069 						if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1)
1070 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
1071 					}
1072 
1073 					for (i = 0; i < su.od; i++) {
1074 						double outi = out[i];
1075 						if (outi < 0.0)			/* Protect against sillies */
1076 							outi = 0.0;
1077 						else if (outi > 1.0)
1078 							outi = 1.0;
1079 						((unsigned char *)outbuf)[x * su.od + i] = (int)(outi * 255.0 + 0.5);
1080 					}
1081 				}
1082 				if (TIFFWriteScanline(wh, outbuf, y, 0) < 0)
1083 					error ("Failed to write TIFF line %d",y);
1084 			}
1085 		} else if (bitspersample == 16) {
1086 			for (y = 0; y < height; y++) {
1087 
1088 				/* Read in the next line */
1089 				if (TIFFReadScanline(rh, inbuf, y, 0) < 0)
1090 					error ("Failed to read TIFF line %d",y);
1091 
1092 				/* Do floating point conversion */
1093 				for (x = 0; x < width; x++) {
1094 					int i;
1095 					double in[MAX_CHAN], out[MAX_CHAN];
1096 
1097 					for (i = 0; i < su.id; i++) {
1098 						in[i] = ((unsigned short *)inbuf)[x * su.id + i]/65535.0;
1099 					}
1100 
1101 					if (su.link) {
1102 						if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1)
1103 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
1104 						if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1)
1105 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
1106 					} else {
1107 						if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1)
1108 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
1109 					}
1110 
1111 					for (i = 0; i < su.od; i++) {
1112 						double outi = out[i];
1113 						if (outi < 0.0)			/* Protect against sillies */
1114 							outi = 0.0;
1115 						else if (outi > 1.0)
1116 							outi = 1.0;
1117 						((unsigned short *)outbuf)[x * su.od + i] = (int)(outi * 65535.0 + 0.5);
1118 					}
1119 				}
1120 				if (TIFFWriteScanline(wh, outbuf, y, 0) < 0)
1121 					error ("Failed to write TIFF line %d",y);
1122 			}
1123 		}
1124 	}
1125 
1126 	if (check) {
1127 		printf("Worst error = %d bits, average error = %f bits\n", mxerr, avgerr/avgcount);
1128 		if (bitspersample == 8)
1129 			printf("Worst error = %f%%, average error = %f%%\n",
1130 			       mxerr/2.55, avgerr/(2.55 * avgcount));
1131 		else
1132 			printf("Worst error = %f%%, average error = %f%%\n",
1133 			       mxerr/655.35, avgerr/(655.35 * avgcount));
1134 	}
1135 
1136 	/* Done with lookup object */
1137 	if (s != NULL)
1138 		s->done(s);
1139 
1140 	if (su.link) {
1141 		su.in.luo->del(su.in.luo);
1142 		su.in.c->del(su.in.c);
1143 		su.in.fp->del(su.in.fp);
1144 		su.out.luo->del(su.out.luo);
1145 		su.out.c->del(su.out.c);
1146 		su.out.fp->del(su.out.fp);
1147 	} else {
1148 		su.dev.luo->del(su.dev.luo);
1149 		su.dev.c->del(su.dev.c);
1150 		su.dev.fp->del(su.dev.fp);
1151 	}
1152 
1153 	_TIFFfree(inbuf);
1154 	_TIFFfree(outbuf);
1155 	if (check)
1156 		_TIFFfree(checkbuf);
1157 
1158 	TIFFClose(rh);		/* Close Input file */
1159 	TIFFClose(wh);		/* Close Output file */
1160 
1161 	return 0;
1162 }
1163 
1164 
1165 /* Basic printf type error() and warning() routines */
1166 
1167 void
error(char * fmt,...)1168 error(char *fmt, ...)
1169 {
1170 	va_list args;
1171 
1172 	fprintf(stderr,"cctiff: Error - ");
1173 	va_start(args, fmt);
1174 	vfprintf(stderr, fmt, args);
1175 	va_end(args);
1176 	fprintf(stderr, "\n");
1177 	exit (-1);
1178 }
1179 
1180 void
warning(char * fmt,...)1181 warning(char *fmt, ...)
1182 {
1183 	va_list args;
1184 
1185 	fprintf(stderr,"cctiff: Warning - ");
1186 	va_start(args, fmt);
1187 	vfprintf(stderr, fmt, args);
1188 	va_end(args);
1189 	fprintf(stderr, "\n");
1190 }
1191