/*******************************************************************************
 *                                                                             *
 *  Filename : uniform.c                                                       *
 *  Author   : Bruno Grossniklaus                                              *
 *  Created  : 5.4.91                                                          *
 *  Modified :                                                                 *
 *                                                                             *
 ******************************************************************************/

/*
 * uniform.c
 *
 * Transforms 32 or 24 Bit pixrect or rayshade files to 8 Bit colormap files
 * with the uniform quantisation algorithm.
 *
 */

/******************************************************************** 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  */
/* more defaults are included in defaults.h */

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

#define MI               17               /* Maximum of intervals             */

#define DEFAULT_NUM_R    8                /* Default number of intervals red  */
#define DEFAULT_NUM_G    8                /* Default number of intervals green*/
#define DEFAULT_NUM_B    4                /* Default number of intervals blue */

#define DEFAULT_EXP_INT  TRUE             /* Default expand interval          */


/****************************************************************** 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_in;             /* file pointer                     */
  FILE                *fp_out;            /* file pointer                     */
  char                fn_in[FNS];         /* file name input                  */
  char                fn_out[FNS];        /* file name output                 */
  char                tmp_str[16];        /* temorary string                  */

  Pixrect             *mem_8_out;         /* pixrect in memory 8 Bit          */
  Pixrect             *mem_in;            /* pixrect in memory 24 or 32 Bit   */

  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                */
  register int        r,g,b;              /* value of red,green,blue          */

  unsigned char       *im_ptr_8_out;      /* pointer into 8 Bit image         */
  register int        *im_ptr_in;         /* pointer into 24 or 32 Bit image  */

  int                 x,y,depth;          /* size and depth of image          */
  int                 out_format;         /* outfileformat (standard,encoded) */
  int                 i,j,k,l;            /* used for loops                   */

  int                 int_num_r;          /* number of intervals for red      */
  int                 int_num_g;          /* number of intervals for green    */
  int                 int_num_b;          /* number of intervals for blue     */
  int                 int_start_r[MI];    /* interval start for red           */
  int                 int_start_g[MI];    /* interval start for green         */
  int                 int_start_b[MI];    /* interval start for blue          */
  unsigned char       int_mean_r[MI];     /* interval mean for red            */
  unsigned char       int_mean_g[MI];     /* interval mean for green          */
  unsigned char       int_mean_b[MI];     /* interval mean for blue           */
  float               int_length_r;       /* length of interval for red       */
  float               int_length_g;       /* length of interval for green     */
  float               int_length_b;       /* length of interval for blue      */
  register int        int_index_r;        /* index for red                    */
  register int        int_index_g;        /* index for green                  */
  register int        int_index_b;        /* index for blue                   */

  unsigned char       min_r;              /* minimum value of red             */
  unsigned char       max_r;              /* maximum value of red             */
  unsigned char       min_g;              /* minimum value of green           */
  unsigned char       max_g;              /* maximum value of green           */
  unsigned char       min_b;              /* minimum value of blue            */
  unsigned char       max_b;              /* maximum value of 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_expand_int;    /* flag if expand interval          */
  unsigned char       flag_option_ok;     /* flag if option ok                */
  unsigned char       flag_write_map;     /* flag if write colormap           */
  unsigned char       flag_floyd_steinb;  /* flag if floyd steinberg dither   */
  unsigned char       flag_stdout;        /* flag if write image to stdout    */
  unsigned char       flag_sunview;       /* flag if sunview                  */

  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                            /* spreading of errors              */
                      spread_hori,        /* spreading horizontal             */
                      spread_diag,        /* spreading diagonal               */
                      spread_vert;        /* spreading vertical               */
  register int        hundred;            /* spreading of errors              */
  register int        spread_percent;     /* spread error in %                */
  unsigned char       flag_percent;       /* for faster access                */


/******************************************************************** INIT    */
  /* set variables with defaults (defined in defaults.h) */

  /* file names with EOS (end of string) */
  for (i = 0; i < FNS; i++) fn_in[i] = fn_out[i] = '\0';

  /* pointers with NULL */
  fp_in = fp_out = NULL;
  mem_8_out = mem_in = NULL;

  /* number of intervals with default */
  int_num_r = DEFAULT_NUM_R;
  int_num_g = DEFAULT_NUM_G;
  int_num_b = DEFAULT_NUM_B;

  /* other default values */
  flag_expand_int = DEFAULT_EXP_INT;
  out_format = DEFAULT_OUT_FORM;
  flag_prompt = DEFAULT_PROMPT;
  flag_euclid = DEFAULT_EUCLID;
  flag_expand_name = DEFAULT_EXP_NAM;
  flag_write_map = DEFAULT_WRITE_MAP;
  flag_stdout = FALSE;
  flag_sunview = DEFAULT_SUNVIEW;

  /* set default for floyd steinberg dither */
  flag_floyd_steinb = DEFAULT_FLOYD;
  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]);
  }

  /* the last parameter is the input file name or the -h option */
  else if (argv[argc - 1] [0] != '-')
      strcpy (fn_in,argv[argc - 1]);
    else
      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);
      }
      else {
        fprintf (stderr,"Last parameter %s is not filename (but option) \n",
                                                                  argv[argc-1]);
        usage_exit (argv[0]);
      }


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

  while ( (i < argc - 1) && (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 - 1) {
        fprintf (stderr,"Missing parameter after %s. \n",argv[i-1]);
        fprintf (stderr,"%s is the 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],"-sv+") == 0) {
      i++;
      flag_option_ok = TRUE;
      flag_sunview = TRUE;
    }

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

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


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

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

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


    if (strcmp (argv[i],"-fs%") == 0) {
      i++;
      flag_option_ok = TRUE;
      if ( (argv[i][0] == '-') || (i >= argc - 1) ) {
        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;
      flag_floyd_steinb = TRUE;
      if ( (argv[i][0] != '-')  && (i < argc - 1) ) {
        spread_hori = atoi (argv[i]);
        i++;
        if ( (argv[i][0] == '-') || (i >= argc - 1) ) {
          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],"-fs-") == 0) {
      i++;
      flag_option_ok = TRUE;
      flag_floyd_steinb = FALSE;
    }

    if (strcmp (argv[i],"-fs") == 0) {
      i++;
      flag_option_ok = TRUE;
      flag_floyd_steinb = !flag_floyd_steinb;
      if (flag_floyd_steinb) {
        if ( (argv[i][0] != '-') && (i < argc - 1) ) {
          spread_hori = atoi (argv[i]);
          i++;
          if ( (argv[i][0] == '-') || (i >= argc - 1) ) {
            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 (strcmp (argv[i],"-m+") == 0) {
      i++;
      flag_option_ok = TRUE;
      flag_write_map = TRUE;
    }

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

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


    if (strcmp (argv[i],"-n") == 0) {
      flag_option_ok = TRUE;
      i++;
      if (i >= argc - 1) {
        fprintf (stderr,"Missing parameter after %s. \n",argv[i-1]);
        fprintf (stderr,"%s is the input filename! \n",argv[i]);
        usage_exit (argv[0]);
      }
      switch (atoi (argv[i])) {
        case 1 : int_num_r = 4; int_num_g = 8; int_num_b = 8; break;
        case 2 : int_num_r = 8; int_num_g = 4; int_num_b = 8; break;
        case 3 : int_num_r = 8; int_num_g = 8; int_num_b = 4; break;
        case 4 : int_num_r = 7; int_num_g = 6; int_num_b = 6; break;
        case 5 : int_num_r =16; int_num_g = 4; int_num_b = 4; break;
        case 6 : int_num_r = 4; int_num_g =16; int_num_b = 4; break;
        case 7 : int_num_r = 4; int_num_g = 4; int_num_b =16; break;
        default :
          fprintf (stderr,"Wrong usage of option -n (%s is not allowed) \n",
                                                                       argv[i]);
          usage_exit (argv[0]);
      }
      i++;
      if (flag_prompt)
        fprintf (stderr,"Number of intervals : ");
        fprintf (stderr,"%d for red, %d for green and %d for blue \n",
                         int_num_r,int_num_g,int_num_b);
    }
  }


  /* 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 (flag_sunview) {
    fprintf (stderr,"Sunview is set. So I take 7 6 6 as number of intervals\n");
    int_num_r = 7;
    int_num_g = 6;
    int_num_b = 6;
  }

  /* function in pic_io.c */
  set_file_names (fn_in,fn_out);


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

    /* u means uniform quantisation algorithm */
    strcat (fn_out,"_u_");

    /* add number of intervals */
    sprintf (tmp_str, "%d", int_num_r);
    strcat (fn_out,tmp_str);
    sprintf (tmp_str, "%d", int_num_g);
    strcat (fn_out,tmp_str);
    sprintf (tmp_str, "%d", int_num_b);
    strcat (fn_out,tmp_str);

    /* add ei if expand interval is set */
    if (flag_expand_int)
      strcat (fn_out,"_ei");

    /* add floyd steinbeg */
    if (flag_floyd_steinb) {
      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 image */
  /* functions in pic_io.c */
  if (open_in_file (fn_in,&fp_in,fp_out,mem_in,mem_8_out) != NULL) exit (1);
  if (open_out_file (fn_out,fp_in,&fp_out,mem_in,mem_8_out) != NULL) exit (1);
  if (    get_image (fn_in,fp_in,fp_out,&mem_in,mem_8_out,
                     flag_prompt,&x,&y,&depth) != NULL) exit (1);

  /* allocate memory for output image */
  if (get_mem (fp_in,fp_out,mem_in,&mem_8_out,flag_prompt,x,y) != NULL)
    exit (1);


/*********************************************************** COLOR STATISTICS */
  /* scan for minimum and maximum values of red, green and blue    */
  /* there could be a break if all minimums are 0 and all maximums */
  /* are 255, but it's time consuming to check                     */
  /* over all it's better this way                                 */

  if (flag_prompt) fprintf (stderr,"Sampling for color statistics ... ");

  /* init min's and max's */
  min_r = min_g = min_b = 255;
  max_r = max_g = max_b = 0;

  /* set image pointer to first pixel. Macro in include pixrect */
  im_ptr_in = (int *) mpr_d(mem_in)->md_image;

  /* loop over all pixels */
  for (i = 0; i < x*y; i++, im_ptr_in++) {

    r =  (*im_ptr_in)        & 0xff;
    g = ((*im_ptr_in) >> 8 ) & 0xff;
    b = ((*im_ptr_in) >> 16) & 0xff;

    if (r < min_r) min_r = r;
    if (r > max_r) max_r = r;
    if (g < min_g) min_g = g;
    if (g > max_g) max_g = g;
    if (b < min_b) min_b = b;
    if (b > max_b) max_b = b;
  }

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


  /* set the length of the intervals. Float is better than int because */
  /* the interval start is calculated with the length in a loop        */
  int_length_r = ((float) (max_r - min_r) / int_num_r);
  int_length_g = ((float) (max_g - min_g) / int_num_g);
  int_length_b = ((float) (max_b - min_b) / int_num_b);

  if (flag_prompt) {
    fprintf (stderr,"  Red   : min=%3d  max=%3d  interval length=%2.2f \n",
                                                     min_r,max_r,int_length_r);
    fprintf (stderr,"  Green : min=%3d  max=%3d  interval length=%2.2f \n",
                                                     min_g,max_g,int_length_g);
    fprintf (stderr,"  Blue  : min=%3d  max=%3d  interval length=%2.2f \n",
                                                     min_b,max_b,int_length_b);
  }


  /* set interval start */
  for (i = 0; i < int_num_r; i++) int_start_r[i] = min_r+(int)(i*int_length_r);
  for (i = 0; i < int_num_g; i++) int_start_g[i] = min_g+(int)(i*int_length_g);
  for (i = 0; i < int_num_b; i++) int_start_b[i] = min_b+(int)(i*int_length_b);

  /* this is needed in the mapping algorithm to break the loop    */
  int_start_r[int_num_r] = 256;
  int_start_g[int_num_g] = 256;
  int_start_b[int_num_b] = 256;


  /* all colors in one interval are set to the mean of the interval   */
  /* so here the means are calculated as the middle of an interval    */
  /* int_mean is unsigned char but with                               */
  /* int_mean_r[i] = (unsigned char) (int_start .... ) it won't work  */
  for (i = 0; i < int_num_r; i++) int_mean_r[i] =
                                       (int_start_r[i] + int_start_r[i+1]) / 2;
  for (i = 0; i < int_num_g; i++) int_mean_g[i] =
                                       (int_start_g[i] + int_start_g[i+1]) / 2;
  for (i = 0; i < int_num_b; i++) int_mean_b[i] =
                                       (int_start_b[i] + int_start_b[i+1]) / 2;


  /* if expand interval is set then black (0,0,0) will be black       */
  /* and white (255,255,255) will be white                            */
  if (flag_expand_int) {
    int_mean_r[0] = min_r; int_mean_r[int_num_r-1] = max_r;
    int_mean_g[0] = min_g; int_mean_g[int_num_g-1] = max_g;
    int_mean_b[0] = min_b; int_mean_b[int_num_b-1] = max_b;
  }


  if (flag_prompt) {
    fprintf (stderr,"  Colormap is made with : \n");
    fprintf (stderr,"    Red   : ");
    for (i = 0; i < int_num_r; i++ ) fprintf (stderr,"%3d ",int_mean_r[i]);
    fprintf (stderr,"\n    Green : ");
    for (i = 0; i < int_num_g; i++ ) fprintf (stderr,"%3d ",int_mean_g[i]);
    fprintf (stderr,"\n    Blue  : ");
    for (i = 0; i < int_num_b; i++ ) fprintf (stderr,"%3d ",int_mean_b[i]);
    fprintf (stderr,"\n");
  }


  /* now set the colormap */
  if (flag_prompt) fprintf (stderr,"Choosing colormap ... ");
  l=0;
  for (i = 0; i < 256; i++) red[i] = green[i] = blue[i] = 0;
  for (i = 0; i < int_num_r; i++)
    for (j = 0; j < int_num_g; j++)
      for (k = 0; k < int_num_b; k++, l++) {
        red[l]   = int_mean_r[i];
        green[l] = int_mean_g[j];
        blue[l]  = int_mean_b[k];
  }

  colormap.type   = RMT_EQUAL_RGB;
  colormap.length = 256;
  colormap.map[0] = red; colormap.map[1] = green; colormap.map[2] = blue;

  if  (flag_sunview) {
    /* reserve sunview black and white in colormap */
    red[254]   = 255;
    green[254] = 255;
    blue[254]  = 255;

    red[255]   = 0;
    green[255] = 0;
    blue[255]  = 0;
  }

  /* fix bug or undocumented function of pixrect functions */
  if ( (red[0] == 0) && (green[0] == 0) && (blue[0] == 0) ) blue[0] = 1;

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


/************************************************************* MAPPING COLORS */
  if (!flag_floyd_steinb) {

    /* map colors to colormap */
    if (flag_prompt) fprintf (stderr,"Mapping colors ... ");

    /* set image pointers to first pixel */
    im_ptr_in = (int *) mpr_d(mem_in)->md_image;
    im_ptr_8_out = (unsigned char *) mpr_d(mem_8_out)->md_image;

    /* loop over all pixels in x direction */
    for (i = 0; i < x*y; i++, im_ptr_in++, im_ptr_8_out++) {

      r =  (*im_ptr_in)        & 0xff;
      g = ((*im_ptr_in) >>  8) & 0xff;
      b = ((*im_ptr_in) >> 16) & 0xff;

      /* if color value is > than interval start then take next interval */
      int_index_r = int_index_g = int_index_b = 0;

      while (r > int_start_r[int_index_r + 1]) int_index_r++;
      while (g > int_start_g[int_index_g + 1]) int_index_g++;
      while (b > int_start_b[int_index_b + 1]) int_index_b++;

      /* look the coding! */
      *im_ptr_8_out = (unsigned char)
                      int_index_r * int_num_g  * int_num_b +
                      int_index_g * int_num_b  +
                      int_index_b;
    }
  }

  else {


/**************************************** MAPPING COLORS WITH FLOYD STEINBERG */
    /* map colors to colormap using floyd steinberg dithering */
    if (flag_prompt) {
      fprintf (stderr,"Error spreading : horizontal=%d  diagonal=%d  ",
                       spread_hori, spread_diag);
      fprintf (stderr,"vertical=%d  percent=%d\n",
                       spread_vert,spread_percent);
      fprintf (stderr,"Mapping colors using Floyd Steinberg dithering ... ");
    }

    /* 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 *) error_r);
      close_error (fp_in,fp_out,mem_in,mem_8_out);
      exit(1);
    }

    if ( (error_g = (int *) malloc ( (unsigned) x * sizeof (int))) == NULL) {
      fprintf (stderr,"\nError in malloc(). Not enough memory!\n");
      free ( (char *) error_r);
      free ( (char *) error_g);
      close_error (fp_in,fp_out,mem_in,mem_8_out);
      exit(1);
    }

    if ( (error_b = (int *) malloc ( (unsigned) x * sizeof (int))) == NULL) {
      fprintf (stderr,"\nError in malloc(). Not enough memory!\n");
      free ( (char *) error_r);
      free ( (char *) error_g);
      free ( (char *) error_b);
      close_error (fp_in,fp_out,mem_in,mem_8_out);
      exit(1);
    }

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

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

    /* set pointers to first pixel */
    im_ptr_in = (int *) mpr_d(mem_in)->md_image;
    im_ptr_8_out = (unsigned char *) mpr_d(mem_8_out)->md_image;

    /* 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_in++, im_ptr_8_out++) {

        /* get new colors and add errors */
        r = ( (*im_ptr_in)        & 0xff) + error_r[j] + hori_err_r;
        g = (((*im_ptr_in) >>  8) & 0xff) + error_g[j] + hori_err_g;
        b = (((*im_ptr_in) >> 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;

        /* if color value is > than interval start then take next interval */
        int_index_r = int_index_g = int_index_b = 0;

        while (r > int_start_r[int_index_r + 1]) int_index_r++;
        while (g > int_start_g[int_index_g + 1]) int_index_g++;
        while (b > int_start_b[int_index_b + 1]) int_index_b++;

        *im_ptr_8_out = (unsigned char)
                        int_index_r * int_num_g  * int_num_b +
                        int_index_g * int_num_b  +
                        int_index_b;

        /* set current errors */
        if (flag_percent) {

          curr_err_r = (int) (spread_percent * (r - red[*im_ptr_8_out]))   /
                                                                      hundred;
          curr_err_g = (int) (spread_percent * (g - green[*im_ptr_8_out])) /
                                                                      hundred;
          curr_err_b = (int) (spread_percent * (b - blue[*im_ptr_8_out]))  /
                                                                      hundred;
        }

        else {
          curr_err_r = r - red[*im_ptr_8_out];
          curr_err_g = g - green[*im_ptr_8_out];
          curr_err_b = b - blue[*im_ptr_8_out];
        }

        /* 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");


/**************************************************************** WRITE IMAGE */
  /* write output image */
  if (put_image (fn_out,fp_in,fp_out,mem_in,mem_8_out,
      flag_prompt,colormap,out_format) != NULL) exit (1);


/************************************************************ WRITE TO STDOUT */
  /* write colormap to stdout if option is set */
  if (flag_write_map)
    if (flag_stdout) {
      fprintf (stderr,"I write the image to stdout. ");
      fprintf (stderr,"So no other writing to stdout allowed! \n");
    }
    else write_map (fn_out,red,green,blue);


  /* calculate euclidean distance if option is set */
  if (flag_euclid)
    print_euclidean_dist
                       (fn_out,mem_in,mem_8_out,flag_prompt,red,green,blue,x,y);


/**************************************************************** ENDING MAIN */
  /* close files and free memory */
  close_success (fp_in,fp_out,mem_in,mem_8_out);

  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} <input filename> \n",name);
  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,"               -sv[+|-]        colors for sunview \n");
  fprintf (stderr,"               -n <1..7>       number of intervals\n");
  fprintf (stderr,"                                 red green blue \n");
  fprintf (stderr,"                   1               4    8    8 \n");
  fprintf (stderr,"                   2               8    4    8 \n");
  fprintf (stderr,"                   3               8    8    4 \n");
  fprintf (stderr,"                   4               7    6    6 \n");
  fprintf (stderr,"                   5              16    4    4 \n");
  fprintf (stderr,"                   6               4   16    4 \n");
  fprintf (stderr,"                   7               4    4   16 \n");
  fprintf (stderr,"               -ei[+|-]        expand interval \n");
  fprintf (stderr,"               -fs[+|-] [h d]  use 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 euclidean ");
  fprintf (stderr,"distance \n");
  fprintf (stderr,"                               and write to stdout \n");
  fprintf (stderr,"               -m[+|-]         write colormap 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 input file   : %s \n",DEFAULT_IN_EXT); */
  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");

  if (DEFAULT_SUNVIEW)
    fprintf (stderr,"  - Colors for sunview        : + \n");
  else
    fprintf (stderr,"  - Colors for sunview        : - \n");

  fprintf (stderr,"  - Number of intervals       : %d red, %d green, %d blue\n"
                                 ,DEFAULT_NUM_R,DEFAULT_NUM_G,DEFAULT_NUM_B);

  if (DEFAULT_EXP_INT)
    fprintf (stderr,"  - Expand interval           : + \n");
  else
    fprintf (stderr,"  - Expand interval           : - \n");

  if (DEFAULT_FLOYD)
    fprintf (stderr,"  - Floyd Steinberg dither    : +  %d %d\n",
                        DEFAULT_SPREAD_HORI,DEFAULT_SPREAD_DIAG);
  else
    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_WRITE_MAP)
    fprintf (stderr,"  - Write colormap to stdout  : + \n");
  else
    fprintf (stderr,"  - Write colormap to stdout  : - \n");

  if (DEFAULT_PROMPT)
    fprintf (stderr,"  - Prompt info to stderr     : + \n");
  else
    fprintf (stderr,"  - Prompt info 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 convert 24 or 32 Bit pixrect or rayshade images\n");
  fprintf (stderr," to 8 Bit colormapped images for suns using the uniform\n");
  fprintf (stderr," algorithm. With the -ei option i will keep the");
  fprintf (stderr," contrast.\n Here are some valid examples for calling:\n");
  fprintf (stderr,"   %s -n 4 -en- -p hsv.pixrect\n",name);
  fprintf (stderr,"   %s -sv -o %s tree.pic | rastool \n",name,DEFAULT_STDOUT);
  fprintf (stderr,"   %s -fs -o test.pic_8 test.pic_32 \n",name);
  fprintf (stderr,"   %s -fs -fs%% 75 -sv hsv\n",name);
  exit (1);
}
















               

  
