/*******************************************************************************
 *                                                                             *
 *  Filename : fs.c                                                            *
 *  Author   : Bruno Grossniklaus                                              *
 *  Created  : 02.5.91                                                         *
 *  Modified :                                                                 *
 *             20.6.91 Gro: fs_get_image new written (taken from pic_io.c)     *
 *                                                                             *
 ******************************************************************************/

/*
 *  performs the floyd steinberg dithering algorithm with a 32 or 24 Bit
 *  pixrect or rayshade file and a 8 Bit pixrect file as input.
 *
 */


/******************************************************************** INCLUDE */
/* global defaults are set in defaults.h */
#include <stdio.h>
/* This version is for use with XView only! */
#include <pixrect/pixrect_hs.h>
#include "defaults.h"


/******************************************************************** DEFINE  */
/* defaults for fs */

#define  VERSION  "1.02 (20.6.91)"        /* Versions Nr.                     */

#define  DEFAULT_SHIFT_R     3            /* Default shift for red            */
#define  DEFAULT_SHIFT_G     3            /* Default shift for green          */
#define  DEFAULT_SHIFT_B     3            /* Default shift for blue           */

#define  DEFAULT_STDIN "stdin"            /* Default name for stdin           */


/********************************************************************* TYPES  */
/* type for mapping_table */
typedef struct {                          /* Type of mapping_table            */
  int                color_index;         /* color index in colormap          */
} MAPPING_TABLE_T;


/****************************************************************** FUNCTIONS */
/* functions in pic_io.c */
extern void set_file_names();
extern void close_success();
extern void close_error();
extern int  open_in_file();
extern int  open_out_file();
extern int  get_image();
extern int  get_mem();
extern int  put_image();
extern void print_euclidean_dist();
extern void write_map();


/*******************************************************************************
 *   main                                                                      *
 *                                                                             *
 *   Input  :                                                                  *
 *            - argv      arguments (options and filename)                     *
 *            - argc      number of arguments                                  *
 *                                                                             *
 *   Return : - NULL if ok, 1 on error                                         *
 *                                                                             *
 ******************************************************************************/

int main (argc,argv)
int argc;
char *argv [];

{
/******************************************************************** DEKLAR  */
  FILE                *fp_24_or_32_in;    /* file pointer 24 or 32 Bit image  */
  FILE                *fp_8_in;           /* file pointer 8 Bit image         */
  FILE                *fp_out;            /* file pointer                     */

  char                fn_24_or_32_in[FNS];/* file name 24 or 32 Bit image     */
  char                fn_8_in[FNS];       /* file name 8 Bit input image      */
  char                fn_out[FNS];        /* file name output                 */
  char                tmp_str[2*FNS];     /* temorary string                  */

  Pixrect             *mem_32;            /* pixrect in memory 24 or 32 Bit   */
  Pixrect             *mem_8;             /* pixrect in memory 8 Bit          */

  struct rasterfile   rh_8;               /* struct to hold fileformat        */

  colormap_t          colormap;           /* colormap of output file          */
  unsigned char       red[256];           /* used for colormap                */
  unsigned char       green[256];         /* used for colormap                */
  unsigned char       blue[256];          /* used for colormap                */
  int                 color_index;        /* index in colormap                */

  unsigned char       *im_ptr_8;          /* pointer into 8 Bit image         */
  int                 *im_ptr_32;         /* pointer into 24 or 32 Bit image  */

  int                 x,y,depth;          /* size and depth of image          */
  int                 x_tmp,y_tmp;        /* temporary size                   */
  int                 out_format;         /* outfileformat (standard,encoded) */
  int                 i,j,k;              /* used for loops                   */
  long int            r,g,b;              /* value of red,green,blue          */

  unsigned char       flag_prompt;        /* flag if prompt                   */
  unsigned char       flag_euclid;        /* flag if calculate euclid         */
  unsigned char       flag_expand_name;   /* flag if expand name              */
  unsigned char       flag_option_ok;     /* flag if option ok                */
  unsigned char       flag_stdout;        /* flag if write to stdout          */
  unsigned char       flag_stdin;         /* flag if read from stdin          */
  int                 flag_with_ext;      /* flag to put extension */

  unsigned char       flag_percent;       /* for faster access                */

  MAPPING_TABLE_T     *mapping_table;     /* pointer into mapping_table       */
  int                 mapp_size;          /* size of mapping_table            */
  register unsigned long int
                      mapping_index;      /* index for mapping_table          */

  unsigned char       shift_r;            /* shift for red                    */
  unsigned char       shift_g;            /* shift for green                  */
  unsigned char       shift_b;            /* shift for blue                   */

  register unsigned char
     red_r,green_r,green_l,blue_r,blue_l; /* for fast access in loops         */

  register long int   euclid_min_dist;    /* to calculate euclid. dist        */
  register long int   euclid_mom_dist;    /* dito                             */
  register int        euclid_tmp;         /* dito                             */

  int                 *error_r;           /* red scanline error               */
  int                 *error_g;           /* green scanline error             */
  int                 *error_b;           /* blue scanline error              */
  int                 hori_err_r;         /* horizontal error red             */
  int                 hori_err_g;         /* horizontal error green           */
  int                 hori_err_b;         /* horizontal error blue            */
  int                 diag_err_r;         /* diagonal error red               */
  int                 diag_err_g;         /* diagonal error green             */
  int                 diag_err_b;         /* diagonal error blue              */
  int                 curr_err_r;         /* current error red                */
  int                 curr_err_g;         /* current error green              */
  int                 curr_err_b;         /* current error blue               */
  register int
                      spread_hori,        /* spreading of errors              */
                      spread_diag,
                      spread_vert;
  register int        hundred;            /* spreading of errors              */
  register int        spread_percent;     /* spread error in %                */



/******************************************************************** INIT    *
  /* set variables with defaults (defined in defaults.h) */
  /* file names with EOS (end of string) */
  for (i = 0; i < FNS; i++) fn_24_or_32_in[i] = fn_8_in[i] = fn_out[i] = '\0';

  /* pointers with NULL */
  fp_24_or_32_in = fp_8_in = fp_out = NULL;
  mem_8 = mem_32 = NULL;

  /* set default shift */
  shift_r = DEFAULT_SHIFT_R;
  shift_g = DEFAULT_SHIFT_G;
  shift_b = DEFAULT_SHIFT_B;

  /* other default values */
  out_format = DEFAULT_OUT_FORM;
  flag_prompt = DEFAULT_PROMPT;
  flag_euclid = DEFAULT_EUCLID;
  flag_expand_name = DEFAULT_EXP_NAM;
  flag_stdout = FALSE;
  flag_stdin  = FALSE;

  /* set default for floyd steinberg dither */
  spread_hori = DEFAULT_SPREAD_HORI;
  spread_diag = DEFAULT_SPREAD_DIAG;
  spread_vert = 100 - spread_hori - spread_diag;
  hundred = 100;
  spread_percent = DEFAULT_SPREAD_PERCENT;

  fprintf (stderr,"\n");


/******************************************************************** OPTIONS */
  /* argv[0] is name of program */
  /* if there are no parameters specified show usage */
  if (argc <= 1) {
    fprintf (stderr,"Called without parameters \n");
    usage_exit (argv[0]);
  }

  /* if there is only one filename specified show usage or help */
  if (argc <= 2) {
    if (strcmp (argv[argc - 1], "-h") == 0) help_exit (argv[0]);
    else if (strcmp (argv[argc - 1],"-c") == 0) {
      fprintf (stderr,"%s was written by Bruno Grossniklaus in 1991\n",
                       argv[0]);
      exit (1);
    }
    fprintf (stderr,"Called only with one filename \n");
    usage_exit (argv[0]);
  }

  /* copy the filenames */
  strcpy (fn_24_or_32_in,argv[argc - 2]);
  strcpy (fn_8_in,argv[argc - 1]);
  if (fn_24_or_32_in[0] == '-') {
    fprintf (stderr,"%s is not allowed as filename for orig. image\n",
                     fn_24_or_32_in);
    usage_exit (argv[0]);
  }
  if (fn_8_in[0] == '-') {
    fprintf (stderr,"%s is not allowed as filename for mapped image\n",
                     fn_8_in);
    usage_exit (argv[0]);
  }

  /* scan the options */
  i=1;
  flag_option_ok = TRUE;

  while ( (i < argc - 2) && (flag_option_ok) ) {
    flag_option_ok = FALSE;

    if (strcmp (argv[i],"-p+") == 0) {
      i++;
      flag_option_ok = TRUE;
      flag_prompt = TRUE;
    }

    if (strcmp (argv[i],"-p-") == 0) {
      i++;
      flag_option_ok = TRUE;
      flag_prompt = FALSE;
    }

    if (strcmp (argv[i],"-p") == 0) {
      i++;
      flag_option_ok = TRUE;
      flag_prompt = !flag_prompt;
    }


    if (strcmp (argv[i],"-o") == 0) {
      flag_option_ok = TRUE;
      i++;
      if (i >= argc - 2) {
        fprintf (stderr,"Missing parameter after %s. \n",argv[i-1]);
        fprintf (stderr,"%s is input filename! \n",argv[i]);
        usage_exit (argv[0]);
      }
      strcpy (fn_out,argv[i]);
      if ( (fn_out[0] == '-') || (fn_out == NULL) ) {
        fprintf (stderr,"Parameter %s after -o is not filename \n",fn_out);
        usage_exit (argv[0]);
      }
      i++;
    }


    if (strcmp (argv[i],"-en+") == 0) {
      i++;
      flag_option_ok = TRUE;
      flag_expand_name = TRUE;
    }

    if (strcmp (argv[i],"-en-") == 0) {
      i++;
      flag_option_ok = TRUE;
      flag_expand_name = FALSE;
    }

    if (strcmp (argv[i],"-en") == 0) {
      i++;
      flag_option_ok = TRUE;
      flag_expand_name = !flag_expand_name;
    }


    if ( (strcmp (argv[i],"-ws+") == 0) || (strcmp (argv[i],"-ws") == 0) ) {
      flag_option_ok = TRUE;
      out_format = RT_STANDARD;
      if (flag_prompt) fprintf (stderr,"Output file format is STANDARD \n");
      i++;
    }

    if (strcmp (argv[i],"-ws-") == 0) {
      flag_option_ok = TRUE;
      out_format = RT_BYTE_ENCODED;
      if (flag_prompt)
            fprintf (stderr,"Output file format is RUN LENGTH ENCODED \n");
      i++;
    }


    if (strcmp (argv[i],"-sa") == 0) {
      i++;
      flag_option_ok = TRUE;
      if (i >= argc - 2) {
        fprintf (stderr,"Missing parameter after %s. \n",argv[i-1]);
        fprintf (stderr,"%s is input filename! \n",argv[i]);
        usage_exit (argv[0]);
      }
      if ( (atoi(argv[i]) < 0) || (atoi(argv[i]) > 8) ) {
        fprintf (stderr," %d is not allowed for %s \n",atoi(argv[i]),argv[i-1]);
        usage_exit (argv[0]);
      }
      else {
        shift_r = atoi(argv[i]);
        shift_g = atoi(argv[i]);
        shift_b = atoi(argv[i]);
      }
      i++;
    }

    if (strcmp (argv[i],"-sr") == 0) {
      i++;
      flag_option_ok = TRUE;
      if (i >= argc - 2) {
        fprintf (stderr,"Missing parameter after %s. \n",argv[i-1]);
        fprintf (stderr,"%s is input filename! \n",argv[i]);
        usage_exit (argv[0]);
      }
      if ( (atoi(argv[i]) < 0) || (atoi(argv[i]) > 8) ) {
        fprintf (stderr," %d is not allowed for %s \n",atoi(argv[i]),argv[i-1]);
        usage_exit (argv[0]);
      }
      else shift_r = atoi(argv[i]);
      i++;
    }

    if (strcmp (argv[i],"-sg") == 0) {
      i++;
      flag_option_ok = TRUE;
      if (i >= argc - 2) {
        fprintf (stderr,"Missing parameter after %s. \n",argv[i-1]);
        fprintf (stderr,"%s is input filename! \n",argv[i]);
        usage_exit (argv[0]);
      }
      if ( (atoi(argv[i]) < 0) || (atoi(argv[i]) > 8) ) {
        fprintf (stderr," %d is not allowed for %s \n",atoi(argv[i]),argv[i-1]);
        usage_exit (argv[0]);
      }
      else shift_g = atoi(argv[i]);
      i++;
    }

    if (strcmp (argv[i],"-sb") == 0) {
      i++;
      flag_option_ok = TRUE;
      if (i >= argc - 2) {
        fprintf (stderr,"Missing parameter after %s. \n",argv[i-1]);
        fprintf (stderr,"%s is input filename! \n",argv[i]);
        usage_exit (argv[0]);
      }
      if ( (atoi(argv[i]) < 0) || (atoi(argv[i]) > 8) ) {
        fprintf (stderr," %d is not allowed for %s \n",atoi(argv[i]),argv[i-1]);
        usage_exit (argv[0]);
      }
      else shift_b = atoi(argv[i]);
      i++;
    }


    if (strcmp (argv[i],"-fs%") == 0) {
      i++;
      flag_option_ok = TRUE;
      if ( (argv[i][0] == '-') || (i >= argc - 2) ) {
        fprintf (stderr,"Missing parameter after %s. \n",argv[i-1]);
        usage_exit (argv[0]);
      }
      else {
        spread_percent = atoi (argv[i]);
        if ( (spread_percent < 0) || (spread_percent > 100) ) {
          fprintf (stderr,"After -fs%% must be a number in percent. ");
          fprintf (stderr,"%d not allowed\n",spread_percent);
          usage_exit (argv[0]);
        }
      }
      i++;
    }

    if (strcmp (argv[i],"-fs") == 0) {
      i++;
      flag_option_ok = TRUE;
        if ( (argv[i][0] != '-') && (i < argc - 2) ) {
          spread_hori = atoi (argv[i]);
          i++;
          if ( (argv[i][0] == '-') || (i >= argc - 2) ) {
            fprintf (stderr,"Missing parameter after %s. \n",argv[i-2]);
            fprintf (stderr,"%s must be diagonal spreading! \n",argv[i]);
            usage_exit (argv[0]);
          }
          spread_diag = atoi (argv[i]);
          i++;
        }
        if ( (spread_hori < 0) || (spread_hori > 100) ) {
          fprintf (stderr,"Horizontal spreading is ");
          fprintf (stderr,"%d, but must be >= 0 and <= 100!\n",spread_hori);
          usage_exit (argv[0]);
        }
        if ( (spread_diag < 0) || (spread_diag > 100) ) {
          fprintf (stderr,"Diagonal spreading is ");
          fprintf (stderr,"%d, but must be >= 0 and <= 100!\n",spread_diag);
          usage_exit (argv[0]);
        }
        spread_vert = 100 - spread_hori - spread_diag;
        if (spread_vert < 0) {
          fprintf (stderr,"Vertical spreading is %d, but must be >= 0!\n",
                   spread_vert);
          usage_exit (argv[0]);
        }

    }


    if (strcmp (argv[i],"-ce+") == 0) {
      i++;
      flag_option_ok = TRUE;
      flag_euclid = TRUE;
    }

    if (strcmp (argv[i],"-ce-") == 0) {
      i++;
      flag_option_ok = TRUE;
      flag_euclid = FALSE;
    }

    if (strcmp (argv[i],"-ce") == 0) {
      i++;
      flag_option_ok = TRUE;
      flag_euclid = !flag_euclid;
    }


    if (strcmp (argv[i],"-h") == 0)
      help_exit (argv[0]);

  }

  /* if current parameter was no good option */
  if (!flag_option_ok) {
    fprintf (stderr,"%s is not an allowed option \n",argv[i]);
    usage_exit (argv[0]);
  }


/******************************************************************** INIT    */
  if (strcmp (fn_out,DEFAULT_STDOUT) == 0) flag_stdout = TRUE;
  if (strcmp (fn_8_in,DEFAULT_STDIN) == 0) flag_stdin = TRUE;
  /* if fs was called from med or oct the 8 Bit input is stored in */
  /* a tmp file (but only if -o stdout was set!)                   */
  if (strcmp (fn_8_in,TMP_FILE_NAME) == 0) flag_stdout = TRUE;


  if ( (flag_stdin) && (!flag_stdout) ) {
    /* if no output filename is given take 24 or 32 input file name */
    if (strlen (fn_out) == 0) {

      flag_with_ext = 0;
      j = 0;

      for (i = 0; i < strlen (fn_24_or_32_in); i++) {
        if (fn_24_or_32_in[i] == '/' ) { j = -1; flag_with_ext = FALSE; }
        if (fn_24_or_32_in[i] == '.' ) flag_with_ext = TRUE;
        if ( (j > -1) && (!flag_with_ext) ) fn_out[j] = fn_24_or_32_in[i];
        if (!flag_with_ext) j++;
      }

      fn_out[j] = '\0';
      strcat (fn_out,DEFAULT_OUT_EXT);
      fprintf (stderr,"There is no output file specified. ");
      fprintf (stderr,"So i take %s\n",fn_out);
    }

    else {
      /* if there is no extension for output file add default extension */
      flag_with_ext = FALSE;

      for (i = 0; i < strlen (fn_out); i++) {
        if (fn_out[i] == '.') flag_with_ext = TRUE;
        if (fn_out[i] == '/') flag_with_ext = FALSE;
      }
      if (!flag_with_ext) strcat (fn_out,DEFAULT_OUT_EXT);
    }
  }
  else if ( (!flag_stdin) && (!flag_stdout)) {
    /* get output filename */
    fn_out[0] = '\0';
    strcat (fn_out,fn_8_in);
  }



  /* if expand name is set then add current settings to the output file name  */
  if ( (flag_expand_name) && (!flag_stdout) ) {

    /* add shift */
    strcat (fn_out,"_");
    sprintf (tmp_str,"%d", shift_r);
    strcat (fn_out,tmp_str);
    sprintf (tmp_str,"%d", shift_g);
    strcat (fn_out,tmp_str);
    sprintf (tmp_str,"%d", shift_b);
    strcat (fn_out,tmp_str);

    /* add floyd steinbeg */
    strcat (fn_out,"_fs_");
    sprintf (tmp_str, "%d", spread_hori);
    strcat (fn_out,tmp_str);
    strcat (fn_out,"_");
    sprintf (tmp_str, "%d", spread_diag);
    strcat (fn_out,tmp_str);
    strcat (fn_out,"_");
    sprintf (tmp_str, "%d", spread_vert);
    strcat (fn_out,tmp_str);
    strcat (fn_out,"_");
    sprintf (tmp_str, "%d", spread_percent);
    strcat (fn_out,tmp_str);
    strcat (fn_out,"%");

    /* print the output file name to console */
    fprintf (stderr,"Expand name is set. So output file is %s \n",fn_out);
  }


/***************************************************************** OPEN & GET */
  /* now open the files and load images */

  /* open 8 Bit file */
  if (flag_stdin) {
    fp_8_in = stdin;
  }
  else
    if (fs_open_in_file (fn_8_in,&fp_8_in,fp_24_or_32_in,fp_out,
       mem_32,mem_8) != NULL) exit (1);

  if (flag_prompt) fprintf (stderr,"Getting 8 Bit Data and Memory ... ");

  /* get the header info */
  if ( pr_load_header (fp_8_in, &rh_8) == PIX_ERR) {
    fprintf (stderr,"\nError loading header of %s\n",fn_8_in);
    fprintf (stderr,"maybe %s is not mapped image (but rayshade) \n",fn_8_in);
    fs_close_error (fp_24_or_32_in,fp_8_in,fp_out,mem_32,mem_8);
    exit (1);
  }

  /* test if file ok */
  if (rh_8.ras_depth != 8) {
    fprintf (stderr,"\nDepth of mapped image is not 8\n");
    fs_close_error (fp_24_or_32_in,fp_8_in,fp_out,mem_32,mem_8);
    exit (1);
  }

  if (rh_8.ras_maptype != RMT_EQUAL_RGB) {
    fprintf (stderr,"\n%s has not RGB colormap\n",fn_8_in);
    fs_close_error (fp_24_or_32_in,fp_8_in,fp_out,mem_32,mem_8);
    exit (1);
  }

  x_tmp = rh_8.ras_width;
  y_tmp = rh_8.ras_height;

  /* init for colormap */
  colormap.type = RMT_EQUAL_RGB;
  colormap.length = 256;
  colormap.map[0] = red; colormap.map[1] = green; colormap.map[2] = blue;
  for (i = 0; i < 256; i++) red[i] = green[i] = blue[i] = 0;

  /* get colormap */
  if ( pr_load_colormap ( fp_8_in, &rh_8, &colormap) == PIX_ERR) {
    fprintf (stderr,"\nError loading colormap of %s\n",fn_8_in);
    fprintf (stderr,"maybe %s is not mapped image (but rayshade) \n",fn_8_in);
    fs_close_error (fp_24_or_32_in,fp_8_in,fp_out,mem_32,mem_8);
    exit (1);
  }

  /* get memory for 8 Bit image */
  if ( (mem_8 = mem_create (x_tmp,y_tmp,8)) == NULL) {
    fprintf (stderr,"\nError in mem_create (). Not enough memory!\n");
    fs_close_error (fp_24_or_32_in,fp_8_in,fp_out,mem_32,mem_8);
    exit (1);
  }

  if (flag_prompt) fprintf (stderr,"done.\n");

  /* open 32 or 24 Bit file */
  if (fs_open_in_file (fn_24_or_32_in,&fp_24_or_32_in,fp_8_in,fp_out,
      mem_32,mem_8) != NULL) exit (1);

  /* get 32 or 24 Bit file */
  if (    fs_get_image (fn_24_or_32_in,fp_24_or_32_in,fp_8_in,fp_out,
             &mem_32,mem_8,flag_prompt,&x,&y,&depth) != NULL) exit (1);

  /* test if fits to 8 Bit data */
  if ( (x != x_tmp) || (y != y_tmp) ) {
    fprintf (stderr,"Original image has not the same size as mapped image\n");
    fs_close_error (fp_24_or_32_in,fp_8_in,fp_out,mem_32,mem_8);
    exit (1);
  }

  /* open now the output file */
  if (fs_open_out_file (fn_out,fp_24_or_32_in,fp_8_in,&fp_out,mem_32,mem_8)
     != NULL) exit (1);


/******************************************************************** INIT    */
  /* set mapping_table */
  mapp_size = (256 >> shift_r) * (256 >> shift_g) * (256 >> shift_b);
  if (flag_prompt) fprintf (stderr,"Mapping table size : %d \n",mapp_size);

  if ( (mapping_table = (MAPPING_TABLE_T *)
         malloc ( (unsigned) mapp_size  * sizeof (MAPPING_TABLE_T))) == NULL) {
    fprintf (stderr,"\nError in malloc for mapping_table. \n");
    fprintf (stderr," Not enough memory!\n");
    free ( (char *) mapping_table);
    fs_close_error (fp_24_or_32_in,fp_8_in,fp_out,mem_32,mem_8);
    exit (1);
  }

  if (flag_prompt) {
    fprintf (stderr,"Error spreading : ");
    fprintf (stderr,"horizontal=%d  diagonal=%d  vertical=%d  percent=%d\n",
              spread_hori, spread_diag, spread_vert,spread_percent);
  }

  /* allocate memory for arrays holding scanline color errors */
  if ( (error_r = (int *) malloc ( (unsigned) x * sizeof (int))) == NULL) {
    fprintf (stderr,"\nError in malloc(). Not enough memory!\n");
    free ( (char *) mapping_table);
    free ( (char *) error_r);
    fs_close_error (fp_24_or_32_in,fp_8_in,fp_out,mem_32,mem_8);
    exit(1);
  }

  if ( (error_g = (int *) malloc ( (unsigned) x * sizeof (int))) == NULL) {
    fprintf (stderr,"\nError in malloc(). Not enough memory!\n");
    free ( (char *) mapping_table);
    free ( (char *) error_r);
    free ( (char *) error_g);
    fs_close_error (fp_24_or_32_in,fp_8_in,fp_out,mem_32,mem_8);
    exit(1);
  }

  if ( (error_b = (int *) malloc ( (unsigned) x * sizeof (int))) == NULL) {
    fprintf (stderr,"\nError in malloc(). Not enough memory!\n");
    free ( (char *) mapping_table);
    free ( (char *) error_r);
    free ( (char *) error_g);
    free ( (char *) error_b);
    fs_close_error (fp_24_or_32_in,fp_8_in,fp_out,mem_32,mem_8);
    exit(1);
  }

  /* initialize them */
  for (i = 0; i < x; i++) error_r[i] = error_g[i] = error_b[i] = 0;

  /* init mapping_table as look up table for mapping */
  for (mapping_index = 0; mapping_index < mapp_size;mapping_index++)
    mapping_table[mapping_index].color_index= -1;

  fprintf (stderr,"Mapping colors using Floyd Steinberg dithering ... ");

  /* set flag_percent for faster access */
  if (spread_percent == 100) flag_percent = FALSE; else flag_percent = TRUE;

  /* set registers for faster access */
  red_r   =  shift_r;
  green_r =  shift_g;
  blue_r  =  shift_b;
  green_l =  8 - shift_r;
  blue_l  = 16 - shift_r - shift_g;

  /* set image pointers to first pixel */
  im_ptr_32 = (int *) mpr_d(mem_32)->md_image;
  im_ptr_8 = (unsigned char *) mpr_d(mem_8)->md_image;

/**************************************** MAPPING COLORS WITH FLOYD STEINBERG */
  /* for all scan lines */
  for (i = 0; i < y; i++) {

    /* initialize diagonal and horizontal errors */
    hori_err_r = hori_err_g = hori_err_b = 0;
    diag_err_r = diag_err_g = diag_err_b = 0;

    /* for all pixels in scan line */
    for (j = 0; j < x; j++, im_ptr_32++, im_ptr_8++) {

      /* get new colors and add errors */
      r = ( (*im_ptr_32)        & 0xff) + error_r[j] + hori_err_r;
      g = (((*im_ptr_32) >>  8) & 0xff) + error_g[j] + hori_err_g;
      b = (((*im_ptr_32) >> 16) & 0xff) + error_b[j] + hori_err_b;

      /* cut if under- or overflow */
      if (r < 0) r = 0; else if (r > 255) r = 255;
      if (g < 0) g = 0; else if (g > 255) g = 255;
      if (b < 0) b = 0; else if (b > 255) b = 255;

      mapping_index =   (r >> red_r) +
                      ( (g >> green_r) << green_l) +
                      ( (b >> blue_r ) << blue_l );

      /* test if color is already set */
      if (mapping_table [mapping_index].color_index == -1) {

        for (k = 0, euclid_min_dist = 256*256*256, color_index = 0;
             k < 256; k++) {
          euclid_tmp       = r - red[k];
          euclid_mom_dist  = euclid_tmp * euclid_tmp;
          euclid_tmp       = g - green[k];
          euclid_mom_dist += euclid_tmp * euclid_tmp;
          euclid_tmp       = b - blue[k];
          euclid_mom_dist += euclid_tmp * euclid_tmp;
          if (euclid_mom_dist < euclid_min_dist) {
            color_index = k;
            euclid_min_dist = euclid_mom_dist;
          }
        }
        mapping_table[mapping_index].color_index = color_index;
      }

      *im_ptr_8 = mapping_table[mapping_index].color_index;

      /* set current errors */
      if (flag_percent) {
        curr_err_r = (int) (spread_percent * (r - red[*im_ptr_8]))   / hundred;
        curr_err_g = (int) (spread_percent * (g - green[*im_ptr_8])) / hundred;
        curr_err_b = (int) (spread_percent * (b - blue[*im_ptr_8]))  / hundred;
      }
      else {
        curr_err_r = r - red[*im_ptr_8];
        curr_err_g = g - green[*im_ptr_8];
        curr_err_b = b - blue[*im_ptr_8];
      }

      /* spread error vertical */
      error_r[j] = diag_err_r + (curr_err_r * spread_vert) / hundred;
      error_g[j] = diag_err_g + (curr_err_g * spread_vert) / hundred;
      error_b[j] = diag_err_b + (curr_err_b * spread_vert) / hundred;

      /* spread error horizontal */
      hori_err_r =  (curr_err_r * spread_hori) / hundred;
      hori_err_g =  (curr_err_g * spread_hori) / hundred;
      hori_err_b =  (curr_err_b * spread_hori) / hundred;

      /* spread error diagonal */
      diag_err_r = (curr_err_r * spread_diag) / hundred;
      diag_err_g = (curr_err_g * spread_diag) / hundred; 
      diag_err_b = (curr_err_b * spread_diag) / hundred; 

    }
  }

  /* free memory for scan line errors */
  free ( (char *) error_r);
  free ( (char *) error_g);
  free ( (char *) error_b);

  if (flag_prompt) fprintf (stderr,"done. \n");

  /* free memory used for mapping_table */
  free ( (char *) mapping_table);


/**************************************************************** WRITE IMAGE */
  /* write output image */
  if (fs_put_image (fn_out,fp_24_or_32_in,fp_8_in,fp_out,mem_32,mem_8,
      flag_prompt,colormap,out_format) != NULL) exit (1);


/************************************************************ WRITE TO STDOUT */
  /* calculate euclidean distance if option is set */
  if (flag_euclid)
    if (flag_stdout) {
      fprintf (stderr,"I write the image to stdout. ");
      fprintf (stderr,"So no other writing to stdout allowed! \n");
    }
    else print_euclidean_dist
                           (fn_out,mem_32,mem_8,flag_prompt,red,green,blue,x,y);


/**************************************************************** ENDING MAIN */
  /* close files and free memory */
  fclose (fp_24_or_32_in);
  fclose (fp_8_in);
  fclose (fp_out);
  if (mem_32 != NULL) pr_close (mem_32);
  if (mem_8  != NULL) pr_close (mem_8);
  fprintf (stderr,"Success \n");

  /* delete tmp file if fs was called from med or oct with -o stdout option */
  if ( strcmp(fn_8_in,TMP_FILE_NAME) == 0) {
    strcpy (tmp_str,"rm ");
    strcat (tmp_str,TMP_FILE_NAME);
    system (tmp_str);
  }

  /* null problemo */
  return (0);
}


/*******************************************************************************
 *   usage_exit                                                                *
 *                                                                             *
 *   Input  :                                                                  *
 *            - name      name of program as string                            *
 *                                                                             *
 *   Output :                                                                  *
 *            - writes the usage and the default values to stderr              *
 *                                                                             *
 *   Return : - exits with 1                                                   *
 *                                                                             *
 ******************************************************************************/
usage_exit (name)
char *name;
{
  fprintf (stderr,"\n%s V%s\n",name,VERSION);
  fprintf (stderr,"Usage: %s {options} <orig._filename  mapped_filename>\n",
                                                                          name);
  fprintf (stderr,"                   |<orig._filename  %s>\n",DEFAULT_STDIN);
  fprintf (stderr,"               -o <output filename|%s> \n",
                                                                DEFAULT_STDOUT);
  fprintf (stderr,"               -en[+|-]        expand name with mode \n");
  fprintf (stderr,"               -ws[+|-]        write standard format \n");
  fprintf (stderr,"               -sa <0..8>      shift all values in Bit \n");
  fprintf (stderr,"               -sr <0..8>      shift red values in Bit \n");
  fprintf (stderr,"               -sg <0..8>      shift green values in Bit\n");
  fprintf (stderr,"               -sb <0..8>      shift blue values in Bit \n");
  fprintf (stderr,"               -fs [h d]       Floyd Steinberg ");
  fprintf (stderr,"dither\n");
  fprintf (stderr,"                                 following parameters ");
  fprintf (stderr,"in %% :\n");
  fprintf (stderr,"                                 h = horizontal error ");
  fprintf (stderr,"spreading\n");
  fprintf (stderr,"                                 d = diagonal error ");
  fprintf (stderr,"spreading\n");
  fprintf (stderr,"                                 h, d are integers ");
  fprintf (stderr,"(h+d<=100)\n");
  fprintf (stderr,"               -fs%% <0..100>   percent for Floyd ");
  fprintf (stderr,"Steinberg \n");
  fprintf (stderr,"                               dithering \n");
  fprintf (stderr,"               -ce[+|-]        calculate euclid. ");
  fprintf (stderr,"distance\n");
  fprintf (stderr,"                               and write to stdout \n");
  fprintf (stderr,"               -p[+|-]         prompt info to stderr \n");
  fprintf (stderr,"               -h              help (to stderr) \n");

  /* print the defaults */
  fprintf (stderr,"\nDefaults:                                       \n");
  fprintf (stderr,"  - Extension of output file  : %s \n",DEFAULT_OUT_EXT);

  if (DEFAULT_EXP_NAM)
    fprintf (stderr,"  - Expand name               : + \n");
  else
    fprintf (stderr,"  - Expand name               : - \n");

  if (DEFAULT_OUT_FORM == RT_STANDARD)
    fprintf (stderr,"  - Write format              : standard \n");
  else
    fprintf (stderr,"  - Write format              : run length encoded \n");

  fprintf (stderr,"  - shift red values in Bit   : %d \n",DEFAULT_SHIFT_R);
  fprintf (stderr,"  - shift green values in Bit : %d \n",DEFAULT_SHIFT_G);
  fprintf (stderr,"  - shift blue values in Bit  : %d \n",DEFAULT_SHIFT_B);

  fprintf (stderr,"  - Floyd Steinberg dither    : -  %d %d\n",
                        DEFAULT_SPREAD_HORI,DEFAULT_SPREAD_DIAG);

  fprintf (stderr,"  - Percent for Floyd Steinb. : %d \n",
           DEFAULT_SPREAD_PERCENT);

  if (DEFAULT_EUCLID)
    fprintf (stderr,"  - Calculate euclidean dist. : + \n");
  else
    fprintf (stderr,"  - Calculate euclidean dist. : - \n");


  if (DEFAULT_PROMPT)
    fprintf (stderr,"  - Prompt to stderr          : + \n");
  else
    fprintf (stderr,"  - Prompt to stderr          : - \n");

  fprintf (stderr,"\n->try %s |& more \n",name);

  exit(1);
}


/*******************************************************************************
 *   help_exit                                                                 *
 *                                                                             *
 *   Input  :                                                                  *
 *            - name      name of program as string                            *
 *                                                                             *
 *   Output : - writes the help to stderr                                      *
 *                                                                             *
 *   Return : - exits with 1                                                   *
 *                                                                             *
 ******************************************************************************/
help_exit (name)
char *name;
{
  fprintf (stderr,"Help for %s V%s\n",name,VERSION);
  fprintf (stderr," I make use of the Floyd Steinberg dithering algorithm.\n");
  fprintf (stderr," If you have an 24 or 32 Bit and an 8 Bit colormapped\n");
  fprintf (stderr," image I will increase the quality of the mapped one.\n");
  fprintf (stderr," Here are some valid examples for calling:\n");
  fprintf (stderr,"   %s hsv.pixrect hsv.im8_u_884\n",name);
  fprintf (stderr,"   %s -fs 40 30 -fs%% 75 -o %s hsv.pixrect hsv.im8\n",
                      name,DEFAULT_STDOUT);
  fprintf (stderr,"   %s -sa 4 -sr 2 -ws- -p en+ hsv.pixrect hsv.im8\n",name);
  exit (1);
}


/*******************************************************************************
 *   fs_close_error                                                            *
 *                                                                             *
 *   Input  :                                                                  *
 *            - fp_1_in      input file pointer 24 or 32 Bit image             *
 *            - fp_2_in      input file pointer 8 Bit image                    *
 *            - fp_out       output file pointer                               *
 *            - mem_in       pixrect in memory 24 or 32 Bit                    *
 *            - mem_8_out    pixrect in memory 8 Bit                           *
 *                                                                             *
 *   close open files and free allocated memory.                               *
 *                                                                             *
 ******************************************************************************/
fs_close_error (fp_1_in,fp_2_in,fp_out,mem_in,mem_8_out)
FILE       *fp_1_in;     /* input file pointer 32 or 24    */
FILE       *fp_2_in;     /* input file pointer 8 Bit       */
FILE       *fp_out;      /* output file pointer            */
Pixrect    *mem_in;      /* pixrect in memory 24 or 32 Bit */
Pixrect    *mem_8_out;   /* pixrect in memory 8 Bit        */

{
 fprintf (stderr,"\nClosing all. No good output because of error! \n");
  fclose (fp_1_in);
  fclose (fp_2_in);
  fclose (fp_out);
  if (mem_in    != NULL) pr_close (mem_in);
  if (mem_8_out != NULL) pr_close (mem_8_out);
}


/*******************************************************************************
 *   fs_open_in_file                                                           *
 *                                                                             *
 *   Input  : - fn_in     name of file to open                                 *
 *            - fp_1_in   input file pointer to set                            *
 *            - fp_2_in   input file pointer                                   *
 *            - fp_out    output file pointer                                  *
 *            - mem_in    pixrect in memory 24 or 32 Bit                       *
 *            - mem_8_out pixrect in memory 8 Bit                              *
 *                                                                             *
 *   Output : - fp_1_in   input file pointer                                   *
 *                                                                             *
 *   Return : - NULL if ok, 1 on error.                                        *
 *                                                                             *
 ******************************************************************************/
int fs_open_in_file (fn_in,fp_1_in,fp_2_in,fp_out,mem_in,mem_8_out)
char    fn_in[FNS];   /* input filename                 */
FILE    **fp_1_in;    /* input file pointer             */
FILE    *fp_2_in;     /* input file pointer             */
FILE    *fp_out;      /* output file pointer            */
Pixrect *mem_in;      /* pixrect in memory 24 or 32 Bit */
Pixrect *mem_8_out;   /* pixrect in memory 8 Bit        */

{
  if ( (*fp_1_in = fopen (fn_in,"r")) == NULL ) {
    fprintf (stderr,"Error opening file %s \n",fn_in);
    fprintf (stderr," -maybe no path specified but file not in current dir.\n");
    fprintf (stderr," -maybe wrong path specified.\n");
    fprintf (stderr," -maybe file does not exists.\n");
    fprintf (stderr," -maybe file is defective.\n");
    fprintf (stderr," -> use Esc function of SUNVIEW to complete filename!\n");
    fs_close_error (*fp_1_in,fp_2_in,fp_out,mem_in,mem_8_out);
    return (1);
  }
  else return (NULL);
}


/*******************************************************************************
 *   fs_open_out_file                                                          *
 *                                                                             *
 *   Input  :                                                                  *
 *            - fn_out    name of output file                                  *
 *            - fp_1_in   input file pointer 24 or 32 Bit image                *
 *            - fp_2_in   input file pointer 8 Bit image                       *
 *            - mem_in    pixrect in memory 24 or 32 Bit                       *
 *            - mem_8_out pixrect in memory 8 Bit                              *
 *                                                                             *
 *   Output : - fp_out    output file pointer                                  *
 *                                                                             *
 *   Return : - NULL if ok, 1 on error.                                        *
 *                                                                             *
 ******************************************************************************/
int fs_open_out_file (fn_out,fp_1_in,fp_2_in,fp_out,mem_in,mem_8_out)
char    fn_out[FNS];  /* output filename                */
FILE    *fp_1_in;     /* input file pointer             */
FILE    *fp_2_in;     /* input file pointer             */
FILE    **fp_out;     /* output file pointer            */
Pixrect *mem_in;      /* pixrect in memory 24 or 32 Bit */
Pixrect *mem_8_out;   /* pixrect in memory 8 Bit        */

{
  if (strcmp (fn_out,DEFAULT_STDOUT) == 0) {
    /* set pointer to stdout */
    *fp_out = stdout;
    fprintf (stderr,"I`ll write image to stdout. \n");
    return (NULL);
  }

  else {

    /* open for reading to see if output file already exists */
    if ( (*fp_out = fopen (fn_out,"r")) != NULL ) {
      fprintf (stderr,"Output file %s already exists. ",fn_out);
      fprintf (stderr,"Overwrite (y/n) : ");
      if ('y' != getchar()) {
        fs_close_error (fp_1_in,fp_2_in,*fp_out,mem_in,mem_8_out);
        return (1);
      }
      fprintf (stderr,"\n");
    }
    else fclose (*fp_out);

    /* now open for writing ... */
    if ( (*fp_out = fopen (fn_out,"w")) == NULL ) {
      fprintf (stderr,"Error creating file %s \n",fn_out);
      fprintf (stderr," -maybe there are forbidden characters in the ");
      fprintf (stderr,"filename.\n");
      fprintf (stderr," -maybe over disk quota.\n");
      fprintf (stderr," -maybe disk is full.\n");
      fprintf (stderr," -maybe server is defective.\n");
      fs_close_error (fp_1_in,fp_2_in,*fp_out,mem_in,mem_8_out);
      return (1);
    }
    else return (NULL);
  }
}


/*******************************************************************************
 *   fs_get_image                                                              *
 *                                                                             *
 *   Input  : - fn_in        name of input file                                *
 *            - fp_1_in      input file pointer 24 or 32 Bit image             *
 *            - fp_2_in      input file pointer 8 Bit image                    *
 *            - fp_out       output file pointer                               *
 *            - mem_in       pixrect in memory 24 or 32 Bit                    *
 *            - mem_8_out    pixrect in memory 8 Bit                           *
 *            - flag_prompt  flag if prompt or not                             *
 *            - x            width of image                                    *
 *            - y            heigth of image                                   *
 *            - depth        depth of image                                    *
 *                                                                             *
 *   Output : - x            width of image                                    *
 *            - y            heigth of image                                   *
 *            - depth        depth of image                                    *
 *            - mem_in       points to image data                              *
 *                                                                             *
 *   Return : - NULL if ok, 1 on error.                                        *
 *                                                                             *
 ******************************************************************************/
int fs_get_image
           (fn_in,fp_1_in,fp_2_in,fp_out,mem_in,mem_8_out,flag_prompt,x,y,depth)
char          fn_in[FNS];   /* input filename                 */
FILE          *fp_1_in;     /* input file pointer             */
FILE          *fp_2_in;     /* input file pointer             */
FILE          *fp_out;      /* output file pointer            */
Pixrect       **mem_in;     /* pixrect in memory 24 or 32 Bit */
Pixrect       *mem_8_out;   /* pixrect in memory 8 Bit        */
unsigned char flag_prompt;  /* flag if prompt                 */
int           *x,*y,*depth; /* size and depth of image        */

{
  /* here I use the get_image function in pic_io.c */
  if ( get_image (fn_in,fp_1_in,fp_out,mem_in,mem_8_out,
                  flag_prompt,x,y,depth) != NULL) {
    fclose (fp_2_in);
    return (1);
  }
  return (NULL);
}


/*******************************************************************************
 *   fs_put_image                                                              *
 *                                                                             *
 *   Input  : - fn_out       output filename                                   *
 *            - fp_1_in      input file pointer 24 or 32 Bit image             *
 *            - fp_2_in      input file pointer 8 Bit image                    *
 *            - fp_out       output file pointer                               *
 *            - mem_in       pixrect in memory 24 or 32 Bit                    *
 *            - mem_8_out    pixrect in memory 8 Bit                           *
 *            - flag_prompt  flag if prompt or not                             *
 *            - colormap     colormap of output image                          *
 *            - out_format   writing format                                    *
 *                                                                             *
 *   Return : - NULL if ok, 1 on error.                                        *
 *                                                                             *
 ******************************************************************************/
int fs_put_image (fn_out,fp_1_in,fp_2_in,fp_out,mem_in,mem_8_out,
               flag_prompt,colormap,out_format)
char           fn_out[FNS];  /* output filename                */
FILE           *fp_1_in;     /* input file pointer             */
FILE           *fp_2_in;     /* input file pointer             */
FILE           *fp_out;      /* output file pointer            */
Pixrect        *mem_in;      /* pixrect in memory 24 or 32 Bit */
Pixrect        *mem_8_out;   /* pixrect in memory 8 Bit        */
unsigned char  flag_prompt;  /* flag if prompt                 */
colormap_t     colormap;     /* colormap of output file        */
int            out_format;   /* output file format             */

{
  /* write the output image */
  if (flag_prompt) fprintf (stderr,"Writing %s ... ",fn_out);
  if ( pr_dump (mem_8_out,fp_out,&colormap,out_format,0) == PIX_ERR) {
    fprintf (stderr,"\nError writing %s \n",fn_out);
    fprintf (stderr," -maybe disk is full.\n");
    fprintf (stderr," -maybe overdisk quota.\n");
    fprintf (stderr," -maybe server is defective.\n");
    fs_close_error (fp_1_in,fp_2_in,fp_out,mem_in,mem_8_out);
    return (1);
  }
  if (flag_prompt) fprintf (stderr,"done. \n");
  return (NULL);
}






















