Imported Upstream version 2.4
[platform/upstream/lcms2.git] / utils / jpgicc / jpgicc.c
index 9b902f6..449c6c8 100644 (file)
-//---------------------------------------------------------------------------------
-//
-//  Little Color Management System
-//  Copyright (c) 1998-2010 Marti Maria Saguer
-//
-// Permission is hereby granted, free of charge, to any person obtaining 
-// a copy of this software and associated documentation files (the "Software"), 
-// to deal in the Software without restriction, including without limitation 
-// the rights to use, copy, modify, merge, publish, distribute, sublicense, 
-// and/or sell copies of the Software, and to permit persons to whom the Software 
-// is furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in 
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
-// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-// This program does apply profiles to (some) JPEG files
-
-
-#include "utils.h"
-
-#include "jpeglib.h"
-#include "iccjpeg.h"
-
-// Flags
-static cmsBool BlackPointCompensation = FALSE;
-static cmsBool IgnoreEmbedded         = FALSE;
-static cmsBool GamutCheck             = FALSE;
-static cmsBool lIsITUFax              = FALSE;
-static cmsBool lIsPhotoshopApp13      = FALSE;
-static cmsBool lIsEXIF;
-static cmsBool lIsDeviceLink          = FALSE;
-static cmsBool EmbedProfile           = FALSE;
-
-static const char* SaveEmbedded = NULL;
-
-static int Intent                  = INTENT_PERCEPTUAL;
-static int ProofingIntent          = INTENT_PERCEPTUAL;
-static int PrecalcMode             = 1;
-
-static int jpegQuality             = 75;
-
-static cmsFloat64Number ObserverAdaptationState = 0;
-
-
-static char *cInpProf  = NULL;
-static char *cOutProf  = NULL;
-static char *cProofing = NULL;
-
-static FILE * InFile;
-static FILE * OutFile;
-
-static struct jpeg_decompress_struct Decompressor;
-static struct jpeg_compress_struct   Compressor;
-
-
-static struct my_error_mgr {
-
-    struct  jpeg_error_mgr pub;   // "public" fields
-    void*   Cargo;                // "private" fields
-
-} ErrorHandler;
-
-
-cmsUInt16Number Alarm[4] = {128,128,128,0};
-
-// Out of mem
-static
-void OutOfMem(size_t size)
-{
-    FatalError("Out of memory on allocating %d bytes.", size);
-}
-
-
-static
-void my_error_exit (j_common_ptr cinfo)
-{  
-  char buffer[JMSG_LENGTH_MAX];
-  
-  (*cinfo->err->format_message) (cinfo, buffer);
-  FatalError(buffer);
-}
-
-/*
-Definition of the APPn Markers Defined for continuous-tone G3FAX
-
-The application code APP1 initiates identification of the image as 
-a G3FAX application and defines the spatial resolution and subsampling. 
-This marker directly follows the SOI marker. The data format will be as follows:
-
-X'FFE1' (APP1), length, FAX identifier, version, spatial resolution.
-
-The above terms are defined as follows:
-
-Length: (Two octets) Total APP1 field octet count including the octet count itself, but excluding the APP1
-marker.
-
-FAX identifier: (Six octets) X'47', X'33', X'46', X'41', X'58', X'00'. This X'00'-terminated string "G3FAX"
-uniquely identifies this APP1 marker.
-
-Version: (Two octets) X'07CA'. This string specifies the year of approval of the standard, for identification
-in the case of future revision (for example, 1994).
-
-Spatial Resolution: (Two octets) Lightness pixel density in pels/25.4 mm. The basic value is 200. Allowed values are
-100, 200, 300, 400, 600 and 1200 pels/25.4 mm, with square (or equivalent) pels.
-
-NOTE \96 The functional equivalence of inch-based and mm-based resolutions is maintained. For example, the 200 × 200
-*/
-
-static
-cmsBool IsITUFax(jpeg_saved_marker_ptr ptr)
-{
-       while (ptr) 
-       {
-        if (ptr -> marker == (JPEG_APP0 + 1) && ptr -> data_length > 5) {
-            
-                       const char* data = (const char*) ptr -> data;
-
-                       if (strcmp(data, "G3FAX") == 0) return TRUE;                                
-               }
-
-               ptr = ptr -> next;
-       }
-
-       return FALSE;
-}
-
-// Save a ITU T.42/Fax marker with defaults on boundaries. This is the only mode we support right now.
-static
-void SetITUFax(j_compress_ptr cinfo)
-{    
-       unsigned char Marker[] = "G3FAX\x00\0x07\xCA\x00\xC8";
-
-       jpeg_write_marker(cinfo, (JPEG_APP0 + 1), Marker, 10);      
-}
-
-
-// Build a profile for decoding ITU T.42/Fax JPEG streams. 
-// The profile has an additional ability in the input direction of
-// gamut compress values between 85 < a < -85 and -75 < b < 125. This conforms
-// the default range for ITU/T.42 -- See RFC 2301, section 6.2.3 for details
-
-//  L*  =   [0, 100]
-//  a*  =   [\9685, 85]
-//  b*  =   [\9675, 125]
-
-
-// These functions does convert the encoding of ITUFAX to floating point
-// and vice-versa. No gamut mapping is performed yet.
-
-static
-void ITU2Lab(const cmsUInt16Number In[3], cmsCIELab* Lab)
-{
-       Lab -> L = (double) In[0] / 655.35;
-       Lab -> a = (double) 170.* (In[1] - 32768.) / 65535.;
-       Lab -> b = (double) 200.* (In[2] - 24576.) / 65535.;
-}
-
-static
-void Lab2ITU(const cmsCIELab* Lab, cmsUInt16Number Out[3])
-{
-       Out[0] = (cmsUInt16Number) floor((double) (Lab -> L / 100.)* 65535. );
-       Out[1] = (cmsUInt16Number) floor((double) (Lab -> a / 170.)* 65535. + 32768. );
-       Out[2] = (cmsUInt16Number) floor((double) (Lab -> b / 200.)* 65535. + 24576. );
-}
-
-// These are the samplers-- They are passed as callbacks to cmsStageSampleCLut16bit()
-// then, cmsSample3DGrid() will sweel whole Lab gamut calling these functions
-// once for each node. In[] will contain the Lab PCS value to convert to ITUFAX
-// on PCS2ITU, or the ITUFAX value to convert to Lab in ITU2PCS
-// You can change the number of sample points if desired, the algorithm will
-// remain same. 33 points gives good accurancy, but you can reduce to 22 or less
-// is space is critical
-
-#define GRID_POINTS 33
-
-static
-int PCS2ITU(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void*  Cargo)
-{      
-       cmsCIELab Lab;
-
-       cmsLabEncoded2Float(&Lab, In);    
-       cmsDesaturateLab(&Lab, 85, -85, 125, -75);    // This function does the necessary gamut remapping  
-       Lab2ITU(&Lab, Out);
-       return TRUE;
-
-    UTILS_UNUSED_PARAMETER(Cargo);
-}
-
-
-static
-int ITU2PCS( register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void*  Cargo)
-{   
-       cmsCIELab Lab;
-
-       ITU2Lab(In, &Lab);
-       cmsFloat2LabEncoded(Out, &Lab);    
-       return TRUE;
-
-    UTILS_UNUSED_PARAMETER(Cargo);
-}
-
-// This function does create the virtual input profile, which decodes ITU to the profile connection space
-static
-cmsHPROFILE CreateITU2PCS_ICC(void)
-{      
-       cmsHPROFILE hProfile;
-       cmsPipeline* AToB0;
-       cmsStage* ColorMap;
-       
-       AToB0 = cmsPipelineAlloc(0, 3, 3);
-       if (AToB0 == NULL) return NULL;
-
-       ColorMap = cmsStageAllocCLut16bit(0, GRID_POINTS, 3, 3, NULL);
-       if (ColorMap == NULL) return NULL;
-
-    cmsPipelineInsertStage(AToB0, cmsAT_BEGIN, ColorMap);
-       cmsStageSampleCLut16bit(ColorMap, ITU2PCS, NULL, 0);
-
-       hProfile = cmsCreateProfilePlaceholder(0);
-       if (hProfile == NULL) {
-               cmsPipelineFree(AToB0);
-               return NULL;
-       }
-       
-       cmsWriteTag(hProfile, cmsSigAToB0Tag, AToB0); 
-       cmsSetColorSpace(hProfile, cmsSigLabData);
-       cmsSetPCS(hProfile, cmsSigLabData);
-       cmsSetDeviceClass(hProfile, cmsSigColorSpaceClass);      
-       cmsPipelineFree(AToB0);
-
-       return hProfile;
-}
-
-
-// This function does create the virtual output profile, with the necessary gamut mapping 
-static
-cmsHPROFILE CreatePCS2ITU_ICC(void)
-{
-    cmsHPROFILE hProfile;
-    cmsPipeline* BToA0;
-    cmsStage* ColorMap;
-        
-    BToA0 = cmsPipelineAlloc(0, 3, 3);
-    if (BToA0 == NULL) return NULL;
-
-    ColorMap = cmsStageAllocCLut16bit(0, GRID_POINTS, 3, 3, NULL);
-    if (ColorMap == NULL) return NULL;
-
-    cmsPipelineInsertStage(BToA0, cmsAT_BEGIN, ColorMap);
-    cmsStageSampleCLut16bit(ColorMap, PCS2ITU, NULL, 0);
-
-    hProfile = cmsCreateProfilePlaceholder(0);
-    if (hProfile == NULL) {
-        cmsPipelineFree(BToA0);
-        return NULL;
-    }
-
-    cmsWriteTag(hProfile, cmsSigBToA0Tag, BToA0); 
-    cmsSetColorSpace(hProfile, cmsSigLabData);
-    cmsSetPCS(hProfile, cmsSigLabData);
-    cmsSetDeviceClass(hProfile, cmsSigColorSpaceClass); 
-
-    cmsPipelineFree(BToA0);
-
-    return hProfile;
-}
-
-
-
-#define PS_FIXED_TO_FLOAT(h, l) ((float) (h) + ((float) (l)/(1<<16)))
-
-static
-cmsBool ProcessPhotoshopAPP13(JOCTET FAR *data, int datalen)
-{
-    int i;
-
-    for (i = 14; i < datalen; )
-    {
-        long len;
-        unsigned int type;
-                
-        if (!(GETJOCTET(data[i]  ) == 0x38 &&
-              GETJOCTET(data[i+1]) == 0x42 &&
-              GETJOCTET(data[i+2]) == 0x49 &&
-              GETJOCTET(data[i+3]) == 0x4D)) break; // Not recognized
-                    
-        i += 4; // identifying string 
-        
-        type = (unsigned int) (GETJOCTET(data[i]<<8) + GETJOCTET(data[i+1]));
-
-        i += 2; // resource type 
-        
-        i += GETJOCTET(data[i]) + ((GETJOCTET(data[i]) & 1) ? 1 : 2);   // resource name 
-        
-        len = ((((GETJOCTET(data[i]<<8) + GETJOCTET(data[i+1]))<<8) + 
-                         GETJOCTET(data[i+2]))<<8) + GETJOCTET(data[i+3]);
-
-        i += 4; // Size 
-        
-        if (type == 0x03ED && len >= 16) {
-            
-            Decompressor.X_density = (UINT16) PS_FIXED_TO_FLOAT(GETJOCTET(data[i]<<8) + GETJOCTET(data[i+1]),
-                                                 GETJOCTET(data[i+2]<<8) + GETJOCTET(data[i+3]));
-            Decompressor.Y_density = (UINT16) PS_FIXED_TO_FLOAT(GETJOCTET(data[i+8]<<8) + GETJOCTET(data[i+9]),
-                                                 GETJOCTET(data[i+10]<<8) + GETJOCTET(data[i+11]));
-            
-            // Set the density unit to 1 since the 
-            // Vertical and Horizontal resolutions 
-            // are specified in Pixels per inch 
-
-            Decompressor.density_unit = 0x01;
-            return TRUE;
-            
-        }
-                
-        i += len + ((len & 1) ? 1 : 0);   // Alignment             
-    }
-    return FALSE;
-}
-
-
-static
-cmsBool HandlePhotoshopAPP13(jpeg_saved_marker_ptr ptr)
-{
-    while (ptr) {
-        
-        if (ptr -> marker == (JPEG_APP0 + 13) && ptr -> data_length > 9)
-        {    
-            JOCTET FAR* data = ptr -> data;    
-            
-            if(GETJOCTET(data[0]) == 0x50 &&
-               GETJOCTET(data[1]) == 0x68 &&
-               GETJOCTET(data[2]) == 0x6F &&
-               GETJOCTET(data[3]) == 0x74 &&
-               GETJOCTET(data[4]) == 0x6F &&
-               GETJOCTET(data[5]) == 0x73 &&
-               GETJOCTET(data[6]) == 0x68 &&
-               GETJOCTET(data[7]) == 0x6F &&
-               GETJOCTET(data[8]) == 0x70) {
-             
-                ProcessPhotoshopAPP13(data, ptr -> data_length);
-                return TRUE;
-            }
-        }
-        
-        ptr = ptr -> next;
-    }
-    
-    return FALSE;    
-}
-
-
-typedef unsigned short uint16_t;
-typedef unsigned char uint8_t;
-typedef unsigned int uint32_t;
-
-#define INTEL_BYTE_ORDER 0x4949
-#define XRESOLUTION 0x011a
-#define YRESOLUTION 0x011b
-#define RESOLUTION_UNIT 0x128
-
-// Read a 16-bit word
-static
-uint16_t read16(uint8_t* arr, int pos,  int swapBytes) 
-{ 
-    uint8_t b1 = arr[pos];
-    uint8_t b2 = arr[pos+1];
-    
-    return (swapBytes) ?  ((b2 << 8) | b1) : ((b1 << 8) | b2);
-}
-
-
-// Read a 32-bit word
-static
-uint32_t read32(uint8_t* arr, int pos,  int swapBytes)
-{
-  
-    if(!swapBytes) {
-
-        return (arr[pos]   << 24) | 
-               (arr[pos+1] << 16) | 
-               (arr[pos+2] << 8) | 
-                arr[pos+3];
-    }
-
-    return arr[pos] | 
-           (arr[pos+1] << 8) | 
-           (arr[pos+2] << 16) | 
-           (arr[pos+3] << 24);
-}
-
-
-
-static
-int read_tag(uint8_t* arr, int pos,  int swapBytes, void* dest)
-{
-        // Format should be 5 over here (rational)
-    uint32_t format = read16(arr, pos + 2, swapBytes);
-    // Components should be 1
-    uint32_t components = read32(arr, pos + 4, swapBytes);
-    // Points to the value
-    uint32_t offset;
-    
-    // sanity
-    if (components != 1) return 0;
-
-    if (format == 3) 
-        offset = pos + 8;
-    else
-        offset =  read32(arr, pos + 8, swapBytes);
-
-    switch (format) {
-
-    case 5: // Rational
-          {
-          double num = read32(arr, offset, swapBytes);
-          double den = read32(arr, offset + 4, swapBytes);
-          *(double *) dest = num / den;
-          }
-          break;
-
-    case 3: // uint 16
-        *(int*) dest = read16(arr, offset, swapBytes);
-        break;
-
-    default:  return 0;
-    }
-
-    return 1;
-}
-
-
-
-// Handler for EXIF data
-static
-    cmsBool HandleEXIF(struct jpeg_decompress_struct* cinfo)
-{
-    jpeg_saved_marker_ptr ptr;
-    uint32_t ifd_ofs;
-    int pos = 0, swapBytes = 0;
-    uint32_t i, numEntries;
-    double XRes = -1, YRes = -1;
-    int Unit = 2; // Inches
-
-
-    for (ptr = cinfo ->marker_list; ptr; ptr = ptr ->next) {
-
-        if ((ptr ->marker == JPEG_APP0+1) && ptr ->data_length > 6) {
-            JOCTET FAR* data = ptr -> data;   
-
-            if (memcmp(data, "Exif\0\0", 6) == 0) {
-
-                data += 6; // Skip EXIF marker
-
-                // 8 byte TIFF header
-                // first two determine byte order
-                pos = 0;
-                if (read16(data, pos, 0) == INTEL_BYTE_ORDER) {
-                    swapBytes = 1;
-                }
-
-                pos += 2;
-
-                // next two bytes are always 0x002A (TIFF version)
-                pos += 2;
-
-                // offset to Image File Directory (includes the previous 8 bytes)
-                ifd_ofs = read32(data, pos, swapBytes);
-
-                // Search the directory for resolution tags          
-                numEntries = read16(data, ifd_ofs, swapBytes);
-
-                for (i=0; i < numEntries; i++) {
-
-                    uint32_t entryOffset = ifd_ofs + 2 + (12 * i);
-                    uint32_t tag = read16(data, entryOffset, swapBytes);
-
-                    switch (tag) {
-
-                    case RESOLUTION_UNIT:
-                        if (!read_tag(data, entryOffset, swapBytes, &Unit)) return FALSE;
-                        break;
-
-                    case XRESOLUTION:
-                        if (!read_tag(data, entryOffset, swapBytes, &XRes)) return FALSE;
-                        break;
-
-                    case YRESOLUTION:
-                        if (!read_tag(data, entryOffset, swapBytes, &YRes)) return FALSE;
-                        break;
-
-                    default:;
-                    }
-
-                }
-
-                // Proceed if all found
-
-                if (XRes != -1 && YRes != -1) 
-                {
-
-                    // 1 = None 
-                    // 2 = inches 
-                    // 3 = cm
-
-                    switch (Unit) {
-
-                    case 2:
-                    
-                        cinfo ->X_density = (UINT16) floor(XRes + 0.5);
-                        cinfo ->Y_density = (UINT16) floor(YRes + 0.5);
-                        break;
-
-                    case 1:
-
-                        cinfo ->X_density = (UINT16) floor(XRes * 2.54 + 0.5);
-                        cinfo ->Y_density = (UINT16) floor(YRes * 2.54 + 0.5);
-                        break;
-
-                    default: return FALSE;
-                    }
-
-                    cinfo ->density_unit = 1;  /* 1 for dots/inch, or 2 for dots/cm.*/
-
-                }
-
-
-            }
-        }    
-    }
-    return FALSE;
-}
-
-
-static
-cmsBool OpenInput(const char* FileName)
-{
-       int m;
-
-       lIsITUFax = FALSE;   
-       InFile  = fopen(FileName, "rb");
-       if (InFile == NULL) {
-               FatalError("Cannot open '%s'", FileName);        
-       }
-
-       // Now we can initialize the JPEG decompression object.
-       Decompressor.err                 = jpeg_std_error(&ErrorHandler.pub);
-       ErrorHandler.pub.error_exit      = my_error_exit;
-       ErrorHandler.pub.output_message  = my_error_exit;
-
-       jpeg_create_decompress(&Decompressor);
-       jpeg_stdio_src(&Decompressor, InFile);
-
-       for (m = 0; m < 16; m++)
-               jpeg_save_markers(&Decompressor, JPEG_APP0 + m, 0xFFFF);
-
-       // setup_read_icc_profile(&Decompressor);  
-
-       fseek(InFile, 0, SEEK_SET);
-       jpeg_read_header(&Decompressor, TRUE);
-
-       return TRUE;
-}
-
-
-static
-cmsBool OpenOutput(const char* FileName)
-{
-
-       OutFile = fopen(FileName, "wb");
-       if (OutFile == NULL) {
-               FatalError("Cannot create '%s'", FileName);
-
-       }
-
-       Compressor.err                   = jpeg_std_error(&ErrorHandler.pub);
-       ErrorHandler.pub.error_exit      = my_error_exit;
-       ErrorHandler.pub.output_message  = my_error_exit;
-
-       Compressor.input_components = Compressor.num_components = 4;
-
-       jpeg_create_compress(&Compressor);
-       jpeg_stdio_dest(&Compressor, OutFile);  
-       return TRUE;
-}
-
-static
-cmsBool Done(void)
-{
-       jpeg_destroy_decompress(&Decompressor);
-       jpeg_destroy_compress(&Compressor);
-       return fclose(InFile) + fclose(OutFile);
-
-}
-
-
-// Build up the pixeltype descriptor
-
-static
-cmsUInt32Number GetInputPixelType(void)
-{
-     int space, bps, extra, ColorChannels, Flavor;
-        
-     lIsITUFax         = IsITUFax(Decompressor.marker_list);
-     lIsPhotoshopApp13 = HandlePhotoshopAPP13(Decompressor.marker_list);
-     lIsEXIF           = HandleEXIF(&Decompressor);
-
-     ColorChannels = Decompressor.num_components;
-     extra  = 0;            // Alpha = None
-     bps    = 1;            // 8 bits
-     Flavor = 0;            // Vanilla
-
-     if (lIsITUFax) {
-
-        space = PT_Lab;
-        Decompressor.out_color_space = JCS_YCbCr;  // Fake to don't touch
-     }
-     else
-     switch (Decompressor.jpeg_color_space) {
-
-     case JCS_GRAYSCALE:        // monochrome
-              space = PT_GRAY;
-              Decompressor.out_color_space = JCS_GRAYSCALE;
-              break;
-
-     case JCS_RGB:             // red/green/blue
-              space = PT_RGB;
-              Decompressor.out_color_space = JCS_RGB;
-              break;
-
-     case JCS_YCbCr:               // Y/Cb/Cr (also known as YUV)
-              space = PT_RGB;      // Let IJG code to do the conversion
-              Decompressor.out_color_space = JCS_RGB;   
-              break;
-
-     case JCS_CMYK:            // C/M/Y/K
-              space = PT_CMYK;
-              Decompressor.out_color_space = JCS_CMYK;
-              if (Decompressor.saw_Adobe_marker)            // Adobe keeps CMYK inverted, so change flavor
-                                Flavor = 1;                 // from vanilla to chocolate
-              break;
-
-     case JCS_YCCK:            // Y/Cb/Cr/K              
-              space = PT_CMYK;
-              Decompressor.out_color_space = JCS_CMYK;
-              if (Decompressor.saw_Adobe_marker)            // ditto
-                                Flavor = 1;                 
-              break;
-
-     default:
-              FatalError("Unsupported color space (0x%x)", Decompressor.jpeg_color_space);
-              return 0;
-     }
-
-     return (EXTRA_SH(extra)|CHANNELS_SH(ColorChannels)|BYTES_SH(bps)|COLORSPACE_SH(space)|FLAVOR_SH(Flavor));
-}
-
-
-// Rearrange pixel type to build output descriptor
-static
-cmsUInt32Number ComputeOutputFormatDescriptor(cmsUInt32Number dwInput, int OutColorSpace)
-{
-       int IsPlanar  = T_PLANAR(dwInput);
-       int Channels  = 0;
-       int Flavor    = 0;
-
-       switch (OutColorSpace) {
-
-   case PT_GRAY:
-          Channels = 1;
-          break;
-   case PT_RGB:
-   case PT_CMY:
-   case PT_Lab:
-   case PT_YUV:
-   case PT_YCbCr:
-          Channels = 3;
-          break;
-
-   case PT_CMYK:
-          if (Compressor.write_Adobe_marker)   // Adobe keeps CMYK inverted, so change flavor to chocolate
-                  Flavor = 1;
-          Channels = 4;
-          break;
-   default:
-          FatalError("Unsupported output color space");
-       }
-
-       return (COLORSPACE_SH(OutColorSpace)|PLANAR_SH(IsPlanar)|CHANNELS_SH(Channels)|BYTES_SH(1)|FLAVOR_SH(Flavor));
-}
-
-
-// Equivalence between ICC color spaces and lcms color spaces
-static
-int GetProfileColorSpace(cmsHPROFILE hProfile)
-{
-    cmsColorSpaceSignature ProfileSpace = cmsGetColorSpace(hProfile);
-
-       return _cmsLCMScolorSpace(ProfileSpace);
-}
-
-static
-int GetDevicelinkColorSpace(cmsHPROFILE hProfile)
-{
-    cmsColorSpaceSignature ProfileSpace = cmsGetPCS(hProfile);
-
-       return _cmsLCMScolorSpace(ProfileSpace);
-}
-
-
-// From TRANSUPP
-
-static 
-void jcopy_markers_execute(j_decompress_ptr srcinfo, j_compress_ptr dstinfo)
-{
-  jpeg_saved_marker_ptr marker;
-
-  /* In the current implementation, we don't actually need to examine the
-   * option flag here; we just copy everything that got saved.
-   * But to avoid confusion, we do not output JFIF and Adobe APP14 markers
-   * if the encoder library already wrote one.
-   */
-  for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) {
-
-    if (dstinfo->write_JFIF_header &&
-        marker->marker == JPEG_APP0 &&
-        marker->data_length >= 5 &&
-        GETJOCTET(marker->data[0]) == 0x4A &&
-        GETJOCTET(marker->data[1]) == 0x46 &&
-        GETJOCTET(marker->data[2]) == 0x49 &&
-        GETJOCTET(marker->data[3]) == 0x46 &&
-        GETJOCTET(marker->data[4]) == 0)
-                          continue;         /* reject duplicate JFIF */
-
-    if (dstinfo->write_Adobe_marker &&
-        marker->marker == JPEG_APP0+14 &&
-        marker->data_length >= 5 &&
-        GETJOCTET(marker->data[0]) == 0x41 &&
-        GETJOCTET(marker->data[1]) == 0x64 &&
-        GETJOCTET(marker->data[2]) == 0x6F &&
-        GETJOCTET(marker->data[3]) == 0x62 &&
-        GETJOCTET(marker->data[4]) == 0x65)
-                         continue;         /* reject duplicate Adobe */
-
-     jpeg_write_marker(dstinfo, marker->marker,
-                       marker->data, marker->data_length);
-  }
-}
-
-static
-void WriteOutputFields(int OutputColorSpace)
-{
-    J_COLOR_SPACE in_space, jpeg_space;
-    int components;
-    
-    switch (OutputColorSpace) {
-        
-    case PT_GRAY: in_space = jpeg_space = JCS_GRAYSCALE; 
-                  components = 1; 
-                  break;
-
-    case PT_RGB:  in_space = JCS_RGB; 
-                  jpeg_space = JCS_YCbCr; 
-                  components = 3; 
-                  break;       // red/green/blue
-
-    case PT_YCbCr: in_space = jpeg_space = JCS_YCbCr; 
-                   components = 3; 
-                   break;               // Y/Cb/Cr (also known as YUV)
-
-    case PT_CMYK: in_space = JCS_CMYK; 
-                  jpeg_space = JCS_YCCK;  
-                  components = 4; 
-                  break;      // C/M/Y/components              
-
-    case PT_Lab:  in_space = jpeg_space = JCS_YCbCr; 
-                  components = 3; 
-                  break;                // Fake to don't touch
-    default:
-                 FatalError("Unsupported output color space");
-                 return;
-    }
-    
-
-    if (jpegQuality >= 100) {
-
-     // avoid destructive conversion when asking for lossless compression 
-        jpeg_space = in_space;
-    }
-
-    Compressor.in_color_space =  in_space;
-    Compressor.jpeg_color_space = jpeg_space;
-    Compressor.input_components = Compressor.num_components = components;
-    jpeg_set_defaults(&Compressor);
-    jpeg_set_colorspace(&Compressor, jpeg_space);
-
-
-    // Make sure to pass resolution through
-    if (OutputColorSpace == PT_CMYK)
-        Compressor.write_JFIF_header = 1;
-
-    // Avoid subsampling on high quality factor
-    jpeg_set_quality(&Compressor, jpegQuality, 1);    
-    if (jpegQuality >= 70) {
-
-      int i;
-      for(i=0; i < Compressor.num_components; i++) {
-
-               Compressor.comp_info[i].h_samp_factor = 1;
-            Compressor.comp_info[i].v_samp_factor = 1;
-      }
-
-    }
-
-}
-
-
-static
-void DoEmbedProfile(const char* ProfileFile)
-{
-    FILE* f;
-    size_t size, EmbedLen;
-    cmsUInt8Number* EmbedBuffer;
-
-        f = fopen(ProfileFile, "rb");
-        if (f == NULL) return;
-
-        size = cmsfilelength(f);
-        EmbedBuffer = (cmsUInt8Number*) malloc(size + 1);
-        EmbedLen = fread(EmbedBuffer, 1, size, f);
-        fclose(f);
-        EmbedBuffer[EmbedLen] = 0;
-
-        write_icc_profile (&Compressor, EmbedBuffer, EmbedLen);                   
-        free(EmbedBuffer);
-}
-
-
-
-static
-int DoTransform(cmsHTRANSFORM hXForm, int OutputColorSpace)
-{       
-    JSAMPROW ScanLineIn;
-    JSAMPROW ScanLineOut;
-
-    
-       //Preserve resolution values from the original
-       // (Thanks to Robert Bergs for finding out this bug)
-       Compressor.density_unit = Decompressor.density_unit;
-       Compressor.X_density    = Decompressor.X_density;
-       Compressor.Y_density    = Decompressor.Y_density;
-
-      //  Compressor.write_JFIF_header = 1;
-     
-       jpeg_start_decompress(&Decompressor);
-       jpeg_start_compress(&Compressor, TRUE);
-
-        if (OutputColorSpace == PT_Lab)
-            SetITUFax(&Compressor);
-
-       // Embed the profile if needed
-       if (EmbedProfile && cOutProf) 
-           DoEmbedProfile(cOutProf);
-
-       ScanLineIn  = (JSAMPROW) malloc(Decompressor.output_width * Decompressor.num_components);
-       ScanLineOut = (JSAMPROW) malloc(Compressor.image_width * Compressor.num_components);
-
-       while (Decompressor.output_scanline <
-                            Decompressor.output_height) {
-
-       jpeg_read_scanlines(&Decompressor, &ScanLineIn, 1);
-
-       cmsDoTransform(hXForm, ScanLineIn, ScanLineOut, Decompressor.output_width);
-
-       jpeg_write_scanlines(&Compressor, &ScanLineOut, 1);
-       }
-
-       free(ScanLineIn); 
-       free(ScanLineOut);
-
-       jpeg_finish_decompress(&Decompressor);
-       jpeg_finish_compress(&Compressor);
-       
-       return TRUE;
-}
-
-
-
-// Transform one image
-
-static
-int TransformImage(char *cDefInpProf, char *cOutProf)
-{
-       cmsHPROFILE hIn, hOut, hProof;
-       cmsHTRANSFORM xform;
-       cmsUInt32Number wInput, wOutput;
-       int OutputColorSpace;
-       cmsUInt32Number dwFlags = 0; 
-       cmsUInt32Number EmbedLen;
-       cmsUInt8Number* EmbedBuffer;
-
-
-       cmsSetAdaptationState(ObserverAdaptationState);
-
-       if (BlackPointCompensation) {
-
-            dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;            
-       }
-
-
-       switch (PrecalcMode) {
-           
-       case 0: dwFlags |= cmsFLAGS_NOOPTIMIZE; break;
-       case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break;
-       case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break;
-       default:;
-       }
-        
-
-       if (GamutCheck) {
-            dwFlags |= cmsFLAGS_GAMUTCHECK;
-            cmsSetAlarmCodes(Alarm);
-       }
-        
-       // Take input color space
-       wInput = GetInputPixelType();
-
-        if (lIsDeviceLink) {
-
-            hIn = cmsOpenProfileFromFile(cDefInpProf, "r");
-            hOut = NULL;
-            hProof = NULL;
-       }
-        else {
-
-        if (!IgnoreEmbedded && read_icc_profile(&Decompressor, &EmbedBuffer, &EmbedLen))
-        {
-              hIn = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen);
-
-               if (Verbose) {
-
-                  fprintf(stdout, " (Embedded profile found)\n");
-                                 PrintProfileInformation(hIn);
-                  fflush(stdout);
-              }
-
-               if (hIn != NULL && SaveEmbedded != NULL)
-                          SaveMemoryBlock(EmbedBuffer, EmbedLen, SaveEmbedded);
-
-              free(EmbedBuffer);
-        }
-        else
-        {
-            // Default for ITU/Fax
-            if (cDefInpProf == NULL && T_COLORSPACE(wInput) == PT_Lab)
-                cDefInpProf = "*Lab";
-
-            if (cDefInpProf != NULL && cmsstrcasecmp(cDefInpProf, "*lab") == 0)
-                hIn = CreateITU2PCS_ICC();
-            else
-                hIn = OpenStockProfile(0, cDefInpProf);
-       }
-
-        if (cOutProf != NULL && cmsstrcasecmp(cOutProf, "*lab") == 0)
-            hOut = CreatePCS2ITU_ICC();
-        else
-        hOut = OpenStockProfile(0, cOutProf);
-
-       hProof = NULL;
-       if (cProofing != NULL) {
-
-           hProof = OpenStockProfile(0, cProofing);
-           if (hProof == NULL) {
-            FatalError("Proofing profile couldn't be read.");
-           }
-           dwFlags |= cmsFLAGS_SOFTPROOFING;
-          }
-       }
-
-        if (!hIn)
-            FatalError("Input profile couldn't be read.");
-        if (!hOut)
-            FatalError("Output profile couldn't be read.");
-
-       // Assure both, input profile and input JPEG are on same colorspace       
-       if (cmsGetColorSpace(hIn) != _cmsICCcolorSpace(T_COLORSPACE(wInput)))
-              FatalError("Input profile is not operating in proper color space");
-       
-
-       // Output colorspace is given by output profile
-
-        if (lIsDeviceLink) {
-            OutputColorSpace = GetDevicelinkColorSpace(hIn);
-        }
-        else {
-            OutputColorSpace = GetProfileColorSpace(hOut);
-        }
-
-       jpeg_copy_critical_parameters(&Decompressor, &Compressor);
-       
-       WriteOutputFields(OutputColorSpace);               
-       
-       wOutput      = ComputeOutputFormatDescriptor(wInput, OutputColorSpace);
-       
-      
-       xform = cmsCreateProofingTransform(hIn, wInput, 
-                                          hOut, wOutput, 
-                                          hProof, Intent, 
-                                          ProofingIntent, dwFlags);
-          if (xform == NULL) 
-                 FatalError("Cannot transform by using the profiles");
-  
-       DoTransform(xform, OutputColorSpace);
-
-       
-       jcopy_markers_execute(&Decompressor, &Compressor);
-       
-       cmsDeleteTransform(xform);
-       cmsCloseProfile(hIn);
-       cmsCloseProfile(hOut);
-       if (hProof) cmsCloseProfile(hProof);
-       
-       return 1;
-}
-
-
-// Simply print help
-
-static
-void Help(int level)
-{
-     fprintf(stderr, "little cms ICC profile applier for JPEG - v3.1 [LittleCMS %2.2f]\n\n", LCMS_VERSION / 1000.0);
-
-     switch(level) {
-
-     default:
-     case 0:
-
-     fprintf(stderr, "usage: jpegicc [flags] input.jpg output.jpg\n");
-
-     fprintf(stderr, "\nflags:\n\n");
-     fprintf(stderr, "%cv - Verbose\n", SW);
-     fprintf(stderr, "%ci<profile> - Input profile (defaults to sRGB)\n", SW);
-     fprintf(stderr, "%co<profile> - Output profile (defaults to sRGB)\n", SW);   
-
-        PrintRenderingIntents();
-
-     
-     fprintf(stderr, "%cb - Black point compensation\n", SW);     
-     fprintf(stderr, "%cd<0..1> - Observer adaptation state (abs.col. only)\n", SW);
-     fprintf(stderr, "%cn - Ignore embedded profile\n", SW);
-     fprintf(stderr, "%ce - Embed destination profile\n", SW);
-     fprintf(stderr, "%cs<new profile> - Save embedded profile as <new profile>\n", SW);
-
-     fprintf(stderr, "\n");
-
-     fprintf(stderr, "%cc<0,1,2,3> - Precalculates transform (0=Off, 1=Normal, 2=Hi-res, 3=LoRes) [defaults to 1]\n", SW);     
-     fprintf(stderr, "\n");
-
-     fprintf(stderr, "%cp<profile> - Soft proof profile\n", SW);
-     fprintf(stderr, "%cm<0,1,2,3> - SoftProof intent\n", SW);
-     fprintf(stderr, "%cg - Marks out-of-gamut colors on softproof\n", SW);
-     fprintf(stderr, "%c!<r>,<g>,<b> - Out-of-gamut marker channel values\n", SW);
-
-     fprintf(stderr, "\n");
-     fprintf(stderr, "%cq<0..100> - Output JPEG quality\n", SW);
-
-     fprintf(stderr, "\n");
-     fprintf(stderr, "%ch<0,1,2,3> - More help\n", SW);
-     break;
-
-     case 1:
-
-     fprintf(stderr, "Examples:\n\n"
-                     "To color correct from scanner to sRGB:\n"
-                     "\tjpegicc %ciscanner.icm in.jpg out.jpg\n"
-                     "To convert from monitor1 to monitor2:\n"
-                     "\tjpegicc %cimon1.icm %comon2.icm in.jpg out.jpg\n"
-                     "To make a CMYK separation:\n"
-                     "\tjpegicc %coprinter.icm inrgb.jpg outcmyk.jpg\n"
-                     "To recover sRGB from a CMYK separation:\n"
-                     "\tjpegicc %ciprinter.icm incmyk.jpg outrgb.jpg\n"
-                     "To convert from CIELab ITU/Fax JPEG to sRGB\n"
-                     "\tjpegicc in.jpg out.jpg\n\n", 
-                     SW, SW, SW, SW, SW, SW);
-     break;
-
-     case 2:
-                PrintBuiltins();
-                break;
-
-     case 3:
-
-     fprintf(stderr, "This program is intended to be a demo of the little cms\n"
-                     "engine. Both lcms and this program are freeware. You can\n"
-                     "obtain both in source code at http://www.littlecms.com\n"
-                     "For suggestions, comments, bug reports etc. send mail to\n"
-                     "marti@littlecms.com\n\n");
-     break;
-     }
-
-     exit(0);
-}
-
-
-// The toggles stuff
-
-static
-void HandleSwitches(int argc, char *argv[])
-{
-    int s;
-    
-    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) {
-        
-        switch (s)
-        {
-            
-        case 'b':
-        case 'B':
-            BlackPointCompensation = TRUE;
-            break;
-            
-        case 'd':
-        case 'D': ObserverAdaptationState = atof(xoptarg);
-            if (ObserverAdaptationState < 0 || 
-                ObserverAdaptationState > 1.0)
-                FatalError("Adaptation state should be 0..1");
-            break;
-            
-        case 'v':
-        case 'V':
-            Verbose = TRUE;
-            break;
-            
-        case 'i':
-        case 'I':            
-            if (lIsDeviceLink)
-                FatalError("Device-link already specified"); 
-            
-            cInpProf = xoptarg;
-            break;
-            
-        case 'o':
-        case 'O':            
-            if (lIsDeviceLink)
-                FatalError("Device-link already specified"); 
-            
-            cOutProf = xoptarg;
-            break;
-            
-        case 'l':
-        case 'L': 
-                       if (cInpProf != NULL || cOutProf != NULL) 
-                               FatalError("input/output profiles already specified");
-
-            cInpProf = xoptarg;
-            lIsDeviceLink = TRUE;
-            break;
-            
-        case 'p':
-        case 'P':
-            cProofing = xoptarg;
-            break;
-            
-        case 't':
-        case 'T':
-            Intent = atoi(xoptarg);
-            break;
-            
-        case 'N':
-        case 'n':
-            IgnoreEmbedded = TRUE;
-            break;
-            
-        case 'e':
-        case 'E':
-            EmbedProfile = TRUE;
-            break;
-            
-
-        case 'g':
-        case 'G':
-            GamutCheck = TRUE;
-            break;
-            
-        case 'c':
-        case 'C':
-            PrecalcMode = atoi(xoptarg);
-            if (PrecalcMode < 0 || PrecalcMode > 2)
-                FatalError("Unknown precalc mode '%d'", PrecalcMode);
-            break;
-            
-        case 'H':
-        case 'h':  {
-            
-            int a =  atoi(xoptarg);
-            Help(a); 
-                   }
-            break;
-            
-        case 'q':
-        case 'Q':
-            jpegQuality = atoi(xoptarg);
-            if (jpegQuality > 100) jpegQuality = 100;
-            if (jpegQuality < 0)   jpegQuality = 0;
-            break;
-            
-        case 'm':
-        case 'M':
-            ProofingIntent = atoi(xoptarg);
-            break;
-            
-        case 's':
-        case 'S': SaveEmbedded = xoptarg;
-            break;
-            
-        case '!':       
-            if (sscanf(xoptarg, "%hu,%hu,%hu", &Alarm[0], &Alarm[1], &Alarm[2]) == 3) {
-                int i;
-                for (i=0; i < 3; i++) {
-                    Alarm[i] = (Alarm[i] << 8) | Alarm[i];
-                }
-            }
-            break;
-            
-        default:
-            
-            FatalError("Unknown option - run without args to see valid ones");
-        }
-        
-    }
-}
-
-
-int main(int argc, char* argv[])
-{
-       InitUtils("jpegicc");
-
-       HandleSwitches(argc, argv);
-
-       if ((argc - xoptind) != 2) {
-               Help(0);              
-       }
-
-       OpenInput(argv[xoptind]);
-       OpenOutput(argv[xoptind+1]);
-
-       TransformImage(cInpProf, cOutProf);
-
-
-       if (Verbose) { fprintf(stdout, "\n"); fflush(stdout); }
-
-       Done();
-
-       return 0;
-}
-
-
-
+//---------------------------------------------------------------------------------\r
+//\r
+//  Little Color Management System\r
+//  Copyright (c) 1998-2010 Marti Maria Saguer\r
+//\r
+// Permission is hereby granted, free of charge, to any person obtaining\r
+// a copy of this software and associated documentation files (the "Software"),\r
+// to deal in the Software without restriction, including without limitation\r
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+// and/or sell copies of the Software, and to permit persons to whom the Software\r
+// is furnished to do so, subject to the following conditions:\r
+//\r
+// The above copyright notice and this permission notice shall be included in\r
+// all copies or substantial portions of the Software.\r
+//\r
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\r
+// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+//\r
+\r
+// This program does apply profiles to (some) JPEG files\r
+\r
+\r
+#include "utils.h"\r
+\r
+#include "jpeglib.h"\r
+#include "iccjpeg.h"\r
+\r
+// Flags\r
+static cmsBool BlackPointCompensation = FALSE;\r
+static cmsBool IgnoreEmbedded         = FALSE;\r
+static cmsBool GamutCheck             = FALSE;\r
+static cmsBool lIsITUFax              = FALSE;\r
+static cmsBool lIsPhotoshopApp13      = FALSE;\r
+static cmsBool lIsEXIF;\r
+static cmsBool lIsDeviceLink          = FALSE;\r
+static cmsBool EmbedProfile           = FALSE;\r
+\r
+static const char* SaveEmbedded = NULL;\r
+\r
+static int Intent                  = INTENT_PERCEPTUAL;\r
+static int ProofingIntent          = INTENT_PERCEPTUAL;\r
+static int PrecalcMode             = 1;\r
+\r
+static int jpegQuality             = 75;\r
+\r
+static cmsFloat64Number ObserverAdaptationState = 0;\r
+\r
+\r
+static char *cInpProf  = NULL;\r
+static char *cOutProf  = NULL;\r
+static char *cProofing = NULL;\r
+\r
+static FILE * InFile;\r
+static FILE * OutFile;\r
+\r
+static struct jpeg_decompress_struct Decompressor;\r
+static struct jpeg_compress_struct   Compressor;\r
+\r
+\r
+static struct my_error_mgr {\r
+\r
+    struct  jpeg_error_mgr pub;   // "public" fields\r
+    void*   Cargo;                // "private" fields\r
+\r
+} ErrorHandler;\r
+\r
+\r
+cmsUInt16Number Alarm[4] = {128,128,128,0};\r
+\r
+// Out of mem\r
+static\r
+void OutOfMem(size_t size)\r
+{\r
+    FatalError("Out of memory on allocating %d bytes.", size);\r
+}\r
+\r
+\r
+static\r
+void my_error_exit (j_common_ptr cinfo)\r
+{\r
+  char buffer[JMSG_LENGTH_MAX];\r
+\r
+  (*cinfo->err->format_message) (cinfo, buffer);\r
+  FatalError(buffer);\r
+}\r
+\r
+/*\r
+Definition of the APPn Markers Defined for continuous-tone G3FAX\r
+\r
+The application code APP1 initiates identification of the image as\r
+a G3FAX application and defines the spatial resolution and subsampling.\r
+This marker directly follows the SOI marker. The data format will be as follows:\r
+\r
+X'FFE1' (APP1), length, FAX identifier, version, spatial resolution.\r
+\r
+The above terms are defined as follows:\r
+\r
+Length: (Two octets) Total APP1 field octet count including the octet count itself, but excluding the APP1\r
+marker.\r
+\r
+FAX identifier: (Six octets) X'47', X'33', X'46', X'41', X'58', X'00'. This X'00'-terminated string "G3FAX"\r
+uniquely identifies this APP1 marker.\r
+\r
+Version: (Two octets) X'07CA'. This string specifies the year of approval of the standard, for identification\r
+in the case of future revision (for example, 1994).\r
+\r
+Spatial Resolution: (Two octets) Lightness pixel density in pels/25.4 mm. The basic value is 200. Allowed values are\r
+100, 200, 300, 400, 600 and 1200 pels/25.4 mm, with square (or equivalent) pels.\r
+\r
+NOTE \96 The functional equivalence of inch-based and mm-based resolutions is maintained. For example, the 200 × 200\r
+*/\r
+\r
+static\r
+cmsBool IsITUFax(jpeg_saved_marker_ptr ptr)\r
+{\r
+       while (ptr)\r
+       {\r
+        if (ptr -> marker == (JPEG_APP0 + 1) && ptr -> data_length > 5) {\r
+\r
+                       const char* data = (const char*) ptr -> data;\r
+\r
+                       if (strcmp(data, "G3FAX") == 0) return TRUE;\r
+               }\r
+\r
+               ptr = ptr -> next;\r
+       }\r
+\r
+       return FALSE;\r
+}\r
+\r
+// Save a ITU T.42/Fax marker with defaults on boundaries. This is the only mode we support right now.\r
+static\r
+void SetITUFax(j_compress_ptr cinfo)\r
+{\r
+       unsigned char Marker[] = "G3FAX\x00\0x07\xCA\x00\xC8";\r
+\r
+       jpeg_write_marker(cinfo, (JPEG_APP0 + 1), Marker, 10);\r
+}\r
+\r
+\r
+// Build a profile for decoding ITU T.42/Fax JPEG streams.\r
+// The profile has an additional ability in the input direction of\r
+// gamut compress values between 85 < a < -85 and -75 < b < 125. This conforms\r
+// the default range for ITU/T.42 -- See RFC 2301, section 6.2.3 for details\r
+\r
+//  L*  =   [0, 100]\r
+//  a*  =   [\9685, 85]\r
+//  b*  =   [\9675, 125]\r
+\r
+\r
+// These functions does convert the encoding of ITUFAX to floating point\r
+// and vice-versa. No gamut mapping is performed yet.\r
+\r
+static\r
+void ITU2Lab(const cmsUInt16Number In[3], cmsCIELab* Lab)\r
+{\r
+       Lab -> L = (double) In[0] / 655.35;\r
+       Lab -> a = (double) 170.* (In[1] - 32768.) / 65535.;\r
+       Lab -> b = (double) 200.* (In[2] - 24576.) / 65535.;\r
+}\r
+\r
+static\r
+void Lab2ITU(const cmsCIELab* Lab, cmsUInt16Number Out[3])\r
+{\r
+       Out[0] = (cmsUInt16Number) floor((double) (Lab -> L / 100.)* 65535. );\r
+       Out[1] = (cmsUInt16Number) floor((double) (Lab -> a / 170.)* 65535. + 32768. );\r
+       Out[2] = (cmsUInt16Number) floor((double) (Lab -> b / 200.)* 65535. + 24576. );\r
+}\r
+\r
+// These are the samplers-- They are passed as callbacks to cmsStageSampleCLut16bit()\r
+// then, cmsSample3DGrid() will sweel whole Lab gamut calling these functions\r
+// once for each node. In[] will contain the Lab PCS value to convert to ITUFAX\r
+// on PCS2ITU, or the ITUFAX value to convert to Lab in ITU2PCS\r
+// You can change the number of sample points if desired, the algorithm will\r
+// remain same. 33 points gives good accurancy, but you can reduce to 22 or less\r
+// is space is critical\r
+\r
+#define GRID_POINTS 33\r
+\r
+static\r
+int PCS2ITU(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void*  Cargo)\r
+{\r
+       cmsCIELab Lab;\r
+\r
+       cmsLabEncoded2Float(&Lab, In);\r
+       cmsDesaturateLab(&Lab, 85, -85, 125, -75);    // This function does the necessary gamut remapping\r
+       Lab2ITU(&Lab, Out);\r
+       return TRUE;\r
+\r
+    UTILS_UNUSED_PARAMETER(Cargo);\r
+}\r
+\r
+\r
+static\r
+int ITU2PCS( register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void*  Cargo)\r
+{\r
+       cmsCIELab Lab;\r
+\r
+       ITU2Lab(In, &Lab);\r
+       cmsFloat2LabEncoded(Out, &Lab);\r
+       return TRUE;\r
+\r
+    UTILS_UNUSED_PARAMETER(Cargo);\r
+}\r
+\r
+// This function does create the virtual input profile, which decodes ITU to the profile connection space\r
+static\r
+cmsHPROFILE CreateITU2PCS_ICC(void)\r
+{\r
+       cmsHPROFILE hProfile;\r
+       cmsPipeline* AToB0;\r
+       cmsStage* ColorMap;\r
+\r
+       AToB0 = cmsPipelineAlloc(0, 3, 3);\r
+       if (AToB0 == NULL) return NULL;\r
+\r
+       ColorMap = cmsStageAllocCLut16bit(0, GRID_POINTS, 3, 3, NULL);\r
+       if (ColorMap == NULL) return NULL;\r
+\r
+    cmsPipelineInsertStage(AToB0, cmsAT_BEGIN, ColorMap);\r
+       cmsStageSampleCLut16bit(ColorMap, ITU2PCS, NULL, 0);\r
+\r
+       hProfile = cmsCreateProfilePlaceholder(0);\r
+       if (hProfile == NULL) {\r
+               cmsPipelineFree(AToB0);\r
+               return NULL;\r
+       }\r
+\r
+       cmsWriteTag(hProfile, cmsSigAToB0Tag, AToB0);\r
+       cmsSetColorSpace(hProfile, cmsSigLabData);\r
+       cmsSetPCS(hProfile, cmsSigLabData);\r
+       cmsSetDeviceClass(hProfile, cmsSigColorSpaceClass);\r
+       cmsPipelineFree(AToB0);\r
+\r
+       return hProfile;\r
+}\r
+\r
+\r
+// This function does create the virtual output profile, with the necessary gamut mapping\r
+static\r
+cmsHPROFILE CreatePCS2ITU_ICC(void)\r
+{\r
+    cmsHPROFILE hProfile;\r
+    cmsPipeline* BToA0;\r
+    cmsStage* ColorMap;\r
+\r
+    BToA0 = cmsPipelineAlloc(0, 3, 3);\r
+    if (BToA0 == NULL) return NULL;\r
+\r
+    ColorMap = cmsStageAllocCLut16bit(0, GRID_POINTS, 3, 3, NULL);\r
+    if (ColorMap == NULL) return NULL;\r
+\r
+    cmsPipelineInsertStage(BToA0, cmsAT_BEGIN, ColorMap);\r
+    cmsStageSampleCLut16bit(ColorMap, PCS2ITU, NULL, 0);\r
+\r
+    hProfile = cmsCreateProfilePlaceholder(0);\r
+    if (hProfile == NULL) {\r
+        cmsPipelineFree(BToA0);\r
+        return NULL;\r
+    }\r
+\r
+    cmsWriteTag(hProfile, cmsSigBToA0Tag, BToA0);\r
+    cmsSetColorSpace(hProfile, cmsSigLabData);\r
+    cmsSetPCS(hProfile, cmsSigLabData);\r
+    cmsSetDeviceClass(hProfile, cmsSigColorSpaceClass);\r
+\r
+    cmsPipelineFree(BToA0);\r
+\r
+    return hProfile;\r
+}\r
+\r
+\r
+\r
+#define PS_FIXED_TO_FLOAT(h, l) ((float) (h) + ((float) (l)/(1<<16)))\r
+\r
+static\r
+cmsBool ProcessPhotoshopAPP13(JOCTET FAR *data, int datalen)\r
+{\r
+    int i;\r
+\r
+    for (i = 14; i < datalen; )\r
+    {\r
+        long len;\r
+        unsigned int type;\r
+\r
+        if (!(GETJOCTET(data[i]  ) == 0x38 &&\r
+              GETJOCTET(data[i+1]) == 0x42 &&\r
+              GETJOCTET(data[i+2]) == 0x49 &&\r
+              GETJOCTET(data[i+3]) == 0x4D)) break; // Not recognized\r
+\r
+        i += 4; // identifying string\r
+\r
+        type = (unsigned int) (GETJOCTET(data[i]<<8) + GETJOCTET(data[i+1]));\r
+\r
+        i += 2; // resource type\r
+\r
+        i += GETJOCTET(data[i]) + ((GETJOCTET(data[i]) & 1) ? 1 : 2);   // resource name\r
+\r
+        len = ((((GETJOCTET(data[i]<<8) + GETJOCTET(data[i+1]))<<8) +\r
+                         GETJOCTET(data[i+2]))<<8) + GETJOCTET(data[i+3]);\r
+\r
+        i += 4; // Size\r
+\r
+        if (type == 0x03ED && len >= 16) {\r
+\r
+            Decompressor.X_density = (UINT16) PS_FIXED_TO_FLOAT(GETJOCTET(data[i]<<8) + GETJOCTET(data[i+1]),\r
+                                                 GETJOCTET(data[i+2]<<8) + GETJOCTET(data[i+3]));\r
+            Decompressor.Y_density = (UINT16) PS_FIXED_TO_FLOAT(GETJOCTET(data[i+8]<<8) + GETJOCTET(data[i+9]),\r
+                                                 GETJOCTET(data[i+10]<<8) + GETJOCTET(data[i+11]));\r
+\r
+            // Set the density unit to 1 since the\r
+            // Vertical and Horizontal resolutions\r
+            // are specified in Pixels per inch\r
+\r
+            Decompressor.density_unit = 0x01;\r
+            return TRUE;\r
+\r
+        }\r
+\r
+        i += len + ((len & 1) ? 1 : 0);   // Alignment\r
+    }\r
+    return FALSE;\r
+}\r
+\r
+\r
+static\r
+cmsBool HandlePhotoshopAPP13(jpeg_saved_marker_ptr ptr)\r
+{\r
+    while (ptr) {\r
+\r
+        if (ptr -> marker == (JPEG_APP0 + 13) && ptr -> data_length > 9)\r
+        {\r
+            JOCTET FAR* data = ptr -> data;\r
+\r
+            if(GETJOCTET(data[0]) == 0x50 &&\r
+               GETJOCTET(data[1]) == 0x68 &&\r
+               GETJOCTET(data[2]) == 0x6F &&\r
+               GETJOCTET(data[3]) == 0x74 &&\r
+               GETJOCTET(data[4]) == 0x6F &&\r
+               GETJOCTET(data[5]) == 0x73 &&\r
+               GETJOCTET(data[6]) == 0x68 &&\r
+               GETJOCTET(data[7]) == 0x6F &&\r
+               GETJOCTET(data[8]) == 0x70) {\r
+\r
+                ProcessPhotoshopAPP13(data, ptr -> data_length);\r
+                return TRUE;\r
+            }\r
+        }\r
+\r
+        ptr = ptr -> next;\r
+    }\r
+\r
+    return FALSE;\r
+}\r
+\r
+\r
+typedef unsigned short uint16_t;\r
+typedef unsigned char uint8_t;\r
+typedef unsigned int uint32_t;\r
+\r
+#define INTEL_BYTE_ORDER 0x4949\r
+#define XRESOLUTION 0x011a\r
+#define YRESOLUTION 0x011b\r
+#define RESOLUTION_UNIT 0x128\r
+\r
+// Read a 16-bit word\r
+static\r
+uint16_t read16(uint8_t* arr, int pos,  int swapBytes)\r
+{\r
+    uint8_t b1 = arr[pos];\r
+    uint8_t b2 = arr[pos+1];\r
+\r
+    return (swapBytes) ?  ((b2 << 8) | b1) : ((b1 << 8) | b2);\r
+}\r
+\r
+\r
+// Read a 32-bit word\r
+static\r
+uint32_t read32(uint8_t* arr, int pos,  int swapBytes)\r
+{\r
+\r
+    if(!swapBytes) {\r
+\r
+        return (arr[pos]   << 24) |\r
+               (arr[pos+1] << 16) |\r
+               (arr[pos+2] << 8) |\r
+                arr[pos+3];\r
+    }\r
+\r
+    return arr[pos] |\r
+           (arr[pos+1] << 8) |\r
+           (arr[pos+2] << 16) |\r
+           (arr[pos+3] << 24);\r
+}\r
+\r
+\r
+\r
+static\r
+int read_tag(uint8_t* arr, int pos,  int swapBytes, void* dest)\r
+{\r
+        // Format should be 5 over here (rational)\r
+    uint32_t format = read16(arr, pos + 2, swapBytes);\r
+    // Components should be 1\r
+    uint32_t components = read32(arr, pos + 4, swapBytes);\r
+    // Points to the value\r
+    uint32_t offset;\r
+\r
+    // sanity\r
+    if (components != 1) return 0;\r
+\r
+    if (format == 3)\r
+        offset = pos + 8;\r
+    else\r
+        offset =  read32(arr, pos + 8, swapBytes);\r
+\r
+    switch (format) {\r
+\r
+    case 5: // Rational\r
+          {\r
+          double num = read32(arr, offset, swapBytes);\r
+          double den = read32(arr, offset + 4, swapBytes);\r
+          *(double *) dest = num / den;\r
+          }\r
+          break;\r
+\r
+    case 3: // uint 16\r
+        *(int*) dest = read16(arr, offset, swapBytes);\r
+        break;\r
+\r
+    default:  return 0;\r
+    }\r
+\r
+    return 1;\r
+}\r
+\r
+\r
+\r
+// Handler for EXIF data\r
+static\r
+    cmsBool HandleEXIF(struct jpeg_decompress_struct* cinfo)\r
+{\r
+    jpeg_saved_marker_ptr ptr;\r
+    uint32_t ifd_ofs;\r
+    int pos = 0, swapBytes = 0;\r
+    uint32_t i, numEntries;\r
+    double XRes = -1, YRes = -1;\r
+    int Unit = 2; // Inches\r
+\r
+\r
+    for (ptr = cinfo ->marker_list; ptr; ptr = ptr ->next) {\r
+\r
+        if ((ptr ->marker == JPEG_APP0+1) && ptr ->data_length > 6) {\r
+            JOCTET FAR* data = ptr -> data;\r
+\r
+            if (memcmp(data, "Exif\0\0", 6) == 0) {\r
+\r
+                data += 6; // Skip EXIF marker\r
+\r
+                // 8 byte TIFF header\r
+                // first two determine byte order\r
+                pos = 0;\r
+                if (read16(data, pos, 0) == INTEL_BYTE_ORDER) {\r
+                    swapBytes = 1;\r
+                }\r
+\r
+                pos += 2;\r
+\r
+                // next two bytes are always 0x002A (TIFF version)\r
+                pos += 2;\r
+\r
+                // offset to Image File Directory (includes the previous 8 bytes)\r
+                ifd_ofs = read32(data, pos, swapBytes);\r
+\r
+                // Search the directory for resolution tags\r
+                numEntries = read16(data, ifd_ofs, swapBytes);\r
+\r
+                for (i=0; i < numEntries; i++) {\r
+\r
+                    uint32_t entryOffset = ifd_ofs + 2 + (12 * i);\r
+                    uint32_t tag = read16(data, entryOffset, swapBytes);\r
+\r
+                    switch (tag) {\r
+\r
+                    case RESOLUTION_UNIT:\r
+                        if (!read_tag(data, entryOffset, swapBytes, &Unit)) return FALSE;\r
+                        break;\r
+\r
+                    case XRESOLUTION:\r
+                        if (!read_tag(data, entryOffset, swapBytes, &XRes)) return FALSE;\r
+                        break;\r
+\r
+                    case YRESOLUTION:\r
+                        if (!read_tag(data, entryOffset, swapBytes, &YRes)) return FALSE;\r
+                        break;\r
+\r
+                    default:;\r
+                    }\r
+\r
+                }\r
+\r
+                // Proceed if all found\r
+\r
+                if (XRes != -1 && YRes != -1)\r
+                {\r
+\r
+                    // 1 = None\r
+                    // 2 = inches\r
+                    // 3 = cm\r
+\r
+                    switch (Unit) {\r
+\r
+                    case 2:\r
+\r
+                        cinfo ->X_density = (UINT16) floor(XRes + 0.5);\r
+                        cinfo ->Y_density = (UINT16) floor(YRes + 0.5);\r
+                        break;\r
+\r
+                    case 1:\r
+\r
+                        cinfo ->X_density = (UINT16) floor(XRes * 2.54 + 0.5);\r
+                        cinfo ->Y_density = (UINT16) floor(YRes * 2.54 + 0.5);\r
+                        break;\r
+\r
+                    default: return FALSE;\r
+                    }\r
+\r
+                    cinfo ->density_unit = 1;  /* 1 for dots/inch, or 2 for dots/cm.*/\r
+\r
+                }\r
+\r
+\r
+            }\r
+        }\r
+    }\r
+    return FALSE;\r
+}\r
+\r
+\r
+static\r
+cmsBool OpenInput(const char* FileName)\r
+{\r
+       int m;\r
+\r
+       lIsITUFax = FALSE;\r
+       InFile  = fopen(FileName, "rb");\r
+       if (InFile == NULL) {\r
+               FatalError("Cannot open '%s'", FileName);\r
+       }\r
+\r
+       // Now we can initialize the JPEG decompression object.\r
+       Decompressor.err                 = jpeg_std_error(&ErrorHandler.pub);\r
+       ErrorHandler.pub.error_exit      = my_error_exit;\r
+       ErrorHandler.pub.output_message  = my_error_exit;\r
+\r
+       jpeg_create_decompress(&Decompressor);\r
+       jpeg_stdio_src(&Decompressor, InFile);\r
+\r
+       for (m = 0; m < 16; m++)\r
+               jpeg_save_markers(&Decompressor, JPEG_APP0 + m, 0xFFFF);\r
+\r
+       // setup_read_icc_profile(&Decompressor);\r
+\r
+       fseek(InFile, 0, SEEK_SET);\r
+       jpeg_read_header(&Decompressor, TRUE);\r
+\r
+       return TRUE;\r
+}\r
+\r
+\r
+static\r
+cmsBool OpenOutput(const char* FileName)\r
+{\r
+\r
+       OutFile = fopen(FileName, "wb");\r
+       if (OutFile == NULL) {\r
+               FatalError("Cannot create '%s'", FileName);\r
+\r
+       }\r
+\r
+       Compressor.err                   = jpeg_std_error(&ErrorHandler.pub);\r
+       ErrorHandler.pub.error_exit      = my_error_exit;\r
+       ErrorHandler.pub.output_message  = my_error_exit;\r
+\r
+       Compressor.input_components = Compressor.num_components = 4;\r
+\r
+       jpeg_create_compress(&Compressor);\r
+       jpeg_stdio_dest(&Compressor, OutFile);\r
+       return TRUE;\r
+}\r
+\r
+static\r
+cmsBool Done(void)\r
+{\r
+       jpeg_destroy_decompress(&Decompressor);\r
+       jpeg_destroy_compress(&Compressor);\r
+       return fclose(InFile) + fclose(OutFile);\r
+\r
+}\r
+\r
+\r
+// Build up the pixeltype descriptor\r
+\r
+static\r
+cmsUInt32Number GetInputPixelType(void)\r
+{\r
+     int space, bps, extra, ColorChannels, Flavor;\r
+\r
+     lIsITUFax         = IsITUFax(Decompressor.marker_list);\r
+     lIsPhotoshopApp13 = HandlePhotoshopAPP13(Decompressor.marker_list);\r
+     lIsEXIF           = HandleEXIF(&Decompressor);\r
+\r
+     ColorChannels = Decompressor.num_components;\r
+     extra  = 0;            // Alpha = None\r
+     bps    = 1;            // 8 bits\r
+     Flavor = 0;            // Vanilla\r
+\r
+     if (lIsITUFax) {\r
+\r
+        space = PT_Lab;\r
+        Decompressor.out_color_space = JCS_YCbCr;  // Fake to don't touch\r
+     }\r
+     else\r
+     switch (Decompressor.jpeg_color_space) {\r
+\r
+     case JCS_GRAYSCALE:        // monochrome\r
+              space = PT_GRAY;\r
+              Decompressor.out_color_space = JCS_GRAYSCALE;\r
+              break;\r
+\r
+     case JCS_RGB:             // red/green/blue\r
+              space = PT_RGB;\r
+              Decompressor.out_color_space = JCS_RGB;\r
+              break;\r
+\r
+     case JCS_YCbCr:               // Y/Cb/Cr (also known as YUV)\r
+              space = PT_RGB;      // Let IJG code to do the conversion\r
+              Decompressor.out_color_space = JCS_RGB;\r
+              break;\r
+\r
+     case JCS_CMYK:            // C/M/Y/K\r
+              space = PT_CMYK;\r
+              Decompressor.out_color_space = JCS_CMYK;\r
+              if (Decompressor.saw_Adobe_marker)            // Adobe keeps CMYK inverted, so change flavor\r
+                                Flavor = 1;                 // from vanilla to chocolate\r
+              break;\r
+\r
+     case JCS_YCCK:            // Y/Cb/Cr/K\r
+              space = PT_CMYK;\r
+              Decompressor.out_color_space = JCS_CMYK;\r
+              if (Decompressor.saw_Adobe_marker)            // ditto\r
+                                Flavor = 1;\r
+              break;\r
+\r
+     default:\r
+              FatalError("Unsupported color space (0x%x)", Decompressor.jpeg_color_space);\r
+              return 0;\r
+     }\r
+\r
+     return (EXTRA_SH(extra)|CHANNELS_SH(ColorChannels)|BYTES_SH(bps)|COLORSPACE_SH(space)|FLAVOR_SH(Flavor));\r
+}\r
+\r
+\r
+// Rearrange pixel type to build output descriptor\r
+static\r
+cmsUInt32Number ComputeOutputFormatDescriptor(cmsUInt32Number dwInput, int OutColorSpace)\r
+{\r
+       int IsPlanar  = T_PLANAR(dwInput);\r
+       int Channels  = 0;\r
+       int Flavor    = 0;\r
+\r
+       switch (OutColorSpace) {\r
+\r
+   case PT_GRAY:\r
+          Channels = 1;\r
+          break;\r
+   case PT_RGB:\r
+   case PT_CMY:\r
+   case PT_Lab:\r
+   case PT_YUV:\r
+   case PT_YCbCr:\r
+          Channels = 3;\r
+          break;\r
+\r
+   case PT_CMYK:\r
+          if (Compressor.write_Adobe_marker)   // Adobe keeps CMYK inverted, so change flavor to chocolate\r
+                  Flavor = 1;\r
+          Channels = 4;\r
+          break;\r
+   default:\r
+          FatalError("Unsupported output color space");\r
+       }\r
+\r
+       return (COLORSPACE_SH(OutColorSpace)|PLANAR_SH(IsPlanar)|CHANNELS_SH(Channels)|BYTES_SH(1)|FLAVOR_SH(Flavor));\r
+}\r
+\r
+\r
+// Equivalence between ICC color spaces and lcms color spaces\r
+static\r
+int GetProfileColorSpace(cmsHPROFILE hProfile)\r
+{\r
+    cmsColorSpaceSignature ProfileSpace = cmsGetColorSpace(hProfile);\r
+\r
+       return _cmsLCMScolorSpace(ProfileSpace);\r
+}\r
+\r
+static\r
+int GetDevicelinkColorSpace(cmsHPROFILE hProfile)\r
+{\r
+    cmsColorSpaceSignature ProfileSpace = cmsGetPCS(hProfile);\r
+\r
+       return _cmsLCMScolorSpace(ProfileSpace);\r
+}\r
+\r
+\r
+// From TRANSUPP\r
+\r
+static\r
+void jcopy_markers_execute(j_decompress_ptr srcinfo, j_compress_ptr dstinfo)\r
+{\r
+  jpeg_saved_marker_ptr marker;\r
+\r
+  /* In the current implementation, we don't actually need to examine the\r
+   * option flag here; we just copy everything that got saved.\r
+   * But to avoid confusion, we do not output JFIF and Adobe APP14 markers\r
+   * if the encoder library already wrote one.\r
+   */\r
+  for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) {\r
+\r
+    if (dstinfo->write_JFIF_header &&\r
+        marker->marker == JPEG_APP0 &&\r
+        marker->data_length >= 5 &&\r
+        GETJOCTET(marker->data[0]) == 0x4A &&\r
+        GETJOCTET(marker->data[1]) == 0x46 &&\r
+        GETJOCTET(marker->data[2]) == 0x49 &&\r
+        GETJOCTET(marker->data[3]) == 0x46 &&\r
+        GETJOCTET(marker->data[4]) == 0)\r
+                          continue;         /* reject duplicate JFIF */\r
+\r
+    if (dstinfo->write_Adobe_marker &&\r
+        marker->marker == JPEG_APP0+14 &&\r
+        marker->data_length >= 5 &&\r
+        GETJOCTET(marker->data[0]) == 0x41 &&\r
+        GETJOCTET(marker->data[1]) == 0x64 &&\r
+        GETJOCTET(marker->data[2]) == 0x6F &&\r
+        GETJOCTET(marker->data[3]) == 0x62 &&\r
+        GETJOCTET(marker->data[4]) == 0x65)\r
+                         continue;         /* reject duplicate Adobe */\r
+\r
+     jpeg_write_marker(dstinfo, marker->marker,\r
+                       marker->data, marker->data_length);\r
+  }\r
+}\r
+\r
+static\r
+void WriteOutputFields(int OutputColorSpace)\r
+{\r
+    J_COLOR_SPACE in_space, jpeg_space;\r
+    int components;\r
+\r
+    switch (OutputColorSpace) {\r
+\r
+    case PT_GRAY: in_space = jpeg_space = JCS_GRAYSCALE;\r
+                  components = 1;\r
+                  break;\r
+\r
+    case PT_RGB:  in_space = JCS_RGB;\r
+                  jpeg_space = JCS_YCbCr;\r
+                  components = 3;\r
+                  break;       // red/green/blue\r
+\r
+    case PT_YCbCr: in_space = jpeg_space = JCS_YCbCr;\r
+                   components = 3;\r
+                   break;               // Y/Cb/Cr (also known as YUV)\r
+\r
+    case PT_CMYK: in_space = JCS_CMYK;\r
+                  jpeg_space = JCS_YCCK;\r
+                  components = 4;\r
+                  break;      // C/M/Y/components\r
+\r
+    case PT_Lab:  in_space = jpeg_space = JCS_YCbCr;\r
+                  components = 3;\r
+                  break;                // Fake to don't touch\r
+    default:\r
+                 FatalError("Unsupported output color space");\r
+                 return;\r
+    }\r
+\r
+\r
+    if (jpegQuality >= 100) {\r
+\r
+     // avoid destructive conversion when asking for lossless compression\r
+        jpeg_space = in_space;\r
+    }\r
+\r
+    Compressor.in_color_space =  in_space;\r
+    Compressor.jpeg_color_space = jpeg_space;\r
+    Compressor.input_components = Compressor.num_components = components;\r
+    jpeg_set_defaults(&Compressor);\r
+    jpeg_set_colorspace(&Compressor, jpeg_space);\r
+\r
+\r
+    // Make sure to pass resolution through\r
+    if (OutputColorSpace == PT_CMYK)\r
+        Compressor.write_JFIF_header = 1;\r
+\r
+    // Avoid subsampling on high quality factor\r
+    jpeg_set_quality(&Compressor, jpegQuality, 1);\r
+    if (jpegQuality >= 70) {\r
+\r
+      int i;\r
+      for(i=0; i < Compressor.num_components; i++) {\r
+\r
+               Compressor.comp_info[i].h_samp_factor = 1;\r
+            Compressor.comp_info[i].v_samp_factor = 1;\r
+      }\r
+\r
+    }\r
+\r
+}\r
+\r
+\r
+static\r
+void DoEmbedProfile(const char* ProfileFile)\r
+{\r
+    FILE* f;\r
+    size_t size, EmbedLen;\r
+    cmsUInt8Number* EmbedBuffer;\r
+\r
+        f = fopen(ProfileFile, "rb");\r
+        if (f == NULL) return;\r
+\r
+        size = cmsfilelength(f);\r
+        EmbedBuffer = (cmsUInt8Number*) malloc(size + 1);\r
+        EmbedLen = fread(EmbedBuffer, 1, size, f);\r
+        fclose(f);\r
+        EmbedBuffer[EmbedLen] = 0;\r
+\r
+        write_icc_profile (&Compressor, EmbedBuffer, EmbedLen);\r
+        free(EmbedBuffer);\r
+}\r
+\r
+\r
+\r
+static\r
+int DoTransform(cmsHTRANSFORM hXForm, int OutputColorSpace)\r
+{\r
+    JSAMPROW ScanLineIn;\r
+    JSAMPROW ScanLineOut;\r
+\r
+\r
+       //Preserve resolution values from the original\r
+       // (Thanks to Robert Bergs for finding out this bug)\r
+       Compressor.density_unit = Decompressor.density_unit;\r
+       Compressor.X_density    = Decompressor.X_density;\r
+       Compressor.Y_density    = Decompressor.Y_density;\r
+\r
+      //  Compressor.write_JFIF_header = 1;\r
+\r
+       jpeg_start_decompress(&Decompressor);\r
+       jpeg_start_compress(&Compressor, TRUE);\r
+\r
+        if (OutputColorSpace == PT_Lab)\r
+            SetITUFax(&Compressor);\r
+\r
+       // Embed the profile if needed\r
+       if (EmbedProfile && cOutProf)\r
+           DoEmbedProfile(cOutProf);\r
+\r
+       ScanLineIn  = (JSAMPROW) malloc(Decompressor.output_width * Decompressor.num_components);\r
+       ScanLineOut = (JSAMPROW) malloc(Compressor.image_width * Compressor.num_components);\r
+\r
+       while (Decompressor.output_scanline <\r
+                            Decompressor.output_height) {\r
+\r
+       jpeg_read_scanlines(&Decompressor, &ScanLineIn, 1);\r
+\r
+       cmsDoTransform(hXForm, ScanLineIn, ScanLineOut, Decompressor.output_width);\r
+\r
+       jpeg_write_scanlines(&Compressor, &ScanLineOut, 1);\r
+       }\r
+\r
+       free(ScanLineIn);\r
+       free(ScanLineOut);\r
+\r
+       jpeg_finish_decompress(&Decompressor);\r
+       jpeg_finish_compress(&Compressor);\r
+\r
+       return TRUE;\r
+}\r
+\r
+\r
+\r
+// Transform one image\r
+\r
+static\r
+int TransformImage(char *cDefInpProf, char *cOutProf)\r
+{\r
+       cmsHPROFILE hIn, hOut, hProof;\r
+       cmsHTRANSFORM xform;\r
+       cmsUInt32Number wInput, wOutput;\r
+       int OutputColorSpace;\r
+       cmsUInt32Number dwFlags = 0;\r
+       cmsUInt32Number EmbedLen;\r
+       cmsUInt8Number* EmbedBuffer;\r
+\r
+\r
+       cmsSetAdaptationState(ObserverAdaptationState);\r
+\r
+       if (BlackPointCompensation) {\r
+\r
+            dwFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;\r
+       }\r
+\r
+\r
+       switch (PrecalcMode) {\r
+\r
+       case 0: dwFlags |= cmsFLAGS_NOOPTIMIZE; break;\r
+       case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break;\r
+       case 3: dwFlags |= cmsFLAGS_LOWRESPRECALC; break;\r
+       default:;\r
+       }\r
+\r
+\r
+       if (GamutCheck) {\r
+            dwFlags |= cmsFLAGS_GAMUTCHECK;\r
+            cmsSetAlarmCodes(Alarm);\r
+       }\r
+\r
+       // Take input color space\r
+       wInput = GetInputPixelType();\r
+\r
+        if (lIsDeviceLink) {\r
+\r
+            hIn = cmsOpenProfileFromFile(cDefInpProf, "r");\r
+            hOut = NULL;\r
+            hProof = NULL;\r
+       }\r
+        else {\r
+\r
+        if (!IgnoreEmbedded && read_icc_profile(&Decompressor, &EmbedBuffer, &EmbedLen))\r
+        {\r
+              hIn = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen);\r
+\r
+               if (Verbose) {\r
+\r
+                  fprintf(stdout, " (Embedded profile found)\n");\r
+                                 PrintProfileInformation(hIn);\r
+                  fflush(stdout);\r
+              }\r
+\r
+               if (hIn != NULL && SaveEmbedded != NULL)\r
+                          SaveMemoryBlock(EmbedBuffer, EmbedLen, SaveEmbedded);\r
+\r
+              free(EmbedBuffer);\r
+        }\r
+        else\r
+        {\r
+            // Default for ITU/Fax\r
+            if (cDefInpProf == NULL && T_COLORSPACE(wInput) == PT_Lab)\r
+                cDefInpProf = "*Lab";\r
+\r
+            if (cDefInpProf != NULL && cmsstrcasecmp(cDefInpProf, "*lab") == 0)\r
+                hIn = CreateITU2PCS_ICC();\r
+            else\r
+                hIn = OpenStockProfile(0, cDefInpProf);\r
+       }\r
+\r
+        if (cOutProf != NULL && cmsstrcasecmp(cOutProf, "*lab") == 0)\r
+            hOut = CreatePCS2ITU_ICC();\r
+        else\r
+        hOut = OpenStockProfile(0, cOutProf);\r
+\r
+       hProof = NULL;\r
+       if (cProofing != NULL) {\r
+\r
+           hProof = OpenStockProfile(0, cProofing);\r
+           if (hProof == NULL) {\r
+            FatalError("Proofing profile couldn't be read.");\r
+           }\r
+           dwFlags |= cmsFLAGS_SOFTPROOFING;\r
+          }\r
+       }\r
+\r
+        if (!hIn)\r
+            FatalError("Input profile couldn't be read.");\r
+        if (!hOut)\r
+            FatalError("Output profile couldn't be read.");\r
+\r
+       // Assure both, input profile and input JPEG are on same colorspace\r
+       if (cmsGetColorSpace(hIn) != _cmsICCcolorSpace(T_COLORSPACE(wInput)))\r
+              FatalError("Input profile is not operating in proper color space");\r
+\r
+\r
+       // Output colorspace is given by output profile\r
+\r
+        if (lIsDeviceLink) {\r
+            OutputColorSpace = GetDevicelinkColorSpace(hIn);\r
+        }\r
+        else {\r
+            OutputColorSpace = GetProfileColorSpace(hOut);\r
+        }\r
+\r
+       jpeg_copy_critical_parameters(&Decompressor, &Compressor);\r
+\r
+       WriteOutputFields(OutputColorSpace);\r
+\r
+       wOutput      = ComputeOutputFormatDescriptor(wInput, OutputColorSpace);\r
+\r
+\r
+       xform = cmsCreateProofingTransform(hIn, wInput,\r
+                                          hOut, wOutput,\r
+                                          hProof, Intent,\r
+                                          ProofingIntent, dwFlags);\r
+          if (xform == NULL)\r
+                 FatalError("Cannot transform by using the profiles");\r
+\r
+       DoTransform(xform, OutputColorSpace);\r
+\r
+\r
+       jcopy_markers_execute(&Decompressor, &Compressor);\r
+\r
+       cmsDeleteTransform(xform);\r
+       cmsCloseProfile(hIn);\r
+       cmsCloseProfile(hOut);\r
+       if (hProof) cmsCloseProfile(hProof);\r
+\r
+       return 1;\r
+}\r
+\r
+\r
+// Simply print help\r
+\r
+static\r
+void Help(int level)\r
+{\r
+     fprintf(stderr, "little cms ICC profile applier for JPEG - v3.2 [LittleCMS %2.2f]\n\n", LCMS_VERSION / 1000.0);\r
+\r
+     switch(level) {\r
+\r
+     default:\r
+     case 0:\r
+\r
+     fprintf(stderr, "usage: jpgicc [flags] input.jpg output.jpg\n");\r
+\r
+     fprintf(stderr, "\nflags:\n\n");\r
+     fprintf(stderr, "%cv - Verbose\n", SW);\r
+     fprintf(stderr, "%ci<profile> - Input profile (defaults to sRGB)\n", SW);\r
+     fprintf(stderr, "%co<profile> - Output profile (defaults to sRGB)\n", SW);\r
+\r
+        PrintRenderingIntents();\r
+\r
+\r
+     fprintf(stderr, "%cb - Black point compensation\n", SW);\r
+     fprintf(stderr, "%cd<0..1> - Observer adaptation state (abs.col. only)\n", SW);\r
+     fprintf(stderr, "%cn - Ignore embedded profile\n", SW);\r
+     fprintf(stderr, "%ce - Embed destination profile\n", SW);\r
+     fprintf(stderr, "%cs<new profile> - Save embedded profile as <new profile>\n", SW);\r
+\r
+     fprintf(stderr, "\n");\r
+\r
+     fprintf(stderr, "%cc<0,1,2,3> - Precalculates transform (0=Off, 1=Normal, 2=Hi-res, 3=LoRes) [defaults to 1]\n", SW);\r
+     fprintf(stderr, "\n");\r
+\r
+     fprintf(stderr, "%cp<profile> - Soft proof profile\n", SW);\r
+     fprintf(stderr, "%cm<0,1,2,3> - SoftProof intent\n", SW);\r
+     fprintf(stderr, "%cg - Marks out-of-gamut colors on softproof\n", SW);\r
+     fprintf(stderr, "%c!<r>,<g>,<b> - Out-of-gamut marker channel values\n", SW);\r
+\r
+     fprintf(stderr, "\n");\r
+     fprintf(stderr, "%cq<0..100> - Output JPEG quality\n", SW);\r
+\r
+     fprintf(stderr, "\n");\r
+     fprintf(stderr, "%ch<0,1,2,3> - More help\n", SW);\r
+     break;\r
+\r
+     case 1:\r
+\r
+     fprintf(stderr, "Examples:\n\n"\r
+                     "To color correct from scanner to sRGB:\n"\r
+                     "\tjpgicc %ciscanner.icm in.jpg out.jpg\n"\r
+                     "To convert from monitor1 to monitor2:\n"\r
+                     "\tjpgicc %cimon1.icm %comon2.icm in.jpg out.jpg\n"\r
+                     "To make a CMYK separation:\n"\r
+                     "\tjpgicc %coprinter.icm inrgb.jpg outcmyk.jpg\n"\r
+                     "To recover sRGB from a CMYK separation:\n"\r
+                     "\tjpgicc %ciprinter.icm incmyk.jpg outrgb.jpg\n"\r
+                     "To convert from CIELab ITU/Fax JPEG to sRGB\n"\r
+                     "\tjpgicc in.jpg out.jpg\n\n",\r
+                     SW, SW, SW, SW, SW, SW);\r
+     break;\r
+\r
+     case 2:\r
+                PrintBuiltins();\r
+                break;\r
+\r
+     case 3:\r
+\r
+     fprintf(stderr, "This program is intended to be a demo of the little cms\n"\r
+                     "engine. Both lcms and this program are freeware. You can\n"\r
+                     "obtain both in source code at http://www.littlecms.com\n"\r
+                     "For suggestions, comments, bug reports etc. send mail to\n"\r
+                     "marti@littlecms.com\n\n");\r
+     break;\r
+     }\r
+\r
+     exit(0);\r
+}\r
+\r
+\r
+// The toggles stuff\r
+\r
+static\r
+void HandleSwitches(int argc, char *argv[])\r
+{\r
+    int s;\r
+\r
+    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
+\r
+        switch (s)\r
+        {\r
+\r
+        case 'b':\r
+        case 'B':\r
+            BlackPointCompensation = TRUE;\r
+            break;\r
+\r
+        case 'd':\r
+        case 'D': ObserverAdaptationState = atof(xoptarg);\r
+            if (ObserverAdaptationState < 0 ||\r
+                ObserverAdaptationState > 1.0)\r
+                FatalError("Adaptation state should be 0..1");\r
+            break;\r
+\r
+        case 'v':\r
+        case 'V':\r
+            Verbose = TRUE;\r
+            break;\r
+\r
+        case 'i':\r
+        case 'I':\r
+            if (lIsDeviceLink)\r
+                FatalError("Device-link already specified");\r
+\r
+            cInpProf = xoptarg;\r
+            break;\r
+\r
+        case 'o':\r
+        case 'O':\r
+            if (lIsDeviceLink)\r
+                FatalError("Device-link already specified");\r
+\r
+            cOutProf = xoptarg;\r
+            break;\r
+\r
+        case 'l':\r
+        case 'L':\r
+                       if (cInpProf != NULL || cOutProf != NULL)\r
+                               FatalError("input/output profiles already specified");\r
+\r
+            cInpProf = xoptarg;\r
+            lIsDeviceLink = TRUE;\r
+            break;\r
+\r
+        case 'p':\r
+        case 'P':\r
+            cProofing = xoptarg;\r
+            break;\r
+\r
+        case 't':\r
+        case 'T':\r
+            Intent = atoi(xoptarg);\r
+            break;\r
+\r
+        case 'N':\r
+        case 'n':\r
+            IgnoreEmbedded = TRUE;\r
+            break;\r
+\r
+        case 'e':\r
+        case 'E':\r
+            EmbedProfile = TRUE;\r
+            break;\r
+\r
+\r
+        case 'g':\r
+        case 'G':\r
+            GamutCheck = TRUE;\r
+            break;\r
+\r
+        case 'c':\r
+        case 'C':\r
+            PrecalcMode = atoi(xoptarg);\r
+            if (PrecalcMode < 0 || PrecalcMode > 2)\r
+                FatalError("Unknown precalc mode '%d'", PrecalcMode);\r
+            break;\r
+\r
+        case 'H':\r
+        case 'h':  {\r
+\r
+            int a =  atoi(xoptarg);\r
+            Help(a);\r
+                   }\r
+            break;\r
+\r
+        case 'q':\r
+        case 'Q':\r
+            jpegQuality = atoi(xoptarg);\r
+            if (jpegQuality > 100) jpegQuality = 100;\r
+            if (jpegQuality < 0)   jpegQuality = 0;\r
+            break;\r
+\r
+        case 'm':\r
+        case 'M':\r
+            ProofingIntent = atoi(xoptarg);\r
+            break;\r
+\r
+        case 's':\r
+        case 'S': SaveEmbedded = xoptarg;\r
+            break;\r
+\r
+        case '!':\r
+            if (sscanf(xoptarg, "%hu,%hu,%hu", &Alarm[0], &Alarm[1], &Alarm[2]) == 3) {\r
+                int i;\r
+                for (i=0; i < 3; i++) {\r
+                    Alarm[i] = (Alarm[i] << 8) | Alarm[i];\r
+                }\r
+            }\r
+            break;\r
+\r
+        default:\r
+\r
+            FatalError("Unknown option - run without args to see valid ones");\r
+        }\r
+\r
+    }\r
+}\r
+\r
+\r
+int main(int argc, char* argv[])\r
+{\r
+       InitUtils("jpgicc");\r
+\r
+       HandleSwitches(argc, argv);\r
+\r
+       if ((argc - xoptind) != 2) {\r
+               Help(0);\r
+       }\r
+\r
+       OpenInput(argv[xoptind]);\r
+       OpenOutput(argv[xoptind+1]);\r
+\r
+       TransformImage(cInpProf, cOutProf);\r
+\r
+\r
+       if (Verbose) { fprintf(stdout, "\n"); fflush(stdout); }\r
+\r
+       Done();\r
+\r
+       return 0;\r
+}\r
+\r
+\r
+\r