//
//  Little cms
//  Copyright (C) 1998-2001 Marti Maria
//
// THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
//
// IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
// INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
// OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
// LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
// OF THIS SOFTWARE.
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// Version 1.07

// This program does apply profiles to (some) TIFF files


#include "lcms.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#include "tiffio.h"


// xgetopt() interface -----------------------------------------------------

extern int   xoptind;
extern char *xoptarg;
extern int   xopterr;
extern char   SW;
int    cdecl xgetopt(int argc, char *argv[], char *optionS);

// ------------------------------------------------------------------------

// Flags

static BOOL Verbose                = FALSE;
static BOOL BlackWhiteCompensation = FALSE;
static BOOL IgnoreEmbedded         = FALSE;
static BOOL Width16                = FALSE;
static BOOL GamutCheck             = FALSE;
static int Intent                  = INTENT_PERCEPTUAL;
static int PrecalcMode             = 1;
static char *cInpProf  = NULL;
static char *cOutProf  = NULL;
static char *cProofing = NULL;


// Console error & warning


static
void ConsoleWarningHandler(const char* module, const char* fmt, va_list ap)
{
        char e[512] = { '\0' };
        if (module != NULL)
              strcat(strcpy(e, module), ": ");

        vsprintf(e+strlen(e), fmt, ap);
        strcat(e, ".");
        if (Verbose) {

              fprintf(stderr, "\nWarning ");
              fprintf(stderr, " %s\n", e);
              }
}

static
void ConsoleErrorHandler(const char* module, const char* fmt, va_list ap)
{
       char e[512] = { '\0' };

       if (module != NULL)
              strcat(strcpy(e, module), ": ");

       vsprintf(e+strlen(e), fmt, ap);
       strcat(e, ".");
       fprintf(stderr, "\nError ");
       fprintf(stderr, " %s\n", e);
}



// Force an error and exit w/ return code 1

static
void FatalError(const char *frm, ...)
{
       va_list args;

       va_start(args, frm);
       ConsoleErrorHandler("TIFFICC", frm, args);
       va_end(args);
       exit(1);
}


// Out of mem

static
void OutOfMem(size_t size)
{
    FatalError("Out of memory on allocating %d bytes.", size);
}


// Build up the pixeltype descriptor

static
DWORD GetInputPixelType(TIFF *Bank)
{
     uint16 Photometric, bps, spp, extra, PlanarConfig, *info;
     uint16 Compression, reverse = 0;
     int ColorChannels, IsPlanar = 0, pt = 0;

     TIFFGetField(Bank,           TIFFTAG_PHOTOMETRIC,   &Photometric);
     TIFFGetFieldDefaulted(Bank, TIFFTAG_BITSPERSAMPLE, &bps);

     if (bps == 1)
       FatalError("Sorry, bilevel TIFFs has nothig to do with ICC profiles");

     if (bps != 8 && bps != 16)
              FatalError("Sorry, 8 or 16 bits per sample only");

     TIFFGetFieldDefaulted(Bank, TIFFTAG_SAMPLESPERPIXEL, &spp);
     TIFFGetFieldDefaulted(Bank, TIFFTAG_PLANARCONFIG, &PlanarConfig);

     switch (PlanarConfig)
     {
     case PLANARCONFIG_CONTIG: IsPlanar = 0; break;
     case PLANARCONFIG_SEPARATE: IsPlanar = 1; break;
     default:

       FatalError("Unsupported planar configuration (=%d) ", (int) PlanarConfig);
     }

     // If Samples per pixel == 1, PlanarConfiguration is irrelevant and need
     // not to be included.

     if (spp == 1) IsPlanar = 0;


     // Any alpha?

     TIFFGetFieldDefaulted(Bank, TIFFTAG_EXTRASAMPLES, &extra, &info);
     ColorChannels = spp - extra;

     switch (Photometric) {

     case PHOTOMETRIC_MINISWHITE:
                                   
            reverse = 1;

     case PHOTOMETRIC_MINISBLACK:
                                   
            pt = PT_GRAY;                                
            break;

     case PHOTOMETRIC_RGB:
                                   
            pt = PT_RGB;
            break;


     case PHOTOMETRIC_PALETTE:
                                             
            FatalError("Sorry, palette images not supported (at least on this version)"); 

     case PHOTOMETRIC_SEPARATED:
           if (ColorChannels == 4)
                  pt = PT_CMYK;
           else
           if (ColorChannels == 3)
                  pt = PT_CMY;
           else
                  FatalError("Sorry, HiFi separations can't be restored");
           break;

     case PHOTOMETRIC_YCBCR:
           TIFFGetField(Bank, TIFFTAG_COMPRESSION, &Compression);
           {
                  uint16 subx, suby;

                  pt = PT_YCbCr;
                  TIFFGetFieldDefaulted(Bank, TIFFTAG_YCBCRSUBSAMPLING, &subx, &suby);
                  if (subx != 1 || suby != 1)
                         FatalError("Sorry, subsampled images not supported");

           }
           break;

     case PHOTOMETRIC_CIELAB:
           pt = PT_Lab;
           break;

     case PHOTOMETRIC_LOGL:     /* CIE Log2(L) */
           FatalError("Hummm... I have been unable of find any of  these, please contact me at marti@littlecms.com, thanx.");

     case PHOTOMETRIC_LOGLUV:      /* CIE Log2(L) (u',v') */

           TIFFSetField(Bank, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_16BIT);
           pt = PT_YUV;             // *ICCSpace = icSigLuvData;
           bps = 16;             // 16 bits forced by LibTiff
           break;

     default:
           FatalError("Unsupported TIFF color space (Photometric %d)", Photometric);
     }

     // Convert bits per sample to bytes per sample

     bps >>= 3; /* /= 8; */

     return (COLORSPACE_SH(pt)|PLANAR_SH(IsPlanar)|EXTRA_SH(extra)|CHANNELS_SH(ColorChannels)|BYTES_SH(bps)|FLAVOR_SH(reverse));
}



// Rearrange pixel type to build output descriptor

static
DWORD ComputeOutputFormatDescriptor(DWORD dwInput, int OutColorSpace, int bps)
{
   int IsPlanar  = T_PLANAR(dwInput);
   int Channels = 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:
           Channels = 4;
               break;

   default:
               FatalError("Unsupported output color space");
   }

    return (COLORSPACE_SH(OutColorSpace)|PLANAR_SH(IsPlanar)|CHANNELS_SH(Channels)|BYTES_SH(bps));
}


// Equivalence between ICC color spaces and lcms color spaces

static
int GetProfileColorSpace(cmsHPROFILE hProfile)
{
    icColorSpaceSignature ProfileSpace = cmsGetColorSpace(hProfile);

       switch (ProfileSpace) {

       case icSigGrayData: return  PT_GRAY;
       case icSigRgbData:  return  PT_RGB;
       case icSigCmyData:  return  PT_CMY;
       case icSigCmykData: return  PT_CMYK;
       case icSigYCbCrData:return  PT_YCbCr;
       case icSigLuvData:  return  PT_YUV;
       case icSigXYZData:  return  PT_XYZ;
       case icSigLabData:  return  PT_Lab;
       case icSigLuvKData: return  PT_YUVK;
       case icSigHsvData:  return  PT_HSV;
       case icSigHlsData:  return  PT_HLS;
       case icSigYxyData:  return  PT_Yxy;

       default:  return icMaxEnumData;
       }
}


static
icColorSpaceSignature GetICCcolorSpace(int OurNotation)
{
       switch (OurNotation) {

       case 1:
       case PT_GRAY: return  icSigGrayData;

       case 2:
       case PT_RGB:  return  icSigRgbData;

       case PT_CMY:  return  icSigCmyData;
       case PT_CMYK: return  icSigCmykData;
       case PT_YCbCr:return  icSigYCbCrData;
       case PT_YUV:  return  icSigLuvData;
       case PT_XYZ:  return  icSigXYZData;
       case PT_Lab:  return  icSigLabData;
       case PT_YUVK: return  icSigLuvKData;
       case PT_HSV:  return  icSigHsvData;
       case PT_HLS:  return  icSigHlsData;
       case PT_Yxy:  return  icSigYxyData;

       default:  return icMaxEnumData;
       }
}



// Tile based transforms

static
int TileBasedXform(cmsHTRANSFORM hXForm, TIFF* in, TIFF* out)
{
    tsize_t BufSizeIn  = TIFFTileSize(in);
    tsize_t BufSizeOut = TIFFTileSize(out);
    unsigned char *BufferIn, *BufferOut;
    ttile_t i, TileCount = TIFFNumberOfTiles(in);
    uint16 tw, tl;
    int PixelCount;


    TIFFGetFieldDefaulted(in, TIFFTAG_TILEWIDTH,  &tw);
    TIFFGetFieldDefaulted(in, TIFFTAG_TILELENGTH, &tl);

     PixelCount = (int) tw * tl;

     BufferIn = (unsigned char *) _TIFFmalloc(BufSizeIn);
    if (!BufferIn) OutOfMem(BufSizeIn);

    BufferOut = (unsigned char *) _TIFFmalloc(BufSizeOut);
    if (!BufferOut) OutOfMem(BufSizeOut);


    for (i = 0; i < TileCount; i++) {

        if (TIFFReadEncodedTile(in, i, BufferIn, BufSizeIn) < 0)   goto cleanup;

      cmsDoTransform(hXForm, BufferIn, BufferOut, PixelCount);

        if (TIFFWriteEncodedTile(out, i, BufferOut, BufSizeOut) < 0) goto cleanup;

    }

    _TIFFfree(BufferIn);
    _TIFFfree(BufferOut);
    return 1;


cleanup:

    _TIFFfree(BufferIn);
   _TIFFfree(BufferOut);
   return 0;
}


// Strip based transforms

static
int StripBasedXform(cmsHTRANSFORM hXForm, TIFF* in, TIFF* out)
{
    tsize_t BufSizeIn  = TIFFStripSize(in);
    tsize_t BufSizeOut = TIFFStripSize(out);
    unsigned char *BufferIn, *BufferOut;
    ttile_t i, StripCount = TIFFNumberOfStrips(in);
    uint32 sw;
    uint32 sl;
    int PixelCount;

    TIFFGetFieldDefaulted(in, TIFFTAG_IMAGEWIDTH,  &sw);
    TIFFGetFieldDefaulted(in, TIFFTAG_ROWSPERSTRIP, &sl);

    PixelCount = (int) sw * sl;

    BufferIn = (unsigned char *) _TIFFmalloc(BufSizeIn);
    if (!BufferIn) OutOfMem(BufSizeIn);

    BufferOut = (unsigned char *) _TIFFmalloc(BufSizeOut);
    if (!BufferOut) OutOfMem(BufSizeOut);


    for (i = 0; i < StripCount; i++) {

        if (TIFFReadEncodedStrip(in, i, BufferIn, BufSizeIn) < 0)   goto cleanup;

      cmsDoTransform(hXForm, BufferIn, BufferOut, PixelCount);

        if (TIFFWriteEncodedStrip(out, i, BufferOut, BufSizeOut) < 0) goto cleanup;

    }

    _TIFFfree(BufferIn);
    _TIFFfree(BufferOut);
    return 1;


cleanup:

    _TIFFfree(BufferIn);
   _TIFFfree(BufferOut);
   return 0;
}





// Creates minimum required tags

static
void WriteOutputTags(TIFF *out, int Colorspace, int BytesPerSample)
{
    int BitsPerSample = (8 * BytesPerSample);

    switch (Colorspace) {

  case PT_GRAY:
           TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
           TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 1);
           TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample);
           break;

   case PT_RGB:
           TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
           TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 3);
           TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample);
           break;

   case PT_CMY:
           TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
           TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 3);
           TIFFSetField(out, TIFFTAG_INKSET, 0);
           TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample);
           break;

   case PT_CMYK:
           TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_SEPARATED);
           TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 4);
           TIFFSetField(out, TIFFTAG_INKSET, INKSET_CMYK);
           TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample);
           break;

    case PT_Lab:

            TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB);
            TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 3);
            TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, BitsPerSample);    // Needed by TIFF Spec
            break;
   default:
           FatalError("Unsupported output colorspace");

   }

}


// Copies a bunch of tages

static
void CopyOtherTags(TIFF* in, TIFF* out)
{
#define CopyField(tag, v) \
    if (TIFFGetField(in, tag, &v)) TIFFSetField(out, tag, v)


        short shortv;
        uint32 ow, ol;
        float floatv;
        char *stringv;
        uint32 longv;

        CopyField(TIFFTAG_SUBFILETYPE, longv);

        TIFFGetField(in, TIFFTAG_IMAGEWIDTH, &ow);
        TIFFGetField(in, TIFFTAG_IMAGELENGTH, &ol);

        TIFFSetField(out, TIFFTAG_IMAGEWIDTH, ow);
        TIFFSetField(out, TIFFTAG_IMAGELENGTH, ol);

        CopyField(TIFFTAG_PLANARCONFIG, shortv);

        CopyField(TIFFTAG_COMPRESSION, shortv);
        CopyField(TIFFTAG_PREDICTOR, shortv);

        CopyField(TIFFTAG_THRESHHOLDING, shortv);
        CopyField(TIFFTAG_FILLORDER, shortv);
        CopyField(TIFFTAG_ORIENTATION, shortv);
        CopyField(TIFFTAG_MINSAMPLEVALUE, shortv);
        CopyField(TIFFTAG_MAXSAMPLEVALUE, shortv);
        CopyField(TIFFTAG_XRESOLUTION, floatv);
        CopyField(TIFFTAG_YRESOLUTION, floatv);
        CopyField(TIFFTAG_RESOLUTIONUNIT, shortv);
        CopyField(TIFFTAG_ROWSPERSTRIP, longv);
        CopyField(TIFFTAG_XPOSITION, floatv);
        CopyField(TIFFTAG_YPOSITION, floatv);
        CopyField(TIFFTAG_IMAGEDEPTH, longv);
        CopyField(TIFFTAG_TILEDEPTH, longv);

        CopyField(TIFFTAG_ARTIST, stringv);
        CopyField(TIFFTAG_IMAGEDESCRIPTION, stringv);
        CopyField(TIFFTAG_MAKE, stringv);
        CopyField(TIFFTAG_MODEL, stringv);

        CopyField(TIFFTAG_DATETIME, stringv);
        CopyField(TIFFTAG_HOSTCOMPUTER, stringv);
        CopyField(TIFFTAG_PAGENAME, stringv);
        CopyField(TIFFTAG_DOCUMENTNAME, stringv);

}

// Transform one image

static
int TransformImage(TIFF* in, TIFF* out, char *cDefInpProf, char *cOutProf)
{
       cmsHPROFILE hIn, hOut, hProof;
       cmsHTRANSFORM xform;
       DWORD wInput, wOutput;
       int OutputColorSpace;
       int bps = (Width16 ? 2 : 1);
       DWORD dwFlags = 0; 
       DWORD EmbedLen;
       LPBYTE EmbedBuffer;



       if (BlackWhiteCompensation) {

            dwFlags |= cmsFLAGS_WHITEBLACKCOMPENSATION;
            if (PrecalcMode == 0) 
                    FatalError("Cannot use White/Black compensation without precalculation. Use /c1 or /c2.");
       }

       switch (PrecalcMode) {
           
       case 0: dwFlags |= cmsFLAGS_NOTPRECALC; break;
       case 2: dwFlags |= cmsFLAGS_HIGHRESPRECALC; break;
       default:;
       }
        

       if (GamutCheck)
            dwFlags |= cmsFLAGS_GAMUTCHECK;
        

       if (!IgnoreEmbedded && TIFFGetField(in, TIFFTAG_ICCPROFILE, &EmbedLen, &EmbedBuffer))
       {
              hIn = cmsOpenProfileFromMem(EmbedBuffer, EmbedLen);
              if (Verbose) fprintf(stdout, " (embedded profile found)");
       }
       else
       {
              if (cDefInpProf == NULL)
                  hIn = cmsCreate_sRGBProfile();
              else
                hIn = cmsOpenProfileFromFile(cDefInpProf, "r");
       }

       if (cOutProf == NULL)
        hOut = cmsCreate_sRGBProfile();
       else
        hOut = cmsOpenProfileFromFile(cOutProf, "r");


       hProof = NULL;
       if (cProofing != NULL) {

           hProof = cmsOpenProfileFromFile(cProofing, "r");
       }

       // Take input color space

       wInput = GetInputPixelType(in);

       // Assure both, input profile and input TIFF are on same colorspace

       if (cmsGetColorSpace(hIn) != GetICCcolorSpace(T_COLORSPACE(wInput)))
              FatalError("Input profile is not operating in proper color space");

       // Output colorspace is given by output profile

       OutputColorSpace = GetProfileColorSpace(hOut);
       wOutput      = ComputeOutputFormatDescriptor(wInput, OutputColorSpace, bps);

       WriteOutputTags(out, OutputColorSpace, bps);
       CopyOtherTags(in, out);

       xform = cmsCreateProofingTransform(hIn, wInput, 
                                          hOut, wOutput, 
                                          hProof, Intent, 
                                          INTENT_ABSOLUTE_COLORIMETRIC, dwFlags);

       // Handle tile by tile or strip by strip

       if (TIFFIsTiled(in)) {

                TileBasedXform(xform, in, out);
       }
       else {

            StripBasedXform(xform, in, out);
       }


       cmsDeleteTransform(xform);
       cmsCloseProfile(hIn);
       cmsCloseProfile(hOut);
       if (hProof) cmsCloseProfile(hProof);

       TIFFWriteDirectory(out);

       return 1;
}


// Simply print help

static
void Help(int level)
{
     fprintf(stderr, "little cms ICC profile applier for tiff  - v2.0\n\n");

     switch(level) {

     default:
     case 0:

     fprintf(stderr, "usage: tifficc [flags] input.tif output.tif\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);   
     fprintf(stderr, "%ct<0,1,2,3> - Intent (0=Perceptual, 1=Colorimetric, 2=Saturation, 3=Absolute)\n", SW);    
     fprintf(stderr, "\n");

     fprintf(stderr, "%cw - Wide output (generate 16 bps tiff)\n", SW);
     fprintf(stderr, "%cb - Black/White compensation\n", SW);
     fprintf(stderr, "%cn - Ignore embedded profile\n", SW);
     fprintf(stderr, "%cc<0,1,2> - Precalculates transform (0=off, 1=normal, 2=Hi-res) [defaults to 1]\n", SW);     
     fprintf(stderr, "\n");

     fprintf(stderr, "%cp<profile> - Soft proof profile\n", SW);
     fprintf(stderr, "%cg - Marks out-of-gamut colors on softproof\n", SW);
     
     fprintf(stderr, "\n");
     fprintf(stderr, "%ch<0,1,2> - More help\n", SW);
     break;

     case 1:

     
     fprintf(stderr, "Examples:\n\n"
                     "To color correct from scanner to sRGB:\n"
                     "\ttifficc %ciscanner.icm in.tif out.tif\n"
                     "To convert from monitor1 to monitor2:\n"
                     "\ttifficc %cimon1.icm %comon2.icm in.tif out.tif\n"
                     "To make a CMYK separation:\n"
                     "\ttifficc %coprinter.icm inrgb.tif outcmyk.tif\n"
                     "To recover sRGB from a CMYK separation:\n"
                     "\ttifficc %ciprinter.icm incmyk.tif outrgb.tif\n"
                     "To convert from CIELab TIFF to sRGB\n"
                     "\ttifficc %ciTiffLab8Spac.icm in.tif out.tif\n\n", 
                     SW, SW, SW, SW, SW, SW);
     break;

     case 2:

     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,"bBwWnNvVGgh:H:i:I:o:O:P:p:t:T:c:C:")) != EOF) {

       switch (s)
       {

       case 'b':
       case 'B':
            BlackWhiteCompensation = TRUE;
            break;

       case 'v':
       case 'V':
            Verbose = TRUE;
            break;

       case 'i':
       case 'I':
            cInpProf = xoptarg;
            break;

       case 'o':
       case 'O':
           cOutProf = xoptarg;
            break;

       case 'p':
       case 'P':
           cProofing = xoptarg;
           break;

       case 't':
       case 'T':
            Intent = atoi(xoptarg);
            if (Intent > 3) Intent = 3;
            if (Intent < 0) Intent = 0;
            break;

       case 'N':
       case 'n':
            IgnoreEmbedded = TRUE;
            break;

       case 'W':
       case 'w':
            Width16 = 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;

  default:

       FatalError("Unknown option - run without args to see valid ones");
    }
       
    }
}


// The main sink

int main(int argc, char* argv[])
{
      TIFF *in, *out;
      char *Intents[] = {"perceptual",
                         "relative colorimetric",
                         "saturation",
                         "absolute colorimetric" };

      HandleSwitches(argc, argv);

      if ((argc - xoptind) != 2) {

              Help(0);              
              }

     
      if (Verbose) 
        fprintf(stdout, "%s(%s) -> %s(%s) [%s]", argv[xoptind],
                                                (cInpProf == NULL ? "sRGB": cInpProf), 
                                                argv[xoptind+1],
                                                (cOutProf == NULL ? "sRGB" : cOutProf), 
                                                Intents[Intent]);

      TIFFSetErrorHandler(ConsoleErrorHandler);
      TIFFSetWarningHandler(ConsoleWarningHandler);

      in = TIFFOpen(argv[xoptind], "r");
      if (in == NULL) FatalError("Unable to open '%s'", argv[xoptind]);

      out = TIFFOpen(argv[xoptind+1], "w");

      if (out == NULL) {

             TIFFClose(in);
             FatalError("Unable to write '%s'", argv[xoptind+1]);
             }

      do {

              TransformImage(in, out, cInpProf, cOutProf);


      } while (TIFFReadDirectory(in));


      if (Verbose) fprintf(stdout, "\n");

      TIFFClose(in);
      TIFFClose(out);

      return 0;
}



