Tizen 2.0 Release
[external/lcms.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(hInput, cmsSigColorantTableTag);
473             OutputColorant = cmsDupNamedColorList(List);
474         }
475         else OutputColorant = ComponentNames(OutputColorSpace, FALSE);
476
477
478         if (cProofing != NULL) {
479
480             hProof = OpenStockProfile(0, cProofing);
481             if (hProof == NULL) return FALSE;
482             dwFlags |= cmsFLAGS_SOFTPROOFING;
483         }
484     }
485
486     // Print information on profiles
487     if (Verbose > 2) {
488
489         printf("Profile:\n");
490         PrintProfileInformation(hInput);
491
492         if (hOutput) {
493
494             printf("Output profile:\n");
495             PrintProfileInformation(hOutput);
496         }  
497
498         if (hProof != NULL) {
499             printf("Proofing profile:\n");
500             PrintProfileInformation(hProof);
501         }
502     }
503
504
505     // Input is always in floating point
506     dwIn  = cmsFormatterForColorspaceOfProfile(hInput, 0, TRUE);
507
508     if (lIsDeviceLink) {
509
510         dwOut = cmsFormatterForPCSOfProfile(hInput, lIsFloat ? 0 : 2, lIsFloat);
511     }
512     else {
513
514         // 16 bits or floating point (only on output)   
515         dwOut = cmsFormatterForColorspaceOfProfile(hOutput, lIsFloat ? 0 : 2, lIsFloat);
516     }
517
518     // For named color, there is a specialized formatter
519     if (cmsGetDeviceClass(hInput) == cmsSigNamedColorClass) {
520         dwOut = dwIn;
521         dwIn = TYPE_NAMED_COLOR_INDEX;
522         InputNamedColor = TRUE;
523     }
524
525     // Precision mode
526     switch (PrecalcMode) {
527
528        case 0: dwFlags |= cmsFLAGS_NOOPTIMIZE; break;
529        case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break;
530        case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break;
531        case 1: break;
532
533        default: 
534            FatalError("Unknown precalculation mode '%d'", PrecalcMode);
535     }
536
537
538     if (BlackPointCompensation) 
539         dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
540
541
542     if (GamutCheck) {
543
544         cmsUInt16Number Alarm[cmsMAXCHANNELS];
545
546         if (hProof == NULL)
547             FatalError("I need proofing profile -p for gamut checking!");
548
549         for (i=0; i < cmsMAXCHANNELS; i++)
550             Alarm[i] = 0xFFFF;
551
552         cmsSetAlarmCodes(Alarm);
553         dwFlags |= cmsFLAGS_GAMUTCHECK;            
554     }
555
556
557     // The main transform
558     hTrans = cmsCreateProofingTransform(hInput,  dwIn, hOutput, dwOut, hProof, Intent, ProofingIntent, dwFlags);
559
560     if (hProof) cmsCloseProfile(hProof);
561
562     if (hTrans == NULL) return FALSE;
563
564
565     // PCS Dump if requested
566     hTransXYZ = NULL; hTransLab = NULL;
567
568     if (hOutput && Verbose > 1) {
569
570         cmsHPROFILE hXYZ = cmsCreateXYZProfile();
571         cmsHPROFILE hLab = cmsCreateLab4Profile(NULL);
572
573         hTransXYZ = cmsCreateTransform(hInput, dwIn, hXYZ,  lIsFloat ? TYPE_XYZ_DBL : TYPE_XYZ_16, Intent, cmsFLAGS_NOCACHE);        
574         if (hTransXYZ == NULL) return FALSE;
575
576         hTransLab = cmsCreateTransform(hInput, dwIn, hLab,  lIsFloat? TYPE_Lab_DBL : TYPE_Lab_16, Intent, cmsFLAGS_NOCACHE);    
577         if (hTransLab == NULL) return FALSE;
578
579         cmsCloseProfile(hXYZ);
580         cmsCloseProfile(hLab);
581     } 
582
583     if (hInput) cmsCloseProfile(hInput);
584     if (hOutput) cmsCloseProfile(hOutput); 
585
586     return TRUE;
587 }
588
589
590 // Free open resources
591 static
592 void CloseTransforms(void)
593 {
594     if (InputColorant) cmsFreeNamedColorList(InputColorant);
595     if (OutputColorant) cmsFreeNamedColorList(OutputColorant);
596
597     if (hTrans) cmsDeleteTransform(hTrans);
598     if (hTransLab) cmsDeleteTransform(hTransLab);
599     if (hTransXYZ) cmsDeleteTransform(hTransXYZ);
600
601 }
602
603 // ---------------------------------------------------------------------------------------------------
604
605 // Get input from user
606 static
607 void GetLine(char* Buffer, const char* frm, ...)
608 {    
609     int res;
610     va_list args;
611
612     va_start(args, frm);
613
614     do {
615         if (xisatty(stdin)) 
616             vfprintf(stderr, frm, args);
617
618         res = scanf("%4095s", Buffer);
619
620         if (res < 0 || toupper(Buffer[0]) == 'Q') { // Quit?
621
622             CloseTransforms();
623
624             if (xisatty(stdin))  
625                 fprintf(stderr, "Done.\n");
626
627             exit(0);        
628         }
629     } while (res == 0);
630
631     va_end(args);  
632 }
633
634
635 // Print a value which is given in double floating point
636 static
637 void PrintFloatResults(cmsFloat64Number Value[])
638 {
639     cmsUInt32Number i, n;
640     char ChannelName[cmsMAX_PATH];
641     cmsFloat64Number v;
642
643     n = cmsChannelsOf(OutputColorSpace);
644     for (i=0; i < n; i++) {
645
646         if (OutputColorant != NULL) {
647
648             cmsNamedColorInfo(OutputColorant, i, ChannelName, NULL, NULL, NULL, NULL);         
649         }
650         else {
651             OutputRange = 1;
652             sprintf(ChannelName, "Channel #%d", i + 1);
653         }
654
655         v = (cmsFloat64Number) Value[i]* OutputRange;
656
657         if (lQuantize) 
658             v = floor(v + 0.5);
659
660         if (Verbose <= 0)
661             printf("%.4f ", v);
662         else
663             printf("%s=%.4f ", ChannelName, v);
664     }   
665
666     printf("\n");
667 }
668
669
670 // Get a named-color index
671 static
672 cmsUInt16Number GetIndex(void)
673 {
674     char Buffer[4096], Name[40], Prefix[40], Suffix[40];
675     int index, max;
676     const cmsNAMEDCOLORLIST* NamedColorList;
677     
678     NamedColorList = cmsGetNamedColorList(hTrans);
679     if (NamedColorList == NULL) return 0;
680
681     max = cmsNamedColorCount(NamedColorList)-1;
682
683     GetLine(Buffer, "Color index (0..%d)? ", max);
684     index = atoi(Buffer);
685
686     if (index > max)
687         FatalError("Named color %d out of range!", index);
688
689     cmsNamedColorInfo(NamedColorList, index, Name, Prefix, Suffix, NULL, NULL);
690
691     printf("\n%s %s %s\n", Prefix, Name, Suffix);
692
693     return (cmsUInt16Number) index;
694 }
695
696 // Read values from a text file or terminal
697 static
698 void TakeFloatValues(cmsFloat64Number Float[])
699 {
700     cmsUInt32Number i, n;
701     char ChannelName[cmsMAX_PATH];
702     char Buffer[cmsMAX_PATH];
703
704     if (xisatty(stdin))
705         fprintf(stderr, "\nEnter values, 'q' to quit\n");
706
707     if (InputNamedColor) {
708
709         // This is named color index, which is always cmsUInt16Number
710         cmsUInt16Number index = GetIndex();
711         memcpy(Float, &index, sizeof(cmsUInt16Number));
712         return;
713     }
714
715     n = cmsChannelsOf(InputColorSpace);
716     for (i=0; i < n; i++) {
717
718         if (InputColorant) {
719             cmsNamedColorInfo(InputColorant, i, ChannelName, NULL, NULL, NULL, NULL);          
720         }
721         else {
722             InputRange = 1;
723             sprintf(ChannelName, "Channel #%d", i+1);
724         }
725
726         GetLine(Buffer, "%s? ", ChannelName);
727
728         Float[i] = (cmsFloat64Number) atof(Buffer) / InputRange;
729     }       
730
731     if (xisatty(stdin))
732         fprintf(stderr, "\n");
733 }
734
735 static
736 void PrintPCSFloat(cmsFloat64Number Input[])
737 {
738     if (Verbose > 1 && hTransXYZ && hTransLab) {
739
740         cmsCIEXYZ XYZ = { 0, 0, 0 };
741         cmsCIELab Lab = { 0, 0, 0 };
742
743         if (hTransXYZ) cmsDoTransform(hTransXYZ, Input, &XYZ, 1);
744         if (hTransLab) cmsDoTransform(hTransLab, Input, &Lab, 1);
745
746         printf("[PCS] Lab=(%.4f,%.4f,%.4f) XYZ=(%.4f,%.4f,%.4f)\n", Lab.L, Lab.a, Lab.b, 
747             XYZ.X * 100.0, XYZ.Y * 100.0, XYZ.Z * 100.0);
748
749     }
750 }
751
752
753
754
755 // -----------------------------------------------------------------------------------------------
756
757 static
758 void PrintEncodedResults(cmsUInt16Number Encoded[])
759 {
760     cmsUInt32Number i, n;
761     char ChannelName[cmsMAX_PATH];
762     cmsUInt32Number v;
763
764     n = cmsChannelsOf(OutputColorSpace);
765     for (i=0; i < n; i++) {
766
767         if (OutputColorant != NULL) {
768
769             cmsNamedColorInfo(OutputColorant, i, ChannelName, NULL, NULL, NULL, NULL);          
770         }
771         else {          
772             sprintf(ChannelName, "Channel #%d", i + 1);
773         }
774
775         if (Verbose > 0)
776             printf("%s=", ChannelName);
777
778         v = Encoded[i];
779
780         if (InHexa) {
781
782             if (Width16)
783                 printf("0x%04X ", (int) floor(v + .5));
784             else
785                 printf("0x%02X ", (int) floor(v / 257. + .5));
786
787         } else {
788
789             if (Width16)
790                 printf("%d ", (int) floor(v + .5));
791             else
792                 printf("%d ", (int) floor(v / 257. + .5));
793         }
794
795     }   
796
797     printf("\n");
798 }
799
800 // Print XYZ/Lab values on verbose mode
801
802 static
803 void PrintPCSEncoded(cmsFloat64Number Input[])
804 {
805     if (Verbose > 1 && hTransXYZ && hTransLab) {
806
807         cmsUInt16Number XYZ[3], Lab[3];
808
809         if (hTransXYZ) cmsDoTransform(hTransXYZ, Input, XYZ, 1);
810         if (hTransLab) cmsDoTransform(hTransLab, Input, Lab, 1);
811
812         printf("[PCS] Lab=(0x%04X,0x%04X,0x%04X) XYZ=(0x%04X,0x%04X,0x%04X)\n", Lab[0], Lab[1], Lab[2], 
813             XYZ[0], XYZ[1], XYZ[2]);
814
815     }
816 }
817
818
819 // --------------------------------------------------------------------------------------
820
821
822
823 // Take a value from IT8 and scale it accordly to fill a cmsUInt16Number (0..FFFF)
824
825 static
826 cmsFloat64Number GetIT8Val(const char* Name, cmsFloat64Number Max)
827 {
828     const char* Val = cmsIT8GetData(hIT8in, CGATSPatch, Name);
829
830     if (Val == NULL) 
831         FatalError("Field '%s' not found", Name);
832
833     return atof(Val) / Max;
834
835 }
836
837
838 // Read input values from CGATS file.
839
840 static
841 void TakeCGATSValues(int nPatch, cmsFloat64Number Float[])
842 {
843
844     // At first take the name if SAMPLE_ID is present
845     if (cmsIT8GetPatchName(hIT8in, nPatch, CGATSPatch) == NULL) {
846         FatalError("Sorry, I need 'SAMPLE_ID' on input CGATS to operate.");
847     }
848
849
850     // Special handling for named color profiles. 
851     // Lookup the name in the names database (the transform)
852
853     if (InputNamedColor) {
854
855       const cmsNAMEDCOLORLIST* NamedColorList;
856       int index;
857
858       NamedColorList = cmsGetNamedColorList(hTrans);
859       if (NamedColorList == NULL) 
860           FatalError("Malformed named color profile");
861       
862       index = cmsNamedColorIndex(NamedColorList, CGATSPatch);
863       if (index < 0) 
864             FatalError("Named color '%s' not found in the profile", CGATSPatch); 
865
866         Float[0] = index;
867         return;
868     }
869
870     // Color is not a spot color, proceed.
871
872     switch (InputColorSpace) {
873
874         // Encoding should follow CGATS specification.
875
876     case cmsSigXYZData:
877         Float[0] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "XYZ_X") / 100.0;
878         Float[1] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "XYZ_Y") / 100.0;
879         Float[2] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "XYZ_Z") / 100.0;        
880         break;
881
882     case cmsSigLabData:
883         Float[0] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "LAB_L");
884         Float[1] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "LAB_A");
885         Float[2] = cmsIT8GetDataDbl(hIT8in, CGATSPatch, "LAB_B");        
886         break;
887
888
889     case cmsSigRgbData:
890         Float[0] = GetIT8Val("RGB_R", 255.0);
891         Float[1] = GetIT8Val("RGB_G", 255.0);
892         Float[2] = GetIT8Val("RGB_B", 255.0);
893         break;
894
895     case cmsSigGrayData:
896         Float[0] = GetIT8Val("GRAY", 255.0);
897         break;
898
899     case cmsSigCmykData:
900         Float[0] = GetIT8Val("CMYK_C", 1.0);
901         Float[1] = GetIT8Val("CMYK_M", 1.0);
902         Float[2] = GetIT8Val("CMYK_Y", 1.0);
903         Float[3] = GetIT8Val("CMYK_K", 1.0);
904         break;
905
906     case cmsSigCmyData:                        
907         Float[0] = GetIT8Val("CMY_C", 1.0);
908         Float[1] = GetIT8Val("CMY_M", 1.0);
909         Float[2] = GetIT8Val("CMY_Y", 1.0);
910         break;
911
912     default: 
913         {
914             cmsUInt32Number i, n;
915
916             n = cmsChannelsOf(InputColorSpace);
917             for (i=0; i < n; i++) { 
918
919                 char Buffer[255];
920
921                 sprintf(Buffer, "CHAN_%d", i+1);
922                 Float[i] = GetIT8Val(Buffer, 1.0);
923             }
924
925         }
926     }
927
928 }
929
930 static
931 void SetCGATSfld(const char* Col, cmsFloat64Number Val)
932 {
933     if (lQuantize) 
934         Val = floor(Val + 0.5);
935
936     if (!cmsIT8SetDataDbl(hIT8out, CGATSPatch, Col, Val)) {
937         FatalError("couldn't set '%s' on output cgats '%s'", Col, CGATSoutFilename);
938     }
939 }
940
941
942
943 static
944 void PutCGATSValues(cmsFloat64Number Float[])
945 {   
946     cmsIT8SetData(hIT8out, CGATSPatch, "SAMPLE_ID", CGATSPatch);
947     switch (OutputColorSpace) {
948
949
950     // Encoding should follow CGATS specification.
951
952     case cmsSigXYZData:
953
954         SetCGATSfld("XYZ_X", Float[0] * 100.0);
955         SetCGATSfld("XYZ_Y", Float[1] * 100.0);
956         SetCGATSfld("XYZ_Z", Float[2] * 100.0);                    
957         break;
958
959     case cmsSigLabData:
960
961         SetCGATSfld("LAB_L", Float[0]);
962         SetCGATSfld("LAB_A", Float[1]);
963         SetCGATSfld("LAB_B", Float[2]);                    
964         break;
965
966
967     case cmsSigRgbData:
968         SetCGATSfld("RGB_R", Float[0] * 255.0);
969         SetCGATSfld("RGB_G", Float[1] * 255.0);
970         SetCGATSfld("RGB_B", Float[2] * 255.0);
971         break;
972
973     case cmsSigGrayData:
974         SetCGATSfld("GRAY", Float[0] * 255.0);                    
975         break;
976
977     case cmsSigCmykData:
978         SetCGATSfld("CMYK_C", Float[0]);
979         SetCGATSfld("CMYK_M", Float[1]);
980         SetCGATSfld("CMYK_Y", Float[2]);
981         SetCGATSfld("CMYK_K", Float[3]);
982         break;
983
984     case cmsSigCmyData:
985         SetCGATSfld("CMY_C", Float[0]);
986         SetCGATSfld("CMY_M", Float[1]);
987         SetCGATSfld("CMY_Y", Float[2]);                 
988         break;
989
990     default: 
991         {
992
993             cmsUInt32Number i, n;
994
995             n = cmsChannelsOf(InputColorSpace);
996             for (i=0; i < n; i++) { 
997
998                 char Buffer[255];
999
1000                 sprintf(Buffer, "CHAN_%d", i+1);
1001
1002                 SetCGATSfld(Buffer, Float[i]);
1003             }
1004         }
1005     }
1006 }
1007
1008
1009
1010 // Create data format 
1011 static
1012 void SetOutputDataFormat(void) 
1013 {
1014     cmsIT8DefineDblFormat(hIT8out, "%.4g");
1015     cmsIT8SetPropertyStr(hIT8out, "ORIGINATOR", "icctrans");
1016
1017     if (IncludePart != NULL) 
1018         cmsIT8SetPropertyStr(hIT8out, ".INCLUDE", IncludePart);
1019
1020     cmsIT8SetComment(hIT8out, "Data follows");
1021     cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_SETS", nMaxPatches);
1022
1023
1024     switch (OutputColorSpace) {
1025
1026
1027         // Encoding should follow CGATS specification.
1028
1029     case cmsSigXYZData:
1030         cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 4);
1031         cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID");
1032         cmsIT8SetDataFormat(hIT8out, 1, "XYZ_X");
1033         cmsIT8SetDataFormat(hIT8out, 2, "XYZ_Y");
1034         cmsIT8SetDataFormat(hIT8out, 3, "XYZ_Z");
1035         break;
1036
1037     case cmsSigLabData:
1038         cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 4);
1039         cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID");
1040         cmsIT8SetDataFormat(hIT8out, 1, "LAB_L");
1041         cmsIT8SetDataFormat(hIT8out, 2, "LAB_A");
1042         cmsIT8SetDataFormat(hIT8out, 3, "LAB_B");
1043         break;
1044
1045
1046     case cmsSigRgbData:
1047         cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 4);
1048         cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID");
1049         cmsIT8SetDataFormat(hIT8out, 1, "RGB_R");
1050         cmsIT8SetDataFormat(hIT8out, 2, "RGB_G");
1051         cmsIT8SetDataFormat(hIT8out, 3, "RGB_B");
1052         break;
1053
1054     case cmsSigGrayData:                
1055         cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 2);
1056         cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID");
1057         cmsIT8SetDataFormat(hIT8out, 1, "GRAY");
1058         break;
1059
1060     case cmsSigCmykData:
1061         cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 5);
1062         cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID");
1063         cmsIT8SetDataFormat(hIT8out, 1, "CMYK_C");
1064         cmsIT8SetDataFormat(hIT8out, 2, "CMYK_M");
1065         cmsIT8SetDataFormat(hIT8out, 3, "CMYK_Y");
1066         cmsIT8SetDataFormat(hIT8out, 4, "CMYK_K");
1067         break;
1068
1069     case cmsSigCmyData:
1070         cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", 4);
1071         cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID");
1072         cmsIT8SetDataFormat(hIT8out, 1, "CMY_C");
1073         cmsIT8SetDataFormat(hIT8out, 2, "CMY_M");
1074         cmsIT8SetDataFormat(hIT8out, 3, "CMY_Y");                   
1075         break;
1076
1077     default: {
1078
1079         int i, n;
1080         char Buffer[255];
1081
1082         n = cmsChannelsOf(OutputColorSpace);
1083         cmsIT8SetPropertyDbl(hIT8out, "NUMBER_OF_FIELDS", n+1);
1084         cmsIT8SetDataFormat(hIT8out, 0, "SAMPLE_ID");
1085
1086         for (i=1; i <= n; i++) {
1087             sprintf(Buffer, "CHAN_%d", i);
1088             cmsIT8SetDataFormat(hIT8out, i, Buffer);
1089         }
1090     }
1091     }
1092 }
1093
1094 // Open CGATS if specified
1095
1096 static
1097 void OpenCGATSFiles(int argc, char *argv[])
1098 {    
1099     int nParams = argc - xoptind;
1100
1101     if (nParams >= 1)  {
1102
1103         hIT8in = cmsIT8LoadFromFile(0, argv[xoptind]);
1104
1105         if (hIT8in == NULL) 
1106             FatalError("'%s' is not recognized as a CGATS file", argv[xoptind]);
1107
1108         nMaxPatches = (int) cmsIT8GetPropertyDbl(hIT8in, "NUMBER_OF_SETS");     
1109     }
1110
1111     if (nParams == 2) {
1112
1113         hIT8out = cmsIT8Alloc(NULL);            
1114         SetOutputDataFormat();
1115         strncpy(CGATSoutFilename, argv[xoptind+1], cmsMAX_PATH-1);      
1116     }
1117
1118     if (nParams > 2) FatalError("Too many CGATS files");
1119 }
1120
1121
1122
1123 // The main sink
1124 int main(int argc, char *argv[])
1125 {    
1126     cmsUInt16Number Output[cmsMAXCHANNELS];
1127     cmsFloat64Number OutputFloat[cmsMAXCHANNELS];
1128     cmsFloat64Number InputFloat[cmsMAXCHANNELS];
1129
1130     int nPatch = 0;
1131
1132     fprintf(stderr, "LittleCMS ColorSpace conversion calculator - 4.1 [LittleCMS %2.2f]\n", LCMS_VERSION / 1000.0);
1133
1134     InitUtils("transicc");
1135
1136     Verbose = 1;
1137
1138     if (argc == 1) {
1139
1140         Help();              
1141         return 0;
1142     }
1143
1144     HandleSwitches(argc, argv);
1145
1146     // Open profiles, create transforms
1147     if (!OpenTransforms()) return 1;
1148
1149     // Open CGATS input if specified
1150     OpenCGATSFiles(argc, argv);
1151
1152     // Main loop: read all values and convert them
1153     for(;;) {
1154
1155         if (hIT8in != NULL) {
1156
1157             if (nPatch >= nMaxPatches) break;
1158             TakeCGATSValues(nPatch++, InputFloat);
1159
1160         } else {
1161
1162             if (feof(stdin)) break;         
1163             TakeFloatValues(InputFloat);
1164
1165         }
1166
1167         if (lIsFloat) 
1168             cmsDoTransform(hTrans, InputFloat, OutputFloat, 1);
1169         else
1170             cmsDoTransform(hTrans, InputFloat, Output, 1);
1171
1172
1173         if (hIT8out != NULL) {
1174
1175             PutCGATSValues(OutputFloat);
1176         }
1177         else {
1178
1179             if (lIsFloat) {
1180                 PrintFloatResults(OutputFloat); PrintPCSFloat(InputFloat);
1181             }
1182             else {
1183                 PrintEncodedResults(Output);   PrintPCSEncoded(InputFloat);      
1184             }
1185
1186         }
1187     }
1188
1189
1190     // Cleanup
1191     CloseTransforms();
1192
1193     if (hIT8in)
1194         cmsIT8Free(hIT8in);
1195
1196     if (hIT8out) {      
1197         cmsIT8SaveToFile(hIT8out, CGATSoutFilename);
1198         cmsIT8Free(hIT8out);
1199     }
1200
1201     // All is ok
1202     return 0;     
1203 }
1204
1205