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