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