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