comparison mupdf-source/thirdparty/lcms2/utils/linkicc/linkicc.c @ 2:b50eed0cc0ef upstream

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