1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2020 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
30 int Verbose = 0;
31
32 static char ProgramName[256] = "";
33
FatalError(const char * frm,...)34 void FatalError(const char *frm, ...)
35 {
36 va_list args;
37
38 va_start(args, frm);
39 fprintf(stderr, "[%s fatal error]: ", ProgramName);
40 vfprintf(stderr, frm, args);
41 fprintf(stderr, "\n");
42 va_end(args);
43
44 exit(1);
45 }
46
47 // Show errors to the end user (unless quiet option)
48 static
MyErrorLogHandler(cmsContext ContextID,cmsUInt32Number ErrorCode,const char * Text)49 void MyErrorLogHandler(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text)
50 {
51 if (Verbose >= 0)
52 fprintf(stderr, "[%s]: %s\n", ProgramName, Text);
53
54 UTILS_UNUSED_PARAMETER(ErrorCode);
55 UTILS_UNUSED_PARAMETER(ContextID);
56 }
57
58
InitUtils(cmsContext ContextID,const char * PName)59 void InitUtils(cmsContext ContextID, const char* PName)
60 {
61 strncpy(ProgramName, PName, sizeof(ProgramName));
62 ProgramName[sizeof(ProgramName)-1] = 0;
63
64 cmsSetLogErrorHandler(ContextID, MyErrorLogHandler);
65 }
66
67
68 // Virtual profiles are handled here.
OpenStockProfile(cmsContext ContextID,const char * File)69 cmsHPROFILE OpenStockProfile(cmsContext ContextID, const char* File)
70 {
71 if (!File)
72 return cmsCreate_sRGBProfile(ContextID);
73
74 if (cmsstrcasecmp(File, "*Lab2") == 0)
75 return cmsCreateLab2Profile(ContextID, NULL);
76
77 if (cmsstrcasecmp(File, "*Lab4") == 0)
78 return cmsCreateLab4Profile(ContextID, NULL);
79
80 if (cmsstrcasecmp(File, "*Lab") == 0)
81 return cmsCreateLab4Profile(ContextID, NULL);
82
83 if (cmsstrcasecmp(File, "*LabD65") == 0) {
84
85 cmsCIExyY D65xyY;
86
87 cmsWhitePointFromTemp(ContextID, &D65xyY, 6504);
88 return cmsCreateLab4Profile(ContextID, &D65xyY);
89 }
90
91 if (cmsstrcasecmp(File, "*XYZ") == 0)
92 return cmsCreateXYZProfile(ContextID);
93
94 if (cmsstrcasecmp(File, "*Gray22") == 0) {
95
96 cmsToneCurve* Curve = cmsBuildGamma(ContextID, 2.2);
97 cmsHPROFILE hProfile = cmsCreateGrayProfile(ContextID, cmsD50_xyY(ContextID), Curve);
98 cmsFreeToneCurve(ContextID, Curve);
99 return hProfile;
100 }
101
102 if (cmsstrcasecmp(File, "*Gray30") == 0) {
103
104 cmsToneCurve* Curve = cmsBuildGamma(ContextID, 3.0);
105 cmsHPROFILE hProfile = cmsCreateGrayProfile(ContextID, cmsD50_xyY(ContextID), Curve);
106 cmsFreeToneCurve(ContextID, Curve);
107 return hProfile;
108 }
109
110 if (cmsstrcasecmp(File, "*srgb") == 0)
111 return cmsCreate_sRGBProfile(ContextID);
112
113 if (cmsstrcasecmp(File, "*null") == 0)
114 return cmsCreateNULLProfile(ContextID);
115
116
117 if (cmsstrcasecmp(File, "*Lin2222") == 0) {
118
119 cmsToneCurve* Gamma = cmsBuildGamma(0, 2.2);
120 cmsToneCurve* Gamma4[4];
121 cmsHPROFILE hProfile;
122
123 Gamma4[0] = Gamma4[1] = Gamma4[2] = Gamma4[3] = Gamma;
124 hProfile = cmsCreateLinearizationDeviceLink(ContextID, cmsSigCmykData, Gamma4);
125 cmsFreeToneCurve(ContextID, Gamma);
126 return hProfile;
127 }
128
129
130 return cmsOpenProfileFromFile(ContextID, File, "r");
131 }
132
133 // Help on available built-ins
PrintBuiltins(void)134 void PrintBuiltins(void)
135 {
136 fprintf(stderr, "\nBuilt-in profiles:\n\n");
137 fprintf(stderr, "\t*Lab2 -- D50-based v2 CIEL*a*b\n"
138 "\t*Lab4 -- D50-based v4 CIEL*a*b\n"
139 "\t*Lab -- D50-based v4 CIEL*a*b\n"
140 "\t*XYZ -- CIE XYZ (PCS)\n"
141 "\t*sRGB -- sRGB color space\n"
142 "\t*Gray22 - Monochrome of Gamma 2.2\n"
143 "\t*Gray30 - Monochrome of Gamma 3.0\n"
144 "\t*null - Monochrome black for all input\n"
145 "\t*Lin2222- CMYK linearization of gamma 2.2 on each channel\n");
146 }
147
148
149 // Auxiliary for printing information on profile
150 static
PrintInfo(cmsContext ContextID,cmsHPROFILE h,cmsInfoType Info)151 void PrintInfo(cmsContext ContextID, cmsHPROFILE h, cmsInfoType Info)
152 {
153 char* text;
154 int len;
155
156 len = cmsGetProfileInfoASCII(ContextID, h, Info, "en", "US", NULL, 0);
157 if (len == 0) return;
158
159 text = (char*) malloc(len * sizeof(char));
160 if (text == NULL) return;
161
162 cmsGetProfileInfoASCII(ContextID, h, Info, "en", "US", text, len);
163
164 if (strlen(text) > 0)
165 printf("%s\n", text);
166
167 free(text);
168 }
169
170
171
172 // Displays the colorant table
173 static
PrintColorantTable(cmsContext ContextID,cmsHPROFILE hInput,cmsTagSignature Sig,const char * Title)174 void PrintColorantTable(cmsContext ContextID, cmsHPROFILE hInput, cmsTagSignature Sig, const char* Title)
175 {
176 cmsNAMEDCOLORLIST* list;
177 int i, n;
178
179 if (cmsIsTag(ContextID, hInput, Sig)) {
180
181 printf("%s:\n", Title);
182
183 list = (cmsNAMEDCOLORLIST*) cmsReadTag(ContextID, hInput, Sig);
184 if (list == NULL) {
185 printf("(Unavailable)\n");
186 return;
187 }
188
189 n = cmsNamedColorCount(ContextID, list);
190 for (i=0; i < n; i++) {
191
192 char Name[cmsMAX_PATH];
193
194 cmsNamedColorInfo(ContextID, list, i, Name, NULL, NULL, NULL, NULL);
195 printf("\t%s\n", Name);
196 }
197
198 printf("\n");
199 }
200
201 }
202
203
PrintProfileInformation(cmsContext ContextID,cmsHPROFILE hInput)204 void PrintProfileInformation(cmsContext ContextID, cmsHPROFILE hInput)
205 {
206 if (hInput == NULL) {
207 fprintf(stderr, "*Wrong or corrupted profile*\n");
208 return;
209 }
210
211 PrintInfo(ContextID, hInput, cmsInfoDescription);
212 PrintInfo(ContextID, hInput, cmsInfoManufacturer);
213 PrintInfo(ContextID, hInput, cmsInfoModel);
214 PrintInfo(ContextID, hInput, cmsInfoCopyright);
215
216 if (Verbose > 2) {
217
218 PrintColorantTable(ContextID, hInput, cmsSigColorantTableTag, "Input colorant table");
219 PrintColorantTable(ContextID, hInput, cmsSigColorantTableOutTag, "Input colorant out table");
220 }
221
222 printf("\n");
223 }
224
225 // -----------------------------------------------------------------------------
226
227
PrintRenderingIntents(cmsContext ContextID)228 void PrintRenderingIntents(cmsContext ContextID)
229 {
230 cmsUInt32Number Codes[200];
231 char* Descriptions[200];
232 cmsUInt32Number n, i;
233
234 fprintf(stderr, "%ct<n> rendering intent:\n\n", SW);
235
236 n = cmsGetSupportedIntents(ContextID, 200, Codes, Descriptions);
237
238 for (i=0; i < n; i++) {
239 fprintf(stderr, "\t%u - %s\n", Codes[i], Descriptions[i]);
240 }
241 fprintf(stderr, "\n");
242 }
243
244
245
246 // ------------------------------------------------------------------------------
247
SaveMemoryBlock(const cmsUInt8Number * Buffer,cmsUInt32Number dwLen,const char * Filename)248 cmsBool SaveMemoryBlock(const cmsUInt8Number* Buffer, cmsUInt32Number dwLen, const char* Filename)
249 {
250 FILE* out = fopen(Filename, "wb");
251 if (out == NULL) {
252 FatalError("Cannot create '%s'", Filename);
253 return FALSE;
254 }
255
256 if (fwrite(Buffer, 1, dwLen, out) != dwLen) {
257 FatalError("Cannot write %ld bytes to %s", dwLen, Filename);
258 return FALSE;
259 }
260
261 if (fclose(out) != 0) {
262 FatalError("Error flushing file '%s'", Filename);
263 return FALSE;
264 }
265
266 return TRUE;
267 }
268
269 // ------------------------------------------------------------------------------
270
271 // Return a pixel type on depending on the number of channels
PixelTypeFromChanCount(int ColorChannels)272 int PixelTypeFromChanCount(int ColorChannels)
273 {
274 switch (ColorChannels) {
275
276 case 1: return PT_GRAY;
277 case 2: return PT_MCH2;
278 case 3: return PT_MCH3;
279 case 4: return PT_CMYK;
280 case 5: return PT_MCH5;
281 case 6: return PT_MCH6;
282 case 7: return PT_MCH7;
283 case 8: return PT_MCH8;
284 case 9: return PT_MCH9;
285 case 10: return PT_MCH10;
286 case 11: return PT_MCH11;
287 case 12: return PT_MCH12;
288 case 13: return PT_MCH13;
289 case 14: return PT_MCH14;
290 case 15: return PT_MCH15;
291
292 default:
293
294 FatalError("What a weird separation of %d channels?!?!", ColorChannels);
295 return -1;
296 }
297 }
298
299
300 // ------------------------------------------------------------------------------
301
302 // Return number of channels of pixel type
ChanCountFromPixelType(int ColorChannels)303 int ChanCountFromPixelType(int ColorChannels)
304 {
305 switch (ColorChannels) {
306
307 case PT_GRAY: return 1;
308
309 case PT_RGB:
310 case PT_CMY:
311 case PT_Lab:
312 case PT_YUV:
313 case PT_YCbCr: return 3;
314
315 case PT_CMYK: return 4 ;
316 case PT_MCH2: return 2 ;
317 case PT_MCH3: return 3 ;
318 case PT_MCH4: return 4 ;
319 case PT_MCH5: return 5 ;
320 case PT_MCH6: return 6 ;
321 case PT_MCH7: return 7 ;
322 case PT_MCH8: return 8 ;
323 case PT_MCH9: return 9 ;
324 case PT_MCH10: return 10;
325 case PT_MCH11: return 11;
326 case PT_MCH12: return 12;
327 case PT_MCH13: return 12;
328 case PT_MCH14: return 14;
329 case PT_MCH15: return 15;
330
331 default:
332
333 FatalError("Unsupported color space of %d channels", ColorChannels);
334 return -1;
335 }
336 }
337