Tizen 2.0 Release
[external/lcms.git] / utils / linkicc / linkicc.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 #include "utils.h"
27
28 // ---------------------------------------------------------------------------------
29
30 static char* Description = "Devicelink profile";
31 static char* Copyright   = "No copyright, use freely";
32 static int   Intent = INTENT_PERCEPTUAL;
33 static char* cOutProf    = "devicelink.icc";
34 static int   PrecalcMode  = 1;
35 static int   NumOfGridPoints = 0;
36
37 static cmsFloat64Number ObserverAdaptationState = 1.0;  // According ICC 4.2 this is the default
38
39 static cmsBool BlackPointCompensation = FALSE;
40
41 static cmsFloat64Number InkLimit   = 400;
42 static cmsBool lUse8bits           = FALSE;
43 static cmsBool TagResult           = FALSE;
44 static cmsBool KeepLinearization   = FALSE;
45 static cmsFloat64Number Version    = 4.3;
46
47
48 // The manual
49 static
50 int Help(int level)
51 {
52      switch (level) {
53
54      default:
55      case 0:
56
57          fprintf(stderr, "\nlinkicc: Links profiles into a single devicelink.\n");     
58
59          fprintf(stderr, "\n");     
60          fprintf(stderr, "usage: linkicc [flags] <profiles>\n\n");
61          fprintf(stderr, "flags:\n\n");         
62          fprintf(stderr, "%co<profile> - Output devicelink profile. [defaults to 'devicelink.icc']\n", SW);     
63
64          PrintRenderingIntents();
65
66          fprintf(stderr, "%cc<0,1,2> - Precision (0=LowRes, 1=Normal, 2=Hi-res) [defaults to 1]\n", SW);     
67          fprintf(stderr, "%cn<gridpoints> - Alternate way to set precision, number of CLUT points\n", SW);     
68          fprintf(stderr, "%cd<description> - description text (quotes can be used)\n", SW);     
69          fprintf(stderr, "%cy<copyright> - copyright notice (quotes can be used)\n", SW);    
70          
71          fprintf(stderr, "\n%ck<0..400> - Ink-limiting in %% (CMYK only)\n", SW);
72          fprintf(stderr, "%c8 - Creates 8-bit devicelink\n", SW);
73          fprintf(stderr, "%cx - Creatively, guess deviceclass of resulting profile.\n", SW);
74          fprintf(stderr, "%cb - Black point compensation\n", SW);
75          fprintf(stderr, "%ca<0..1> - Observer adaptation state (abs.col. only)\n\n", SW);
76          fprintf(stderr, "%cl - Use linearization curves (may affect accuracy)\n", SW);
77          fprintf(stderr, "%cr<v.r> - Profile version. (CAUTION: may change the profile implementation)\n", SW);
78          fprintf(stderr, "\n");    
79          fprintf(stderr, "Colorspaces must be paired except Lab/XYZ, that can be interchanged.\n\n");
80
81          fprintf(stderr, "%ch<0,1,2,3> - More help\n", SW);
82          break;
83
84      case 1:
85          PrintBuiltins();
86          break;
87
88      case 2:
89
90          fprintf(stderr, "\nExamples:\n\n"
91              "To create 'devicelink.icm' from a.icc to b.icc:\n"
92              "\tlinkicc a.icc b.icc\n\n"
93              "To create 'out.icc' from sRGB to cmyk.icc:\n"
94              "\tlinkicc -o out.icc *sRGB cmyk.icc\n\n"
95              "To create a sRGB input profile working in Lab:\n"
96              "\tlinkicc -x -o sRGBLab.icc *sRGB *Lab\n\n"
97              "To create a XYZ -> sRGB output profile:\n"
98              "\tlinkicc -x -o sRGBLab.icc *XYZ *sRGB\n\n"
99              "To create a abstract profile doing softproof for cmyk.icc:\n"
100              "\tlinkicc -t1 -x -o softproof.icc *Lab cmyk.icc cmyk.icc *Lab\n\n"
101              "To create a 'grayer' sRGB input profile:\n"
102              "\tlinkicc -x -o grayer.icc *sRGB gray.icc gray.icc *Lab\n\n"
103              "To embed ink limiting into a cmyk output profile:\n"
104              "\tlinkicc -x -o cmyklimited.icc -k 250 cmyk.icc *Lab\n\n");                     
105          break;                       
106
107      case 3:
108
109          fprintf(stderr, "This program is intended to be a demo of the little cms\n"
110              "engine. Both lcms and this program are freeware. You can\n"
111              "obtain both in source code at http://www.littlecms.com\n"
112              "For suggestions, comments, bug reports etc. send mail to\n"
113              "info@littlecms.com\n\n");
114     }
115
116    exit(0);
117 }
118
119 // The toggles stuff
120 static
121 void HandleSwitches(int argc, char *argv[])
122 {
123     int s;
124
125     while ((s = xgetopt(argc,argv,"a:A:BbC:c:D:d:h:H:k:K:lLn:N:O:o:r:R:T:t:V:v:xX8y:Y:")) != EOF) {
126
127     switch (s) {
128
129
130         case 'a':
131         case 'A':             
132             ObserverAdaptationState = atof(xoptarg);
133             if (ObserverAdaptationState < 0 || 
134                 ObserverAdaptationState > 1.0)
135                        FatalError("Adaptation state should be 0..1");
136             break;      
137
138         case 'b':
139         case 'B':
140             BlackPointCompensation = TRUE;
141            break;
142
143         case 'c':
144         case 'C':
145             PrecalcMode = atoi(xoptarg);
146             if (PrecalcMode < 0 || PrecalcMode > 2) {
147                 FatalError("Unknown precalc mode '%d'", PrecalcMode);
148             }
149            break;
150
151        case 'd':
152        case 'D':
153            // Doing that is correct and safe: Description points to memory allocated in the command line.
154            // same for Copyright and output devicelink.
155            Description = xoptarg;
156            break;
157
158         case 'h':
159         case 'H':
160             Help(atoi(xoptarg));
161             return;
162
163         case 'k':
164         case 'K':
165             InkLimit = atof(xoptarg);
166             if (InkLimit < 0.0 || InkLimit > 400.0) {
167                 FatalError("Ink limit must be 0%%..400%%");
168             }
169            break;
170
171
172         case 'l':
173         case 'L': KeepLinearization = TRUE;
174            break;
175
176        case 'n':
177        case 'N':
178            if (PrecalcMode != 1) {
179                FatalError("Precalc mode already specified");
180            }
181            NumOfGridPoints = atoi(xoptarg);
182            break;
183
184         case 'o':
185         case 'O':
186             cOutProf = xoptarg;
187            break;
188
189
190        case 'r':
191        case 'R':
192           Version = atof(xoptarg);
193           if (Version < 2.0 || Version > 4.3) {
194               fprintf(stderr, "WARNING: lcms was not aware of this version, tag types may be wrong!\n");
195           }
196           break;
197
198         case 't':
199         case 'T':
200             Intent = atoi(xoptarg);  // Will be validated latter on
201             break;
202
203         case 'V':
204         case 'v':
205             Verbose = atoi(xoptarg);
206             if (Verbose < 0 || Verbose > 3) {
207                 FatalError("Unknown verbosity level '%d'", Verbose);
208             }
209             break;
210
211         case '8':
212             lUse8bits = TRUE;
213             break;
214
215
216
217         case 'y':
218         case 'Y':
219             Copyright = xoptarg;
220             break;
221
222
223
224        case 'x':
225        case 'X': TagResult = TRUE;
226            break;
227
228
229            
230        default:
231
232            FatalError("Unknown option - run without args to see valid ones.\n");          
233         }       
234     }
235 }
236
237 // Set the copyright and description
238 static
239 cmsBool SetTextTags(cmsHPROFILE hProfile)
240 {
241     cmsMLU *DescriptionMLU, *CopyrightMLU;
242     cmsBool  rc = FALSE;
243     cmsContext ContextID = cmsGetProfileContextID(hProfile);
244
245     DescriptionMLU  = cmsMLUalloc(ContextID, 1);
246     CopyrightMLU    = cmsMLUalloc(ContextID, 1);
247
248     if (DescriptionMLU == NULL || CopyrightMLU == NULL) goto Error;
249
250     if (!cmsMLUsetASCII(DescriptionMLU,  "en", "US", Description)) goto Error;
251     if (!cmsMLUsetASCII(CopyrightMLU,    "en", "US", Copyright)) goto Error;
252
253     if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag,  DescriptionMLU)) goto Error;
254     if (!cmsWriteTag(hProfile, cmsSigCopyrightTag,           CopyrightMLU)) goto Error;     
255
256     rc = TRUE;
257
258 Error:
259
260     if (DescriptionMLU)
261         cmsMLUfree(DescriptionMLU);
262     if (CopyrightMLU)
263         cmsMLUfree(CopyrightMLU);
264     return rc;
265 }
266
267
268
269 int main(int argc, char *argv[])
270 {
271     int i, nargs, rc;
272     cmsHPROFILE Profiles[257];
273     cmsHPROFILE hProfile;
274     cmsUInt32Number dwFlags;
275     cmsHTRANSFORM hTransform = NULL;
276
277     // Here we are
278     fprintf(stderr, "little cms ICC device link generator - v2.2 [LittleCMS %2.2f]\n", LCMS_VERSION / 1000.0);
279     fflush(stderr);
280
281     // Initialize
282     InitUtils("linkicc");
283     rc = 0;
284     
285     // Get the options
286     HandleSwitches(argc, argv);
287
288     // How many profiles to link?
289     nargs = (argc - xoptind);
290     if (nargs < 1)
291         return Help(0); 
292
293     if (nargs > 255) {
294         FatalError("Holy profile! what are you trying to do with so many profiles!?");
295         goto Cleanup;
296     }
297
298     // Open all profiles
299     memset(Profiles, 0, sizeof(Profiles));
300     for (i=0; i < nargs; i++) {
301
302         Profiles[i] = OpenStockProfile(0, argv[i + xoptind]);
303         if (Profiles[i] == NULL) goto Cleanup;      
304
305         if (Verbose >= 1) {
306             PrintProfileInformation(Profiles[i]);
307         }
308     }
309
310     // Ink limiting
311     if (InkLimit != 400.0) {        
312         cmsColorSpaceSignature EndingColorSpace = cmsGetColorSpace(Profiles[nargs-1]);
313         Profiles[nargs++] = cmsCreateInkLimitingDeviceLink(EndingColorSpace, InkLimit);
314     }
315
316     // Set the flags
317     dwFlags = cmsFLAGS_KEEP_SEQUENCE;
318     switch (PrecalcMode) {
319
320         case 0: dwFlags |= cmsFLAGS_LOWRESPRECALC; break;
321         case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break;
322         case 1: 
323             if (NumOfGridPoints > 0)
324                 dwFlags |= cmsFLAGS_GRIDPOINTS(NumOfGridPoints);
325             break;
326
327         default: 
328             {
329                 FatalError("Unknown precalculation mode '%d'", PrecalcMode);
330                 goto Cleanup;
331             }
332     }
333
334     if (BlackPointCompensation)
335         dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
336
337     if (TagResult)
338         dwFlags |= cmsFLAGS_GUESSDEVICECLASS;
339
340     if (KeepLinearization)
341         dwFlags |= cmsFLAGS_CLUT_PRE_LINEARIZATION|cmsFLAGS_CLUT_POST_LINEARIZATION;
342
343     if (lUse8bits) dwFlags |= cmsFLAGS_8BITS_DEVICELINK;
344
345      cmsSetAdaptationState(ObserverAdaptationState);
346      
347     // Create the color transform. Specify 0 for the format is safe as the transform 
348     // is intended to be used only for the devicelink.
349     hTransform = cmsCreateMultiprofileTransform(Profiles, nargs, 0, 0, Intent, dwFlags);
350     if (hTransform == NULL) {
351         FatalError("Transform creation failed");
352         goto Cleanup;
353     }
354
355     hProfile =  cmsTransform2DeviceLink(hTransform, Version, dwFlags);
356     if (hProfile == NULL) {
357         FatalError("Devicelink creation failed");
358         goto Cleanup;
359     }
360
361     SetTextTags(hProfile);
362     cmsSetHeaderRenderingIntent(hProfile, Intent);
363
364     if (cmsSaveProfileToFile(hProfile, cOutProf)) {
365
366         if (Verbose > 0) 
367             fprintf(stderr, "Ok");
368     }
369     else 
370         FatalError("Error saving file!");
371
372     cmsCloseProfile(hProfile);
373
374
375 Cleanup:
376
377     if (hTransform != NULL) cmsDeleteTransform(hTransform);
378     for (i=0; i < nargs; i++) {
379
380         if (Profiles[i] != NULL) cmsCloseProfile(Profiles[i]);
381     }
382
383     return rc;     
384 }