Imported Upstream version 2.4
[platform/upstream/lcms2.git] / utils / jpgicc / jpgicc.c
1 //---------------------------------------------------------------------------------\r
2 //\r
3 //  Little Color Management System\r
4 //  Copyright (c) 1998-2010 Marti Maria Saguer\r
5 //\r
6 // Permission is hereby granted, free of charge, to any person obtaining\r
7 // a copy of this software and associated documentation files (the "Software"),\r
8 // to deal in the Software without restriction, including without limitation\r
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
10 // and/or sell copies of the Software, and to permit persons to whom the Software\r
11 // is furnished to do so, subject to the following conditions:\r
12 //\r
13 // The above copyright notice and this permission notice shall be included in\r
14 // all copies or substantial portions of the Software.\r
15 //\r
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\r
18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
23 //\r
24 \r
25 // This program does apply profiles to (some) JPEG files\r
26 \r
27 \r
28 #include "utils.h"\r
29 \r
30 #include "jpeglib.h"\r
31 #include "iccjpeg.h"\r
32 \r
33 // Flags\r
34 static cmsBool BlackPointCompensation = FALSE;\r
35 static cmsBool IgnoreEmbedded         = FALSE;\r
36 static cmsBool GamutCheck             = FALSE;\r
37 static cmsBool lIsITUFax              = FALSE;\r
38 static cmsBool lIsPhotoshopApp13      = FALSE;\r
39 static cmsBool lIsEXIF;\r
40 static cmsBool lIsDeviceLink          = FALSE;\r
41 static cmsBool EmbedProfile           = FALSE;\r
42 \r
43 static const char* SaveEmbedded = NULL;\r
44 \r
45 static int Intent                  = INTENT_PERCEPTUAL;\r
46 static int ProofingIntent          = INTENT_PERCEPTUAL;\r
47 static int PrecalcMode             = 1;\r
48 \r
49 static int jpegQuality             = 75;\r
50 \r
51 static cmsFloat64Number ObserverAdaptationState = 0;\r
52 \r
53 \r
54 static char *cInpProf  = NULL;\r
55 static char *cOutProf  = NULL;\r
56 static char *cProofing = NULL;\r
57 \r
58 static FILE * InFile;\r
59 static FILE * OutFile;\r
60 \r
61 static struct jpeg_decompress_struct Decompressor;\r
62 static struct jpeg_compress_struct   Compressor;\r
63 \r
64 \r
65 static struct my_error_mgr {\r
66 \r
67     struct  jpeg_error_mgr pub;   // "public" fields\r
68     void*   Cargo;                // "private" fields\r
69 \r
70 } ErrorHandler;\r
71 \r
72 \r
73 cmsUInt16Number Alarm[4] = {128,128,128,0};\r
74 \r
75 // Out of mem\r
76 static\r
77 void OutOfMem(size_t size)\r
78 {\r
79     FatalError("Out of memory on allocating %d bytes.", size);\r
80 }\r
81 \r
82 \r
83 static\r
84 void my_error_exit (j_common_ptr cinfo)\r
85 {\r
86   char buffer[JMSG_LENGTH_MAX];\r
87 \r
88   (*cinfo->err->format_message) (cinfo, buffer);\r
89   FatalError(buffer);\r
90 }\r
91 \r
92 /*\r
93 Definition of the APPn Markers Defined for continuous-tone G3FAX\r
94 \r
95 The application code APP1 initiates identification of the image as\r
96 a G3FAX application and defines the spatial resolution and subsampling.\r
97 This marker directly follows the SOI marker. The data format will be as follows:\r
98 \r
99 X'FFE1' (APP1), length, FAX identifier, version, spatial resolution.\r
100 \r
101 The above terms are defined as follows:\r
102 \r
103 Length: (Two octets) Total APP1 field octet count including the octet count itself, but excluding the APP1\r
104 marker.\r
105 \r
106 FAX identifier: (Six octets) X'47', X'33', X'46', X'41', X'58', X'00'. This X'00'-terminated string "G3FAX"\r
107 uniquely identifies this APP1 marker.\r
108 \r
109 Version: (Two octets) X'07CA'. This string specifies the year of approval of the standard, for identification\r
110 in the case of future revision (for example, 1994).\r
111 \r
112 Spatial Resolution: (Two octets) Lightness pixel density in pels/25.4 mm. The basic value is 200. Allowed values are\r
113 100, 200, 300, 400, 600 and 1200 pels/25.4 mm, with square (or equivalent) pels.\r
114 \r
115 NOTE \96 The functional equivalence of inch-based and mm-based resolutions is maintained. For example, the 200 × 200\r
116 */\r
117 \r
118 static\r
119 cmsBool IsITUFax(jpeg_saved_marker_ptr ptr)\r
120 {\r
121         while (ptr)\r
122         {\r
123         if (ptr -> marker == (JPEG_APP0 + 1) && ptr -> data_length > 5) {\r
124 \r
125                         const char* data = (const char*) ptr -> data;\r
126 \r
127                         if (strcmp(data, "G3FAX") == 0) return TRUE;\r
128                 }\r
129 \r
130                 ptr = ptr -> next;\r
131         }\r
132 \r
133         return FALSE;\r
134 }\r
135 \r
136 // Save a ITU T.42/Fax marker with defaults on boundaries. This is the only mode we support right now.\r
137 static\r
138 void SetITUFax(j_compress_ptr cinfo)\r
139 {\r
140         unsigned char Marker[] = "G3FAX\x00\0x07\xCA\x00\xC8";\r
141 \r
142         jpeg_write_marker(cinfo, (JPEG_APP0 + 1), Marker, 10);\r
143 }\r
144 \r
145 \r
146 // Build a profile for decoding ITU T.42/Fax JPEG streams.\r
147 // The profile has an additional ability in the input direction of\r
148 // gamut compress values between 85 < a < -85 and -75 < b < 125. This conforms\r
149 // the default range for ITU/T.42 -- See RFC 2301, section 6.2.3 for details\r
150 \r
151 //  L*  =   [0, 100]\r
152 //  a*  =   [\9685, 85]\r
153 //  b*  =   [\9675, 125]\r
154 \r
155 \r
156 // These functions does convert the encoding of ITUFAX to floating point\r
157 // and vice-versa. No gamut mapping is performed yet.\r
158 \r
159 static\r
160 void ITU2Lab(const cmsUInt16Number In[3], cmsCIELab* Lab)\r
161 {\r
162         Lab -> L = (double) In[0] / 655.35;\r
163         Lab -> a = (double) 170.* (In[1] - 32768.) / 65535.;\r
164         Lab -> b = (double) 200.* (In[2] - 24576.) / 65535.;\r
165 }\r
166 \r
167 static\r
168 void Lab2ITU(const cmsCIELab* Lab, cmsUInt16Number Out[3])\r
169 {\r
170         Out[0] = (cmsUInt16Number) floor((double) (Lab -> L / 100.)* 65535. );\r
171         Out[1] = (cmsUInt16Number) floor((double) (Lab -> a / 170.)* 65535. + 32768. );\r
172         Out[2] = (cmsUInt16Number) floor((double) (Lab -> b / 200.)* 65535. + 24576. );\r
173 }\r
174 \r
175 // These are the samplers-- They are passed as callbacks to cmsStageSampleCLut16bit()\r
176 // then, cmsSample3DGrid() will sweel whole Lab gamut calling these functions\r
177 // once for each node. In[] will contain the Lab PCS value to convert to ITUFAX\r
178 // on PCS2ITU, or the ITUFAX value to convert to Lab in ITU2PCS\r
179 // You can change the number of sample points if desired, the algorithm will\r
180 // remain same. 33 points gives good accurancy, but you can reduce to 22 or less\r
181 // is space is critical\r
182 \r
183 #define GRID_POINTS 33\r
184 \r
185 static\r
186 int PCS2ITU(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void*  Cargo)\r
187 {\r
188         cmsCIELab Lab;\r
189 \r
190         cmsLabEncoded2Float(&Lab, In);\r
191         cmsDesaturateLab(&Lab, 85, -85, 125, -75);    // This function does the necessary gamut remapping\r
192         Lab2ITU(&Lab, Out);\r
193         return TRUE;\r
194 \r
195     UTILS_UNUSED_PARAMETER(Cargo);\r
196 }\r
197 \r
198 \r
199 static\r
200 int ITU2PCS( register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void*  Cargo)\r
201 {\r
202         cmsCIELab Lab;\r
203 \r
204         ITU2Lab(In, &Lab);\r
205         cmsFloat2LabEncoded(Out, &Lab);\r
206         return TRUE;\r
207 \r
208     UTILS_UNUSED_PARAMETER(Cargo);\r
209 }\r
210 \r
211 // This function does create the virtual input profile, which decodes ITU to the profile connection space\r
212 static\r
213 cmsHPROFILE CreateITU2PCS_ICC(void)\r
214 {\r
215         cmsHPROFILE hProfile;\r
216         cmsPipeline* AToB0;\r
217         cmsStage* ColorMap;\r
218 \r
219         AToB0 = cmsPipelineAlloc(0, 3, 3);\r
220         if (AToB0 == NULL) return NULL;\r
221 \r
222         ColorMap = cmsStageAllocCLut16bit(0, GRID_POINTS, 3, 3, NULL);\r
223         if (ColorMap == NULL) return NULL;\r
224 \r
225     cmsPipelineInsertStage(AToB0, cmsAT_BEGIN, ColorMap);\r
226         cmsStageSampleCLut16bit(ColorMap, ITU2PCS, NULL, 0);\r
227 \r
228         hProfile = cmsCreateProfilePlaceholder(0);\r
229         if (hProfile == NULL) {\r
230                 cmsPipelineFree(AToB0);\r
231                 return NULL;\r
232         }\r
233 \r
234         cmsWriteTag(hProfile, cmsSigAToB0Tag, AToB0);\r
235         cmsSetColorSpace(hProfile, cmsSigLabData);\r
236         cmsSetPCS(hProfile, cmsSigLabData);\r
237         cmsSetDeviceClass(hProfile, cmsSigColorSpaceClass);\r
238         cmsPipelineFree(AToB0);\r
239 \r
240         return hProfile;\r
241 }\r
242 \r
243 \r
244 // This function does create the virtual output profile, with the necessary gamut mapping\r
245 static\r
246 cmsHPROFILE CreatePCS2ITU_ICC(void)\r
247 {\r
248     cmsHPROFILE hProfile;\r
249     cmsPipeline* BToA0;\r
250     cmsStage* ColorMap;\r
251 \r
252     BToA0 = cmsPipelineAlloc(0, 3, 3);\r
253     if (BToA0 == NULL) return NULL;\r
254 \r
255     ColorMap = cmsStageAllocCLut16bit(0, GRID_POINTS, 3, 3, NULL);\r
256     if (ColorMap == NULL) return NULL;\r
257 \r
258     cmsPipelineInsertStage(BToA0, cmsAT_BEGIN, ColorMap);\r
259     cmsStageSampleCLut16bit(ColorMap, PCS2ITU, NULL, 0);\r
260 \r
261     hProfile = cmsCreateProfilePlaceholder(0);\r
262     if (hProfile == NULL) {\r
263         cmsPipelineFree(BToA0);\r
264         return NULL;\r
265     }\r
266 \r
267     cmsWriteTag(hProfile, cmsSigBToA0Tag, BToA0);\r
268     cmsSetColorSpace(hProfile, cmsSigLabData);\r
269     cmsSetPCS(hProfile, cmsSigLabData);\r
270     cmsSetDeviceClass(hProfile, cmsSigColorSpaceClass);\r
271 \r
272     cmsPipelineFree(BToA0);\r
273 \r
274     return hProfile;\r
275 }\r
276 \r
277 \r
278 \r
279 #define PS_FIXED_TO_FLOAT(h, l) ((float) (h) + ((float) (l)/(1<<16)))\r
280 \r
281 static\r
282 cmsBool ProcessPhotoshopAPP13(JOCTET FAR *data, int datalen)\r
283 {\r
284     int i;\r
285 \r
286     for (i = 14; i < datalen; )\r
287     {\r
288         long len;\r
289         unsigned int type;\r
290 \r
291         if (!(GETJOCTET(data[i]  ) == 0x38 &&\r
292               GETJOCTET(data[i+1]) == 0x42 &&\r
293               GETJOCTET(data[i+2]) == 0x49 &&\r
294               GETJOCTET(data[i+3]) == 0x4D)) break; // Not recognized\r
295 \r
296         i += 4; // identifying string\r
297 \r
298         type = (unsigned int) (GETJOCTET(data[i]<<8) + GETJOCTET(data[i+1]));\r
299 \r
300         i += 2; // resource type\r
301 \r
302         i += GETJOCTET(data[i]) + ((GETJOCTET(data[i]) & 1) ? 1 : 2);   // resource name\r
303 \r
304         len = ((((GETJOCTET(data[i]<<8) + GETJOCTET(data[i+1]))<<8) +\r
305                          GETJOCTET(data[i+2]))<<8) + GETJOCTET(data[i+3]);\r
306 \r
307         i += 4; // Size\r
308 \r
309         if (type == 0x03ED && len >= 16) {\r
310 \r
311             Decompressor.X_density = (UINT16) PS_FIXED_TO_FLOAT(GETJOCTET(data[i]<<8) + GETJOCTET(data[i+1]),\r
312                                                  GETJOCTET(data[i+2]<<8) + GETJOCTET(data[i+3]));\r
313             Decompressor.Y_density = (UINT16) PS_FIXED_TO_FLOAT(GETJOCTET(data[i+8]<<8) + GETJOCTET(data[i+9]),\r
314                                                  GETJOCTET(data[i+10]<<8) + GETJOCTET(data[i+11]));\r
315 \r
316             // Set the density unit to 1 since the\r
317             // Vertical and Horizontal resolutions\r
318             // are specified in Pixels per inch\r
319 \r
320             Decompressor.density_unit = 0x01;\r
321             return TRUE;\r
322 \r
323         }\r
324 \r
325         i += len + ((len & 1) ? 1 : 0);   // Alignment\r
326     }\r
327     return FALSE;\r
328 }\r
329 \r
330 \r
331 static\r
332 cmsBool HandlePhotoshopAPP13(jpeg_saved_marker_ptr ptr)\r
333 {\r
334     while (ptr) {\r
335 \r
336         if (ptr -> marker == (JPEG_APP0 + 13) && ptr -> data_length > 9)\r
337         {\r
338             JOCTET FAR* data = ptr -> data;\r
339 \r
340             if(GETJOCTET(data[0]) == 0x50 &&\r
341                GETJOCTET(data[1]) == 0x68 &&\r
342                GETJOCTET(data[2]) == 0x6F &&\r
343                GETJOCTET(data[3]) == 0x74 &&\r
344                GETJOCTET(data[4]) == 0x6F &&\r
345                GETJOCTET(data[5]) == 0x73 &&\r
346                GETJOCTET(data[6]) == 0x68 &&\r
347                GETJOCTET(data[7]) == 0x6F &&\r
348                GETJOCTET(data[8]) == 0x70) {\r
349 \r
350                 ProcessPhotoshopAPP13(data, ptr -> data_length);\r
351                 return TRUE;\r
352             }\r
353         }\r
354 \r
355         ptr = ptr -> next;\r
356     }\r
357 \r
358     return FALSE;\r
359 }\r
360 \r
361 \r
362 typedef unsigned short uint16_t;\r
363 typedef unsigned char uint8_t;\r
364 typedef unsigned int uint32_t;\r
365 \r
366 #define INTEL_BYTE_ORDER 0x4949\r
367 #define XRESOLUTION 0x011a\r
368 #define YRESOLUTION 0x011b\r
369 #define RESOLUTION_UNIT 0x128\r
370 \r
371 // Read a 16-bit word\r
372 static\r
373 uint16_t read16(uint8_t* arr, int pos,  int swapBytes)\r
374 {\r
375     uint8_t b1 = arr[pos];\r
376     uint8_t b2 = arr[pos+1];\r
377 \r
378     return (swapBytes) ?  ((b2 << 8) | b1) : ((b1 << 8) | b2);\r
379 }\r
380 \r
381 \r
382 // Read a 32-bit word\r
383 static\r
384 uint32_t read32(uint8_t* arr, int pos,  int swapBytes)\r
385 {\r
386 \r
387     if(!swapBytes) {\r
388 \r
389         return (arr[pos]   << 24) |\r
390                (arr[pos+1] << 16) |\r
391                (arr[pos+2] << 8) |\r
392                 arr[pos+3];\r
393     }\r
394 \r
395     return arr[pos] |\r
396            (arr[pos+1] << 8) |\r
397            (arr[pos+2] << 16) |\r
398            (arr[pos+3] << 24);\r
399 }\r
400 \r
401 \r
402 \r
403 static\r
404 int read_tag(uint8_t* arr, int pos,  int swapBytes, void* dest)\r
405 {\r
406         // Format should be 5 over here (rational)\r
407     uint32_t format = read16(arr, pos + 2, swapBytes);\r
408     // Components should be 1\r
409     uint32_t components = read32(arr, pos + 4, swapBytes);\r
410     // Points to the value\r
411     uint32_t offset;\r
412 \r
413     // sanity\r
414     if (components != 1) return 0;\r
415 \r
416     if (format == 3)\r
417         offset = pos + 8;\r
418     else\r
419         offset =  read32(arr, pos + 8, swapBytes);\r
420 \r
421     switch (format) {\r
422 \r
423     case 5: // Rational\r
424           {\r
425           double num = read32(arr, offset, swapBytes);\r
426           double den = read32(arr, offset + 4, swapBytes);\r
427           *(double *) dest = num / den;\r
428           }\r
429           break;\r
430 \r
431     case 3: // uint 16\r
432         *(int*) dest = read16(arr, offset, swapBytes);\r
433         break;\r
434 \r
435     default:  return 0;\r
436     }\r
437 \r
438     return 1;\r
439 }\r
440 \r
441 \r
442 \r
443 // Handler for EXIF data\r
444 static\r
445     cmsBool HandleEXIF(struct jpeg_decompress_struct* cinfo)\r
446 {\r
447     jpeg_saved_marker_ptr ptr;\r
448     uint32_t ifd_ofs;\r
449     int pos = 0, swapBytes = 0;\r
450     uint32_t i, numEntries;\r
451     double XRes = -1, YRes = -1;\r
452     int Unit = 2; // Inches\r
453 \r
454 \r
455     for (ptr = cinfo ->marker_list; ptr; ptr = ptr ->next) {\r
456 \r
457         if ((ptr ->marker == JPEG_APP0+1) && ptr ->data_length > 6) {\r
458             JOCTET FAR* data = ptr -> data;\r
459 \r
460             if (memcmp(data, "Exif\0\0", 6) == 0) {\r
461 \r
462                 data += 6; // Skip EXIF marker\r
463 \r
464                 // 8 byte TIFF header\r
465                 // first two determine byte order\r
466                 pos = 0;\r
467                 if (read16(data, pos, 0) == INTEL_BYTE_ORDER) {\r
468                     swapBytes = 1;\r
469                 }\r
470 \r
471                 pos += 2;\r
472 \r
473                 // next two bytes are always 0x002A (TIFF version)\r
474                 pos += 2;\r
475 \r
476                 // offset to Image File Directory (includes the previous 8 bytes)\r
477                 ifd_ofs = read32(data, pos, swapBytes);\r
478 \r
479                 // Search the directory for resolution tags\r
480                 numEntries = read16(data, ifd_ofs, swapBytes);\r
481 \r
482                 for (i=0; i < numEntries; i++) {\r
483 \r
484                     uint32_t entryOffset = ifd_ofs + 2 + (12 * i);\r
485                     uint32_t tag = read16(data, entryOffset, swapBytes);\r
486 \r
487                     switch (tag) {\r
488 \r
489                     case RESOLUTION_UNIT:\r
490                         if (!read_tag(data, entryOffset, swapBytes, &Unit)) return FALSE;\r
491                         break;\r
492 \r
493                     case XRESOLUTION:\r
494                         if (!read_tag(data, entryOffset, swapBytes, &XRes)) return FALSE;\r
495                         break;\r
496 \r
497                     case YRESOLUTION:\r
498                         if (!read_tag(data, entryOffset, swapBytes, &YRes)) return FALSE;\r
499                         break;\r
500 \r
501                     default:;\r
502                     }\r
503 \r
504                 }\r
505 \r
506                 // Proceed if all found\r
507 \r
508                 if (XRes != -1 && YRes != -1)\r
509                 {\r
510 \r
511                     // 1 = None\r
512                     // 2 = inches\r
513                     // 3 = cm\r
514 \r
515                     switch (Unit) {\r
516 \r
517                     case 2:\r
518 \r
519                         cinfo ->X_density = (UINT16) floor(XRes + 0.5);\r
520                         cinfo ->Y_density = (UINT16) floor(YRes + 0.5);\r
521                         break;\r
522 \r
523                     case 1:\r
524 \r
525                         cinfo ->X_density = (UINT16) floor(XRes * 2.54 + 0.5);\r
526                         cinfo ->Y_density = (UINT16) floor(YRes * 2.54 + 0.5);\r
527                         break;\r
528 \r
529                     default: return FALSE;\r
530                     }\r
531 \r
532                     cinfo ->density_unit = 1;  /* 1 for dots/inch, or 2 for dots/cm.*/\r
533 \r
534                 }\r
535 \r
536 \r
537             }\r
538         }\r
539     }\r
540     return FALSE;\r
541 }\r
542 \r
543 \r
544 static\r
545 cmsBool OpenInput(const char* FileName)\r
546 {\r
547         int m;\r
548 \r
549         lIsITUFax = FALSE;\r
550         InFile  = fopen(FileName, "rb");\r
551         if (InFile == NULL) {\r
552                 FatalError("Cannot open '%s'", FileName);\r
553         }\r
554 \r
555         // Now we can initialize the JPEG decompression object.\r
556         Decompressor.err                 = jpeg_std_error(&ErrorHandler.pub);\r
557         ErrorHandler.pub.error_exit      = my_error_exit;\r
558         ErrorHandler.pub.output_message  = my_error_exit;\r
559 \r
560         jpeg_create_decompress(&Decompressor);\r
561         jpeg_stdio_src(&Decompressor, InFile);\r
562 \r
563         for (m = 0; m < 16; m++)\r
564                 jpeg_save_markers(&Decompressor, JPEG_APP0 + m, 0xFFFF);\r
565 \r
566         // setup_read_icc_profile(&Decompressor);\r
567 \r
568         fseek(InFile, 0, SEEK_SET);\r
569         jpeg_read_header(&Decompressor, TRUE);\r
570 \r
571         return TRUE;\r
572 }\r
573 \r
574 \r
575 static\r
576 cmsBool OpenOutput(const char* FileName)\r
577 {\r
578 \r
579         OutFile = fopen(FileName, "wb");\r
580         if (OutFile == NULL) {\r
581                 FatalError("Cannot create '%s'", FileName);\r
582 \r
583         }\r
584 \r
585         Compressor.err                   = jpeg_std_error(&ErrorHandler.pub);\r
586         ErrorHandler.pub.error_exit      = my_error_exit;\r
587         ErrorHandler.pub.output_message  = my_error_exit;\r
588 \r
589         Compressor.input_components = Compressor.num_components = 4;\r
590 \r
591         jpeg_create_compress(&Compressor);\r
592         jpeg_stdio_dest(&Compressor, OutFile);\r
593         return TRUE;\r
594 }\r
595 \r
596 static\r
597 cmsBool Done(void)\r
598 {\r
599         jpeg_destroy_decompress(&Decompressor);\r
600         jpeg_destroy_compress(&Compressor);\r
601         return fclose(InFile) + fclose(OutFile);\r
602 \r
603 }\r
604 \r
605 \r
606 // Build up the pixeltype descriptor\r
607 \r
608 static\r
609 cmsUInt32Number GetInputPixelType(void)\r
610 {\r
611      int space, bps, extra, ColorChannels, Flavor;\r
612 \r
613      lIsITUFax         = IsITUFax(Decompressor.marker_list);\r
614      lIsPhotoshopApp13 = HandlePhotoshopAPP13(Decompressor.marker_list);\r
615      lIsEXIF           = HandleEXIF(&Decompressor);\r
616 \r
617      ColorChannels = Decompressor.num_components;\r
618      extra  = 0;            // Alpha = None\r
619      bps    = 1;            // 8 bits\r
620      Flavor = 0;            // Vanilla\r
621 \r
622      if (lIsITUFax) {\r
623 \r
624         space = PT_Lab;\r
625         Decompressor.out_color_space = JCS_YCbCr;  // Fake to don't touch\r
626      }\r
627      else\r
628      switch (Decompressor.jpeg_color_space) {\r
629 \r
630      case JCS_GRAYSCALE:        // monochrome\r
631               space = PT_GRAY;\r
632               Decompressor.out_color_space = JCS_GRAYSCALE;\r
633               break;\r
634 \r
635      case JCS_RGB:             // red/green/blue\r
636               space = PT_RGB;\r
637               Decompressor.out_color_space = JCS_RGB;\r
638               break;\r
639 \r
640      case JCS_YCbCr:               // Y/Cb/Cr (also known as YUV)\r
641               space = PT_RGB;      // Let IJG code to do the conversion\r
642               Decompressor.out_color_space = JCS_RGB;\r
643               break;\r
644 \r
645      case JCS_CMYK:            // C/M/Y/K\r
646               space = PT_CMYK;\r
647               Decompressor.out_color_space = JCS_CMYK;\r
648               if (Decompressor.saw_Adobe_marker)            // Adobe keeps CMYK inverted, so change flavor\r
649                                 Flavor = 1;                 // from vanilla to chocolate\r
650               break;\r
651 \r
652      case JCS_YCCK:            // Y/Cb/Cr/K\r
653               space = PT_CMYK;\r
654               Decompressor.out_color_space = JCS_CMYK;\r
655               if (Decompressor.saw_Adobe_marker)            // ditto\r
656                                 Flavor = 1;\r
657               break;\r
658 \r
659      default:\r
660               FatalError("Unsupported color space (0x%x)", Decompressor.jpeg_color_space);\r
661               return 0;\r
662      }\r
663 \r
664      return (EXTRA_SH(extra)|CHANNELS_SH(ColorChannels)|BYTES_SH(bps)|COLORSPACE_SH(space)|FLAVOR_SH(Flavor));\r
665 }\r
666 \r
667 \r
668 // Rearrange pixel type to build output descriptor\r
669 static\r
670 cmsUInt32Number ComputeOutputFormatDescriptor(cmsUInt32Number dwInput, int OutColorSpace)\r
671 {\r
672         int IsPlanar  = T_PLANAR(dwInput);\r
673         int Channels  = 0;\r
674         int Flavor    = 0;\r
675 \r
676         switch (OutColorSpace) {\r
677 \r
678    case PT_GRAY:\r
679            Channels = 1;\r
680            break;\r
681    case PT_RGB:\r
682    case PT_CMY:\r
683    case PT_Lab:\r
684    case PT_YUV:\r
685    case PT_YCbCr:\r
686            Channels = 3;\r
687            break;\r
688 \r
689    case PT_CMYK:\r
690            if (Compressor.write_Adobe_marker)   // Adobe keeps CMYK inverted, so change flavor to chocolate\r
691                    Flavor = 1;\r
692            Channels = 4;\r
693            break;\r
694    default:\r
695            FatalError("Unsupported output color space");\r
696         }\r
697 \r
698         return (COLORSPACE_SH(OutColorSpace)|PLANAR_SH(IsPlanar)|CHANNELS_SH(Channels)|BYTES_SH(1)|FLAVOR_SH(Flavor));\r
699 }\r
700 \r
701 \r
702 // Equivalence between ICC color spaces and lcms color spaces\r
703 static\r
704 int GetProfileColorSpace(cmsHPROFILE hProfile)\r
705 {\r
706     cmsColorSpaceSignature ProfileSpace = cmsGetColorSpace(hProfile);\r
707 \r
708         return _cmsLCMScolorSpace(ProfileSpace);\r
709 }\r
710 \r
711 static\r
712 int GetDevicelinkColorSpace(cmsHPROFILE hProfile)\r
713 {\r
714     cmsColorSpaceSignature ProfileSpace = cmsGetPCS(hProfile);\r
715 \r
716         return _cmsLCMScolorSpace(ProfileSpace);\r
717 }\r
718 \r
719 \r
720 // From TRANSUPP\r
721 \r
722 static\r
723 void jcopy_markers_execute(j_decompress_ptr srcinfo, j_compress_ptr dstinfo)\r
724 {\r
725   jpeg_saved_marker_ptr marker;\r
726 \r
727   /* In the current implementation, we don't actually need to examine the\r
728    * option flag here; we just copy everything that got saved.\r
729    * But to avoid confusion, we do not output JFIF and Adobe APP14 markers\r
730    * if the encoder library already wrote one.\r
731    */\r
732   for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) {\r
733 \r
734     if (dstinfo->write_JFIF_header &&\r
735         marker->marker == JPEG_APP0 &&\r
736         marker->data_length >= 5 &&\r
737         GETJOCTET(marker->data[0]) == 0x4A &&\r
738         GETJOCTET(marker->data[1]) == 0x46 &&\r
739         GETJOCTET(marker->data[2]) == 0x49 &&\r
740         GETJOCTET(marker->data[3]) == 0x46 &&\r
741         GETJOCTET(marker->data[4]) == 0)\r
742                           continue;         /* reject duplicate JFIF */\r
743 \r
744     if (dstinfo->write_Adobe_marker &&\r
745         marker->marker == JPEG_APP0+14 &&\r
746         marker->data_length >= 5 &&\r
747         GETJOCTET(marker->data[0]) == 0x41 &&\r
748         GETJOCTET(marker->data[1]) == 0x64 &&\r
749         GETJOCTET(marker->data[2]) == 0x6F &&\r
750         GETJOCTET(marker->data[3]) == 0x62 &&\r
751         GETJOCTET(marker->data[4]) == 0x65)\r
752                          continue;         /* reject duplicate Adobe */\r
753 \r
754      jpeg_write_marker(dstinfo, marker->marker,\r
755                        marker->data, marker->data_length);\r
756   }\r
757 }\r
758 \r
759 static\r
760 void WriteOutputFields(int OutputColorSpace)\r
761 {\r
762     J_COLOR_SPACE in_space, jpeg_space;\r
763     int components;\r
764 \r
765     switch (OutputColorSpace) {\r
766 \r
767     case PT_GRAY: in_space = jpeg_space = JCS_GRAYSCALE;\r
768                   components = 1;\r
769                   break;\r
770 \r
771     case PT_RGB:  in_space = JCS_RGB;\r
772                   jpeg_space = JCS_YCbCr;\r
773                   components = 3;\r
774                   break;       // red/green/blue\r
775 \r
776     case PT_YCbCr: in_space = jpeg_space = JCS_YCbCr;\r
777                    components = 3;\r
778                    break;               // Y/Cb/Cr (also known as YUV)\r
779 \r
780     case PT_CMYK: in_space = JCS_CMYK;\r
781                   jpeg_space = JCS_YCCK;\r
782                   components = 4;\r
783                   break;      // C/M/Y/components\r
784 \r
785     case PT_Lab:  in_space = jpeg_space = JCS_YCbCr;\r
786                   components = 3;\r
787                   break;                // Fake to don't touch\r
788     default:\r
789                  FatalError("Unsupported output color space");\r
790                  return;\r
791     }\r
792 \r
793 \r
794     if (jpegQuality >= 100) {\r
795 \r
796      // avoid destructive conversion when asking for lossless compression\r
797         jpeg_space = in_space;\r
798     }\r
799 \r
800     Compressor.in_color_space =  in_space;\r
801     Compressor.jpeg_color_space = jpeg_space;\r
802     Compressor.input_components = Compressor.num_components = components;\r
803     jpeg_set_defaults(&Compressor);\r
804     jpeg_set_colorspace(&Compressor, jpeg_space);\r
805 \r
806 \r
807     // Make sure to pass resolution through\r
808     if (OutputColorSpace == PT_CMYK)\r
809         Compressor.write_JFIF_header = 1;\r
810 \r
811     // Avoid subsampling on high quality factor\r
812     jpeg_set_quality(&Compressor, jpegQuality, 1);\r
813     if (jpegQuality >= 70) {\r
814 \r
815       int i;\r
816       for(i=0; i < Compressor.num_components; i++) {\r
817 \r
818                 Compressor.comp_info[i].h_samp_factor = 1;\r
819             Compressor.comp_info[i].v_samp_factor = 1;\r
820       }\r
821 \r
822     }\r
823 \r
824 }\r
825 \r
826 \r
827 static\r
828 void DoEmbedProfile(const char* ProfileFile)\r
829 {\r
830     FILE* f;\r
831     size_t size, EmbedLen;\r
832     cmsUInt8Number* EmbedBuffer;\r
833 \r
834         f = fopen(ProfileFile, "rb");\r
835         if (f == NULL) return;\r
836 \r
837         size = cmsfilelength(f);\r
838         EmbedBuffer = (cmsUInt8Number*) malloc(size + 1);\r
839         EmbedLen = fread(EmbedBuffer, 1, size, f);\r
840         fclose(f);\r
841         EmbedBuffer[EmbedLen] = 0;\r
842 \r
843         write_icc_profile (&Compressor, EmbedBuffer, EmbedLen);\r
844         free(EmbedBuffer);\r
845 }\r
846 \r
847 \r
848 \r
849 static\r
850 int DoTransform(cmsHTRANSFORM hXForm, int OutputColorSpace)\r
851 {\r
852     JSAMPROW ScanLineIn;\r
853     JSAMPROW ScanLineOut;\r
854 \r
855 \r
856        //Preserve resolution values from the original\r
857        // (Thanks to Robert Bergs for finding out this bug)\r
858        Compressor.density_unit = Decompressor.density_unit;\r
859        Compressor.X_density    = Decompressor.X_density;\r
860        Compressor.Y_density    = Decompressor.Y_density;\r
861 \r
862       //  Compressor.write_JFIF_header = 1;\r
863 \r
864        jpeg_start_decompress(&Decompressor);\r
865        jpeg_start_compress(&Compressor, TRUE);\r
866 \r
867         if (OutputColorSpace == PT_Lab)\r
868             SetITUFax(&Compressor);\r
869 \r
870        // Embed the profile if needed\r
871        if (EmbedProfile && cOutProf)\r
872            DoEmbedProfile(cOutProf);\r
873 \r
874        ScanLineIn  = (JSAMPROW) malloc(Decompressor.output_width * Decompressor.num_components);\r
875        ScanLineOut = (JSAMPROW) malloc(Compressor.image_width * Compressor.num_components);\r
876 \r
877        while (Decompressor.output_scanline <\r
878                             Decompressor.output_height) {\r
879 \r
880        jpeg_read_scanlines(&Decompressor, &ScanLineIn, 1);\r
881 \r
882        cmsDoTransform(hXForm, ScanLineIn, ScanLineOut, Decompressor.output_width);\r
883 \r
884        jpeg_write_scanlines(&Compressor, &ScanLineOut, 1);\r
885        }\r
886 \r
887        free(ScanLineIn);\r
888        free(ScanLineOut);\r
889 \r
890        jpeg_finish_decompress(&Decompressor);\r
891        jpeg_finish_compress(&Compressor);\r
892 \r
893        return TRUE;\r
894 }\r
895 \r
896 \r
897 \r
898 // Transform one image\r
899 \r
900 static\r
901 int TransformImage(char *cDefInpProf, char *cOutProf)\r
902 {\r
903        cmsHPROFILE hIn, hOut, hProof;\r
904        cmsHTRANSFORM xform;\r
905        cmsUInt32Number wInput, wOutput;\r
906        int OutputColorSpace;\r
907        cmsUInt32Number dwFlags = 0;\r
908        cmsUInt32Number EmbedLen;\r
909        cmsUInt8Number* EmbedBuffer;\r
910 \r
911 \r
912        cmsSetAdaptationState(ObserverAdaptationState);\r
913 \r
914        if (BlackPointCompensation) {\r
915 \r
916             dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;\r
917        }\r
918 \r
919 \r
920        switch (PrecalcMode) {\r
921 \r
922        case 0: dwFlags |= cmsFLAGS_NOOPTIMIZE; break;\r
923        case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break;\r
924        case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break;\r
925        default:;\r
926        }\r
927 \r
928 \r
929        if (GamutCheck) {\r
930             dwFlags |= cmsFLAGS_GAMUTCHECK;\r
931             cmsSetAlarmCodes(Alarm);\r
932        }\r
933 \r
934        // Take input color space\r
935        wInput = GetInputPixelType();\r
936 \r
937         if (lIsDeviceLink) {\r
938 \r
939             hIn = cmsOpenProfileFromFile(cDefInpProf, "r");\r
940             hOut = NULL;\r
941             hProof = NULL;\r
942        }\r
943         else {\r
944 \r
945         if (!IgnoreEmbedded && read_icc_profile(&Decompressor, &EmbedBuffer, &EmbedLen))\r
946         {\r
947               hIn = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen);\r
948 \r
949                if (Verbose) {\r
950 \r
951                   fprintf(stdout, " (Embedded profile found)\n");\r
952                                   PrintProfileInformation(hIn);\r
953                   fflush(stdout);\r
954               }\r
955 \r
956                if (hIn != NULL && SaveEmbedded != NULL)\r
957                           SaveMemoryBlock(EmbedBuffer, EmbedLen, SaveEmbedded);\r
958 \r
959               free(EmbedBuffer);\r
960         }\r
961         else\r
962         {\r
963             // Default for ITU/Fax\r
964             if (cDefInpProf == NULL && T_COLORSPACE(wInput) == PT_Lab)\r
965                 cDefInpProf = "*Lab";\r
966 \r
967             if (cDefInpProf != NULL && cmsstrcasecmp(cDefInpProf, "*lab") == 0)\r
968                 hIn = CreateITU2PCS_ICC();\r
969             else\r
970                 hIn = OpenStockProfile(0, cDefInpProf);\r
971        }\r
972 \r
973         if (cOutProf != NULL && cmsstrcasecmp(cOutProf, "*lab") == 0)\r
974             hOut = CreatePCS2ITU_ICC();\r
975         else\r
976         hOut = OpenStockProfile(0, cOutProf);\r
977 \r
978        hProof = NULL;\r
979        if (cProofing != NULL) {\r
980 \r
981            hProof = OpenStockProfile(0, cProofing);\r
982            if (hProof == NULL) {\r
983             FatalError("Proofing profile couldn't be read.");\r
984            }\r
985            dwFlags |= cmsFLAGS_SOFTPROOFING;\r
986           }\r
987        }\r
988 \r
989         if (!hIn)\r
990             FatalError("Input profile couldn't be read.");\r
991         if (!hOut)\r
992             FatalError("Output profile couldn't be read.");\r
993 \r
994        // Assure both, input profile and input JPEG are on same colorspace\r
995        if (cmsGetColorSpace(hIn) != _cmsICCcolorSpace(T_COLORSPACE(wInput)))\r
996               FatalError("Input profile is not operating in proper color space");\r
997 \r
998 \r
999        // Output colorspace is given by output profile\r
1000 \r
1001         if (lIsDeviceLink) {\r
1002             OutputColorSpace = GetDevicelinkColorSpace(hIn);\r
1003         }\r
1004         else {\r
1005             OutputColorSpace = GetProfileColorSpace(hOut);\r
1006         }\r
1007 \r
1008        jpeg_copy_critical_parameters(&Decompressor, &Compressor);\r
1009 \r
1010        WriteOutputFields(OutputColorSpace);\r
1011 \r
1012        wOutput      = ComputeOutputFormatDescriptor(wInput, OutputColorSpace);\r
1013 \r
1014 \r
1015        xform = cmsCreateProofingTransform(hIn, wInput,\r
1016                                           hOut, wOutput,\r
1017                                           hProof, Intent,\r
1018                                           ProofingIntent, dwFlags);\r
1019            if (xform == NULL)\r
1020                  FatalError("Cannot transform by using the profiles");\r
1021 \r
1022        DoTransform(xform, OutputColorSpace);\r
1023 \r
1024 \r
1025        jcopy_markers_execute(&Decompressor, &Compressor);\r
1026 \r
1027        cmsDeleteTransform(xform);\r
1028        cmsCloseProfile(hIn);\r
1029        cmsCloseProfile(hOut);\r
1030        if (hProof) cmsCloseProfile(hProof);\r
1031 \r
1032        return 1;\r
1033 }\r
1034 \r
1035 \r
1036 // Simply print help\r
1037 \r
1038 static\r
1039 void Help(int level)\r
1040 {\r
1041      fprintf(stderr, "little cms ICC profile applier for JPEG - v3.2 [LittleCMS %2.2f]\n\n", LCMS_VERSION / 1000.0);\r
1042 \r
1043      switch(level) {\r
1044 \r
1045      default:\r
1046      case 0:\r
1047 \r
1048      fprintf(stderr, "usage: jpgicc [flags] input.jpg output.jpg\n");\r
1049 \r
1050      fprintf(stderr, "\nflags:\n\n");\r
1051      fprintf(stderr, "%cv - Verbose\n", SW);\r
1052      fprintf(stderr, "%ci<profile> - Input profile (defaults to sRGB)\n", SW);\r
1053      fprintf(stderr, "%co<profile> - Output profile (defaults to sRGB)\n", SW);\r
1054 \r
1055          PrintRenderingIntents();\r
1056 \r
1057 \r
1058      fprintf(stderr, "%cb - Black point compensation\n", SW);\r
1059      fprintf(stderr, "%cd<0..1> - Observer adaptation state (abs.col. only)\n", SW);\r
1060      fprintf(stderr, "%cn - Ignore embedded profile\n", SW);\r
1061      fprintf(stderr, "%ce - Embed destination profile\n", SW);\r
1062      fprintf(stderr, "%cs<new profile> - Save embedded profile as <new profile>\n", SW);\r
1063 \r
1064      fprintf(stderr, "\n");\r
1065 \r
1066      fprintf(stderr, "%cc<0,1,2,3> - Precalculates transform (0=Off, 1=Normal, 2=Hi-res, 3=LoRes) [defaults to 1]\n", SW);\r
1067      fprintf(stderr, "\n");\r
1068 \r
1069      fprintf(stderr, "%cp<profile> - Soft proof profile\n", SW);\r
1070      fprintf(stderr, "%cm<0,1,2,3> - SoftProof intent\n", SW);\r
1071      fprintf(stderr, "%cg - Marks out-of-gamut colors on softproof\n", SW);\r
1072      fprintf(stderr, "%c!<r>,<g>,<b> - Out-of-gamut marker channel values\n", SW);\r
1073 \r
1074      fprintf(stderr, "\n");\r
1075      fprintf(stderr, "%cq<0..100> - Output JPEG quality\n", SW);\r
1076 \r
1077      fprintf(stderr, "\n");\r
1078      fprintf(stderr, "%ch<0,1,2,3> - More help\n", SW);\r
1079      break;\r
1080 \r
1081      case 1:\r
1082 \r
1083      fprintf(stderr, "Examples:\n\n"\r
1084                      "To color correct from scanner to sRGB:\n"\r
1085                      "\tjpgicc %ciscanner.icm in.jpg out.jpg\n"\r
1086                      "To convert from monitor1 to monitor2:\n"\r
1087                      "\tjpgicc %cimon1.icm %comon2.icm in.jpg out.jpg\n"\r
1088                      "To make a CMYK separation:\n"\r
1089                      "\tjpgicc %coprinter.icm inrgb.jpg outcmyk.jpg\n"\r
1090                      "To recover sRGB from a CMYK separation:\n"\r
1091                      "\tjpgicc %ciprinter.icm incmyk.jpg outrgb.jpg\n"\r
1092                      "To convert from CIELab ITU/Fax JPEG to sRGB\n"\r
1093                      "\tjpgicc in.jpg out.jpg\n\n",\r
1094                      SW, SW, SW, SW, SW, SW);\r
1095      break;\r
1096 \r
1097      case 2:\r
1098                  PrintBuiltins();\r
1099                  break;\r
1100 \r
1101      case 3:\r
1102 \r
1103      fprintf(stderr, "This program is intended to be a demo of the little cms\n"\r
1104                      "engine. Both lcms and this program are freeware. You can\n"\r
1105                      "obtain both in source code at http://www.littlecms.com\n"\r
1106                      "For suggestions, comments, bug reports etc. send mail to\n"\r
1107                      "marti@littlecms.com\n\n");\r
1108      break;\r
1109      }\r
1110 \r
1111      exit(0);\r
1112 }\r
1113 \r
1114 \r
1115 // The toggles stuff\r
1116 \r
1117 static\r
1118 void HandleSwitches(int argc, char *argv[])\r
1119 {\r
1120     int s;\r
1121 \r
1122     while ((s=xgetopt(argc,argv,"bBnNvVGgh:H:i:I:o:O:P:p:t:T:c:C:Q:q:M:m:L:l:eEs:S:!:D:d:")) != EOF) {\r
1123 \r
1124         switch (s)\r
1125         {\r
1126 \r
1127         case 'b':\r
1128         case 'B':\r
1129             BlackPointCompensation = TRUE;\r
1130             break;\r
1131 \r
1132         case 'd':\r
1133         case 'D': ObserverAdaptationState = atof(xoptarg);\r
1134             if (ObserverAdaptationState < 0 ||\r
1135                 ObserverAdaptationState > 1.0)\r
1136                 FatalError("Adaptation state should be 0..1");\r
1137             break;\r
1138 \r
1139         case 'v':\r
1140         case 'V':\r
1141             Verbose = TRUE;\r
1142             break;\r
1143 \r
1144         case 'i':\r
1145         case 'I':\r
1146             if (lIsDeviceLink)\r
1147                 FatalError("Device-link already specified");\r
1148 \r
1149             cInpProf = xoptarg;\r
1150             break;\r
1151 \r
1152         case 'o':\r
1153         case 'O':\r
1154             if (lIsDeviceLink)\r
1155                 FatalError("Device-link already specified");\r
1156 \r
1157             cOutProf = xoptarg;\r
1158             break;\r
1159 \r
1160         case 'l':\r
1161         case 'L':\r
1162                         if (cInpProf != NULL || cOutProf != NULL)\r
1163                                 FatalError("input/output profiles already specified");\r
1164 \r
1165             cInpProf = xoptarg;\r
1166             lIsDeviceLink = TRUE;\r
1167             break;\r
1168 \r
1169         case 'p':\r
1170         case 'P':\r
1171             cProofing = xoptarg;\r
1172             break;\r
1173 \r
1174         case 't':\r
1175         case 'T':\r
1176             Intent = atoi(xoptarg);\r
1177             break;\r
1178 \r
1179         case 'N':\r
1180         case 'n':\r
1181             IgnoreEmbedded = TRUE;\r
1182             break;\r
1183 \r
1184         case 'e':\r
1185         case 'E':\r
1186             EmbedProfile = TRUE;\r
1187             break;\r
1188 \r
1189 \r
1190         case 'g':\r
1191         case 'G':\r
1192             GamutCheck = TRUE;\r
1193             break;\r
1194 \r
1195         case 'c':\r
1196         case 'C':\r
1197             PrecalcMode = atoi(xoptarg);\r
1198             if (PrecalcMode < 0 || PrecalcMode > 2)\r
1199                 FatalError("Unknown precalc mode '%d'", PrecalcMode);\r
1200             break;\r
1201 \r
1202         case 'H':\r
1203         case 'h':  {\r
1204 \r
1205             int a =  atoi(xoptarg);\r
1206             Help(a);\r
1207                    }\r
1208             break;\r
1209 \r
1210         case 'q':\r
1211         case 'Q':\r
1212             jpegQuality = atoi(xoptarg);\r
1213             if (jpegQuality > 100) jpegQuality = 100;\r
1214             if (jpegQuality < 0)   jpegQuality = 0;\r
1215             break;\r
1216 \r
1217         case 'm':\r
1218         case 'M':\r
1219             ProofingIntent = atoi(xoptarg);\r
1220             break;\r
1221 \r
1222         case 's':\r
1223         case 'S': SaveEmbedded = xoptarg;\r
1224             break;\r
1225 \r
1226         case '!':\r
1227             if (sscanf(xoptarg, "%hu,%hu,%hu", &Alarm[0], &Alarm[1], &Alarm[2]) == 3) {\r
1228                 int i;\r
1229                 for (i=0; i < 3; i++) {\r
1230                     Alarm[i] = (Alarm[i] << 8) | Alarm[i];\r
1231                 }\r
1232             }\r
1233             break;\r
1234 \r
1235         default:\r
1236 \r
1237             FatalError("Unknown option - run without args to see valid ones");\r
1238         }\r
1239 \r
1240     }\r
1241 }\r
1242 \r
1243 \r
1244 int main(int argc, char* argv[])\r
1245 {\r
1246         InitUtils("jpgicc");\r
1247 \r
1248         HandleSwitches(argc, argv);\r
1249 \r
1250         if ((argc - xoptind) != 2) {\r
1251                 Help(0);\r
1252         }\r
1253 \r
1254         OpenInput(argv[xoptind]);\r
1255         OpenOutput(argv[xoptind+1]);\r
1256 \r
1257         TransformImage(cInpProf, cOutProf);\r
1258 \r
1259 \r
1260         if (Verbose) { fprintf(stdout, "\n"); fflush(stdout); }\r
1261 \r
1262         Done();\r
1263 \r
1264         return 0;\r
1265 }\r
1266 \r
1267 \r
1268 \r