Imported Upstream version 2.4
[platform/upstream/lcms2.git] / utils / transicc / transicc.c
1 //---------------------------------------------------------------------------------
2 //
3 //  Little Color Management System
4 //  Copyright (c) 1998-2011 Marti Maria Saguer
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining 
7 // a copy of this software and associated documentation files (the "Software"), 
8 // to deal in the Software without restriction, including without limitation 
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 
10 // and/or sell copies of the Software, and to permit persons to whom the Software 
11 // is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in 
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24 //---------------------------------------------------------------------------------
25 //
26
27 #include "utils.h"
28
29 #ifndef _MSC_VER 
30 #    include <unistd.h> 
31 #endif 
32
33 #ifdef CMS_IS_WINDOWS_
34 #    include <io.h>
35 #endif
36
37 #define MAX_INPUT_BUFFER 4096
38
39 // Global options
40
41 static cmsBool           InHexa                 = FALSE;
42 static cmsBool           GamutCheck             = FALSE;
43 static cmsBool           Width16                = FALSE;
44 static cmsBool           BlackPointCompensation = FALSE;
45 static cmsBool           lIsDeviceLink          = FALSE;
46 static cmsBool           lQuantize              = FALSE;
47 static cmsBool           lIsFloat               = TRUE;
48
49 static cmsUInt32Number   Intent           = INTENT_PERCEPTUAL;
50 static cmsUInt32Number   ProofingIntent   = INTENT_PERCEPTUAL;
51
52 static int PrecalcMode  = 0;
53
54 // --------------------------------------------------------------
55
56 static char *cInProf   = NULL;
57 static char *cOutProf  = NULL;
58 static char *cProofing = NULL;
59
60 static char *IncludePart = NULL;
61
62 static cmsHANDLE hIT8in = NULL;        // CGATS input 
63 static cmsHANDLE hIT8out = NULL;       // CGATS output
64
65 static char CGATSPatch[1024];   // Actual Patch Name
66 static char CGATSoutFilename[cmsMAX_PATH];
67
68 static int nMaxPatches;
69
70 static cmsHTRANSFORM hTrans, hTransXYZ, hTransLab;
71 static cmsBool InputNamedColor = FALSE;
72
73 static cmsColorSpaceSignature InputColorSpace, OutputColorSpace;
74
75 static cmsNAMEDCOLORLIST* InputColorant = NULL;
76 static cmsNAMEDCOLORLIST* OutputColorant = NULL;
77
78 static cmsFloat64Number InputRange, OutputRange;
79
80
81 // isatty replacement
82 #ifdef _MSC_VER
83 #define xisatty(x) _isatty( _fileno( (x) ) )
84 #else
85 #define xisatty(x) isatty( fileno( (x) ) )
86 #endif
87
88 //---------------------------------------------------------------------------------------------------
89
90 // Print usage to stderr
91 static
92 void Help(void)
93 {           
94
95     fprintf(stderr, "usage: transicc [flags] [CGATS input] [CGATS output]\n\n");
96
97     fprintf(stderr, "flags:\n\n");
98     fprintf(stderr, "%cv<0..3> - Verbosity level\n", SW); 
99
100     fprintf(stderr, "%ce[op] - Encoded representation of numbers\n", SW);
101     fprintf(stderr, "\t%cw - use 16 bits\n", SW);     
102     fprintf(stderr, "\t%cx - Hexadecimal\n", SW);
103     fprintf(stderr, "%cq - Quantize CGATS to 8 bits\n\n", SW);
104
105
106     fprintf(stderr, "%ci<profile> - Input profile (defaults to sRGB)\n", SW);
107     fprintf(stderr, "%co<profile> - Output profile (defaults to sRGB)\n", SW);   
108     fprintf(stderr, "%cl<profile> - Transform by device-link profile\n", SW);   
109
110     fprintf(stderr, "\nYou can use '*Lab', '*xyz' and others as built-in profiles\n\n");
111
112     PrintRenderingIntents();
113
114     fprintf(stderr, "\n");
115
116     fprintf(stderr, "%cd<0..1> - Observer adaptation state (abs.col. only)\n\n", SW);
117
118     fprintf(stderr, "%cb - Black point compensation\n", SW);
119
120     fprintf(stderr, "%cc<0,1,2,3> Precalculates transform (0=Off, 1=Normal, 2=Hi-res, 3=LoRes)\n\n", SW);     
121     fprintf(stderr, "%cn - Terse output, intended for pipe usage\n", SW);
122
123     fprintf(stderr, "%cp<profile> - Soft proof profile\n", SW);
124     fprintf(stderr, "%cm<0,1,2,3> - Soft proof intent\n", SW);
125     fprintf(stderr, "%cg - Marks out-of-gamut colors on softproof\n\n", SW);
126
127
128
129     fprintf(stderr, "This program is intended to be a demo of the little cms\n"
130         "engine. Both lcms and this program are freeware. You can\n"
131         "obtain both in source code at http://www.littlecms.com\n"
132         "For suggestions, comments, bug reports etc. send mail to\n"
133         "info@littlecms.com\n\n");
134 }
135
136
137
138 // The toggles stuff
139
140 static
141 void HandleSwitches(int argc, char *argv[])
142 {
143     int s;
144
145     while ((s = xgetopt(argc, argv,
146         "bBC:c:d:D:eEgGI:i:L:l:m:M:nNO:o:p:P:QqT:t:V:v:WwxX!:")) != EOF) {
147
148     switch (s){
149
150         case '!': 
151             IncludePart = xoptarg;
152             break;
153
154         case 'b':
155         case 'B': 
156             BlackPointCompensation = TRUE;
157             break;
158
159         case 'c':
160         case 'C':
161             PrecalcMode = atoi(xoptarg);
162             if (PrecalcMode < 0 || PrecalcMode > 3)
163                 FatalError("Unknown precalc mode '%d'", PrecalcMode);
164             break;
165
166         case 'd':
167         case 'D': {
168             cmsFloat64Number ObserverAdaptationState = atof(xoptarg);
169             if (ObserverAdaptationState < 0 && 
170                 ObserverAdaptationState > 1.0)
171                 FatalError("Adaptation states should be between 0 and 1");
172
173             cmsSetAdaptationState(ObserverAdaptationState);
174                   }
175                   break;
176
177         case 'e':
178         case 'E': 
179             lIsFloat = FALSE;
180             break;
181
182         case 'g':
183         case 'G':
184             GamutCheck = TRUE;
185             break;
186
187         case 'i':
188         case 'I':
189             if (lIsDeviceLink)
190                 FatalError("icctrans: Device-link already specified");
191
192             cInProf = xoptarg;
193             break;  
194
195         case 'l':
196         case 'L': 
197             cInProf = xoptarg;
198             lIsDeviceLink = TRUE;
199             break;
200
201             // No extra intents for proofing
202         case 'm':
203         case 'M':
204             ProofingIntent = atoi(xoptarg);
205             if (ProofingIntent > 3)
206                 FatalError("Unknown Proofing Intent '%d'", ProofingIntent);        
207             break;      
208
209             // For compatibility
210         case 'n':
211         case 'N':
212             Verbose = 0;
213             break;
214
215             // Output profile        
216         case 'o':
217         case 'O':
218             if (lIsDeviceLink)
219                 FatalError("icctrans: Device-link already specified"); 
220             cOutProf = xoptarg;
221             break;
222
223             // Proofing profile
224         case 'p':
225         case 'P':
226             cProofing = xoptarg;
227             break;      
228
229             // Quantize to 16 bits
230         case 'q':
231         case 'Q': 
232             lQuantize = TRUE;
233             break;
234
235             // The intent
236         case 't':
237         case 'T':
238             Intent = atoi(xoptarg);            
239             break;
240
241             // Verbosity level
242         case 'V':
243         case 'v':
244             Verbose = atoi(xoptarg);
245             if (Verbose < 0 || Verbose > 3) {
246                 FatalError("Unknown verbosity level '%d'", Verbose);
247             }
248             break;
249
250             // Wide (16 bits)
251         case 'W':
252         case 'w':
253             Width16 = TRUE;
254             break;
255
256             // Hexadecimal        
257         case 'x':
258         case 'X':
259             InHexa = TRUE;
260             break;
261
262         default:            
263             FatalError("Unknown option - run without args to see valid ones.\n");
264             }       
265     }
266
267
268     // If output CGATS involved, switch to float
269     if ((argc - xoptind) > 2) {
270         lIsFloat = TRUE;
271     }
272 }
273
274
275
276 static
277 void SetRange(cmsFloat64Number range, cmsBool IsInput)
278 {
279     if (IsInput)
280         InputRange = range;
281     else
282         OutputRange = range;
283 }
284
285 // Populate a named color list with usual component names. 
286 // I am using the first Colorant channel to store the range, but it works since 
287 // this space is not used anyway.
288 static
289 cmsNAMEDCOLORLIST* ComponentNames(cmsColorSpaceSignature space, cmsBool IsInput)
290 {
291     cmsNAMEDCOLORLIST* out;
292     int i, n;
293     char Buffer[cmsMAX_PATH];
294
295     out = cmsAllocNamedColorList(0, 12, cmsMAXCHANNELS, "", "");
296     if (out == NULL) return NULL;
297
298     switch (space) {
299
300     case cmsSigXYZData:
301         SetRange(100, IsInput);
302         cmsAppendNamedColor(out, "X", NULL, NULL);
303         cmsAppendNamedColor(out, "Y", NULL, NULL);
304         cmsAppendNamedColor(out, "Z", NULL, NULL);
305         break;
306
307     case cmsSigLabData:
308         SetRange(1, IsInput);
309         cmsAppendNamedColor(out, "L*", NULL, NULL);
310         cmsAppendNamedColor(out, "a*", NULL, NULL);
311         cmsAppendNamedColor(out, "b*", NULL, NULL);
312         break;
313
314     case cmsSigLuvData:
315         SetRange(1, IsInput);
316         cmsAppendNamedColor(out, "L", NULL, NULL);
317         cmsAppendNamedColor(out, "u", NULL, NULL);
318         cmsAppendNamedColor(out, "v", NULL, NULL);
319         break;
320
321     case cmsSigYCbCrData:
322         SetRange(255, IsInput);
323         cmsAppendNamedColor(out, "Y", NULL, NULL );
324         cmsAppendNamedColor(out, "Cb", NULL, NULL);
325         cmsAppendNamedColor(out, "Cr", NULL, NULL);
326         break;
327
328
329     case cmsSigYxyData:
330         SetRange(1, IsInput);
331         cmsAppendNamedColor(out, "Y", NULL, NULL);
332         cmsAppendNamedColor(out, "x", NULL, NULL);
333         cmsAppendNamedColor(out, "y", NULL, NULL);
334         break;
335
336     case cmsSigRgbData:
337         SetRange(255, IsInput);
338         cmsAppendNamedColor(out, "R", NULL, NULL);
339         cmsAppendNamedColor(out, "G", NULL, NULL);
340         cmsAppendNamedColor(out, "B", NULL, NULL);
341         break;
342
343     case cmsSigGrayData:
344         SetRange(255, IsInput);
345         cmsAppendNamedColor(out, "G", NULL, NULL);      
346         break;
347
348     case cmsSigHsvData:
349         SetRange(255, IsInput);
350         cmsAppendNamedColor(out, "H", NULL, NULL);
351         cmsAppendNamedColor(out, "s", NULL, NULL);
352         cmsAppendNamedColor(out, "v", NULL, NULL);
353         break;
354
355     case cmsSigHlsData:
356         SetRange(255, IsInput);
357         cmsAppendNamedColor(out, "H", NULL, NULL);
358         cmsAppendNamedColor(out, "l", NULL, NULL);
359         cmsAppendNamedColor(out, "s", NULL, NULL);
360         break;
361
362     case cmsSigCmykData:
363         SetRange(1, IsInput);
364         cmsAppendNamedColor(out, "C", NULL, NULL);
365         cmsAppendNamedColor(out, "M", NULL, NULL);
366         cmsAppendNamedColor(out, "Y", NULL, NULL);                     
367         cmsAppendNamedColor(out, "K", NULL, NULL);
368         break;
369
370     case cmsSigCmyData:
371         SetRange(1, IsInput);
372         cmsAppendNamedColor(out, "C", NULL, NULL);
373         cmsAppendNamedColor(out, "M", NULL, NULL);
374         cmsAppendNamedColor(out, "Y", NULL, NULL);
375         break;
376
377     default:
378
379         SetRange(1, IsInput);
380
381         n = cmsChannelsOf(space);
382
383         for (i=0; i < n; i++) {
384
385             sprintf(Buffer, "Channel #%d", i + 1);
386             cmsAppendNamedColor(out, Buffer, NULL, NULL);
387         }
388     }
389
390     return out;
391
392 }
393
394
395 // Creates all needed color transforms
396 static
397 cmsBool OpenTransforms(void)
398 {
399     cmsHPROFILE hInput, hOutput, hProof;
400     cmsUInt32Number dwIn, dwOut, dwFlags;
401     cmsNAMEDCOLORLIST* List;
402     int i;
403
404     // We don't need cache
405     dwFlags = cmsFLAGS_NOCACHE;
406
407     if (lIsDeviceLink) {
408
409         hInput  = OpenStockProfile(0, cInProf);
410         if (hInput == NULL) return FALSE; 
411         hOutput = NULL;
412         hProof  = NULL;
413
414         if (cmsGetDeviceClass(hInput) == cmsSigNamedColorClass) {
415             OutputColorSpace  = cmsGetColorSpace(hInput);
416             InputColorSpace = cmsGetPCS(hInput);
417         }
418         else {
419             InputColorSpace  = cmsGetColorSpace(hInput);
420             OutputColorSpace = cmsGetPCS(hInput);
421         }
422
423         // Read colorant tables if present
424         if (cmsIsTag(hInput, cmsSigColorantTableTag)) {
425             List = cmsReadTag(hInput, cmsSigColorantTableTag);
426             InputColorant = cmsDupNamedColorList(List);
427             InputRange = 1;
428         }
429         else InputColorant = ComponentNames(InputColorSpace, TRUE);
430
431         if (cmsIsTag(hInput, cmsSigColorantTableOutTag)){
432
433             List = cmsReadTag(hInput, cmsSigColorantTableOutTag);
434             OutputColorant = cmsDupNamedColorList(List);
435             OutputRange = 1;
436         }
437         else OutputColorant = ComponentNames(OutputColorSpace, FALSE);
438
439     }
440     else {
441
442         hInput  = OpenStockProfile(0, cInProf);
443         if (hInput == NULL) return FALSE;
444
445         hOutput = OpenStockProfile(0, cOutProf);    
446         if (hOutput == NULL) return FALSE;
447         hProof  = NULL;
448
449
450         if (cmsGetDeviceClass(hInput) == cmsSigLinkClass ||
451             cmsGetDeviceClass(hOutput) == cmsSigLinkClass)   
452             FatalError("Use %cl flag for devicelink profiles!\n", SW);
453
454
455         InputColorSpace   = cmsGetColorSpace(hInput);
456         OutputColorSpace  = cmsGetColorSpace(hOutput);
457
458         // Read colorant tables if present
459         if (cmsIsTag(hInput, cmsSigColorantTableTag)) {
460             List = cmsReadTag(hInput, cmsSigColorantTableTag);
461             InputColorant = cmsDupNamedColorList(List);
462             if (cmsNamedColorCount(InputColorant) <= 3) 
463                 SetRange(255, TRUE);
464             else
465                 SetRange(1, TRUE);  // Inks are already divided by 100 in the formatter
466
467         }
468         else InputColorant = ComponentNames(InputColorSpace, TRUE);
469
470         if (cmsIsTag(hOutput, cmsSigColorantTableTag)){
471
472             List = cmsReadTag(hOutput, cmsSigColorantTableTag);
473             OutputColorant = cmsDupNamedColorList(List);
474             if (cmsNamedColorCount(OutputColorant) <= 3) 
475                 SetRange(255, FALSE);
476             else
477                 SetRange(1, FALSE);  // Inks are already divided by 100 in the formatter
478         }
479         else OutputColorant = ComponentNames(OutputColorSpace, FALSE);
480
481
482         if (cProofing != NULL) {
483
484             hProof = OpenStockProfile(0, cProofing);
485             if (hProof == NULL) return FALSE;
486             dwFlags |= cmsFLAGS_SOFTPROOFING;
487         }
488     }
489
490     // Print information on profiles
491     if (Verbose > 2) {
492
493         printf("Profile:\n");
494         PrintProfileInformation(hInput);
495
496         if (hOutput) {
497
498             printf("Output profile:\n");
499             PrintProfileInformation(hOutput);
500         }  
501
502         if (hProof != NULL) {
503             printf("Proofing profile:\n");
504             PrintProfileInformation(hProof);
505         }
506     }
507
508
509     // Input is always in floating point
510     dwIn  = cmsFormatterForColorspaceOfProfile(hInput, 0, TRUE);
511
512     if (lIsDeviceLink) {
513
514         dwOut = cmsFormatterForPCSOfProfile(hInput, lIsFloat ? 0 : 2, lIsFloat);
515     }
516     else {
517
518         // 16 bits or floating point (only on output)   
519         dwOut = cmsFormatterForColorspaceOfProfile(hOutput, lIsFloat ? 0 : 2, lIsFloat);
520     }
521
522     // For named color, there is a specialized formatter
523     if (cmsGetDeviceClass(hInput) == cmsSigNamedColorClass) {
524         dwOut = dwIn;
525         dwIn = TYPE_NAMED_COLOR_INDEX;
526         InputNamedColor = TRUE;
527     }
528
529     // Precision mode
530     switch (PrecalcMode) {
531
532        case 0: dwFlags |= cmsFLAGS_NOOPTIMIZE; break;
533        case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break;
534        case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break;
535        case 1: break;
536
537        default: 
538            FatalError("Unknown precalculation mode '%d'", PrecalcMode);
539     }
540
541
542     if (BlackPointCompensation) 
543         dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
544
545
546     if (GamutCheck) {
547
548         cmsUInt16Number Alarm[cmsMAXCHANNELS];
549
550         if (hProof == NULL)
551             FatalError("I need proofing profile -p for gamut checking!");
552
553         for (i=0; i < cmsMAXCHANNELS; i++)
554             Alarm[i] = 0xFFFF;
555
556         cmsSetAlarmCodes(Alarm);
557         dwFlags |= cmsFLAGS_GAMUTCHECK;            
558     }
559
560
561     // The main transform
562     hTrans = cmsCreateProofingTransform(hInput,  dwIn, hOutput, dwOut, hProof, Intent, ProofingIntent, dwFlags);
563
564     if (hProof) cmsCloseProfile(hProof);
565
566     if (hTrans == NULL) return FALSE;
567
568
569     // PCS Dump if requested
570     hTransXYZ = NULL; hTransLab = NULL;
571
572     if (hOutput && Verbose > 1) {
573
574         cmsHPROFILE hXYZ = cmsCreateXYZProfile();
575         cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
576
577         hTransXYZ = cmsCreateTransform(hInput, dwIn, hXYZ,  lIsFloat ? TYPE_XYZ_DBL : TYPE_XYZ_16, Intent, cmsFLAGS_NOCACHE);        
578         if (hTransXYZ == NULL) return FALSE;
579
580         hTransLab = cmsCreateTransform(hInput, dwIn, hLab,  lIsFloat? TYPE_Lab_DBL : TYPE_Lab_16, Intent, cmsFLAGS_NOCACHE);    
581         if (hTransLab == NULL) return FALSE;
582
583         cmsCloseProfile(hXYZ);
584         cmsCloseProfile(hLab);
585     } 
586
587     if (hInput) cmsCloseProfile(hInput);
588     if (hOutput) cmsCloseProfile(hOutput); 
589
590     return TRUE;
591 }
592
593
594 // Free open resources
595 static
596 void CloseTransforms(void)
597 {
598     if (InputColorant) cmsFreeNamedColorList(InputColorant);
599     if (OutputColorant) cmsFreeNamedColorList(OutputColorant);
600
601     if (hTrans) cmsDeleteTransform(hTrans);
602     if (hTransLab) cmsDeleteTransform(hTransLab);
603     if (hTransXYZ) cmsDeleteTransform(hTransXYZ);
604
605 }
606
607 // ---------------------------------------------------------------------------------------------------
608
609 // Get input from user
610 static
611 void GetLine(char* Buffer, const char* frm, ...)
612 {    
613     int res;
614     va_list args;
615
616     va_start(args, frm);
617
618     do {
619         if (xisatty(stdin)) 
620             vfprintf(stderr, frm, args);
621
622         res = scanf("%4095s", Buffer);
623
624         if (res < 0 || toupper(Buffer[0]) == 'Q') { // Quit?
625
626             CloseTransforms();
627
628             if (xisatty(stdin))  
629                 fprintf(stderr, "Done.\n");
630
631             exit(0);        
632         }
633     } while (res == 0);
634
635     va_end(args);  
636 }
637
638
639 // Print a value which is given in double floating point
640 static
641 void PrintFloatResults(cmsFloat64Number Value[])
642 {
643     cmsUInt32Number i, n;
644     char ChannelName[cmsMAX_PATH];
645     cmsFloat64Number v;
646
647     n = cmsChannelsOf(OutputColorSpace);
648     for (i=0; i < n; i++) {
649
650         if (OutputColorant != NULL) {
651
652             cmsNamedColorInfo(OutputColorant, i, ChannelName, NULL, NULL, NULL, NULL);         
653         }
654         else {
655             OutputRange = 1;
656             sprintf(ChannelName, "Channel #%d", i + 1);
657         }
658
659         v = (cmsFloat64Number) Value[i]* OutputRange;
660
661         if (lQuantize) 
662             v = floor(v + 0.5);
663
664         if (Verbose <= 0)
665             printf("%.4f ", v);
666         else
667             printf("%s=%.4f ", ChannelName, v);
668     }   
669
670     printf("\n");
671 }
672
673
674 // Get a named-color index
675 static
676 cmsUInt16Number GetIndex(void)
677 {
678     char Buffer[4096], Name[40], Prefix[40], Suffix[40];
679     int index, max;
680     const cmsNAMEDCOLORLIST* NamedColorList;
681     
682     NamedColorList = cmsGetNamedColorList(hTrans);
683     if (NamedColorList == NULL) return 0;
684
685     max = cmsNamedColorCount(NamedColorList)-1;
686
687     GetLine(Buffer, "Color index (0..%d)? ", max);
688     index = atoi(Buffer);
689
690     if (index > max)
691         FatalError("Named color %d out of range!", index);
692
693     cmsNamedColorInfo(NamedColorList, index, Name, Prefix, Suffix, NULL, NULL);
694
695     printf("\n%s %s %s\n", Prefix, Name, Suffix);
696
697     return (cmsUInt16Number) index;
698 }
699
700 // Read values from a text file or terminal
701 static
702 void TakeFloatValues(cmsFloat64Number Float[])
703 {
704     cmsUInt32Number i, n;
705     char ChannelName[cmsMAX_PATH];
706     char Buffer[cmsMAX_PATH];
707
708     if (xisatty(stdin))
709         fprintf(stderr, "\nEnter values, 'q' to quit\n");
710
711     if (InputNamedColor) {
712
713         // This is named color index, which is always cmsUInt16Number
714         cmsUInt16Number index = GetIndex();
715         memcpy(Float, &index, sizeof(cmsUInt16Number));
716         return;
717     }
718
719     n = cmsChannelsOf(InputColorSpace);
720     for (i=0; i < n; i++) {
721
722         if (InputColorant) {
723             cmsNamedColorInfo(InputColorant, i, ChannelName, NULL, NULL, NULL, NULL);          
724         }
725         else {
726             InputRange = 1;
727             sprintf(ChannelName, "Channel #%d", i+1);
728         }
729
730         GetLine(Buffer, "%s? ", ChannelName);
731
732         Float[i] = (cmsFloat64Number) atof(Buffer) / InputRange;
733     }       
734
735     if (xisatty(stdin))
736         fprintf(stderr, "\n");
737 }
738
739 static
740 void PrintPCSFloat(cmsFloat64Number Input[])
741 {
742     if (Verbose > 1 && hTransXYZ && hTransLab) {
743
744         cmsCIEXYZ XYZ = { 0, 0, 0 };
745         cmsCIELab Lab = { 0, 0, 0 };
746
747         if (hTransXYZ) cmsDoTransform(hTransXYZ, Input, &XYZ, 1);
748         if (hTransLab) cmsDoTransform(hTransLab, Input, &Lab, 1);
749
750         printf("[PCS] Lab=(%.4f,%.4f,%.4f) XYZ=(%.4f,%.4f,%.4f)\n", Lab.L, Lab.a, Lab.b, 
751             XYZ.X * 100.0, XYZ.Y * 100.0, XYZ.Z * 100.0);
752
753     }
754 }
755
756
757
758
759 // -----------------------------------------------------------------------------------------------
760
761 static
762 void PrintEncodedResults(cmsUInt16Number Encoded[])
763 {
764     cmsUInt32Number i, n;
765     char ChannelName[cmsMAX_PATH];
766     cmsUInt32Number v;
767
768     n = cmsChannelsOf(OutputColorSpace);
769     for (i=0; i < n; i++) {
770
771         if (OutputColorant != NULL) {
772
773             cmsNamedColorInfo(OutputColorant, i, ChannelName, NULL, NULL, NULL, NULL);          
774         }
775         else {          
776             sprintf(ChannelName, "Channel #%d", i + 1);
777         }
778
779         if (Verbose > 0)
780             printf("%s=", ChannelName);
781
782         v = Encoded[i];
783
784         if (InHexa) {
785
786             if (Width16)
787                 printf("0x%04X ", (int) floor(v + .5));
788             else
789                 printf("0x%02X ", (int) floor(v / 257. + .5));
790
791         } else {
792
793             if (Width16)
794                 printf("%d ", (int) floor(v + .5));
795             else
796                 printf("%d ", (int) floor(v / 257. + .5));
797         }
798
799     }   
800
801     printf("\n");
802 }
803
804 // Print XYZ/Lab values on verbose mode
805
806 static
807 void PrintPCSEncoded(cmsFloat64Number Input[])
808 {
809     if (Verbose > 1 && hTransXYZ && hTransLab) {
810
811         cmsUInt16Number XYZ[3], Lab[3];
812
813         if (hTransXYZ) cmsDoTransform(hTransXYZ, Input, XYZ, 1);
814         if (hTransLab) cmsDoTransform(hTransLab, Input, Lab, 1);
815
816         printf("[PCS] Lab=(0x%04X,0x%04X,0x%04X) XYZ=(0x%04X,0x%04X,0x%04X)\n", Lab[0], Lab[1], Lab[2], 
817             XYZ[0], XYZ[1], XYZ[2]);
818
819     }
820 }
821
822
823 // --------------------------------------------------------------------------------------
824
825
826
827 // Take a value from IT8 and scale it accordly to fill a cmsUInt16Number (0..FFFF)
828
829 static
830 cmsFloat64Number GetIT8Val(const char* Name, cmsFloat64Number Max)
831 {
832     const char* Val = cmsIT8GetData(hIT8in, CGATSPatch, Name);
833
834     if (Val == NULL) 
835         FatalError("Field '%s' not found", Name);
836
837     return atof(Val) / Max;
838
839 }
840
841
842 // Read input values from CGATS file.
843
844 static
845 void TakeCGATSValues(int nPatch, cmsFloat64Number Float[])
846 {
847
848     // At first take the name if SAMPLE_ID is present
849     if (cmsIT8GetPatchName(hIT8in, nPatch, CGATSPatch) == NULL) {
850         FatalError("Sorry, I need 'SAMPLE_ID' on input CGATS to operate.");
851     }
852
853
854     // Special handling for named color profiles. 
855     // Lookup the name in the names database (the transform)
856
857     if (InputNamedColor) {
858
859       const cmsNAMEDCOLORLIST* NamedColorList;
860       int index;
861
862       NamedColorList = cmsGetNamedColorList(hTrans);
863       if (NamedColorList == NULL) 
864           FatalError("Malformed named color profile");
865       
866       index = cmsNamedColorIndex(NamedColorList, CGATSPatch);
867       if (index < 0) 
868             FatalError("Named color '%s' not found in the profile", CGATSPatch); 
869
870         Float[0] = index;
871         return;
872     }
873
874     // Color is not a spot color, proceed.
875
876     switch (InputColorSpace) {
877
878         // Encoding should follow CGATS specification.
879
880     case cmsSigXYZData:
881         Float[0] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "XYZ_X") / 100.0;
882         Float[1] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "XYZ_Y") / 100.0;
883         Float[2] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "XYZ_Z") / 100.0;        
884         break;
885
886     case cmsSigLabData:
887         Float[0] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "LAB_L");
888         Float[1] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "LAB_A");
889         Float[2] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "LAB_B");        
890         break;
891
892
893     case cmsSigRgbData:
894         Float[0] = GetIT8Val("RGB_R", 255.0);
895         Float[1] = GetIT8Val("RGB_G", 255.0);
896         Float[2] = GetIT8Val("RGB_B", 255.0);
897         break;
898
899     case cmsSigGrayData:
900         Float[0] = GetIT8Val("GRAY", 255.0);
901         break;
902
903     case cmsSigCmykData:
904         Float[0] = GetIT8Val("CMYK_C", 1.0);
905         Float[1] = GetIT8Val("CMYK_M", 1.0);
906         Float[2] = GetIT8Val("CMYK_Y", 1.0);
907         Float[3] = GetIT8Val("CMYK_K", 1.0);
908         break;
909
910     case cmsSigCmyData:                        
911         Float[0] = GetIT8Val("CMY_C", 1.0);
912         Float[1] = GetIT8Val("CMY_M", 1.0);
913         Float[2] = GetIT8Val("CMY_Y", 1.0);
914         break;
915
916     case cmsSig1colorData:
917     case cmsSig2colorData:
918     case cmsSig3colorData:
919     case cmsSig4colorData:
920     case cmsSig5colorData:
921     case cmsSig6colorData:
922     case cmsSig7colorData:
923     case cmsSig8colorData:
924     case cmsSig9colorData:
925     case cmsSig10colorData:
926     case cmsSig11colorData:
927     case cmsSig12colorData:
928     case cmsSig13colorData:
929     case cmsSig14colorData:
930     case cmsSig15colorData:
931         {
932             cmsUInt32Number i, n;
933
934             n = cmsChannelsOf(InputColorSpace);
935             for (i=0; i < n; i++) { 
936
937                 char Buffer[255];
938
939                 sprintf(Buffer, "%dCLR_%d", n, i+1);
940                 Float[i] = GetIT8Val(Buffer, 100.0);
941             }
942
943         }
944         break;
945
946     default: 
947         {
948             cmsUInt32Number i, n;
949
950             n = cmsChannelsOf(InputColorSpace);
951             for (i=0; i < n; i++) { 
952
953                 char Buffer[255];
954
955                 sprintf(Buffer, "CHAN_%d", i+1);
956                 Float[i] = GetIT8Val(Buffer, 1.0);
957             }
958
959         }
960     }
961
962 }
963
964 static
965 void SetCGATSfld(const char* Col, cmsFloat64Number Val)
966 {
967     if (lQuantize) 
968         Val = floor(Val + 0.5);
969
970     if (!cmsIT8SetDataDbl(hIT8out, CGATSPatch, Col, Val)) {
971         FatalError("couldn't set '%s' on output cgats '%s'", Col, CGATSoutFilename);
972     }
973 }
974
975
976
977 static
978 void PutCGATSValues(cmsFloat64Number Float[])
979 {   
980     cmsIT8SetData(hIT8out, CGATSPatch, "SAMPLE_ID", CGATSPatch);
981     switch (OutputColorSpace) {
982
983
984     // Encoding should follow CGATS specification.
985
986     case cmsSigXYZData:
987
988         SetCGATSfld("XYZ_X", Float[0] * 100.0);
989         SetCGATSfld("XYZ_Y", Float[1] * 100.0);
990         SetCGATSfld("XYZ_Z", Float[2] * 100.0);                    
991         break;
992
993     case cmsSigLabData:
994
995         SetCGATSfld("LAB_L", Float[0]);
996         SetCGATSfld("LAB_A", Float[1]);
997         SetCGATSfld("LAB_B", Float[2]);                    
998         break;
999
1000
1001     case cmsSigRgbData:
1002         SetCGATSfld("RGB_R", Float[0] * 255.0);
1003         SetCGATSfld("RGB_G", Float[1] * 255.0);
1004         SetCGATSfld("RGB_B", Float[2] * 255.0);
1005         break;
1006
1007     case cmsSigGrayData:
1008         SetCGATSfld("GRAY", Float[0] * 255.0);                    
1009         break;
1010
1011     case cmsSigCmykData:
1012         SetCGATSfld("CMYK_C", Float[0]);
1013         SetCGATSfld("CMYK_M", Float[1]);
1014         SetCGATSfld("CMYK_Y", Float[2]);
1015         SetCGATSfld("CMYK_K", Float[3]);
1016         break;
1017
1018     case cmsSigCmyData:
1019         SetCGATSfld("CMY_C", Float[0]);
1020         SetCGATSfld("CMY_M", Float[1]);
1021         SetCGATSfld("CMY_Y", Float[2]);                 
1022         break;
1023
1024     case cmsSig1colorData:
1025     case cmsSig2colorData:
1026     case cmsSig3colorData:
1027     case cmsSig4colorData:
1028     case cmsSig5colorData:
1029     case cmsSig6colorData:
1030     case cmsSig7colorData:
1031     case cmsSig8colorData:
1032     case cmsSig9colorData:
1033     case cmsSig10colorData:
1034     case cmsSig11colorData:
1035     case cmsSig12colorData:
1036     case cmsSig13colorData:
1037     case cmsSig14colorData:
1038     case cmsSig15colorData:
1039         {
1040
1041             cmsUInt32Number i, n;
1042
1043             n = cmsChannelsOf(InputColorSpace);
1044             for (i=0; i < n; i++) { 
1045
1046                 char Buffer[255];
1047
1048                 sprintf(Buffer, "%dCLR_%d", n, i+1);
1049
1050                 SetCGATSfld(Buffer, Float[i] * 100.0);
1051             }
1052         }
1053         break;
1054
1055     default: 
1056         {
1057
1058             cmsUInt32Number i, n;
1059
1060             n = cmsChannelsOf(InputColorSpace);
1061             for (i=0; i < n; i++) { 
1062
1063                 char Buffer[255];
1064
1065                 sprintf(Buffer, "CHAN_%d", i+1);
1066
1067                 SetCGATSfld(Buffer, Float[i]);
1068             }
1069         }
1070     }
1071 }
1072
1073
1074
1075 // Create data format 
1076 static
1077 void SetOutputDataFormat(void) 
1078 {
1079     cmsIT8DefineDblFormat(hIT8out, "%.4g");
1080     cmsIT8SetPropertyStr(hIT8out, "ORIGINATOR", "icctrans");
1081
1082     if (IncludePart != NULL) 
1083         cmsIT8SetPropertyStr(hIT8out, ".INCLUDE", IncludePart);
1084
1085     cmsIT8SetComment(hIT8out, "Data follows");
1086     cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_SETS", nMaxPatches);
1087
1088
1089     switch (OutputColorSpace) {
1090
1091
1092         // Encoding should follow CGATS specification.
1093
1094     case cmsSigXYZData:
1095         cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 4);
1096         cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID");
1097         cmsIT8SetDataFormat(hIT8out, 1, "XYZ_X");
1098         cmsIT8SetDataFormat(hIT8out, 2, "XYZ_Y");
1099         cmsIT8SetDataFormat(hIT8out, 3, "XYZ_Z");
1100         break;
1101
1102     case cmsSigLabData:
1103         cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 4);
1104         cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID");
1105         cmsIT8SetDataFormat(hIT8out, 1, "LAB_L");
1106         cmsIT8SetDataFormat(hIT8out, 2, "LAB_A");
1107         cmsIT8SetDataFormat(hIT8out, 3, "LAB_B");
1108         break;
1109
1110
1111     case cmsSigRgbData:
1112         cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 4);
1113         cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID");
1114         cmsIT8SetDataFormat(hIT8out, 1, "RGB_R");
1115         cmsIT8SetDataFormat(hIT8out, 2, "RGB_G");
1116         cmsIT8SetDataFormat(hIT8out, 3, "RGB_B");
1117         break;
1118
1119     case cmsSigGrayData:                
1120         cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 2);
1121         cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID");
1122         cmsIT8SetDataFormat(hIT8out, 1, "GRAY");
1123         break;
1124
1125     case cmsSigCmykData:
1126         cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 5);
1127         cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID");
1128         cmsIT8SetDataFormat(hIT8out, 1, "CMYK_C");
1129         cmsIT8SetDataFormat(hIT8out, 2, "CMYK_M");
1130         cmsIT8SetDataFormat(hIT8out, 3, "CMYK_Y");
1131         cmsIT8SetDataFormat(hIT8out, 4, "CMYK_K");
1132         break;
1133
1134     case cmsSigCmyData:
1135         cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 4);
1136         cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID");
1137         cmsIT8SetDataFormat(hIT8out, 1, "CMY_C");
1138         cmsIT8SetDataFormat(hIT8out, 2, "CMY_M");
1139         cmsIT8SetDataFormat(hIT8out, 3, "CMY_Y");                   
1140         break;
1141
1142     case cmsSig1colorData:
1143     case cmsSig2colorData:
1144     case cmsSig3colorData:
1145     case cmsSig4colorData:
1146     case cmsSig5colorData:
1147     case cmsSig6colorData:
1148     case cmsSig7colorData:
1149     case cmsSig8colorData:
1150     case cmsSig9colorData:
1151     case cmsSig10colorData:
1152     case cmsSig11colorData:
1153     case cmsSig12colorData:
1154     case cmsSig13colorData:
1155     case cmsSig14colorData:
1156     case cmsSig15colorData:
1157         {
1158             int i, n;
1159             char Buffer[255];
1160
1161             n = cmsChannelsOf(OutputColorSpace);
1162             cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", n+1);
1163             cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID");
1164
1165             for (i=1; i <= n; i++) {
1166                 sprintf(Buffer, "%dCLR_%d", n, i);
1167                 cmsIT8SetDataFormat(hIT8out, i, Buffer);
1168             }
1169         }
1170         break;
1171
1172     default: {
1173
1174         int i, n;
1175         char Buffer[255];
1176
1177         n = cmsChannelsOf(OutputColorSpace);
1178         cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", n+1);
1179         cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID");
1180
1181         for (i=1; i <= n; i++) {
1182             sprintf(Buffer, "CHAN_%d", i);
1183             cmsIT8SetDataFormat(hIT8out, i, Buffer);
1184         }
1185     }
1186     }
1187 }
1188
1189 // Open CGATS if specified
1190
1191 static
1192 void OpenCGATSFiles(int argc, char *argv[])
1193 {    
1194     int nParams = argc - xoptind;
1195
1196     if (nParams >= 1)  {
1197
1198         hIT8in = cmsIT8LoadFromFile(0, argv[xoptind]);
1199
1200         if (hIT8in == NULL) 
1201             FatalError("'%s' is not recognized as a CGATS file", argv[xoptind]);
1202
1203         nMaxPatches = (int) cmsIT8GetPropertyDbl(hIT8in, "NUMBER_OF_SETS");     
1204     }
1205
1206     if (nParams == 2) {
1207
1208         hIT8out = cmsIT8Alloc(NULL);            
1209         SetOutputDataFormat();
1210         strncpy(CGATSoutFilename, argv[xoptind+1], cmsMAX_PATH-1);      
1211     }
1212
1213     if (nParams > 2) FatalError("Too many CGATS files");
1214 }
1215
1216
1217
1218 // The main sink
1219 int main(int argc, char *argv[])
1220 {    
1221     cmsUInt16Number Output[cmsMAXCHANNELS];
1222     cmsFloat64Number OutputFloat[cmsMAXCHANNELS];
1223     cmsFloat64Number InputFloat[cmsMAXCHANNELS];
1224
1225     int nPatch = 0;
1226
1227     fprintf(stderr, "LittleCMS ColorSpace conversion calculator - 4.2 [LittleCMS %2.2f]\n", LCMS_VERSION / 1000.0);
1228
1229     InitUtils("transicc");
1230
1231     Verbose = 1;
1232
1233     if (argc == 1) {
1234
1235         Help();              
1236         return 0;
1237     }
1238
1239     HandleSwitches(argc, argv);
1240
1241     // Open profiles, create transforms
1242     if (!OpenTransforms()) return 1;
1243
1244     // Open CGATS input if specified
1245     OpenCGATSFiles(argc, argv);
1246
1247     // Main loop: read all values and convert them
1248     for(;;) {
1249
1250         if (hIT8in != NULL) {
1251
1252             if (nPatch >= nMaxPatches) break;
1253             TakeCGATSValues(nPatch++, InputFloat);
1254
1255         } else {
1256
1257             if (feof(stdin)) break;         
1258             TakeFloatValues(InputFloat);
1259
1260         }
1261
1262         if (lIsFloat) 
1263             cmsDoTransform(hTrans, InputFloat, OutputFloat, 1);
1264         else
1265             cmsDoTransform(hTrans, InputFloat, Output, 1);
1266
1267
1268         if (hIT8out != NULL) {
1269
1270             PutCGATSValues(OutputFloat);
1271         }
1272         else {
1273
1274             if (lIsFloat) {
1275                 PrintFloatResults(OutputFloat); PrintPCSFloat(InputFloat);
1276             }
1277             else {
1278                 PrintEncodedResults(Output);   PrintPCSEncoded(InputFloat);      
1279             }
1280
1281         }
1282     }
1283
1284
1285     // Cleanup
1286     CloseTransforms();
1287
1288     if (hIT8in)
1289         cmsIT8Free(hIT8in);
1290
1291     if (hIT8out) {      
1292         cmsIT8SaveToFile(hIT8out, CGATSoutFilename);
1293         cmsIT8Free(hIT8out);
1294     }
1295
1296     // All is ok
1297     return 0;     
1298 }
1299
1300