/* tiff loading and saving for the GIMP
 *  -Peter Mattis
 * tiff can read and write multipaged files
 *  web@tiscali.de -- 11.November 2002
 * The TIFF loading code has been completely revamped by Nick Lamb
 * njl195@zepler.org.uk -- 18 May 1998
 * And it now gains support for tiles (and doubtless a zillion bugs)
 * njl195@zepler.org.uk -- 12 June 1999
 * LZW patent fuss continues :(
 * njl195@zepler.org.uk -- 20 April 2000
 * khk@khk.net -- 13 May 2000
 * Added support for ICCPROFILE tiff tag. If this tag is present in a 
 * TIFF file, then a parasite is created and vice versa.
 * 
 * merged with tiff.c functions from above (as in gimp-1.3.10)
 * better layerhandling 
 *  web@tiscali.de -- 03 Dezember 2002
 * sampleformat-tag for floats is written out, basic Lab loading
 * saving and loading of float16_gray without alpha
 *  web@tiscali.de -- 18 Dezember 2002
 * added dummy layer for marking gimps viewport while saving tif
 *  web@tiscali.de -- February 2003
 * bugfixes lossy strings
 *  web@tiscali.de -- Febr./March/April 2003
 * bugfixes for order missmatched function arguments and implementations
 *  web@tiscali.de -- 15 April 2003
 * infos stored in an list in IMAGE_info_list to avoid double libtiff calls
 *  web@tiscali.de -- 22.April 2003
 * changed and added calls to set viewport in cinepaint while opening an tif
 *  web@tiscali.de -- 27.April 2003
 * <http://cch.loria.fr/documentation/IEEE754/> for 16bit floats no standard
 * using first R&H 16bit float - alpha therein is buggy
 * Put the TIFF structure to the end of save_image, to avoid exeeding of it's size.
 *  web@tiscali.de -- 05.Mai 2003
 *
 * The code for this filter is based on "tifftopnm" and "pnmtotiff",
 *  2 programs that are a part of the netpbm package.
 */

/*
** tifftopnm.c - converts a Tagged Image File to a portable anymap
**
** Derived by Jef Poskanzer from tif2ras.c, which is:
**
** Copyright (c) 1990 by Sun Microsystems, Inc.
**
** Author: Patrick J. Naughton
** naughton@wind.sun.com
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted,
** provided that the above copyright notice appear in all copies and that
** both that copyright notice and this permission notice appear in
** supporting documentation.
**
** This file is provided AS IS with no warranties of any kind.  The author
** shall have no liability with respect to the infringement of copyrights,
** trade secrets or any patents by this file or any part thereof.  In no
** event will the author be liable for any lost revenue or profits or
** other special, indirect and consequential damages.
*/

#include <stdlib.h>
#include <string.h>
#include <tiffio.h>
#include <time.h>
#include "gtk/gtk.h"
#include "libgimp/config.h"
#include "libgimp/gimp.h"
#include "libgimp/gimpfloat16.h"

static char plug_in_version[] = "TIFF plug-in for cinepaint: v.1.3.04";

#ifndef PHOTOMETRIC_ITULAB
/* This is not defined in older versions of tiff.h */
#define PHOTOMETRIC_ITULAB 10
#endif

typedef struct
{
  gint  compression;		// compression type
  gint  fillorder;		// msb versus lsb
} TiffSaveVals;

typedef struct
{
  gint  run;
} TiffSaveInterface;

typedef struct {
  gint32 ID;
  GDrawable *drawable;
  GPixelRgn pixel_rgn;
  guchar *pixels, *pixel;
} channel_data;

struct IMAGE_info_list {
  struct IMAGE_info_list  *parent, *next;	// neighbour list chains
  struct IMAGE_info_list  *top;	// top list chain for standard values
  gint32   cols, rows;		// count of pixel per row/col
  gfloat   xres, yres;		// tif resolution
  gushort  read_unit;		// physical resolution unit
  gfloat   xpos, ypos;		// position corresponding to x[y]res
  gint32   offx, offy,		// offsets of IFD
	   max_offx, max_offy,  // offsets over whole tif
           viewport_offx, viewport_offy; // offsets for viewing area

  gint32   image_width, image_height;  // size   over all or viewport 
  // GimpUnit unit = GIMP_UNIT_PIXEL; // invalid unit 
  gushort  bps, spp, photomet;	// infos from tif
				// bits per sample, samples per pixel
				// photometric - colortype ( gray, rgb, lab... )
  gint     image_type;		// colortype and bitdepht information
  gint     layer_type;		// colortype / bitdepht / alpha
  gushort  extra, *extra_types;	// additional channels
  gint     alpha;		// extra == alpha channel?
  guchar color[3];		// ??
  // GimpRGB  color;  // for gimp-1.3 , filmgimp needs: guchar color 

  gint   worst_case;		// fallback switch

  TiffSaveVals save_vals;	// storing the save settings
  // GimpParasite *parasite; // not yet available in filmgimp 
  guint16 tmp;			// ?
#ifdef TIFFTAG_ICCPROFILE
  //uint32 profile_size; // not yet available in filmgimp
  //guchar *icc_profile;
#endif
  uint16 planar;		// RGBRGBRGB versus RRRGGGBBB
  int grayscale;
  int maxval, minval, numcolors;// grayscale related
  gushort sampleformat;		// sort of alpha
  long pagecount;		// count of all IFD's
  long aktuelles_dir;		// local IFD counter 
  guchar filetype;		// TIFFTAG_(O)SUBFILETYPE
  char *pagename;		// IFD page <-> gimp layer name
  char *img_desc;  		// image description
};

typedef struct IMAGE_info_list IMAGE_info;

//typedef struct IMAGE_info_liste IMAGE_info;

//TIFF *tif;
  /*unsigned short photomet;
  int  alpha;
  int  num_extra; */
  //gint align;

  /* Make sure this is used with a, b, t 32bit unsigned ints */
#define INT_MULT_16(a,b,t) ((t) = (a) * (b) + 0x8000, ((((t) >> 16) + (t)) >> 16))
/* Declare some local functions.
 */
static void   query      (void);
static void   run        (char    *name,
                          int      nparams,
                          GParam  *param,
                          int     *nreturn_vals,
                          GParam **return_vals);

static gint   get_image_info (	TIFF 		*tif,
				IMAGE_info	*current_info);
static gint32 load_image    (	gchar		*filename);
static gint32 load_IFD 	    (	TIFF		*tif,
				gint		image,
				gint		layer,
				channel_data	*channel,
				IMAGE_info	*current_info);

static void   load_rgba     (	TIFF		*tif,
			     	channel_data	*channel,
				IMAGE_info	*current_info);
static void   load_lines    (	TIFF         	*tif,
			     	channel_data 	*channel,
				IMAGE_info	*current_info);
static void   load_tiles    (	TIFF         	*tif,
			     	channel_data 	*channel,
				IMAGE_info	*current_info);

static void   read_separate (guchar       *source,
			     channel_data *channel,
                             gushort       bps,
			     gushort       photomet,
                             gint32        startcol,
			     gint32        startrow,
			     gint32        cols,
			     gint32        rows,
                             gint          alpha,
			     gint          extra,
			     gint          sample);
static void   read_32bit    (guchar       *source,
			     channel_data *channel,
			     gushort       photomet,
			     gint32        startcol,
			     gint32        startrow,
			     gint32        cols,
			     gint32        rows,
			     gint          alpha,
			     gint          extra,
			     gint          align);
static void   read_f16bit    (guchar       *source,
			     channel_data *channel,
			     gushort       photomet,
			     gint32        startcol,
			     gint32        startrow,
			     gint32        cols,
			     gint32        rows,
			     gint          alpha,
			     gint          extra,
			     gint          align);
static void   read_u16bit    (guchar       *source,
			     channel_data *channel,
			     gushort       photomet,
			     gint32        startcol,
			     gint32        startrow,
			     gint32        cols,
			     gint32        rows,
			     gint          alpha,
			     gint          extra,
			     gint          align);
static void   read_8bit     (guchar       *source,
			     channel_data *channel,
			     gushort       photomet,
			     gint32        startcol,
			     gint32        startrow,
			     gint32        cols,
			     gint32        rows,
			     gint          alpha,
			     gint          extra,
			     gint          align);
static void   read_default  (guchar       *source,
			     channel_data *channel,
			     gushort       bps,
			     gushort       photomet,
			     gint32        startcol,
			     gint32        startrow,
			     gint32        cols,
			     gint32        rows,
			     gint          alpha,
			     gint          extra,
			     gint          align);

static gint   save_image (char   *filename,
			  gint32  image_ID,
			  gint32  drawable_ID);

static gint   save_dialog ();

static void   save_close_callback  (GtkWidget *widget,
				    gpointer   data);
static void   save_ok_callback     (GtkWidget *widget,
				    gpointer   data);
static void   save_toggle_update   (GtkWidget *widget,
				    gpointer   data);


static TiffSaveVals tsvals =
{
  COMPRESSION_NONE,    /*  compression  */
  FILLORDER_LSB2MSB,  /*  fillorder    */
};

static TiffSaveInterface tsint =
{
  FALSE                /*  run  */
};

GPlugInInfo PLUG_IN_INFO =
{
  NULL,    /* init_proc */
  NULL,    /* quit_proc */
  query,   /* query_proc */
  run,     /* run_proc */
};


MAIN()

static void
query ()
{
  static GParamDef load_args[] =
  {
    { PARAM_INT32, "run_mode", "Interactive, non-interactive" },
    { PARAM_STRING, "filename", "The name of the file to load" },
    { PARAM_STRING, "raw_filename", "The name of the file to load" },
  };
  static GParamDef load_return_vals[] =
  {
    { PARAM_IMAGE, "image", "Output image" },
  };
  static int nload_args = (int)sizeof (load_args) / (int)sizeof (load_args[0]);
  static int nload_return_vals = (int)sizeof (load_return_vals) / (int)sizeof (load_return_vals[0]);

  static GParamDef save_args[] =
  {
    { PARAM_INT32, "run_mode", "Interactive, non-interactive" },
    { PARAM_IMAGE, "image", "Input image" },
    { PARAM_DRAWABLE, "drawable", "Drawable to save" },
    { PARAM_STRING, "filename", "The name of the file to save the image in" },
    { PARAM_STRING, "raw_filename", "The name of the file to save the image in" },
    { PARAM_INT32, "compression", "Compression type: { NONE (0), LZW (1), PACKBITS (2), DEFLATE (3)"},
    { PARAM_INT32, "fillorder", "Fill Order: { MSB to LSB (0), LSB to MSB (1)" }
  };
  static int nsave_args = (int)sizeof (save_args) / (int)sizeof (save_args[0]);

  gimp_install_procedure ("file_tiff_load",
                          "loads files of the tiff file format",
                          "FIXME: write help for tiff_load",
                          "Spencer Kimball & Peter Mattis",
                          "Spencer Kimball & Peter Mattis",
                          "1995-1996",
                          "<Load>/Tiff",
                          NULL,
                          PROC_PLUG_IN,
                          nload_args, nload_return_vals,
                          load_args, load_return_vals);

  gimp_install_procedure ("file_tiff_save",
                          "saves files in the tiff file format",
                          "FIXME: write help for tiff_save",
                          "Spencer Kimball & Peter Mattis",
                          "Spencer Kimball & Peter Mattis",
                          "1995-1996",
                          "<Save>/Tiff",
                          "RGB*, GRAY*, U16_RGB*, U16_GRAY*, FLOAT16_GRAY*, FLOAT_RGB*, FLOAT_GRAY*",
                          PROC_PLUG_IN,
                          nsave_args, 0,
                          save_args, NULL);

  gimp_register_magic_load_handler ("file_tiff_load", "tif,tiff", "",
             "0,string,II*\\0,0,string,MM\\0*");
  gimp_register_save_handler ("file_tiff_save", "tif,tiff", "");
}

static void
run (char    *name,
     int      nparams,
     GParam  *param,
     int     *nreturn_vals,
     GParam **return_vals)
{
  static GParam values[2];
  GRunModeType run_mode;
  GStatusType status = STATUS_SUCCESS;
  gint32 image_ID;

  run_mode = param[0].data.d_int32;

  *nreturn_vals = 1;
  *return_vals = values;
  values[0].type = PARAM_STATUS;
  values[0].data.d_status = STATUS_CALLING_ERROR;

  if (strcmp (name, "file_tiff_load") == 0)
    {
      image_ID = load_image (param[1].data.d_string);

      if (image_ID != -1)
	{
	  *nreturn_vals = 2;
	  values[0].data.d_status = STATUS_SUCCESS;
	  values[1].type = PARAM_IMAGE;
	  values[1].data.d_image = image_ID;
	}
      else
	{
	  values[0].data.d_status = STATUS_EXECUTION_ERROR;
	}
    }
  else if (strcmp (name, "file_tiff_save") == 0)
    {
      switch (run_mode)
	{
	case RUN_INTERACTIVE:
	  /*  Possibly retrieve data  */
	  gimp_get_data ("file_tiff_save", &tsvals);

	  /*  First acquire information with a dialog  */
	  if (save_dialog () != 1)
	    return;
	  break;

	case RUN_NONINTERACTIVE:
	  /*  Make sure all the arguments are there!  */
	  if (nparams != 7)
	    status = STATUS_CALLING_ERROR;
	  if (status == STATUS_SUCCESS)
	    {
	      switch (param[5].data.d_int32)
		{
		case 0: tsvals.compression = COMPRESSION_NONE;     break;
		case 1: tsvals.compression = COMPRESSION_LZW;      break;
		case 2: tsvals.compression = COMPRESSION_PACKBITS; break;
		case 3: tsvals.compression = COMPRESSION_DEFLATE;  break;
		/* case 4: tsvals.compression = COMPRESSION_JPEG;  break; */
		default: status = STATUS_CALLING_ERROR; break;
		}
	      switch (param[6].data.d_int32)
		{
		case 0: tsvals.fillorder = FILLORDER_MSB2LSB; break;
		case 1: tsvals.fillorder = FILLORDER_LSB2MSB; break;
		default: status = STATUS_CALLING_ERROR; break;
		}
	    }
	  break;

	case RUN_WITH_LAST_VALS:
	  /*  Possibly retrieve data  */
	  gimp_get_data ("file_tiff_save", &tsvals);
	  break;

	default:
	  break;
	}

      *nreturn_vals = 1;
      if (save_image (param[3].data.d_string, param[1].data.d_int32, param[2].data.d_int32) == 1)
	{
	  /*  Store mvals data  */
	  gimp_set_data ("file_tiff_save", &tsvals, (guint32)sizeof (TiffSaveVals));

	  values[0].data.d_status = STATUS_SUCCESS;
	}
      else
	values[0].data.d_status = STATUS_EXECUTION_ERROR;
    }
}

static void
tiff_warning(const char* module, const char* fmt, va_list ap)
{
  g_logv ((gchar *)0/*G_LOG_DOMAIN*/, G_LOG_LEVEL_MESSAGE, fmt, ap);
}
  
static void
tiff_error(const char* module, const char* fmt, va_list ap)
{
  g_logv ((gchar *)0/*G_LOG_DOMAIN*/, G_LOG_LEVEL_MESSAGE, fmt, ap);
}

static gint
get_image_info ( TIFF *tif, IMAGE_info *current_info)
{
  current_info->worst_case = 0;

  if (!TIFFGetFieldDefaulted (tif, TIFFTAG_BITSPERSAMPLE, &current_info->bps))
    current_info->bps = 0;
  // supported bps have to go here
  if (current_info->bps > 8
    	&& (current_info->bps != 16 && current_info->bps != 32)) {
    g_print("difficult samplevalue: %d\n", current_info->bps);
    current_info->worst_case = 1; /* Wrong sample width => RGBA */
  }

  if (!TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &current_info->spp))
    current_info->spp = 0;

  if (!TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &current_info->photomet)) {
    g_message("TIFF Can't get photometric\nAssuming min-is-black\n");
    /* old AppleScan software misses out the photometric tag (and
     * incidentally assumes min-is-white, but xv assumes min-is-black,
     * so we follow xv's lead.  It's not much hardship to invert the
     * image later). */
    current_info->photomet = PHOTOMETRIC_MINISBLACK;
  }

  if (!TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &current_info->planar))
    current_info->planar = PLANARCONFIG_CONTIG; // PLANARCONFIG_SEPARATE


  if (!TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &current_info->extra,
	&current_info->extra_types)) {
    current_info->extra = 0;
    current_info->extra_types = malloc ( 16); 	// not shure with memory bounds
    //current_info->extra_types = NULL;
  }

  if (!TIFFGetField (tif, TIFFTAG_SAMPLEFORMAT, &current_info->sampleformat))
    current_info->sampleformat = SAMPLEFORMAT_UINT;

  // HACKING!!!
  if(current_info->sampleformat == 196608) {
     current_info->sampleformat = 3;
  }
  /*if(photomet == 32845)  {
     photomet = PHOTOMETRIC_RGB;
     bps = 16;
  }*/

     /* test if the extrasample represents an associated alpha channel... */
      if (current_info->extra > 0	// extra_types[0] relies on libtiff
	   && (current_info->extra_types[0] == EXTRASAMPLE_ASSOCALPHA)) {
        current_info->alpha = 1;
        --current_info->extra;
      } else {
        current_info->alpha = 0;
      }

      if ((current_info->photomet == PHOTOMETRIC_CIELAB
	  || current_info->photomet == PHOTOMETRIC_ITULAB
	  || current_info->photomet == PHOTOMETRIC_RGB)
	 && current_info->spp > 3 + current_info->extra) {
        current_info->alpha= 1;
        current_info->extra= current_info->spp - 4; 
      } else if ((current_info->photomet != PHOTOMETRIC_CIELAB
	 && current_info->photomet != PHOTOMETRIC_ITULAB
	 && current_info->photomet != PHOTOMETRIC_RGB)
	 && current_info->spp > 1 + current_info->extra) {
        current_info->alpha= 1;
        current_info->extra= current_info->spp - 2;
      }
      if ((current_info->photomet == PHOTOMETRIC_CIELAB
	  || current_info->photomet == PHOTOMETRIC_ITULAB
	  || current_info->photomet == PHOTOMETRIC_RGB)
	 && current_info->spp == 3 ) {
        current_info->alpha= 0;
        current_info->extra= 0; 
      }
    //g_print ("1.layer_type = %d, alpha = %d\n", layer_type , alpha);


    current_info->image_type = -1;
    current_info->layer_type = -1;
    /* Gimp accepts layers with different alphas in an image. */
    current_info->maxval = (1 << current_info->bps) - 1;
    current_info->minval = 0; 
    if (current_info->maxval == 1 && current_info->spp == 1)
      {
        current_info->grayscale = 1;
        current_info->image_type = GRAY;
        current_info->layer_type = (current_info->alpha) ? GRAYA_IMAGE : GRAY_IMAGE;
      }
    else
      {
      switch (current_info->photomet) {
        case PHOTOMETRIC_MINISBLACK:
          current_info->grayscale = 1;
          if (current_info->bps <= 8) {
            current_info->image_type = GRAY;
          }
          else if (current_info->bps == 16 && current_info->sampleformat == 1) { /* uint16 */ 
            current_info->image_type = U16_GRAY;
          }
          else if (current_info->bps == 16 && (current_info->sampleformat == 3 || current_info->sampleformat == 4)) { /* floats16 */ 
            current_info->image_type = FLOAT16_GRAY; /* TODO */
          }
          else if (current_info->bps == 32 && (current_info->sampleformat == 1 || current_info->sampleformat == 3)) { /* floating point data */ 
            current_info->image_type = FLOAT_GRAY;
          }
          break;
 
        case PHOTOMETRIC_MINISWHITE:
          current_info->grayscale = 1;
          if (current_info->bps <= 8) {
            current_info->image_type = GRAY;
          }
          else if (current_info->bps == 16 && current_info->sampleformat == 1) { /* uint16 */ 
            current_info->image_type = U16_GRAY;
          }
          else if (current_info->bps == 16 && (current_info->sampleformat == 3 || current_info->sampleformat == 4)) { /* floats16 */ 
            current_info->image_type = FLOAT16_GRAY;
          }
          else if (current_info->bps == 32 && (current_info->sampleformat == 1 || current_info->sampleformat == 3)) { /* floating point data */
            current_info->image_type = FLOAT_GRAY;
          }
          break;
 
        case PHOTOMETRIC_PALETTE:
          if (current_info->alpha)
            g_print ("ignoring alpha channel on indexed color image\n");
          if (current_info->bps <= 8) {
            /*if (!TIFFGetField (tif, TIFFTAG_COLORMAP,
			       &current_info->redcolormap,
                               &current_info->greencolormap,
			       &current_info->bluecolormap))
              {
                g_print ("error getting colormaps\n");
                gimp_quit ();
              }*/
            current_info->numcolors = current_info->maxval + 1;
            current_info->maxval = 255;
            current_info->grayscale = 0;

            /*for (i = 0; i < current_info->numcolors; i++)
              {
                current_info->redcolormap[i] >>= 8;
                current_info->greencolormap[i] >>= 8;
                current_info->bluecolormap[i] >>= 8;
              }*/

            if (current_info->numcolors > 256) {
                current_info->image_type = RGB;
              } else {
                current_info->image_type = INDEXED;
              }
          }
          else if (current_info->bps == 16)
            g_print("16bit indexed color image not implemented yet\n");
          break;
	  
        case PHOTOMETRIC_RGB:
        case PHOTOMETRIC_CIELAB: case PHOTOMETRIC_ITULAB: /* This is a hack. */
         g_print("RGB datas; bps = %d, sampleformat = %d\n", current_info->bps, current_info->sampleformat);
          current_info->grayscale = 0;
          if (current_info->bps <= 8) {
            //g_print("uint datas\n");
            current_info->image_type = RGB;
          }
          else if (current_info->bps == 16
	    && current_info->sampleformat == SAMPLEFORMAT_UINT) { // uint16 
            current_info->image_type = U16_RGB;
          }
          else if (current_info->bps == 16
	    && ((current_info->sampleformat == SAMPLEFORMAT_IEEEFP)
	     || (current_info->sampleformat == SAMPLEFORMAT_VOID)) ) {//floats16
            current_info->image_type = FLOAT16_RGB;
          }
          else if (current_info->bps == 32 && (current_info->sampleformat == 1 || current_info->sampleformat == 3)) { /* floating point data */
            current_info->image_type = FLOAT_RGB;
            //g_print("float datas\n");
          }
          break;
 
      default:
        current_info->worst_case = 1;
      }
    }

    switch (current_info->image_type) {
      case GRAY:
        current_info->layer_type = (current_info->alpha) ? GRAYA_IMAGE : GRAY_IMAGE;
      break;
      case U16_GRAY:
        current_info->layer_type = (current_info->alpha) ? U16_GRAYA_IMAGE : U16_GRAY_IMAGE;
      break;
      case FLOAT16_GRAY:
        current_info->layer_type = (current_info->alpha) ? FLOAT16_GRAYA_IMAGE : FLOAT16_GRAY_IMAGE;
      break;
      case FLOAT_GRAY:
        current_info->layer_type = (current_info->alpha) ? FLOAT_GRAYA_IMAGE : FLOAT_GRAY_IMAGE;
      break;
      case INDEXED:
        current_info->layer_type = (current_info->alpha) ? INDEXEDA_IMAGE : INDEXED_IMAGE;
      break;
      case RGB:
        current_info->layer_type = (current_info->alpha) ? RGBA_IMAGE : RGB_IMAGE;
      break;
      case U16_RGB:
        current_info->layer_type = (current_info->alpha) ? U16_RGBA_IMAGE : U16_RGB_IMAGE;
      break;
      case FLOAT16_RGB:
        current_info->layer_type = (current_info->alpha) ? FLOAT16_RGBA_IMAGE : FLOAT16_RGB_IMAGE;
      break;
      case FLOAT_RGB:
        current_info->layer_type = (current_info->alpha) ? FLOAT_RGBA_IMAGE : FLOAT_RGB_IMAGE;
      break;
    }

  if (current_info->worst_case) {
        g_print ("fall back\n");
    current_info->image_type = RGB_IMAGE;
    current_info->layer_type = RGBA_IMAGE;
  }


    /* attach a parasite containing an ICC profile - if found in the TIFF file */

  #ifdef TIFFTAG_ICCPROFILE
	  /* If TIFFTAG_ICCPROFILE is defined we are dealing with a libtiff version 
           * that can handle ICC profiles. Otherwise just ignore this section. */
    /* if (TIFFGetField (tif, TIFFTAG_ICCPROFILE, &profile_size, &icc_profile)) {
      parasite = gimp_parasite_new("icc-profile", 0,
			      profile_size, icc_profile);
      gimp_image_parasite_attach(image, parasite);
      gimp_parasite_free(parasite);
    } */ 
  #endif

    /* attach a parasite containing the compression */
    if (!TIFFGetField (tif, TIFFTAG_FILLORDER, &current_info->save_vals.fillorder))
      current_info->save_vals.fillorder = 0;
    if (!TIFFGetField (tif, TIFFTAG_COMPRESSION, &current_info->tmp))
      current_info->save_vals.compression = COMPRESSION_NONE;
    else
      current_info->save_vals.compression = (gint)current_info->tmp;

    /* parasite = gimp_parasite_new ("tiff-save-options", 0,
				  sizeof (save_vals), &save_vals);
    gimp_image_parasite_attach (image, parasite);
    gimp_parasite_free (parasite); */

  {	// size and resolution
    if (!TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &current_info->cols))
      current_info->cols = 0;
    if (!TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &current_info->rows))
      current_info->rows = 0;
    current_info->image_width = current_info->cols;
    current_info->image_height = current_info->rows;

    if (!TIFFGetField (tif, TIFFTAG_XPOSITION, &current_info->xpos))
      current_info->xpos = (gfloat)0.0;
    if (!TIFFGetField (tif, TIFFTAG_YPOSITION, &current_info->ypos))
      current_info->ypos = (gfloat)0.0;
    if (!TIFFGetField (tif, TIFFTAG_XRESOLUTION, &current_info->xres))
      current_info->xres = (gfloat)0.0;
    if (!TIFFGetField (tif, TIFFTAG_YRESOLUTION, &current_info->yres))
      current_info->yres = (gfloat)0.0;
	/* yres but no xres */
    if ( ( current_info->xres == (gfloat)0.0 )
		 && ( current_info->yres != (gfloat)0.0 ) ) {
      g_message("TIFF warning: no x resolution info, assuming same as y\n");
      current_info->xres = current_info->yres;
	/* xres but no yres */
    } else if ( ( current_info->yres == (gfloat)0.0 )
		 && ( current_info->xres != (gfloat)0.0 ) ) { 
      g_message("TIFF warning: no y resolution info, assuming same as x\n");
      current_info->yres = current_info->xres;
    } else {
      /* no res tags => we assume we have no resolution info, so we
       * don't care.  Older versions of this plugin used to write files
       * with no resolution tags at all. */

      /* If it is invalid, instead of forcing 72dpi, do not set the resolution 
       * at all. Gimp will then use the default set by the user */

      /*xres = 72.0;
      yres = 72.0;*/
    }  
    if (TIFFGetFieldDefaulted (tif, TIFFTAG_RESOLUTIONUNIT, &current_info->read_unit)) 
	{
	  switch (current_info->read_unit) 
	    {
	    case RESUNIT_NONE:
	      /* ImageMagick writes files with this silly resunit */
	      g_message ("TIFF warning: resolution units meaningless\n");
	      break;
		
	    case RESUNIT_INCH:
  	      // unit = GIMP_UNIT_INCH;
	      break;
		
	    case RESUNIT_CENTIMETER:
	      current_info->xres *= (gfloat)2.54;
	      current_info->yres *= (gfloat)2.54;
	      // unit = GIMP_UNIT_MM; // as this is our default metric unit 
	      break;
		
	    default:
	      g_message ("TIFF file error: unknown resolution unit type %d, "
			     "assuming dpi\n", current_info->read_unit);
	      break;
	    }
        } 
    else 
    { /* no res unit tag */
      /* old AppleScan software produces these */
      g_message ("TIFF warning: resolution specified without\n"
      "any units tag, assuming dpi\n");
    }
	// position information
    current_info->max_offx = 0;
    current_info->max_offy = 0;
    current_info->offx = (int)(current_info->xpos * current_info->xres);
    current_info->offy = (int)(current_info->ypos * current_info->yres);
    if ( current_info->offx > current_info->top->max_offx ) {
      current_info->top->max_offx = current_info->offx;
    }
    if ( current_info->offy > current_info->top->max_offy ) {
      current_info->top->max_offy = current_info->offy;
    }
  }

  if (!TIFFGetField (tif, TIFFTAG_SUBFILETYPE, &current_info->filetype))
    current_info->filetype = 0x0 ;

  {	// string informations
    int viewport = FALSE; /* further viewport search */
    int len = 0;
    char *text;
    char  text_temp[65535];		// stringsafe
    char *temp_ptr = &text_temp[0];

	// This is the example wich doesnt work - why ?
//    char *text = malloc(65535);
/*    if (TIFFGetField (tif, TIFFTAG_IMAGEDESCRIPTION, &text)) {
//      current_info->img_desc = malloc (strlen (text));
      current_info->img_desc = g_strdup_printf ("%s",text);
    } else {
//      current_info->img_desc = malloc (1);
      current_info->img_desc = g_strdup_printf("");
    }
    if (TIFFGetField (tif, TIFFTAG_PAGENAME, &text)) {
//      current_info->pagename = malloc (strlen (text));
      current_info->pagename = g_strdup_printf ("%s",text);
    } else {	// ...we count the IFD's here for layer naming
//      current_info->pagename = malloc (32);
      current_info->pagename = g_strdup_printf ("layer # %ld", current_info->aktuelles_dir );
    }*/

    if (TIFFGetField (tif, TIFFTAG_IMAGEDESCRIPTION, &temp_ptr)) {
      len = strlen(temp_ptr) + 1;
      len = MIN(len, 65535);
      temp_ptr[len-1] = '\000';

      text = malloc (len);
      strcpy (text, temp_ptr);
      current_info->img_desc = text;
    } else {
      current_info->img_desc = NULL;
    }
    len = 0;
    if (TIFFGetField (tif, TIFFTAG_PAGENAME, &temp_ptr)) {
      len = strlen(temp_ptr) + 1;
      len = MIN(len, 65535);
      temp_ptr[len-1] = '\000';
    } else {	// ...we count the IFD's here
      sprintf (temp_ptr, "layer # %ld", current_info->aktuelles_dir );
      len = strlen(temp_ptr);
    }
    text = malloc (len);
    strcpy (text, temp_ptr);
    current_info->pagename = text;
    	// searching for viewport position and size
    current_info->viewport_offx = 0;
    current_info->viewport_offy = 0;
    if ( (viewport == FALSE) && (!strcmp (text, "viewport")) // test for our own
     && (current_info->filetype & FILETYPE_MASK) )
    { //viewport definitions
      current_info->top->image_height = 
	( current_info->rows * current_info->top->yres / current_info->yres);
      current_info->top->image_width = 
	( current_info->cols * current_info->top->xres / current_info->xres);
      current_info->top->viewport_offx = current_info->offx;
      current_info->top->viewport_offy = current_info->offy;
      viewport = TRUE;
    } else {
      if ( ( current_info->rows + (guint16)(current_info->ypos * current_info->yres) )
	  >  current_info->top->image_height ) 
        current_info->top->image_height = current_info->rows
		 + (guint16)(current_info->ypos * current_info->yres);
      if ( ( current_info->cols + (guint16)(current_info->xpos * current_info->xres) )
	  	>  current_info->top->image_width ) 
        current_info->top->image_width = current_info->cols
			 + (guint16)(current_info->xpos * current_info->xres);
    }
//    free (text);
  }

  return 1;
}

static gint32
load_image ( gchar *filename) 
{
  TIFF    *tif;
  IMAGE_info *info = NULL,		// top IFD info
	     *current_info = NULL,	// list pointer
  /*struct IMAGE_info_list*/ *tmp_info = NULL;
  gchar   *name;
  int     image;
  long	  i;
  gint    layer=0;
  channel_data *channel= NULL;
  /* needed ?? -->  gimp_rgb_set (&color, 0.0, 0.0, 0.0); */

  if (!(info = malloc (sizeof (IMAGE_info)))) {	// first info field
    g_message ("memory allocation for image information failed: quit");
    gimp_quit();
  }

  TIFFSetWarningHandler (tiff_warning);
  TIFFSetErrorHandler (tiff_error);

  if (!(tif = TIFFOpen (filename, "r"))) {
    g_message ("TIFF Can't open %s\n", filename);
    gimp_quit ();
  }

  //save_dialog(); // Wait till my debugger is attached to this process.

  info->top  = info;


  name = g_strdup_printf( ("Loading %s:"), filename);
  gimp_progress_init (name);
  g_free (name);

  if (!(get_image_info (tif, info))) {	// get infos for the top IFD
    g_message ("get_image_info(...) failed");
    TIFFClose (tif);
    gimp_quit ();
  }
  //get_image_info (tif, info);
  current_info = info;		// copy the top IFD - *info is for defaults
  current_info->top = info;
  current_info->parent = info;


  /* Attach a parasite containing the image description.  Pretend to
   * be a gimp comment so other plugins will use this description as
   * an image comment where appropriate. */
  {
      /* parasite = gimp_parasite_new ("gimp-comment",
				    GIMP_PARASITE_PERSISTENT,
				    len, img_desc);
      gimp_image_parasite_attach (image, parasite);
      gimp_parasite_free (parasite); */
  }

  {
    /* any resolution info in the file? */
    /* now set the new image's resolution info */
    if (info->read_unit != RESUNIT_NONE)
      {
      /* gimp_image_set_resolution (image, xres, yres);
         if (unit != GIMP_UNIT_PIXEL)
         gimp_image_set_unit (image, unit); */
      }
  }

  info->top->image_height = info->rows;		// TODO needed ?
  info->top->image_width = info->cols;

  if (tif) {
    long dircount = -1;  /* directory counter */
    info->aktuelles_dir = -1; /* starting on the top */
    info->pagecount = -1;

    do { // get all image informations we need

	// first info field
      if (!(tmp_info = malloc (sizeof (struct IMAGE_info_list)))) {
	g_message ("memory allocation for image information failed: quit");
	TIFFClose (tif);
	gimp_quit();
      }
      //g_message ("tmp_info = %p/%p",tmp_info,current_info);
      dircount++;
      tmp_info->top = current_info->top;		// linking to defaults
      tmp_info->parent = current_info->parent;	// link to the previous
      tmp_info->parent->next = tmp_info;	// link from the previous
      current_info->parent = tmp_info;		// relink from the chain end
      tmp_info->next = current_info;		// relink to the chain end

      tmp_info->aktuelles_dir = dircount;	// set bevore get_image_info()
      tmp_info->pagecount = dircount;
      if (!(get_image_info (tif, tmp_info))) {	// get IFD's infos
	g_message ("get_image_info(...) failed");
	TIFFClose (tif);
	gimp_quit ();
      }

      info->aktuelles_dir++ ; /* starting on the top */
      info->pagecount++ ;
    } while (TIFFReadDirectory(tif)) ;
    TIFFSetDirectory( tif, 0); // reset
  }


  if ((image = gimp_image_new ((guint)info->top->image_width,
	 (guint)info->top->image_height, info->image_type)) == -1) {
      g_message("TIFF Can't create a new image\n%dx%d %d",
	   info->top->image_width, info->top->image_height, info->image_type);
      TIFFClose (tif);
      gimp_quit ();
  }
  gimp_image_set_filename (image, filename);

  /* Here starts the directory loop.
   * We are starting with the last directory for the buttom layer 
   * and count the tiff-directories down to let the layerstack
   * in gimp grow up.
   */
  current_info = info;	// select the top of the infos of the first IFD

  for ( i = 0 ; i <= info->pagecount ; ++i ) { // select the last IFD
    current_info = current_info->next; 
  }

  for ( i = 0 ; i <= info->pagecount ; i++ ) {
      /* We only accept pages with the same colordepth and photometric indent.
       * We could also test against (TIFFTAG_SUBFILETYPE, FILETYPE_PAGE)
       * but this would be insecure.
       * To test against page-numbers would be additional. */
      if (!TIFFSetDirectory( tif, (guint16) current_info->aktuelles_dir )) {
	g_message("IFD %d not found",(int)current_info->aktuelles_dir);
	TIFFClose (tif);
	gimp_quit();
      }

      // load the IFD after certain conditions, divergine IFDs are not converted
      if ( (info->top->photomet == current_info->photomet)
	&& (info->top->bps == current_info->bps)
	&& (&current_info->cols != NULL)
	&& (&current_info->rows != NULL) )
      {
	if (!load_IFD (tif, image, layer, channel, current_info))
	{
	  g_message("load_IFD with IFD %d failed",
			(int)current_info->aktuelles_dir);
	  TIFFClose (tif);
	  gimp_quit();
	}
      }
	else if ( strcmp (current_info->pagename, "viewport")
	&& (current_info->filetype & FILETYPE_MASK) )
      {
        g_message ("TIFF IFD %d not readable\n", (int)current_info->aktuelles_dir);
      }
      current_info = current_info->parent;
  }
  TIFFClose (tif);

  gimp_image_resize ( image, (guint)current_info->top->image_width,
			     (guint)current_info->top->image_height,
                            - current_info->top->viewport_offx,
	 		    - current_info->top->viewport_offy);

  return image;
}

static gint32
load_IFD 	(	TIFF		*tif,
			gint 		image,
			gint		layer,
			channel_data	*channel,
			IMAGE_info	*current_info)
{
  gushort *redmap, *greenmap, *bluemap;
  guchar   cmap[768];

  gint   i, j;

  /* GimpParasite *parasite; */ /* not available in filmgimp */


    /* Install colormap for INDEXED images only */
    if (current_info->image_type == INDEXED)
      {
        if (!TIFFGetField (tif, TIFFTAG_COLORMAP, &redmap, &greenmap, &bluemap)) 
          {
            g_message ("TIFF Can't get colormaps\n");
	    gimp_quit ();
	  }
	// - let it here for maybe debugging
/*  if (!TIFFGetField (tif, TIFFTAG_COLORMAP, &redcolormap,
                               &greencolormap, &bluecolormap))
              {
                g_print ("error getting colormaps\n");
                gimp_quit ();
              }
            current_info->numcolors = current_info->maxval + 1;
            current_info->maxval = 255;
            current_info->grayscale = 0;
 
            for (i = 0; i < current_info->numcolors; i++)
              {
                redcolormap[i] >>= 8;
                greencolormap[i] >>= 8;
                bluecolormap[i] >>= 8;
              }
 
            if (current_info->numcolors > 256) {
                current_info->image_type = RGB;
              } else {
                current_info->image_type = INDEXED;
              }*/
        for (i = 0, j = 0; i < (1 << current_info->bps); i++) 
	  {
	    cmap[j++] = redmap[i] >> 8;
	    cmap[j++] = greenmap[i] >> 8;
	    cmap[j++] = bluemap[i] >> 8;
	  }
	gimp_image_set_cmap (image, cmap, (1 << current_info->bps));
      }

    /* Allocate channel_data for all channels, even the background layer */
    channel = g_new (channel_data, current_info->extra + 1);
    layer = gimp_layer_new (image, current_info->pagename, 
				(guint)current_info->cols, (guint)current_info->rows,
				(guint)current_info->layer_type,
				100.0, NORMAL_MODE);

    channel[0].ID= layer;
    gimp_image_add_layer (image, layer, 0);
    channel[0].drawable= gimp_drawable_get(layer);

    if (current_info->extra > 0 && !current_info->worst_case) {
      /* Add alpha channels as appropriate */
      for (i= 1; i <= (gint)current_info->extra; ++i) {
        channel[i].ID= gimp_channel_new(image, "TIFF Channel",
			(guint)current_info->cols, (guint)current_info->rows,
		        100.0, current_info->color);
        gimp_image_add_channel(image, channel[i].ID, 0);
        channel[i].drawable= gimp_drawable_get (channel[i].ID);
      }
    }

    if (current_info->worst_case) {
      load_rgba  (tif, channel, current_info);
    } else if (TIFFIsTiled(tif)) {
      load_tiles (tif, channel, current_info);
    } else { /* Load scanlines in tile_height chunks */
      load_lines (tif, channel, current_info);
    }

    if ( current_info->offx > 0 || current_info->offy > 0 ) { /* position the layer */ 
      gimp_layer_set_offsets( layer, current_info->offx, current_info->offy);
    }

  for (i= 0; !current_info->worst_case && i < (gint)current_info->extra; ++i) {
    gimp_drawable_flush (channel[i].drawable);
    gimp_drawable_detach (channel[i].drawable);
  }
  return 1;
}

static void
load_rgba (TIFF *tif, channel_data *channel, IMAGE_info	*current_info)
{
  uint32 row;
  uint32 *buffer;

  gimp_pixel_rgn_init (&(channel[0].pixel_rgn), channel[0].drawable, 0, 0,
		 current_info->cols, current_info->rows,
		 TRUE, FALSE);
  buffer =g_new(uint32,(uint32)current_info->cols * (uint32)current_info->rows);
  channel[0].pixels = (guchar*) buffer;
  if (buffer == NULL) {
    g_message("TIFF Unable to allocate temporary buffer\n");
  }
  if (!TIFFReadRGBAImage(tif, (uint32)current_info->cols,
		 (uint32)current_info->rows, buffer, 0))
    g_message("TIFF Unsupported layout, no RGBA loader\n");

  for (row = 0; row < (uint32)current_info->rows; ++row) {
    gimp_pixel_rgn_set_rect(&(channel[0].pixel_rgn),
                 channel[0].pixels + row * current_info->cols * 4, 0,
                 current_info->rows -row -1,
		 current_info->cols, 1);
    gimp_progress_update ((double) row / (double) current_info->rows);
  }
}

static void
load_tiles (TIFF *tif,                /* the tiff data */ 
            channel_data *channel,    /* some infos, where to save the samples*/
	    IMAGE_info	 *current_info)
{
  uint32 tileWidth=0, tileLength=0;
  uint32 x, y, rows, cols/*,  **tileoffsets */;
  guchar *buffer;
  double progress= 0.0, one_row;
  gushort i;

  TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tileWidth);
  TIFFGetField(tif, TIFFTAG_TILELENGTH, &tileLength);
  /* TIFFGetField(tif, TIFFTAG_TILEOFFSETS, &tileoffsets); // not handled
  g_print("TIFFTAG_TILEOFFSETS= %d\n", tileoffsets); */

  one_row = (double) tileLength / (double) current_info->rows;
  buffer = g_malloc(TIFFTileSize(tif));  // memory for the buffer and his adress

  for (i= 0; i <= current_info->extra; ++i) {
    channel[i].pixels= g_new(guchar, tileWidth * tileLength *
                                      channel[i].drawable->bpp);
  }
  for (y = 0; y < (uint32)current_info->rows; y += tileLength) {
    for (x = 0; x < (uint32)current_info->cols; x += tileWidth) {
      gimp_progress_update (progress + one_row *
                            ( (double) x / (double) current_info->cols));
      if (!TIFFReadTile(tif, buffer, x, y, 0, 0))
	g_message("TIFFReadTile failed");
      cols= MIN(current_info->cols - x, tileWidth);
      rows= MIN(current_info->rows - y, tileLength);
      if (current_info->bps == 32) {
        read_32bit(buffer, channel, current_info->photomet,
		 (gint)x, (gint)y, (gint)cols, (gint)rows, current_info->alpha,
                 (gint)current_info->extra, (gint)(tileWidth - cols));
      } else if ( (current_info->bps == 16)
	 && (current_info->sampleformat == SAMPLEFORMAT_IEEEFP
	  || current_info->sampleformat == SAMPLEFORMAT_VOID) ) {
        read_f16bit(buffer, channel, current_info->photomet,
		  (gint)x, (gint)y, (gint)cols, (gint)rows, current_info->alpha,
                  (gint)current_info->extra, (gint)(tileWidth - cols));
      } else if ( current_info->bps == 16
	 && (current_info->sampleformat == SAMPLEFORMAT_UINT) ) {
        read_u16bit(buffer, channel, current_info->photomet,
		  (gint)x, (gint)y, (gint)cols, (gint)rows, current_info->alpha,
                  (gint)current_info->extra, (gint)(tileWidth - cols));
      } else if (current_info->bps == 8) {
        read_8bit(buffer, channel, current_info->photomet,
		  (gint)x, (gint)y, (gint)cols, (gint)rows, current_info->alpha,
                  (gint)current_info->extra, (gint)(tileWidth - cols));
      } else {
        read_default(buffer, channel, current_info->bps, current_info->photomet,
		  (gint)x, (gint)y, (gint)cols, (gint)rows, current_info->alpha,
		  (gint)current_info->extra, (gint)(tileWidth - cols));
      }
    }
    progress+= one_row;
  }
  for (i= 0; i <= current_info->extra; ++i) {
    g_free(channel[i].pixels);
  }
  g_free(buffer);
}

static void
load_lines( TIFF *tif,                /* the tiff data */ 
            channel_data *channel,    /* some infos, where to save the samples*/
	    IMAGE_info	*current_info)
{
  gint32 lineSize,    /* needed for alocating memory */
         cols,        /* imageWidth in pixel */
         rows;        /* an variable for saying how many lines to load*/
         /* **stripoffsets ; */  /* determine the offset */
  guchar *buffer;
  gint32 g_row,        /* growing row */
      j_row,        /* jumping (tile-height) row */
      i, tile_height = (gint32)gimp_tile_height ();
  
  cols = current_info->cols;
  /* TODO strip offsets are'nt handled */
  /* TIFFGetField(tif, TIFFTAG_STRIPOFFSETS, &stripoffsets);
  g_print("TIFFTAG_STRIPOFFSETS= %d\n", stripoffsets);
  g_print("tif =  %d\n", tif); */

  lineSize = TIFFScanlineSize(tif);  /*lineSize should get aware of bpp and spp*/

  for (i= 0; i <= (gint32)current_info->extra; ++i) { /* for all channels define an offset */
    channel[i].pixels= g_new(guchar, tile_height * cols /* initialize   */
                       * channel[i].drawable->bpp * 16); /* tile-height chunk */
  }

  buffer = g_malloc((gulong)(lineSize * tile_height)); /* The buffer's adress */
  if (current_info->planar == PLANARCONFIG_CONTIG) {
    for (j_row = 0; j_row < current_info->rows; j_row+= tile_height ) { 
      /* jumping progress */
      gimp_progress_update ( (double) j_row / (double) current_info->rows);
      /* is the remainder smaller? - then not tile_height  */
      rows = MIN(tile_height, current_info->rows - j_row);
      /* walking along the rows within tile_height */
      for (g_row = 0; g_row < rows; ++g_row)
        /* buffer is only for one tile-height (* bpp * cols)  */
	if (!TIFFReadScanline(tif, buffer + g_row * lineSize, (uint32)(j_row + g_row), 0))
	  g_message("Scanline %d not readable", (int)g_row);
      if (current_info->bps == 32) {
	read_32bit(buffer, channel, current_info->photomet, 0, j_row,
		 cols, rows, current_info->alpha, (gint)current_info->extra, 0);
      } else if (current_info->bps == 16
	 && (current_info->sampleformat == SAMPLEFORMAT_IEEEFP
	  || current_info->sampleformat == SAMPLEFORMAT_VOID) ) {
        read_f16bit (buffer, channel, current_info->photomet, 0, j_row,
		 cols, rows, current_info->alpha, (gint)current_info->extra, 0);
      } else if (current_info->bps == 16) {
	read_u16bit (buffer, channel, current_info->photomet, 0, j_row,
		 cols, rows, current_info->alpha, (gint)current_info->extra, 0);
      } else if (current_info->bps == 8) {
	read_8bit   (buffer, channel, current_info->photomet, 0, j_row,
		 cols, rows, current_info->alpha, (gint)current_info->extra, 0);
      } else {
	read_default(buffer, channel, current_info->bps,
		                      current_info->photomet, 0, j_row,
		 cols, rows, current_info->alpha, (gint)current_info->extra, 0);
      }
    }
  } else { /* PLANARCONFIG_SEPARATE  -- Just say "No" */
    uint16 s;
    for (s = 0; s < current_info->spp; ++s) {
      for (j_row = 0; j_row < current_info->rows; j_row+= tile_height) {
	gimp_progress_update ( (double) j_row / (double) current_info->rows);
	rows = MIN(tile_height, current_info->rows - j_row);
	for (g_row = 0; g_row < rows; ++g_row) {
	  TIFFReadScanline(tif, buffer + g_row * lineSize,
		 (uint32)(j_row + g_row), s);
	}
	read_separate (buffer, channel, current_info->bps,
		 current_info->photomet, 0, j_row, cols, rows,
		 current_info->alpha, (gint)current_info->extra, (gint)s);
      }
    }
  }
  for (i= 0; i <= (gint32)current_info->extra; ++i) {
    g_free(channel[i].pixels);
  }
  g_free(buffer);
}

static void
read_32bit (guchar       *source,
	    channel_data *channel,
	    gushort       photomet,
	    gint32        startcol, /* where to start? */
	    gint32        startrow,
	    gint32        cols,     /* how many cols? */
	    gint32        rows,     /* how many rows? */
	    gint          alpha,
            gint          extra,
            gint          align)
{
  guchar *destination;
  gfloat  red32_val, green32_val, blue32_val, gray32_val, alpha32_val;
  gfloat *s_32 = (gfloat*)source; /* source don't changes any longer here */
  gfloat *d_32;  /* 32bit variant of dest(ination) */
  gint32  col, /* colums */
          g_row, /* growing row */
          i;

  /* g_print ("loading with read_32bit()"); */ // for debugging
  for (i= 0; i <= extra; ++i) {
    gimp_pixel_rgn_init (&(channel[i].pixel_rgn), channel[i].drawable,
                          startcol, startrow, cols, rows, TRUE, FALSE);
  }

#if G_BYTE_ORDER == G_LITTLE_ENDIAN
  source++; /* offset source once, to look at the high byte */
#endif

  for (g_row = 0; g_row < rows; ++g_row) { /* the rows to process */
    destination = channel[0].pixels + g_row * cols * channel[0].drawable->bpp;
    d_32 = (gfloat*)destination;

    for (i= 1; i <= extra; ++i) { /* don't leaf any extra-sample out */
      channel[i].pixel= channel[i].pixels + g_row * cols;
    }

    for (col = 0; col < cols; col++) { /* walking through all cols */
      switch (photomet) {
        case PHOTOMETRIC_MINISBLACK:
          if (alpha) {
            gray32_val= *s_32++ ;  /* assuming first is graysample */
            alpha32_val= *s_32++ ;  /* next is alphasample */
            if (alpha32_val) { /* detach associated alpha */
              gray32_val= MIN(gray32_val, alpha32_val); /* verify datas */
              *d_32++ = (gfloat)gray32_val  / (gfloat)alpha32_val ;
            } else /* there is no alpha: why pick up alpha16_val ?? */
              *d_32++ = 0.0;
              *d_32++ = alpha32_val;
          } else {
            *d_32++ = *s_32++ ;
          }
#if 0
                              for (k= 0; alpha + k < num_extra; ++k)
                              {
                                  NEXTSAMPLE;
                                  *channel[k].pixel++ = sample;
                              }
#endif
          break;

        case PHOTOMETRIC_MINISWHITE:
          if (alpha) {
            gray32_val= *s_32++ ; 
            alpha32_val= *s_32++ ; 
            if (alpha32_val) {
              gray32_val= MIN(gray32_val, alpha32_val);
              *d_32++ = ((gfloat)alpha32_val - (gfloat)gray32_val)  / (gfloat)alpha32_val ;
            } else 
              *d_32++ = 0.0;
              *d_32++ = alpha32_val;
          } else {
            *d_32++ = 1.0 - *s_32++ ; /* not tested */
          }
          break;

        case PHOTOMETRIC_PALETTE: 
          g_message("indexed images with 32bit ignored\n");
          gimp_quit ();
          break;
  
        case PHOTOMETRIC_RGB:
	case PHOTOMETRIC_CIELAB: case PHOTOMETRIC_ITULAB: /* This is a hack. */
          red32_val = *s_32++;
          green32_val = *s_32++;
          blue32_val = *s_32++; 
          if (alpha) {
            alpha32_val = *s_32++;
            if (alpha32_val) {
              /* if (red32_val > alpha32_val) 
                red32_val = alpha32_val;
              if (green32_val > alpha32_val) 
                green32_val = alpha32_val;
              if (blue32_val > alpha32_val) 
                blue32_val = alpha32_val; */
              red32_val  = MIN(red32_val, alpha32_val);
              green32_val= MIN(green32_val, alpha32_val);
              blue32_val = MIN(blue32_val, alpha32_val);
              *d_32++ = red32_val    / alpha32_val ;
              *d_32++ = green32_val / alpha32_val ;
              *d_32++ = blue32_val / alpha32_val ;
            } else {
              *d_32++ = 0.0;
              *d_32++ = 0.0;
              *d_32++ = 0.0;
            }
              *d_32++ = alpha32_val;
          } else {
            *d_32++ = red32_val;
            *d_32++ = green32_val;
            *d_32++ = blue32_val;
          }
#if 0
                            for (k= 0; alpha + k < num_extra; ++k)
                            {
                              NEXTSAMPLE;
                              *channel[k].pixel++ = sample;
                            }
#endif
          break; 

        default:
          /* This case was handled earlier */
          g_assert_not_reached();
          break; 
      }
      for (i= 1; i <= extra; ++i) {
        *channel[i].pixel++ = *source; source+= 2;
      }
    }
    if (align) {
      switch (photomet) {
        case PHOTOMETRIC_MINISBLACK:
        case PHOTOMETRIC_MINISWHITE:
        case PHOTOMETRIC_PALETTE:
          source+= align * (1 + alpha + extra) * 2;
          break;
        case PHOTOMETRIC_RGB:
	case PHOTOMETRIC_CIELAB: case PHOTOMETRIC_ITULAB: /* This is a hack. */
          source+= align * (3 + alpha + extra) * 2;
          break;
      }
    }
  }
  for (i= 0; i <= extra; ++i) {
    gimp_pixel_rgn_set_rect(&(channel[i].pixel_rgn), channel[i].pixels,
                              startcol, startrow, cols, rows);
  }
}

static void
read_f16bit (guchar       *source,
	    channel_data *channel,
	    gushort       photomet,
	    gint          startcol, /* where to start? */
	    gint          startrow,
	    gint          cols,
	    gint          rows,
	    gint          alpha,
            gint          extra,
            gint          align)
{
  guchar *destination;    /* pointer for setting target adress */
  guint16 red16_val, green16_val, blue16_val, gray16_val, alpha16_val;
  guint16 *s_16 = (guint16*)source; /* source don't changes any longer here */
  guint16 *d_16;  /* 16bit variant of dest(ination) */
  gint    col, /* colums */
          g_row, /* growing row */
          i;


  for (i= 0; i <= extra; ++i) {
    gimp_pixel_rgn_init (&(channel[i].pixel_rgn), channel[i].drawable,
                          startcol, startrow, cols, rows, TRUE, FALSE);
  }

#if G_BYTE_ORDER == G_LITTLE_ENDIAN
  source++; /* offset source once, to look at the high byte */
#endif

  for (g_row = 0; g_row < rows; ++g_row) { /* the rows to process */
    destination = channel[0].pixels + g_row * cols * channel[0].drawable->bpp;
    d_16 = (guint16*)destination;

    for (i= 1; i <= extra; ++i) { /* don't leaf any extra-sample out */
      channel[i].pixel= channel[i].pixels + g_row * cols;
    }

    for (col = 0; col < cols; col++) { /* walking through all cols */
      switch (photomet) {
        case PHOTOMETRIC_MINISBLACK:
          if (alpha) {
            gray16_val= *s_16++ ; 
            alpha16_val= *s_16++ ;
            gray16_val= MIN(gray16_val, alpha16_val);
            if (alpha16_val) { 
              *d_16++ = ((guint32)gray16_val  * 65535) / (gfloat)alpha16_val + .5;
            } else 
              *d_16++ = 0;
              *d_16++ = alpha16_val;
          } else {
            *d_16++ = *s_16++ ;
          }
          break;

        case PHOTOMETRIC_MINISWHITE:
          if (alpha) {
            gray16_val= *s_16++ ; 
            alpha16_val= *s_16++ ; 
            gray16_val= MIN(gray16_val, alpha16_val);
            if (alpha16_val) {
              *d_16++ = ((guint32)gray16_val  * 65535) / (gfloat)alpha16_val + .5;
            } else 
              *d_16++ = 0;
              *d_16++ = alpha16_val;
          } else {
            *d_16++ = ~(*s_16++) ;
          }
          break;

        case PHOTOMETRIC_PALETTE:
          g_message("indexed images with 16bit ignored\n");
          gimp_quit ();
          break;
  
        case PHOTOMETRIC_RGB:
	case PHOTOMETRIC_CIELAB: case PHOTOMETRIC_ITULAB: /* This is a hack. */
          red16_val = *s_16++;
          green16_val = *s_16++;
          blue16_val = *s_16++; 
          if (alpha) {
            alpha16_val = *s_16++;
            if (alpha16_val) {
              if (red16_val > alpha16_val) 
                red16_val = alpha16_val;
              if (green16_val > alpha16_val) 
                green16_val = alpha16_val;
              if (blue16_val > alpha16_val) 
                blue16_val = alpha16_val;
              *d_16++ = ((guint32)red16_val * 65535) / (gfloat)alpha16_val + .5;
              *d_16++ = ((guint32)green16_val * 65535) / (gfloat)alpha16_val + .5;
              *d_16++ = ((guint32)blue16_val * 65535) / (gfloat)alpha16_val + .5;
            } else {
              *d_16++ = 0;
              *d_16++ = 0;
              *d_16++ = 0;
            }
              *d_16++ = alpha16_val;
          } else {
            *d_16++ = red16_val;
            *d_16++ = green16_val;
            *d_16++ = blue16_val;
          }
#if 0
                            for (k= 0; alpha + k < num_extra; ++k)
                            {
                              NEXTSAMPLE;
                              *channel[k].pixel++ = sample;
                            }
#endif
          break; 

        default:
          /* This case was handled earlier */
          g_assert_not_reached();
      }
      for (i= 1; i <= extra; ++i) {
        *channel[i].pixel++ = *source; source+= 2;
      }
    }
    if (align) {
      switch (photomet) {
        case PHOTOMETRIC_MINISBLACK:
        case PHOTOMETRIC_MINISWHITE:
        case PHOTOMETRIC_PALETTE:
          source+= align * (1 + alpha + extra) * 2;
          break;
        case PHOTOMETRIC_RGB:
	case PHOTOMETRIC_CIELAB: case PHOTOMETRIC_ITULAB: /* This is a hack. */
          source+= align * (3 + alpha + extra) * 2;
          break;
      }
    }
  }
  for (i= 0; i <= extra; ++i) {
    gimp_pixel_rgn_set_rect(&(channel[i].pixel_rgn), channel[i].pixels,
                              startcol, startrow, cols, rows);
  }
}

static void
read_u16bit (guchar       *source,
	    channel_data *channel,
	    gushort       photomet,
	    gint          startcol, /* where to start? */
	    gint          startrow,
	    gint          cols,
	    gint          rows,
	    gint          alpha,
            gint          extra,
            gint          align)
{
  guchar *destination;    /* pointer for setting target adress */
  guint16 red16_val, green16_val, blue16_val, gray16_val, alpha16_val;
  guint16 *s_16 = (guint16*)source; /* source don't changes any longer here */
  guint16 *d_16;  /* 16bit variant of dest(ination) */
  gint    col, /* colums */
          g_row, /* growing row */
          i;


  for (i= 0; i <= extra; ++i) {
    gimp_pixel_rgn_init (&(channel[i].pixel_rgn), channel[i].drawable,
                          startcol, startrow, cols, rows, TRUE, FALSE);
  }

#if G_BYTE_ORDER == G_LITTLE_ENDIAN
  source++; /* offset source once, to look at the high byte */
#endif

  for (g_row = 0; g_row < rows; ++g_row) { /* the rows to process */
    destination = channel[0].pixels + g_row * cols * channel[0].drawable->bpp;
    d_16 = (guint16*)destination;

    for (i= 1; i <= extra; ++i) { /* don't leaf any extra-sample out */
      channel[i].pixel= channel[i].pixels + g_row * cols;
    }

    for (col = 0; col < cols; col++) { /* walking through all cols */
      switch (photomet) {
        case PHOTOMETRIC_MINISBLACK:
          if (alpha) {
            gray16_val= *s_16++ ; 
            alpha16_val= *s_16++ ;
            gray16_val= MIN(gray16_val, alpha16_val);
            if (alpha16_val) { 
              *d_16++ = ((guint32)gray16_val  * 65535) / (gfloat)alpha16_val + .5;
            } else 
              *d_16++ = 0;
              *d_16++ = alpha16_val;
          } else {
            *d_16++ = *s_16++ ;
          }
          break;

        case PHOTOMETRIC_MINISWHITE:
          if (alpha) {
            gray16_val= *s_16++ ; 
            alpha16_val= *s_16++ ; 
            gray16_val= MIN(gray16_val, alpha16_val);
            if (alpha16_val) {
              *d_16++ = ((guint32)gray16_val  * 65535) / (gfloat)alpha16_val + .5;
            } else 
              *d_16++ = 0;
              *d_16++ = alpha16_val;
          } else {
            *d_16++ = ~(*s_16++) ;
          }
          break;

        case PHOTOMETRIC_PALETTE:
          g_message("indexed images with 16bit ignored\n");
          gimp_quit ();
          break;
  
        case PHOTOMETRIC_RGB:
	case PHOTOMETRIC_CIELAB: case PHOTOMETRIC_ITULAB: /* This is a hack. */
          red16_val = *s_16++;
          green16_val = *s_16++;
          blue16_val = *s_16++; 
          if (alpha) {
            alpha16_val = *s_16++;
            if (alpha16_val) {
              if (red16_val > alpha16_val) 
                red16_val = alpha16_val;
              if (green16_val > alpha16_val) 
                green16_val = alpha16_val;
              if (blue16_val > alpha16_val) 
                blue16_val = alpha16_val;
              *d_16++ = ((guint32)red16_val * 65535) / (gfloat)alpha16_val + .5;
              *d_16++ = ((guint32)green16_val * 65535) / (gfloat)alpha16_val + .5;
              *d_16++ = ((guint32)blue16_val * 65535) / (gfloat)alpha16_val + .5;
            } else {
              *d_16++ = 0;
              *d_16++ = 0;
              *d_16++ = 0;
            }
              *d_16++ = alpha16_val;
          } else {
            *d_16++ = red16_val;
            *d_16++ = green16_val;
            *d_16++ = blue16_val;
          }
#if 0
                            for (k= 0; alpha + k < num_extra; ++k)
                            {
                              NEXTSAMPLE;
                              *channel[k].pixel++ = sample;
                            }
#endif
          break; 

        default:
          /* This case was handled earlier */
          g_assert_not_reached();
      }
      for (i= 1; i <= extra; ++i) {
        *channel[i].pixel++ = *source; source+= 2;
      }
    }
    if (align) {
      switch (photomet) {
        case PHOTOMETRIC_MINISBLACK:
        case PHOTOMETRIC_MINISWHITE:
        case PHOTOMETRIC_PALETTE:
          source+= align * (1 + alpha + extra) * 2;
          break;
        case PHOTOMETRIC_RGB:
	case PHOTOMETRIC_CIELAB: case PHOTOMETRIC_ITULAB: /* This is a hack. */
          source+= align * (3 + alpha + extra) * 2;
          break;
      }
    }
  }
  for (i= 0; i <= extra; ++i) {
    gimp_pixel_rgn_set_rect(&(channel[i].pixel_rgn), channel[i].pixels,
                              startcol, startrow, cols, rows);
  }
}

static void
read_8bit (guchar       *source,
	   channel_data *channel,
	   gushort       photomet,
	   gint32        startcol,
	   gint32        startrow,
	   gint32        cols,
	   gint32        rows,
	   gint          alpha,
	   gint          extra,
	   gint          align)
{
  guchar *dest;
  gint    gray_val, red_val, green_val, blue_val, alpha_val;
  gint32  col, row, i;

  //g_message("read_8bit(%p,%p,%d,%d,%d,%d,%d,%d,%d,%d)",source, channel, photomet, startcol, startrow, cols, rows, alpha, extra, align);
  for (i= 0; i <= extra; ++i) {
    gimp_pixel_rgn_init (&(channel[i].pixel_rgn), channel[i].drawable,
                          startcol, startrow, cols, rows, TRUE, FALSE);
  }

  for (row = 0; row < rows; ++row) {
    dest= channel[0].pixels + row * cols * channel[0].drawable->bpp;

    for (i= 1; i <= extra; ++i) {
      channel[i].pixel= channel[i].pixels + row * cols;
    }

    for (col = 0; col < cols; col++) {
      switch (photomet) {
        case PHOTOMETRIC_MINISBLACK:
          if (alpha) {
            gray_val= *source++;
            alpha_val= *source++;
            gray_val= MIN(gray_val, alpha_val);
            if (alpha_val)
              *dest++ = gray_val * 255 / alpha_val;
            else
              *dest++ = 0;
            *dest++ = alpha_val;
          } else {
            *dest++ = *source++;
          }
          break;

        case PHOTOMETRIC_MINISWHITE:
          if (alpha) {
            gray_val= *source++;
            alpha_val= *source++;
            gray_val= MIN(gray_val, alpha_val);
            if (alpha_val)
              *dest++ = ((alpha_val - gray_val) * 255) / alpha_val;
            else
              *dest++ = 0;
            *dest++ = alpha_val;
          } else {
            *dest++ = ~(*source++);
          }
          break;

        case PHOTOMETRIC_PALETTE:
          *dest++= *source++;
          if (alpha) *dest++= *source++;
          break;
  
        case PHOTOMETRIC_RGB:
	case PHOTOMETRIC_CIELAB: case PHOTOMETRIC_ITULAB: /* This is a hack. */
          if (alpha) {
            red_val= *source++;
            green_val= *source++;
            blue_val= *source++;
            alpha_val= *source++;
            red_val= MIN(red_val, alpha_val);
            blue_val= MIN(blue_val, alpha_val);
            green_val= MIN(green_val, alpha_val);
            if (alpha_val) {
              *dest++ = (red_val * 255) / alpha_val;
              *dest++ = (green_val * 255) / alpha_val;
              *dest++ = (blue_val * 255) / alpha_val;
            } else {
              *dest++ = 0;
              *dest++ = 0;
              *dest++ = 0;
	    }
	    *dest++ = alpha_val;
	  } else {
	    *dest++ = *source++;
	    *dest++ = *source++;
	    *dest++ = *source++;
	  }
          break;

        default:
          /* This case was handled earlier */
          g_assert_not_reached();
      }
      for (i= 1; i <= extra; ++i) {
        *channel[i].pixel++ = *source++;
      }
    }
    if (align) {
      switch (photomet) {
        case PHOTOMETRIC_MINISBLACK:
        case PHOTOMETRIC_MINISWHITE:
        case PHOTOMETRIC_PALETTE:
          source+= align * (1 + alpha + extra);
          break;
        case PHOTOMETRIC_RGB:
	case PHOTOMETRIC_CIELAB: case PHOTOMETRIC_ITULAB: /* This is a hack. */
          source+= align * (3 + alpha + extra);
          break;
      }
    }
  }
  for (i= 0; i <= extra; ++i) {
    gimp_pixel_rgn_set_rect(&(channel[i].pixel_rgn), channel[i].pixels,
                              startcol, startrow, cols, rows);
  }
}

/* Step through all <= 8-bit samples in an image */

#define NEXTSAMPLE(var)                       \
  {                                           \
      if (bitsleft == 0)                      \
      {                                       \
	  source++;                           \
	  bitsleft = 8;                       \
      }                                       \
      bitsleft -= bps;                        \
      var = ( *source >> bitsleft ) & maxval; \
  }

static void
read_default (guchar       *source,
	      channel_data *channel,
	      gushort       bps,
	      gushort       photomet,
	      gint32       startcol,
	      gint32        startrow,
	      gint32        cols,
	      gint32        rows,
	      gint          alpha,
	      gint          extra,
              gint          align)
{
  guchar *dest;
  gint    gray_val, red_val, green_val, blue_val, alpha_val;
  gint32  col, row, i;
  gint    bitsleft = 8, maxval = (1 << bps) - 1;

  for (i= 0; i <= extra; ++i) {
    gimp_pixel_rgn_init (&(channel[i].pixel_rgn), channel[i].drawable,
		startcol, startrow, cols, rows, TRUE, FALSE);
  }

  for (row = 0; row < rows; ++row) {
    dest= channel[0].pixels + row * cols * channel[0].drawable->bpp;

    for (i= 1; i <= extra; ++i) {
      channel[i].pixel= channel[i].pixels + row * cols;
    }

    for (col = 0; col < cols; col++) {
      switch (photomet) {
        case PHOTOMETRIC_MINISBLACK:
          NEXTSAMPLE(gray_val);
          if (alpha) {
            NEXTSAMPLE(alpha_val);
            gray_val= MIN(gray_val, alpha_val);
            if (alpha_val)
              *dest++ = (gray_val * 65025) / (alpha_val * maxval);
            else
              *dest++ = 0;
            *dest++ = alpha_val;
          } else {
            *dest++ = (gray_val * 255) / maxval;
          }
          break;

        case PHOTOMETRIC_MINISWHITE:
          NEXTSAMPLE(gray_val);
          if (alpha) {
            NEXTSAMPLE(alpha_val);
            gray_val= MIN(gray_val, alpha_val);
            if (alpha_val)
              *dest++ = ((maxval - gray_val) * 65025) / (alpha_val * maxval);
            else
              *dest++ = 0;
            *dest++ = alpha_val;
          } else {
            *dest++ = ((maxval - gray_val) * 255) / maxval;
          }
          break;

        case PHOTOMETRIC_PALETTE:
          NEXTSAMPLE(*dest++);
          if (alpha) {
            NEXTSAMPLE(*dest++);
          }
          break;
  
        case PHOTOMETRIC_RGB:
	case PHOTOMETRIC_CIELAB: case PHOTOMETRIC_ITULAB: /* This is a hack. */
          NEXTSAMPLE(red_val)
          NEXTSAMPLE(green_val)
          NEXTSAMPLE(blue_val)
          if (alpha) {
            NEXTSAMPLE(alpha_val)
            red_val= MIN(red_val, alpha_val);
            blue_val= MIN(blue_val, alpha_val);
            green_val= MIN(green_val, alpha_val);
            if (alpha_val) {
              *dest++ = (red_val * 255) / alpha_val;
              *dest++ = (green_val * 255) / alpha_val;
              *dest++ = (blue_val * 255) / alpha_val;
            } else {
              *dest++ = 0;
              *dest++ = 0;
              *dest++ = 0;
	    }
	    *dest++ = alpha_val;
	  } else {
	    *dest++ = red_val;
	    *dest++ = green_val;
	    *dest++ = blue_val;
	  }
          break;

        default:
          /* This case was handled earlier */
          g_assert_not_reached();
      }
      for (i= 1; i <= extra; ++i) {
        NEXTSAMPLE(alpha_val);
        *channel[i].pixel++ = alpha_val;
      }
    }
    if (align) {
      switch (photomet) {
        case PHOTOMETRIC_MINISBLACK:
        case PHOTOMETRIC_MINISWHITE:
        case PHOTOMETRIC_PALETTE:
          for (i= 0; i < align * (1 + alpha + extra); ++i) {
            NEXTSAMPLE(alpha_val);
          }
          break;
        case PHOTOMETRIC_RGB:
	case PHOTOMETRIC_CIELAB: case PHOTOMETRIC_ITULAB: /* This is a hack. */
          for (i= 0; i < align * (3 + alpha + extra); ++i) {
            NEXTSAMPLE(alpha_val);
          }
          break;
      }
    }
    bitsleft= 0;
  }
  for (i= 0; i <= extra; ++i) {
    gimp_pixel_rgn_set_rect(&(channel[i].pixel_rgn), channel[i].pixels,
                              startcol, startrow, cols, rows);
  }
}

static void
read_separate (guchar       *source,
	       channel_data *channel,
               gushort       bps,
	       gushort       photomet,
               gint32        startcol,
	       gint32        startrow,
	       gint32        cols,
	       gint32        rows,
               gint          alpha,
	       gint          extra,
	       gint          sample)
{
  guchar *dest;
  gint32  col, row, c;
  gint    bitsleft = 8, maxval = (1 << bps) - 1;

  if (bps > 8) {
    g_message("TIFF Unsupported layout\n");
    gimp_quit();
  }

  if (sample < (gint)channel[0].drawable->bpp) {
    c = 0;
  } else {
    c = (sample - channel[0].drawable->bpp) + 4;
    photomet = PHOTOMETRIC_MINISBLACK;
  }

  gimp_pixel_rgn_init (&(channel[c].pixel_rgn), channel[c].drawable,
                         startcol, startrow, cols, rows, TRUE, FALSE);

  gimp_pixel_rgn_get_rect(&(channel[c].pixel_rgn), channel[c].pixels,
                            startcol, startrow, cols, rows);
  for (row = 0; row < rows; ++row) {
    dest = channel[c].pixels + row * cols * channel[c].drawable->bpp;
    if (c == 0) {
      for (col = 0; col < cols; ++col) {
        NEXTSAMPLE(dest[col * channel[0].drawable->bpp + sample]);
      }
    } else {
      for (col = 0; col < cols; ++col)
        NEXTSAMPLE(dest[col]);
    }
  }
  gimp_pixel_rgn_set_rect(&(channel[c].pixel_rgn), channel[c].pixels,
                            startcol, startrow, cols, rows);
}




/*
** pnmtotiff.c - converts a portable anymap to a Tagged Image File
**
** Derived by Jef Poskanzer from ras2tif.c, which is:
**
** Copyright (c) 1990 by Sun Microsystems, Inc.
**
** Author: Patrick J. Naughton
** naughton@wind.sun.com
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted,
** provided that the above copyright notice appear in all copies and that
** both that copyright notice and this permission notice appear in
** supporting documentation.
**
** This file is provided AS IS with no warranties of any kind.  The author
** shall have no liability with respect to the infringement of copyrights,
** trade secrets or any patents by this file or any part thereof.  In no
** event will the author be liable for any lost revenue or profits or
** other special, indirect and consequential damages.
*/

static gint
save_image (char   *filename,
	    gint32  image_ID,
	    gint32  drawable_ID)
{
  unsigned short red[256];
  unsigned short grn[256];
  unsigned short blu[256];
  guint32  cols=0, rows=0;
  guint32  row, col;
  gfloat   xpos = (gfloat)0.0, ypos = (gfloat)0.0;  /* relative position */
  gfloat   res_x, res_y; /* resolution */
  gint	   offx = 0,		   /* offset in pixel for x */
           offy = 0,		  /* offset in pixel for y */
           off_max_x = 0,	 /* maximum offset */
  	   off_max_y = 0;
  guint    width, height;	/* view sizes */
  long g3options;
  long rowsperstrip;
  unsigned short compression;
  unsigned short fillorder;
  unsigned short extra_samples[1];
  int alpha;
  uint32 predictor;
  uint32 photometric=0;
  uint32 samplesperpixel=0;
  uint32 bitspersample=0;
  uint32 bytesperrow;
  guchar *t, *src, *data = 0;
  guchar *cmap;
  int colors;
  int success = 0;
  GDrawable *drawable = 0;
  GDrawableType drawable_type;
  GPixelRgn pixel_rgn;
  int tile_height;
  int y, yend;
  char name_[256] = " ";
  char *name = &name_[0];
  gint nlayers, i;
  gint visible=0;
  gint32 *layers;
  long aktuelles_dir = 0;
 
  compression = (unsigned short int)tsvals.compression;
  fillorder = (unsigned short int)tsvals.fillorder;
  TIFF *tif;	// should be the last one, because it overwrites the other variables
  guint32 distance[65535]; 		// TODO debug memory allocation

  for ( i=0 ; i < 65535 ; i++ ) { 	// for debugging
    distance[i] = 0;
  }
 
  res_x = (gfloat)72.0;
  res_y = (gfloat)72.0; /* resolution */
  g3options = 0;
  predictor = 0;
  rowsperstrip = 0;

  //name = malloc (strlen (filename) + 11);
  sprintf (name, "Saving %s:", filename);
  name = g_strdup_printf( ("Saving %s:"), filename);
  gimp_progress_init (name);

  nlayers = -1;

  /* Die Zahl der Layer / number of layers */
  layers = gimp_image_get_layers (image_ID, &nlayers);
  //g_message ("nlayers = %d", nlayers);

  if ( nlayers == -1 ) {
   g_message("nothing image data to save");
   gimp_quit ();
  }

  for ( i=0; i< nlayers; i++ ) {
    drawable_ID = layers[i];
    gimp_drawable_offsets( drawable_ID, &offx, &offy);
    if ( offx < off_max_x )
      off_max_x = offx;
    if ( offy < off_max_y )
      off_max_y = offy;
  }  
  //g_message("max_x=%d max_y=%d", off_max_x, off_max_y);   

  tif = TIFFOpen (filename, "w");
  if (!tif)
    {
      g_print ("can't open \"%s\"\n", filename);
      return 0;
    }

  for ( i=0 ; i < nlayers; i++) {  /* from top to buttom like in dokuments*/
  /*for ( i=nlayers ; i >= 0; i--) { *//* from buttom to top like in animations*/

    /* Ist unser Layer sichbar? / is this layer visible? */
    visible = gimp_layer_get_visible( layers[i]);
    if ( visible == 1 ) {
      /* switch for TIFFTAG_SAMPLEFORMAT ; default = 1 = uint */
      int sampleformat = SAMPLEFORMAT_UINT; 
      drawable_ID = layers[i];


      TIFFCreateDirectory (tif);
      aktuelles_dir++ ;

      drawable = gimp_drawable_get (drawable_ID);
      drawable_type = gimp_drawable_type (drawable_ID);
      gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, (int)drawable->width, (int)drawable->height, FALSE, FALSE);

      cols = drawable->width;
      rows = drawable->height;

      switch (drawable_type)
        {
        case RGB_IMAGE:
          samplesperpixel = 3;
          bitspersample = 8;
          photometric = PHOTOMETRIC_RGB;
          bytesperrow = cols * 3;
          alpha = 0;
          break;
        case GRAY_IMAGE:
          samplesperpixel = 1;
          bitspersample = 8;
          photometric = PHOTOMETRIC_MINISBLACK;
          bytesperrow = cols;
          alpha = 0;
          break;
        case RGBA_IMAGE:
          samplesperpixel = 4;
          bitspersample = 8;
          photometric = PHOTOMETRIC_RGB;
          bytesperrow = cols * 4;
          alpha = 1;
          break;
        case GRAYA_IMAGE:
          samplesperpixel = 2;
          bitspersample = 8;
          photometric = PHOTOMETRIC_MINISBLACK;
          bytesperrow = cols * 2;
          alpha = 1;
          break;
        case INDEXED_IMAGE:
          samplesperpixel = 1;
          bitspersample = 8;
          photometric = PHOTOMETRIC_PALETTE;
          bytesperrow = cols;
          alpha = 0;

          cmap = gimp_image_get_cmap (image_ID, &colors);

          for (i = 0; i < colors; i++)
	    {
	      red[i] = *cmap++ << 8;
	      grn[i] = *cmap++ << 8;
	      blu[i] = *cmap++ << 8;
	    }
          break;
        case INDEXEDA_IMAGE:
          g_print ("tiff save_image: can't save INDEXEDA_IMAGE\n");
	  TIFFClose (tif);
	  free (layers);
	  free (pixel_rgn.data);
	  free (pixel_rgn.drawable);
	  free (drawable);
          return 0;
        case U16_RGB_IMAGE:
          samplesperpixel = 3;
          bitspersample = 16;
          photometric = PHOTOMETRIC_RGB;
          bytesperrow = cols * 6;
          alpha = 0;
          break;
        case U16_GRAY_IMAGE:
          samplesperpixel = 1;
          bitspersample = 16;
          photometric = PHOTOMETRIC_MINISBLACK;
          bytesperrow = cols * 2;
          alpha = 0;
          break;
        case U16_RGBA_IMAGE:
          samplesperpixel = 4;
          bitspersample = 16;
          photometric = PHOTOMETRIC_RGB;
          bytesperrow = cols * 8;
          alpha = 1;
          break;
        case U16_GRAYA_IMAGE:
          samplesperpixel = 2;
          bitspersample = 16;
          photometric = PHOTOMETRIC_MINISBLACK;
          bytesperrow = cols * 4;
          alpha = 1;
          break;
        case U16_INDEXED_IMAGE:
          g_print ("tiff save_image: U16_INDEXED_IMAGE save not implemented\n");
	  TIFFClose (tif);
	  //free (layers);
	  //free (pixel_rgn.data);
	  //free (pixel_rgn.drawable);
	  //free (drawable);
          return 0;
/* 
          samplesperpixel = 1;
          bitspersample = 16;
          photometric = PHOTOMETRIC_PALETTE;
          bytesperrow = cols * 2;
          alpha = 0;
          cmap = gimp_image_get_cmap (image_ID, &colors);

          for (i = 0; i < colors; i++)
	    {
	      red[i] = *cmap++ << 8;
	      grn[i] = *cmap++ << 8;
	      blu[i] = *cmap++ << 8;
	    }
*/
          break;
        case U16_INDEXEDA_IMAGE:
          g_print ("tiff save_image: can't save U16_INDEXEDA_IMAGE\n");
	  TIFFClose (tif);
	  //free (layers);
	  //free (pixel_rgn.data);
	  //free (pixel_rgn.drawable);
	  //free (drawable);
          return 0;
       case FLOAT16_GRAY_IMAGE:
          g_message("saving 16bit float data is not standard\nbetter switch to 32bit float format");
          samplesperpixel = 1;
          bitspersample = 16;
          sampleformat = SAMPLEFORMAT_VOID;
          photometric = PHOTOMETRIC_MINISBLACK;
          bytesperrow = cols * 2;
          alpha = 0;
          break;
        case FLOAT16_RGB_IMAGE:
          g_message("saving 16bit float data is not standard\nbetter switch to 32bit float format");
          samplesperpixel = 3;
          bitspersample = 16;
          sampleformat = SAMPLEFORMAT_VOID;
          photometric = PHOTOMETRIC_RGB;
          bytesperrow = cols * 6;
          alpha = 0;
          break;
       case FLOAT16_GRAYA_IMAGE:
	  /*TIFFClose (tif);
	  free (layers);
	  free (pixel_rgn.data);
	  free (pixel_rgn.drawable);
	  free (drawable);
	  return (0);*/
          g_message("saving 16bit float data is not standard\nbetter switch to 32bit float format");
          bitspersample = 16;
          alpha = 1;
          samplesperpixel = 1 + alpha;	// 2 TODO
          sampleformat = SAMPLEFORMAT_VOID;
          photometric = PHOTOMETRIC_MINISBLACK;
          bytesperrow = cols * (bitspersample/8 + bitspersample/8*alpha) ;
          break;
        case FLOAT16_RGBA_IMAGE:
	  /*TIFFClose (tif);
	  free (layers);
	  free (pixel_rgn.data);
	  free (pixel_rgn.drawable);
	  free (drawable);
	  TIFFClose (tif);
          return 0;*/
          g_message("saving 16bit float data is not standard\nbetter switch to 32bit float format");
          bitspersample = 16;
          alpha = 1;
          samplesperpixel = 3 + alpha; // 4
          sampleformat = SAMPLEFORMAT_VOID;
          photometric = PHOTOMETRIC_RGB;
          bytesperrow = cols * (bitspersample*3/8 + bitspersample/8*alpha); // 8
          break;
        case FLOAT_RGB_IMAGE:
          samplesperpixel = 3;
          bitspersample = 32;
          sampleformat = SAMPLEFORMAT_IEEEFP;
          photometric = PHOTOMETRIC_RGB;
          bytesperrow = cols * 12;
          alpha = 0;
          break;
       case FLOAT_GRAY_IMAGE:
          samplesperpixel = 1;
          bitspersample = 32;
          sampleformat = SAMPLEFORMAT_IEEEFP;
          photometric = PHOTOMETRIC_MINISBLACK;
          bytesperrow = cols * 4;
          alpha = 0;
          break;
        case FLOAT_RGBA_IMAGE:
          samplesperpixel = 4;
          bitspersample = 32;
          sampleformat = SAMPLEFORMAT_IEEEFP;
          photometric = PHOTOMETRIC_RGB;
          bytesperrow = cols * 16;
          alpha = 1;
          break;
        case FLOAT_GRAYA_IMAGE:
          samplesperpixel = 2;
          bitspersample = 32;
          sampleformat = SAMPLEFORMAT_IEEEFP;
          photometric = PHOTOMETRIC_MINISBLACK;
          bytesperrow = cols * 8;
          alpha = 1;
          break;
        default:
          g_print ("Can't save this image type\n");
	  TIFFClose (tif);
	  free (layers);
	  free (pixel_rgn.data);
	  free (pixel_rgn.drawable);
	  free (drawable);
          return 0;
        }

      if (rowsperstrip == 0)
        rowsperstrip = (8 * 1024) / bytesperrow;
      if (rowsperstrip == 0)
        rowsperstrip = 1;

      /* Set TIFF parameters. */
      TIFFSetField (tif, TIFFTAG_IMAGEWIDTH, cols);
      TIFFSetField (tif, TIFFTAG_IMAGELENGTH, rows);
      TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE, bitspersample);
      TIFFSetField (tif, TIFFTAG_SAMPLEFORMAT, sampleformat);
      TIFFSetField (tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
      TIFFSetField (tif, TIFFTAG_COMPRESSION, compression);
      if ((compression == COMPRESSION_LZW) && (predictor != 0))
        TIFFSetField (tif, TIFFTAG_PREDICTOR, predictor);
      if (alpha != 0) {
          extra_samples [0] = EXTRASAMPLE_ASSOCALPHA;
          TIFFSetField (tif, TIFFTAG_EXTRASAMPLES, 1, extra_samples);
      }
      TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, photometric);
      TIFFSetField (tif, TIFFTAG_FILLORDER, fillorder);
      TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
   /* TIFFSetField( tif, TIFFTAG_STRIPBYTECOUNTS, rows / rowsperstrip ); */
      TIFFSetField (tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
      TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
      if (gimp_drawable_type (drawable_ID) == INDEXED_IMAGE) {
          TIFFSetField (tif, TIFFTAG_COLORMAP, red, grn, blu);
      }
      /* Handle offsets and resolution */ 
      TIFFSetField (tif, TIFFTAG_XRESOLUTION, res_x);
      TIFFSetField (tif, TIFFTAG_YRESOLUTION, res_y);
      gimp_drawable_offsets( drawable_ID, &offx, &offy);
      if ( offx != off_max_x ) {
        xpos = (gfloat)(offx - off_max_x) / res_x;
        TIFFSetField (tif, TIFFTAG_XPOSITION, xpos);
      }
      if ( offy != off_max_y ) {
        ypos = (gfloat)(offy - off_max_y) / res_y;
        TIFFSetField (tif, TIFFTAG_YPOSITION, ypos);
      }
      
      TIFFSetField (tif, TIFFTAG_DOCUMENTNAME, /*g_basename (*/filename/*)*/);
      TIFFSetField (tif, TIFFTAG_PAGENAME, gimp_layer_get_name (drawable_ID) );
      TIFFSetField (tif, TIFFTAG_IMAGEDESCRIPTION, "created with cinepaint");
      TIFFSetField (tif, TIFFTAG_SOFTWARE, plug_in_version);
      //name = malloc (64);
      sprintf( name, "saved by user: %s", g_get_real_name () );
      TIFFSetField (tif, TIFFTAG_ARTIST, name);
      sprintf( name, "%s",g_getenv ("HOSTNAME"));	//TODO works on mac/win?
      TIFFSetField (tif, TIFFTAG_HOSTCOMPUTER, name);
      {
	time_t	cutime;         /* Time since epoch */
	struct tm	*gmt;
	gchar time_str[24];

	cutime = time(NULL); /* time right NOW */
	gmt = gmtime(&cutime);
	strftime(time_str, 24, "%Y/%m/%d %H:%M:%S", gmt);
	TIFFSetField (tif, TIFFTAG_DATETIME, time_str);
      }
      if ( nlayers > 1 ) { /* Tags for multipages */
        TIFFSetField (tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
        TIFFSetField (tif, TIFFTAG_PAGENUMBER,
                      (unsigned short) aktuelles_dir, (unsigned short) nlayers);
      }
                    
      /* array to rearrange data */
      tile_height = gimp_tile_height ();
      src = g_new (guchar, bytesperrow * tile_height);
      data = g_new (guchar, bytesperrow);

      /* Now write the TIFF data. */
      for (y = 0; y < rows; y = yend)
        {
          yend = y + tile_height;
          yend = MIN (yend, rows);

          gimp_pixel_rgn_get_rect (&pixel_rgn, src, 0, y, cols, yend - y);

          for (row = y; row < yend; row++)
	    {
	      t = src + bytesperrow * (row - y);

	      switch (drawable_type)
	        {
	        case INDEXED_IMAGE:
	          success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
	          break;
	        case GRAY_IMAGE:
	          success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
	          break;
	        case GRAYA_IMAGE:
	          for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel)
		    {
		      /* pre-multiply gray by alpha */
		      data[col + 0] = (t[col + 0] * t[col + 1]) / 255.0 + .5;
		      data[col + 1] = t[col + 1];  /* alpha channel */
		    }
	          success = (TIFFWriteScanline (tif, data, row, 0) >= 0);
	          break;
	        case RGB_IMAGE:
	          success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
	          break;
	        case RGBA_IMAGE:
	          for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel)
		    {
		      /* pre-multiply rgb by alpha */
		      data[col+0] = t[col + 0] * t[col + 3] / 255.0 + .5;
		      data[col+1] = t[col + 1] * t[col + 3] / 255.0 + .5;
		      data[col+2] = t[col + 2] * t[col + 3] / 255.0 + .5;
		      data[col+3] = t[col + 3];  /* alpha channel */
		    }
	          success = (TIFFWriteScanline (tif, data, row, 0) >= 0);
                  /* g_print("    TIFFWriteScanline = %d\n", success); */
	          break;
	        case U16_INDEXED_IMAGE:
	          break;
	        case U16_GRAY_IMAGE:
	          success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
	          break;
	        case U16_GRAYA_IMAGE:
		    { 
		      guint16 * s = (guint16*)t;
		      guint16 * d = (guint16*)data;
                      gint32 temp;
		      for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel)
		        {
		          /* pre-multiply gray by alpha */
		          d[col + 0] = INT_MULT_16 ((gint32)s[col + 0], s[col + 1], temp);
		          d[col + 1] = s[col + 1];  /* alpha channel */
		        }
		    }
	          success = (TIFFWriteScanline (tif, data, row, 0) >= 0);
	          break;
	        case U16_RGB_IMAGE:
	          success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
	          break;
	        case U16_RGBA_IMAGE:
		    {
		      guint16 * s = (guint16*)t;
		      guint16 * d = (guint16*)data;
		      guint32 temp;
		      for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel)
		        {
		          /* pre-multiply rgb by alpha */
		          d[col + 0] = INT_MULT_16 ((guint32)s[col + 0], s[col + 3], temp);
		          d[col + 1] = INT_MULT_16 ((guint32)s[col + 1], s[col + 3], temp);
		          d[col + 2] = INT_MULT_16 ((guint32)s[col + 2], s[col + 3], temp);
		          d[col+3] = s[col + 3];  /* alpha channel */
		        }
		    }
	          success = (TIFFWriteScanline (tif, data, row, 0) >= 0);
	          break;
	        case FLOAT16_GRAY_IMAGE:
	          success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
	          break;
	        case FLOAT16_GRAYA_IMAGE:
		    { 
		      gfloat *s = (gfloat*)t;    /* source */
                      gfloat *d = (gfloat*)data; /* destination */
		      gfloat tmp;
		      ShortsFloat u, u_a;		// temporary floats
		      for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel)
		        { // TODO float16
		          /* pre-multiply gray by alpha */
			  tmp = FLT( s[col + 0],u) * FLT( s[col + 1],u_a);
			  d[col + 0] = FLT16( tmp,u );
		          d[col + 1] = s[col + 1];  /* alpha channel */
		        }
		    }
	          success = (TIFFWriteScanline (tif, data, row, 0) >= 0);
	          break;
	        case FLOAT16_RGB_IMAGE:
                  success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
	          break;
	        case FLOAT16_RGBA_IMAGE:
		    {
		      gfloat *s = (gfloat*)t;
                      gfloat *d = (gfloat*)data;
		      gfloat tmp;
		      ShortsFloat u, u_a;
		      for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel)
		        {
		          /* pre-multiply rgb by alpha */
			  tmp = FLT( s[col + 0],u) * FLT( s[col + 3],u_a);
			  d[col + 0] = FLT16( tmp,u );
			  tmp = FLT( s[col + 1],u) * FLT( s[col + 3],u_a);
			  d[col + 1] = FLT16( tmp,u );
			  tmp = FLT( s[col + 2],u) * FLT( s[col + 3],u_a);
			  d[col + 2] = FLT16( tmp,u );
		          d[col+3] = s[col + 3];  /* alpha channel */
		        }
		    }
	          success = (TIFFWriteScanline (tif, data, row, 0) >= 0);
	          break;
	        case FLOAT_GRAY_IMAGE:
	          success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
	          break;
	        case FLOAT_GRAYA_IMAGE:
		    {
		      gfloat *s = (gfloat*)t;
                      gfloat *d = (gfloat*)data;
		      for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel)
		        {
		          /* pre-multiply gray by alpha */
		          d[col + 0] = s[col + 0] * s[col + 1];
		          d[col + 1] = s[col + 1];  /* alpha channel */
		        }
		    }
	          success = (TIFFWriteScanline (tif, data, row, 0) >= 0);
	          break;
	        case FLOAT_RGB_IMAGE:
                  success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
	          break;
	        case FLOAT_RGBA_IMAGE:
		    {
		      gfloat *s = (gfloat*)t;
                      gfloat *d = (gfloat*)data;
		      for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel)
		        {
		          /* pre-multiply rgb by alpha */
		          d[col+0] = s[col + 0] * s[col + 3];
		          d[col+1] = s[col + 1] * s[col + 3];
		          d[col+2] = s[col + 2] * s[col + 3];
		          d[col+3] = s[col + 3];  /* alpha channel */
		        }
		    }
	          success = (TIFFWriteScanline (tif, data, row, 0) >= 0);
	          break;
	        default:
	          break;
	        }

	      if (success == 0)
	        {
	          g_print ("failed a scanline write on row %d\n", row);
	          return 0;
	        }
	    }

	//g_message("M2:%d:%d",drawable_type,row);
          gimp_progress_update ((double) row / (double) rows);
        }

      (void) TIFFWriteDirectory (tif); /* for multi-pages */

      TIFFFlushData (tif);
  
    }
  }
  // create dummy directory for viewport as background - Kai-Uwe_02-04/2003
  width = gimp_image_width(image_ID);
  height = gimp_image_height(image_ID);
  if (( off_max_x != 0 ) || ( off_max_y != 0 ) || ( cols != width )
        || ( rows != height ) ) {
    TIFFCreateDirectory (tif);
    TIFFSetField (tif, TIFFTAG_IMAGEWIDTH, 1);//width);
    TIFFSetField (tif, TIFFTAG_IMAGELENGTH, 1);//height);
    TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE, 1);
    TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISWHITE);
    TIFFSetField (tif, TIFFTAG_DOCUMENTNAME, filename);
    TIFFSetField (tif, TIFFTAG_PAGENAME, "viewport" );
    TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, 1);
    //TIFFSetField (tif, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
    TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    /* Handle offsets and resolution */
    TIFFSetField (tif, TIFFTAG_XRESOLUTION, res_x / (gfloat)width);
    TIFFSetField (tif, TIFFTAG_YRESOLUTION, res_y / (gfloat)height);
    //g_message("%d: offx=%d offy=%d", (int)aktuelles_dir, offx, offy);   
    xpos = (gfloat)(- off_max_x) / res_x * (gfloat)width;
    ypos = (gfloat)(- off_max_y) / res_y * (gfloat)height;
    TIFFSetField (tif, TIFFTAG_XPOSITION, xpos);
    TIFFSetField (tif, TIFFTAG_YPOSITION, ypos);
    TIFFSetField (tif, TIFFTAG_SUBFILETYPE, FILETYPE_MASK);
    TIFFSetField (tif, TIFFTAG_PAGENUMBER,
                     (unsigned short) (aktuelles_dir + 1), (unsigned short) nlayers);
    data = g_new (guchar, 2/*width/8*/);
    cols = 1;//width/8;
    for ( col=0; col < cols ; col++ ) {
      data[col] = 0;
    }
    rows = 1;//height;
    for ( row=0 ; row < rows ; row++ ) {
      success = TIFFWriteScanline (tif, data, row, 0);
      //gimp_progress_update ((double) row / (double) rows);
    }
    //g_message ("The last layer.in saved tiff is viewport area.");
  }

  TIFFClose (tif);
  gimp_drawable_detach (drawable);
  g_free (data);
  g_free (name);

  return 1;
}

static gint
save_dialog ()
{
  GtkWidget *dlg;
  GtkWidget *button;
  GtkWidget *toggle;
  GtkWidget *frame;
  GtkWidget *toggle_vbox;
  GSList *group;
  gchar **argv;
  gint argc;
  gint use_none = (tsvals.compression == COMPRESSION_NONE);
  gint use_lzw = (tsvals.compression == COMPRESSION_LZW);
  gint use_packbits = (tsvals.compression == COMPRESSION_PACKBITS);
  gint use_deflate = (tsvals.compression == COMPRESSION_DEFLATE);
  gint use_lsb2msb = (tsvals.fillorder == FILLORDER_LSB2MSB);
  gint use_msb2lsb = (tsvals.fillorder == FILLORDER_MSB2LSB);

  argc = 1;
  argv = g_new (gchar *, 1);
  argv[0] = g_strdup ("save");

  gtk_init (&argc, &argv);
  gtk_rc_parse (gimp_gtkrc ());

  dlg = gtk_dialog_new ();
  gtk_window_set_title (GTK_WINDOW (dlg), "Save as Tiff");
  gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE);
  gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
		      (GtkSignalFunc) save_close_callback,
		      NULL);

  /*  Action area  */
  button = gtk_button_new_with_label ("OK");
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
                      (GtkSignalFunc) save_ok_callback,
                      dlg);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0);
  gtk_widget_grab_default (button);
  gtk_widget_show (button);

  button = gtk_button_new_with_label ("Cancel");
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
			     (GtkSignalFunc) gtk_widget_destroy,
			     GTK_OBJECT (dlg));
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0);
  gtk_widget_show (button);

  /*  compression  */
  frame = gtk_frame_new ("Compression");
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  gtk_container_border_width (GTK_CONTAINER (frame), 10);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, FALSE, TRUE, 0);
  toggle_vbox = gtk_vbox_new (FALSE, 5);
  gtk_container_border_width (GTK_CONTAINER (toggle_vbox), 5);
  gtk_container_add (GTK_CONTAINER (frame), toggle_vbox);

  group = NULL;
  toggle = gtk_radio_button_new_with_label (group, "None");
  group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
		      (GtkSignalFunc) save_toggle_update,
		      &use_none);
  gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), use_none);
  gtk_widget_show (toggle);

  toggle = gtk_radio_button_new_with_label (group, "LZW");
  group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
		      (GtkSignalFunc) save_toggle_update,
		      &use_lzw);
  gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), use_lzw);
  gtk_widget_show (toggle);

  toggle = gtk_radio_button_new_with_label (group, "Pack Bits");
  group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
		      (GtkSignalFunc) save_toggle_update,
		      &use_packbits);
  gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), use_packbits);
  gtk_widget_show (toggle);
  
  toggle = gtk_radio_button_new_with_label (group, "Deflate");
  group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
		      (GtkSignalFunc) save_toggle_update,
		      &use_deflate);
  gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), use_deflate);
  gtk_widget_show (toggle);

  gtk_widget_show (toggle_vbox);
  gtk_widget_show (frame);

  /*  fillorder  */
  frame = gtk_frame_new ("Fill Order");
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  gtk_container_border_width (GTK_CONTAINER (frame), 10);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, FALSE, TRUE, 0);
  toggle_vbox = gtk_vbox_new (FALSE, 5);
  gtk_container_border_width (GTK_CONTAINER (toggle_vbox), 5);
  gtk_container_add (GTK_CONTAINER (frame), toggle_vbox);

  group = NULL;
  toggle = gtk_radio_button_new_with_label (group, "LSB to MSB");
  group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
		      (GtkSignalFunc) save_toggle_update,
		      &use_lsb2msb);
  gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), use_lsb2msb);
  gtk_widget_show (toggle);

  toggle = gtk_radio_button_new_with_label (group, "MSB to LSB");
  group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
  gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
		      (GtkSignalFunc) save_toggle_update,
		      &use_msb2lsb);
  gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), use_msb2lsb);
  gtk_widget_show (toggle);

  gtk_widget_show (toggle_vbox);
  gtk_widget_show (frame);

  gtk_widget_show (dlg);

  gtk_main ();
  gdk_flush ();

  if (use_none)
    tsvals.compression = COMPRESSION_NONE;
  else if (use_lzw)
    tsvals.compression = COMPRESSION_LZW;
  else if (use_packbits)
    tsvals.compression = COMPRESSION_PACKBITS;
  else if (use_deflate)
    tsvals.compression = COMPRESSION_DEFLATE;

  if (use_lsb2msb)
    tsvals.fillorder = FILLORDER_LSB2MSB;
  else if (use_msb2lsb)
    tsvals.fillorder = FILLORDER_MSB2LSB;

  //g_message ("%d",tsint.run);
  return tsint.run;
}


/*  Save interface functions  */

static void
save_close_callback (GtkWidget *widget,
		     gpointer   data)
{
  gtk_main_quit ();
}

static void
save_ok_callback (GtkWidget *widget,
		  gpointer   data)
{
  tsint.run = TRUE;
  gtk_widget_destroy (GTK_WIDGET (data));
}

static void
save_toggle_update (GtkWidget *widget,
		    gpointer   data)
{
  int *toggle_val;

  toggle_val = (int *) data;

  if (GTK_TOGGLE_BUTTON (widget)->active)
    *toggle_val = TRUE;
  else
    *toggle_val = FALSE;
}
