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 AFFERO GENERAL PUBLIC LICENSE Version 3 :-
13  * see the License.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 "aconfig.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 AGPL Version 3\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 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
65 
66 /* Convert an ICC colorspace to the corresponding possible TIFF Photometric tags. */
67 /* Return the number of matching tags, and 0 if there is no corresponding tag. */
68 int
ColorSpaceSignature2TiffPhotometric(uint16 tags[10],icColorSpaceSignature cspace)69 ColorSpaceSignature2TiffPhotometric(
70 uint16 tags[10],					/* Pointer to return array, up to 10 */
71 icColorSpaceSignature cspace	/* Input ICC colorspace */
72 ) {
73 	switch(cspace) {
74 		case icSigGrayData:
75 			tags[0] = PHOTOMETRIC_MINISBLACK;
76 			return 1;
77 		case icSigRgbData:
78 #ifdef TREAT_CMY_AS_RGB
79 		case icSigCmyData:
80 #endif
81 			tags[0] = PHOTOMETRIC_RGB;
82 			return 1;
83 #ifndef TREAT_CMY_AS_RGB
84 		case icSigCmyData:
85 #endif
86 		case icSigCmykData:
87 			tags[0] = PHOTOMETRIC_SEPARATED;
88 			return 1;
89 		case icSigYCbCrData:
90 			tags[0] = PHOTOMETRIC_YCBCR;
91 			return 1;
92 		case icSigLabData:
93 			tags[0] = PHOTOMETRIC_CIELAB;
94 #ifdef PHOTOMETRIC_ICCLAB
95 			tags[1] = PHOTOMETRIC_ICCLAB;
96 			tags[2] = PHOTOMETRIC_ITULAB;
97 #endif
98 			return 3;
99 
100 		case icSigXYZData:
101 		case icSigLuvData:
102 		case icSigYxyData:
103 		case icSigHsvData:
104 		case icSigHlsData:
105 			return 0;
106 
107 		case icSig2colorData:
108 			tags[0] = PHOTOMETRIC_SEPARATED;
109 			tags[1] = 2;			/* Cheat */
110 			return 1;
111 
112 		case icSig3colorData:
113 			tags[0] = PHOTOMETRIC_SEPARATED;
114 			tags[1] = 3;			/* Cheat */
115 			return 1;
116 
117 		case icSig4colorData:
118 			tags[0] = PHOTOMETRIC_SEPARATED;
119 			tags[1] = 4;			/* Cheat */
120 			return 1;
121 
122 		case icSig5colorData:
123 		case icSigMch5Data:
124 			tags[0] = PHOTOMETRIC_SEPARATED;
125 			tags[1] = 5;			/* Cheat */
126 			return 1;
127 
128 		case icSig6colorData:
129 		case icSigMch6Data:
130 			tags[0] = PHOTOMETRIC_SEPARATED;
131 			tags[1] = 6;			/* Cheat */
132 			return 1;
133 
134 		case icSig7colorData:
135 		case icSigMch7Data:
136 			tags[0] = PHOTOMETRIC_SEPARATED;
137 			tags[1] = 7;			/* Cheat */
138 			return 1;
139 
140 		case icSig8colorData:
141 		case icSigMch8Data:
142 			tags[0] = PHOTOMETRIC_SEPARATED;
143 			tags[1] = 8;			/* Cheat */
144 			return 1;
145 
146 		case icSig9colorData:
147 			tags[0] = PHOTOMETRIC_SEPARATED;
148 			tags[1] = 9;			/* Cheat */
149 			return 1;
150 
151 		case icSig10colorData:
152 			tags[0] = PHOTOMETRIC_SEPARATED;
153 			tags[1] = 10;			/* Cheat */
154 			return 1;
155 
156 		case icSig11colorData:
157 			tags[0] = PHOTOMETRIC_SEPARATED;
158 			tags[1] = 11;			/* Cheat */
159 			return 1;
160 
161 		case icSig12colorData:
162 			tags[0] = PHOTOMETRIC_SEPARATED;
163 			tags[1] = 12;			/* Cheat */
164 			return 1;
165 
166 		case icSig13colorData:
167 			tags[0] = PHOTOMETRIC_SEPARATED;
168 			tags[1] = 13;			/* Cheat */
169 			return 1;
170 
171 		case icSig14colorData:
172 			tags[0] = PHOTOMETRIC_SEPARATED;
173 			tags[1] = 14;			/* Cheat */
174 			return 1;
175 
176 		case icSig15colorData:
177 			tags[0] = PHOTOMETRIC_SEPARATED;
178 			tags[1] = 15;			/* Cheat */
179 			return 1;
180 
181 		default:
182 			return 0;
183 	}
184 	return 0;
185 }
186 
187 
188 /* Compute the length of a double nul terminated string, including */
189 /* the nuls. */
zzstrlen(char * s)190 static int zzstrlen(char *s) {
191 	int i;
192 	for (i = 0;; i++) {
193 		if (s[i] == '\000' && s[i+1] == '\000')
194 			return i+2;
195 	}
196 	return 0;
197 }
198 
199 /* Convert an ICC colorspace to the corresponding TIFF Inkset tag */
200 /* return 0xffff if not possible or applicable. */
201 
202 int
ColorSpaceSignature2TiffInkset(icColorSpaceSignature cspace,int * len,char ** inknames)203 ColorSpaceSignature2TiffInkset(
204 icColorSpaceSignature cspace,
205 int *len,						/* Return length of ASCII inknames */
206 char **inknames					/* Return ASCII inknames if non NULL */
207 ) {
208 	switch(cspace) {
209 		case icSigCmyData:
210 			return 0xffff;	// ~~9999
211 			if (inknames != NULL) {
212 				*inknames = "cyan\000magenta\000yellow\000\000";
213 				*len = zzstrlen(*inknames);
214 			}
215 			return 0;				/* Not CMYK */
216 		case icSigCmykData:
217 			if (inknames != NULL) {
218 				*inknames = NULL;	/* No inknames */
219 				*len = 0;
220 			}
221 			return INKSET_CMYK;
222 
223 		case icSigGrayData:
224 		case icSigRgbData:
225 		case icSigYCbCrData:
226 		case icSigLabData:
227 		case icSigXYZData:
228 		case icSigLuvData:
229 		case icSigYxyData:
230 		case icSigHsvData:
231 		case icSigHlsData:
232 		case icSig2colorData:
233 		case icSig3colorData:
234 		case icSig4colorData:
235 		case icSig5colorData:
236 		case icSigMch5Data:
237 			return 0xffff;
238 
239 		case icSig6colorData:
240 		case icSigMch6Data:
241 				/* This is a cheat and a hack. Should really make use of the */
242 				/* ColorantTable to determine the colorant names. */
243 				/* allowing cctiff to read it. */
244 			if (inknames != NULL) {
245 				*inknames = "cyan\000magenta\000yellow\000black\000orange\000green\000\000";
246 				*len = zzstrlen(*inknames);
247 			}
248 			return 0;				/* Not CMYK */
249 
250 		case icSig7colorData:
251 		case icSigMch7Data:
252 			return 0xffff;
253 
254 		case icSig8colorData:
255 		case icSigMch8Data:
256 				/* This is a cheat and a hack. Should really make use of the */
257 				/* ColorantTable to determine the colorant names. */
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 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
315 
316 /* Callbacks used to initialise imdi */
317 
318 /* Information needed from a profile */
319 struct _profinfo {
320 	char name[100];
321 	icmFile *fp;
322 	icc *c;
323 	icmHeader *h;
324 	icRenderingIntent intent;
325 	icmLuBase *luo;				/* Base Lookup type object */
326 	icmLuAlgType alg;			/* Type of lookup algorithm */
327 	int chan;					/* Device channels */
328 }; typedef struct _profinfo profinfo;
329 
330 /* Context for imdi setup callbacks */
331 typedef struct {
332 	/* Overall parameters */
333 	int verb;			/* Non-zero if verbose */
334 	icColorSpaceSignature ins, outs;	/* Input/Output spaces */
335 	int id, od;			/* Input/Output dimensions */
336 	int icombine;		/* Non-zero if input curves are to be combined */
337 	int ocombine;		/* Non-zero if output curves are to be combined */
338 	int dolink;			/* Non-zero if input and output profiles are to be linked */
339 
340 	profinfo dev;		/* Device link profile */
341 	profinfo in;		/* Device to PCS profile */
342 	profinfo out;		/* PCS to Device profile */
343 } sucntx;
344 
345 /* Input curve function */
input_curves(void * cntx,double * out_vals,double * in_vals)346 static void input_curves(
347 	void *cntx,
348 	double *out_vals,
349 	double *in_vals
350 ) {
351 	sucntx *rx = (sucntx *)cntx;
352 
353 //printf("~1 incurve in %f %f %f %f\n",in_vals[0],in_vals[1],in_vals[2],in_vals[3]);
354 	if (rx->icombine) {
355 		int i;
356 		for (i = 0; i < rx->id; i++)
357 			out_vals[i] = in_vals[i];
358 	} else {
359 		if (rx->dolink) {		/* Two ICC profiles */
360 			rx->in.luo->lookup_in(rx->in.luo, out_vals, in_vals);
361 		} else {			/* Device link */
362 			rx->dev.luo->lookup_in(rx->dev.luo, out_vals, in_vals);
363 		}
364 	}
365 //printf("~1 incurve out %f %f %f %f\n",out_vals[0],out_vals[1],out_vals[2],out_vals[3]);
366 }
367 
368 /* Multi-dim table function */
md_table(void * cntx,double * out_vals,double * in_vals)369 static void md_table(
370 void *cntx,
371 double *out_vals,
372 double *in_vals
373 ) {
374 	sucntx *rx = (sucntx *)cntx;
375 	double vals[MAX_CHAN];
376 
377 //printf("~1 md_table in %f %f %f %f\n",in_vals[0],in_vals[1],in_vals[2]);
378 	if (rx->dolink) {		/* Two ICC profiles */
379 
380 		if (rx->icombine) {
381 			rx->in.luo->lookup_in(rx->in.luo, vals, in_vals);
382 			rx->in.luo->lookup_core(rx->in.luo, vals, vals);
383 		} else {
384 			rx->in.luo->lookup_core(rx->in.luo, vals, in_vals);
385 		}
386 		rx->in.luo->lookup_out(rx->in.luo, vals, vals);
387 		rx->out.luo->lookup_in(rx->out.luo, vals, vals);
388 		rx->out.luo->lookup_core(rx->out.luo, out_vals, vals);
389 		if (rx->ocombine)
390 			rx->out.luo->lookup_out(rx->out.luo, out_vals, out_vals);
391 
392 	} else {			/* Device link */
393 
394 		if (rx->icombine) {
395 			rx->dev.luo->lookup_in(rx->dev.luo, vals, in_vals);
396 			rx->dev.luo->lookup_core(rx->dev.luo, out_vals, vals);
397 		} else {
398 			rx->dev.luo->lookup_core(rx->dev.luo, out_vals, in_vals);
399 		}
400 		if (rx->ocombine)
401 			rx->dev.luo->lookup_out(rx->dev.luo, out_vals, out_vals);
402 	}
403 //printf("~1 md_table returns %f %f %f %f\n",out_vals[0],out_vals[1],out_vals[2],out_vals[3]);
404 }
405 
406 /* Output curve function */
output_curves(void * cntx,double * out_vals,double * in_vals)407 static void output_curves(
408 	void *cntx,
409 	double *out_vals,
410 	double *in_vals
411 ) {
412 	sucntx *rx = (sucntx *)cntx;
413 
414 //printf("~1 outurve in %f %f %f %f\n",in_vals[0],in_vals[1],in_vals[2],in_vals[3]);
415 	if (rx->ocombine) {
416 		int i;
417 		for (i = 0; i < rx->od; i++)
418 			out_vals[i] = in_vals[i];
419 	} else {
420 		if (rx->dolink) {	/* Two ICC profiles */
421 			rx->out.luo->lookup_out(rx->out.luo, out_vals, in_vals);
422 		} else {	/* Device link */
423 			rx->dev.luo->lookup_out(rx->dev.luo, out_vals, in_vals);
424 		}
425 	}
426 //printf("~1 outurve out %f %f %f %f\n",out_vals[0],out_vals[1],out_vals[2],out_vals[3]);
427 }
428 
429 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
430 
431 
432 int
main(int argc,char * argv[])433 main(int argc, char *argv[]) {
434 	int fa,nfa;				/* argument we're looking at */
435 	char in_name[100];		/* Raster file name */
436 	char out_name[100];		/* Raster file name */
437 	int slow = 0;
438 	int check = 0;
439 	int i, rv = 0;
440 
441 	TIFF *rh = NULL, *wh = NULL;
442 	int x, y, width, height;					/* Size of image */
443 	uint16 samplesperpixel, bitspersample;
444 	int no_pmtc;								/* Number of input photometrics */
445 	uint16 photometric, pmtc[10];				/* Photometrics of input file, and input profile */
446 	uint16 pconfig;								/* Planar configuration */
447 	uint16 resunits;
448 	float resx, resy;
449 	tdata_t *inbuf, *outbuf, *checkbuf = NULL;
450 
451 	/* IMDI */
452 	imdi *s = NULL;
453 	sucntx su;		/* Setup context */
454 	unsigned char *inp[MAX_CHAN];
455 	unsigned char *outp[MAX_CHAN];
456 	int clutres = 33;
457 
458 	/* Error check */
459 	int mxerr = 0;
460 	double avgerr = 0.0;
461 	double avgcount = 0.0;
462 
463 	if (argc < 2)
464 		usage();
465 
466 	su.verb = 0;
467 	su.icombine = 0;
468 	su.ocombine = 0;
469 	su.dolink = 0;
470 	su.in.intent = icmDefaultIntent;
471 	su.out.intent = icmDefaultIntent;
472 
473 	/* Process the arguments */
474 	for(fa = 1;fa < argc;fa++) {
475 		nfa = fa;					/* skip to nfa if next argument is used */
476 		if (argv[fa][0] == '-')	{	/* Look for any flags */
477 			char *na = NULL;		/* next argument after flag, null if none */
478 
479 			if (argv[fa][2] != '\000')
480 				na = &argv[fa][2];		/* next is directly after flag */
481 			else {
482 				if ((fa+1) < argc) {
483 					if (argv[fa+1][0] != '-') {
484 						nfa = fa + 1;
485 						na = argv[nfa];		/* next is seperate non-flag argument */
486 					}
487 				}
488 			}
489 
490 			if (argv[fa][1] == '?')
491 				usage();
492 
493 			/* Slow, Precise */
494 			else if (argv[fa][1] == 'p' || argv[fa][1] == 'P') {
495 				slow = 1;
496 			}
497 
498 			/* Combine per channel curves */
499 			else if (argv[fa][1] == 'c' || argv[fa][1] == 'C') {
500 				su.icombine = 1;
501 				su.ocombine = 1;
502 			}
503 
504 			/* Check curves */
505 			else if (argv[fa][1] == 'k' || argv[fa][1] == 'K') {
506 				check = 1;
507 			}
508 
509 			/* Link profiles */
510 			else if (argv[fa][1] == 'l' || argv[fa][1] == 'L') {
511 				su.dolink = 1;
512 			}
513 
514 			/* Input profile Intent */
515 			else if (argv[fa][1] == 'i' || argv[fa][1] == 'I') {
516 				fa = nfa;
517 				if (na == NULL) usage();
518     			switch (na[0]) {
519 					case 'p':
520 					case 'P':
521 						su.in.intent = icPerceptual;
522 						break;
523 					case 'r':
524 					case 'R':
525 						su.in.intent = icRelativeColorimetric;
526 						break;
527 					case 's':
528 					case 'S':
529 						su.in.intent = icSaturation;
530 						break;
531 					case 'a':
532 					case 'A':
533 						su.in.intent = icAbsoluteColorimetric;
534 						break;
535 					default:
536 						usage();
537 				}
538 			}
539 
540 			/* Output profile Intent */
541 			else if (argv[fa][1] == 'o' || argv[fa][1] == 'O') {
542 				fa = nfa;
543 				if (na == NULL) usage();
544     			switch (na[0]) {
545 					case 'p':
546 					case 'P':
547 						su.out.intent = icPerceptual;
548 						break;
549 					case 'r':
550 					case 'R':
551 						su.out.intent = icRelativeColorimetric;
552 						break;
553 					case 's':
554 					case 'S':
555 						su.out.intent = icSaturation;
556 						break;
557 					case 'a':
558 					case 'A':
559 						su.out.intent = icAbsoluteColorimetric;
560 						break;
561 					default:
562 						usage();
563 				}
564 			}
565 
566 			/* Verbosity */
567 			else if (argv[fa][1] == 'v' || argv[fa][1] == 'V') {
568 				su.verb = 1;
569 			}
570 
571 			else
572 				usage();
573 		} else
574 			break;
575 	}
576 
577 	if (su.dolink) {
578 		if (fa >= argc || argv[fa][0] == '-') usage();
579 		strcpy(su.in.name,argv[fa++]);
580 
581 		if (fa >= argc || argv[fa][0] == '-') usage();
582 		strcpy(su.out.name,argv[fa++]);
583 	} else {
584 		if (fa >= argc || argv[fa][0] == '-') usage();
585 		strcpy(su.dev.name,argv[fa++]);
586 	}
587 
588 	if (fa >= argc || argv[fa][0] == '-') usage();
589 	strcpy(in_name,argv[fa++]);
590 
591 	if (fa >= argc || argv[fa][0] == '-') usage();
592 	strcpy(out_name,argv[fa++]);
593 
594 	/* - - - - - - - - - - - - - - - - */
595 
596 	if (su.dolink) {
597 		icColorSpaceSignature natpcs;
598 
599 		/* Open up the input device profile for reading */
600 		if ((su.in.fp = new_icmFileStd_name(su.in.name,"r")) == NULL)
601 			error ("Can't open file '%s'",su.in.name);
602 
603 		if ((su.in.c = new_icc()) == NULL)
604 			error ("Creation of Input profile ICC object failed");
605 
606 		/* Read header etc. */
607 		if ((rv = su.in.c->read(su.in.c,su.in.fp,0)) != 0)
608 			error ("%d, %s on file '%s'",rv,su.in.c->err,su.in.name);
609 		su.in.h = su.in.c->header;
610 
611 		/* Check that it is a suitable device input icc */
612 		if (su.in.h->deviceClass != icSigInputClass
613 		 && su.in.h->deviceClass != icSigDisplayClass
614 		 && su.in.h->deviceClass != icSigOutputClass
615 		 && su.in.h->deviceClass != icSigColorSpaceClass)	/* For sRGB etc. */
616 			error("Input profile isn't a device profile");
617 
618 		/* Get a conversion object */
619 		if ((su.in.luo = su.in.c->get_luobj(su.in.c, icmFwd, su.in.intent,
620 		                                             icSigLabData, icmLuOrdNorm)) == NULL)
621 			error ("%d, %s for profile '%s'",su.in.c->errc, su.in.c->err, su.in.name);
622 
623 		/* Get details of conversion (Arguments may be NULL if info not needed) */
624 		su.in.luo->spaces(su.in.luo, &su.ins, &su.id, NULL, NULL, &su.in.alg, NULL, NULL, NULL);
625 
626 		/* Get native PCS space */
627 		su.in.luo->lutspaces(su.in.luo, NULL, NULL, NULL, NULL, &natpcs);
628 
629 		if (natpcs == icSigXYZData) {
630 			su.icombine = 1;			/* XYZ is to non-linear to be a benefit */
631 		}
632 
633 		/* Open up the output device profile for reading */
634 		if ((su.out.fp = new_icmFileStd_name(su.out.name,"r")) == NULL)
635 			error ("Can't open file '%s'",su.out.name);
636 
637 		if ((su.out.c = new_icc()) == NULL)
638 			error ("Creation of Output profile ICC object failed");
639 
640 		/* Read header etc. */
641 		if ((rv = su.out.c->read(su.out.c,su.out.fp,0)) != 0)
642 			error ("%d, %s on file '%s'",rv,su.out.c->err,su.out.name);
643 		su.out.h = su.out.c->header;
644 
645 		/* Check that it is a suitable device output icc */
646 		if (su.out.h->deviceClass != icSigInputClass
647 		 && su.out.h->deviceClass != icSigDisplayClass
648 		 && su.out.h->deviceClass != icSigOutputClass
649 		 && su.out.h->deviceClass != icSigColorSpaceClass)	/* For sRGB etc. */
650 			error("Output profile isn't a device profile");
651 
652 		/* Get a conversion object */
653 		if ((su.out.luo = su.out.c->get_luobj(su.out.c, icmBwd, su.out.intent,
654 		                                             icSigLabData, icmLuOrdNorm)) == NULL)
655 			error ("%d, %s for profile '%s'",su.out.c->errc, su.out.c->err, su.out.name);
656 
657 		/* Get details of conversion (Arguments may be NULL if info not needed) */
658 		su.out.luo->spaces(su.out.luo, NULL, NULL, &su.outs, &su.od, &su.out.alg, NULL, NULL, NULL);
659 
660 		/* Get native PCS space */
661 		su.out.luo->lutspaces(su.out.luo, NULL, NULL, NULL, NULL, &natpcs);
662 
663 		if (natpcs == icSigXYZData) {
664 			su.ocombine = 1;			/* XYZ is to non-linear to be a benefit */
665 		}
666 
667 		/* See discussion in imdi/imdi_gen.c for ideal numbers */
668 		/* Use "high quality" resolution numbers */
669 		switch (su.id) {
670 			case 0:
671 				error ("Illegal number of input chanels");
672 			case 1:
673 	  		  	clutres = 256;
674 				break;
675 			case 2:
676 	  		  	clutres = 256;
677 				break;
678 			case 3:
679 	  		  	clutres = 33;
680 				break;
681 			case 4:
682   		  		clutres = 18;
683 				break;
684 			case 5:
685   		  		clutres = 16;
686 				break;
687 			case 6:
688   		  		clutres = 9;
689 				break;
690 			case 7:
691   		  		clutres = 7;
692 				break;
693 			case 8:
694   		  		clutres = 6;
695 				break;
696 			default: /* > 8 chan */
697 				clutres = 3;
698 				break;
699 		}
700 
701 	} else {
702 		icmLut *lut;						/* ICC LUT table */
703 		icmLuLut *luluo;					/* LUT lookup object */
704 
705 		/* Open up the device link profile for reading */
706 		if ((su.dev.fp = new_icmFileStd_name(su.dev.name,"r")) == NULL)
707 			error ("Can't open file '%s'",su.dev.name);
708 
709 		if ((su.dev.c = new_icc()) == NULL)
710 			error ("Creation of ICC object failed");
711 
712 		if ((rv = su.dev.c->read(su.dev.c, su.dev.fp, 0)) != 0)
713 			error ("%d, %s",rv,su.dev.c->err);
714 		su.dev.h = su.dev.c->header;
715 
716 		if (su.verb) {
717 			icmFile *op;
718 			if ((op = new_icmFileStd_fp(stdout)) == NULL)
719 				error ("Can't open stdout");
720 			su.dev.h->dump(su.dev.h, op, 1);
721 			op->del(op);
722 		}
723 
724 		/* Check that the profile is appropriate */
725 		if (su.dev.h->deviceClass != icSigLinkClass)
726 			error("Profile isn't a device link profile");
727 
728 		/* Get a conversion object */
729 		if ((su.dev.luo = su.dev.c->get_luobj(su.dev.c, icmFwd, icmDefaultIntent,
730 		                                             icmSigDefaultData, icmLuOrdNorm)) == NULL)
731 			error ("%d, %s",su.dev.c->errc, su.dev.c->err);
732 
733 		/* Get details of conversion (Arguments may be NULL if info not needed) */
734 		su.dev.luo->spaces(su.dev.luo, &su.ins, &su.id, &su.outs, &su.od, &su.dev.alg, NULL, NULL, NULL);
735 
736 		if (su.dev.alg != icmLutType)
737 			error ("DeviceLink profile doesn't have Lut !");
738 
739 		luluo = (icmLuLut *)su.dev.luo;		/* Safe to coerce */
740 		luluo->get_info(luluo, &lut, NULL, NULL, NULL);	/* Get some details */
741 		clutres = lut->clutPoints;			/* Desired table resolution */
742 	}
743 
744 	/* - - - - - - - - - - - - - - - */
745 	/* Open up input tiff file ready for reading */
746 	/* Got arguments, so setup to process the file */
747 	if ((rh = TIFFOpen(in_name, "r")) == NULL)
748 		error("error opening read file '%s'",in_name);
749 
750 	TIFFGetField(rh, TIFFTAG_IMAGEWIDTH,  &width);
751 	TIFFGetField(rh, TIFFTAG_IMAGELENGTH, &height);
752 
753 	TIFFGetField(rh, TIFFTAG_BITSPERSAMPLE, &bitspersample);
754 	if (bitspersample != 8 && bitspersample != 16) {
755 		error("TIFF Input file must be 8 or 16 bit/channel");
756 	}
757 
758 	TIFFGetField(rh, TIFFTAG_PHOTOMETRIC, &photometric);
759 	if  ((no_pmtc = ColorSpaceSignature2TiffPhotometric(pmtc, su.ins)) == 0)
760 		error("ICC  input colorspace '%s' can't be handled by a TIFF file!",
761 		      icm2str(icmColorSpaceSignature, su.ins));
762 	for (i = 0; i < no_pmtc; i++) {
763 		if (pmtc[i] == photometric)
764 			break;				/* Matches */
765 	}
766 	if (i >= no_pmtc) {
767 		/* These error reports are a bit sloppy */
768 		switch (no_pmtc) {
769 			case 1:
770 				error("TIFF colorspace '%s' doesn't match ICC colorspace '%s'!",
771 			      Photometric2str(photometric), Photometric2str(pmtc[0]));
772 			case 2:
773 				error("TIFF colorspace '%s' doesn't match ICC colorspace '%s' or '%s'!",
774 			      Photometric2str(photometric), Photometric2str(pmtc[0]),
775 			      Photometric2str(pmtc[1]));
776 			default:
777 				error("TIFF colorspace '%s' doesn't match ICC colorspace '%s', '%s' or '%s'!",
778 			      Photometric2str(photometric), Photometric2str(pmtc[0]),
779 			      Photometric2str(pmtc[1]), Photometric2str(pmtc[2]));
780 		}
781 	}
782 
783 	TIFFGetField(rh, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);
784 	if (su.id != samplesperpixel)
785 		error ("TIFF Input file has %d input channels mismatched to colorspace '%s'",
786 		       samplesperpixel, icm2str(icmColorSpaceSignature, su.ins));
787 
788 	TIFFGetField(rh, TIFFTAG_PLANARCONFIG, &pconfig);
789 	if (pconfig != PLANARCONFIG_CONTIG)
790 		error ("TIFF Input file must be planar");
791 
792 	TIFFGetField(rh, TIFFTAG_RESOLUTIONUNIT, &resunits);
793 	TIFFGetField(rh, TIFFTAG_XRESOLUTION, &resx);
794 	TIFFGetField(rh, TIFFTAG_YRESOLUTION, &resy);
795 
796 	/* - - - - - - - - - - - - - - - */
797 	if ((wh = TIFFOpen(out_name, "w")) == NULL)
798 		error("Can\'t create TIFF file '%s'!",out_name);
799 
800 	TIFFSetField(wh, TIFFTAG_IMAGEWIDTH,  width);
801 	TIFFSetField(wh, TIFFTAG_IMAGELENGTH, height);
802 	TIFFSetField(wh, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
803 	TIFFSetField(wh, TIFFTAG_SAMPLESPERPIXEL, su.od);
804 	TIFFSetField(wh, TIFFTAG_BITSPERSAMPLE, bitspersample);
805 	TIFFSetField(wh, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
806 	if  ((no_pmtc = ColorSpaceSignature2TiffPhotometric(pmtc, su.outs)) == 0)
807 		error("TIFF file can't handle output colorspace '%s'!",
808 		      icm2str(icmColorSpaceSignature, su.outs));
809 	TIFFSetField(wh, TIFFTAG_PHOTOMETRIC, pmtc[0]);	/* Use first returned */
810 	if (pmtc[0] == PHOTOMETRIC_SEPARATED) {
811 		int iset;
812 		int inlen;
813 		char *inames;
814 		iset = ColorSpaceSignature2TiffInkset(su.outs, &inlen, &inames);
815 		if (iset != 0xffff && inlen > 0 && inames != NULL) {
816 			TIFFSetField(wh, TIFFTAG_INKSET, iset);
817 			if (inames != NULL) {
818 				TIFFSetField(wh, TIFFTAG_INKNAMES, inlen, inames);
819 			}
820 		}
821 	}
822 	TIFFSetField(wh, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
823 	if (resunits) {
824 		TIFFSetField(wh, TIFFTAG_RESOLUTIONUNIT, resunits);
825 		TIFFSetField(wh, TIFFTAG_XRESOLUTION, resx);
826 		TIFFSetField(wh, TIFFTAG_YRESOLUTION, resy);
827 	}
828 	TIFFSetField(wh, TIFFTAG_IMAGEDESCRIPTION, "Color corrected by Argyll");
829 
830 	/* - - - - - - - - - - - - - - - */
831 	/* Setup the imdi */
832 
833 	if (!slow) {
834 		s = new_imdi(
835 			su.id,			/* Number of input dimensions */
836 			su.od,			/* Number of output dimensions */
837 							/* Input pixel representation */
838 			bitspersample == 8 ? pixint8 : pixint16,
839 							/* Output pixel representation */
840 			0x0,			/* Treat every channel as unsigned */
841 			NULL,			/* No raster to callback mapping */
842 			prec_min,		/* Minimum of input and output precision */
843 			bitspersample == 8 ? pixint8 : pixint16,
844 			0x0,			/* Treat every channel as unsigned */
845 			NULL,			/* No raster to callback mapping */
846 			clutres,		/* Desired table resolution */
847 			oopts_none,		/* Desired per channel output options */
848 			NULL,			/* Output channel check values */
849 			opts_none,		/* Desired processing direction and stride support */
850 			input_curves,	/* Callback functions */
851 			md_table,
852 			output_curves,
853 			(void *)&su		/* Context to callbacks */
854 		);
855 
856 		if (s == NULL)
857 			error("new_imdi failed");
858 	}
859 
860 	/* - - - - - - - - - - - - - - - */
861 	/* Process colors to translate */
862 	/* (Should fix this to process a group of lines at a time ?) */
863 
864 	inbuf  = _TIFFmalloc(TIFFScanlineSize(rh));
865 	outbuf = _TIFFmalloc(TIFFScanlineSize(wh));
866 	if (check)
867 		checkbuf = _TIFFmalloc(TIFFScanlineSize(wh));
868 
869 	inp[0] = (unsigned char *)inbuf;
870 	outp[0] = (unsigned char *)outbuf;
871 
872 	if (!slow) {		/* Fast */
873 		for (y = 0; y < height; y++) {
874 
875 			/* Read in the next line */
876 			if (TIFFReadScanline(rh, inbuf, y, 0) < 0)
877 				error ("Failed to read TIFF line %d",y);
878 
879 			/* Do fast conversion */
880 			s->interp(s, (void **)outp, 0, (void **)inp, 0, width);
881 
882 			if (check) {
883 				/* Do floating point conversion */
884 				for (x = 0; x < width; x++) {
885 					int i;
886 					double in[MAX_CHAN], out[MAX_CHAN];
887 
888 					if (bitspersample == 8)
889 						for (i = 0; i < su.id; i++)
890 							in[i] = ((unsigned char *)inbuf)[x * su.id + i]/255.0;
891 					else
892 						for (i = 0; i < su.id; i++)
893 							in[i] = ((unsigned short *)inbuf)[x * su.id + i]/65535.0;
894 
895 #ifdef NEVER
896 					if (su.dolink) {
897 						if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1)
898 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
899 						if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1)
900 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
901 					} else {
902 						if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1)
903 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
904 					}
905 #else
906 					/* Apply the reference conversion */
907 					input_curves((void *)&su, out, in);
908 					md_table((void *)&su, out, out);
909 					output_curves((void *)&su, out, out);
910 #endif
911 					if (bitspersample == 8)
912 						for (i = 0; i < su.od; i++)
913 							((unsigned char *)checkbuf)[x * su.od + i] = (int)(out[i] * 255.0 + 0.5);
914 					else
915 						for (i = 0; i < su.od; i++)
916 							((unsigned short *)checkbuf)[x * su.od + i] = (int)(out[i] * 65535.0 + 0.5);
917 				}
918 				/* Compute the errors */
919 				for (x = 0; x < (width * su.od); x++) {
920 					int err;
921 					if (bitspersample == 8)
922 						err = ((unsigned char *)outbuf)[x] - ((unsigned char *)checkbuf)[x];
923 					else
924 						err = ((unsigned short *)outbuf)[x] - ((unsigned short *)checkbuf)[x];
925 					if (err < 0)
926 						err = -err;
927 					if (err > mxerr)
928 						mxerr = err;
929 					avgerr += (double)err;
930 					avgcount++;
931 				}
932 			}
933 
934 			if (TIFFWriteScanline(wh, outbuf, y, 0) < 0)
935 				error ("Failed to write TIFF line %d",y);
936 
937 		}
938 
939 	} else {	/* Slow but precise */
940 		if (bitspersample == 8) {
941 			for (y = 0; y < height; y++) {
942 
943 				/* Read in the next line */
944 				if (TIFFReadScanline(rh, inbuf, y, 0) < 0)
945 					error ("Failed to read TIFF line %d",y);
946 
947 				/* Do floating point conversion */
948 				for (x = 0; x < width; x++) {
949 					int i;
950 					double in[MAX_CHAN], out[MAX_CHAN];
951 
952 					for (i = 0; i < su.id; i++) {
953 						in[i] = ((unsigned char *)inbuf)[x * su.id + i]/255.0;
954 					}
955 
956 #ifdef NEVER
957 					if (su.dolink) {
958 						if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1)
959 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
960 						if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1)
961 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
962 					} else {
963 						if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1)
964 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
965 					}
966 #else
967 					/* Apply the reference conversion */
968 					input_curves((void *)&su, out, in);
969 					md_table((void *)&su, out, out);
970 					output_curves((void *)&su, out, out);
971 #endif
972 
973 					for (i = 0; i < su.od; i++) {
974 						double outi = out[i];
975 						if (outi < 0.0)			/* Protect against sillies */
976 							outi = 0.0;
977 						else if (outi > 1.0)
978 							outi = 1.0;
979 						((unsigned char *)outbuf)[x * su.od + i] = (int)(outi * 255.0 + 0.5);
980 					}
981 				}
982 				if (TIFFWriteScanline(wh, outbuf, y, 0) < 0)
983 					error ("Failed to write TIFF line %d",y);
984 			}
985 		} else if (bitspersample == 16) {
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 floating point conversion */
993 				for (x = 0; x < width; x++) {
994 					int i;
995 					double in[MAX_CHAN], out[MAX_CHAN];
996 
997 					for (i = 0; i < su.id; i++) {
998 						in[i] = ((unsigned short *)inbuf)[x * su.id + i]/65535.0;
999 					}
1000 
1001 #ifdef NEVER
1002 					if (su.dolink) {
1003 						if ((rv = su.in.luo->lookup(su.in.luo, out, in)) > 1)
1004 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
1005 						if ((rv = su.out.luo->lookup(su.out.luo, out, out)) > 1)
1006 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
1007 					} else {
1008 						if ((rv = su.dev.luo->lookup(su.dev.luo, out, in)) > 1)
1009 							error ("%d, %s",su.dev.c->errc,su.dev.c->err);
1010 					}
1011 #else
1012 					/* Apply the reference conversion */
1013 					input_curves((void *)&su, out, in);
1014 					md_table((void *)&su, out, out);
1015 					output_curves((void *)&su, out, out);
1016 #endif
1017 
1018 					for (i = 0; i < su.od; i++) {
1019 						double outi = out[i];
1020 						if (outi < 0.0)			/* Protect against sillies */
1021 							outi = 0.0;
1022 						else if (outi > 1.0)
1023 							outi = 1.0;
1024 						((unsigned short *)outbuf)[x * su.od + i] = (int)(outi * 65535.0 + 0.5);
1025 					}
1026 				}
1027 				if (TIFFWriteScanline(wh, outbuf, y, 0) < 0)
1028 					error ("Failed to write TIFF line %d",y);
1029 			}
1030 		}
1031 	}
1032 
1033 	if (check) {
1034 		printf("Worst error = %d bits, average error = %f bits\n", mxerr, avgerr/avgcount);
1035 		if (bitspersample == 8)
1036 			printf("Worst error = %f%%, average error = %f%%\n",
1037 			       mxerr/2.55, avgerr/(2.55 * avgcount));
1038 		else
1039 			printf("Worst error = %f%%, average error = %f%%\n",
1040 			       mxerr/655.35, avgerr/(655.35 * avgcount));
1041 	}
1042 
1043 	/* Done with lookup object */
1044 	if (s != NULL)
1045 		s->del(s);
1046 
1047 	if (su.dolink) {
1048 		su.in.luo->del(su.in.luo);
1049 		su.in.c->del(su.in.c);
1050 		su.in.fp->del(su.in.fp);
1051 		su.out.luo->del(su.out.luo);
1052 		su.out.c->del(su.out.c);
1053 		su.out.fp->del(su.out.fp);
1054 	} else {
1055 		su.dev.luo->del(su.dev.luo);
1056 		su.dev.c->del(su.dev.c);
1057 		su.dev.fp->del(su.dev.fp);
1058 	}
1059 
1060 	_TIFFfree(inbuf);
1061 	_TIFFfree(outbuf);
1062 	if (check)
1063 		_TIFFfree(checkbuf);
1064 
1065 	TIFFClose(rh);		/* Close Input file */
1066 	TIFFClose(wh);		/* Close Output file */
1067 
1068 	return 0;
1069 }
1070 
1071 
1072 /* Basic printf type error() and warning() routines */
1073 
1074 void
error(char * fmt,...)1075 error(char *fmt, ...)
1076 {
1077 	va_list args;
1078 
1079 	fprintf(stderr,"cctiff: Error - ");
1080 	va_start(args, fmt);
1081 	vfprintf(stderr, fmt, args);
1082 	va_end(args);
1083 	fprintf(stderr, "\n");
1084 	exit (-1);
1085 }
1086 
1087 void
warning(char * fmt,...)1088 warning(char *fmt, ...)
1089 {
1090 	va_list args;
1091 
1092 	fprintf(stderr,"cctiff: Warning - ");
1093 	va_start(args, fmt);
1094 	vfprintf(stderr, fmt, args);
1095 	va_end(args);
1096 	fprintf(stderr, "\n");
1097 }
1098