/* 
 * rle - read in a Utah RLE Toolkit type image.
 * 
 * Author:	Graeme Gill
 * Date: 	30/5/90
 * 
 * Bugs - doesn't free up memory used by rle functions.
 *
 */

#undef  DEBUG

#ifdef DEBUG
# define debug(xx)	fprintf(stderr,xx)
#else
# define debug(xx)
#endif


#define MIN(a,b) ( (a)<(b) ? (a) : (b))

#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include "image.h"
#include "rle.h"

void dithermap();

/* input file stuff */
static int ptype;				/* picture type : */
#define BW_NM	0		/* black and white, no map */
#define BW_M	1		/* black and white, and a map */
#define SC_M	2		/* single colour channel and colour map */
#define C_NM	3		/* full colour, no maps */
#define C_M	4		/* full colour with colour maps */
static rle_pixel **fmaps;	/* file colour maps from buildmap() */
static unsigned char **scan;	/* buffer for input data */
static int x_min;		/* copy of picture x_min */
static int y_min;		/* copy of picture y_min */

/* option stuff (Not functional) */
static float disp_gam = 1.0;	/* default display gamma correction factor */
static int iflag=0;			/* user suplied image gamma */
static float img_gam = 1.0;			/* image gamma */
static int bwflag = 0;			/* black and white flag */
static int gammamap[256];
static int background[3] = {0,0,0};	/* our background colour */

/* stuff for dithering colour pictures */
int colmap[216][3];		/* for dither map */
int magic[16][16];
int modN[256];
int divN[256];

#define DMAP(v,x,y)	(modN[v]>magic[x][y] ? divN[v] + 1 : divN[v])

/* run the black and white through its map */
static void bw_m_line(dp,number)
     int number;
     register unsigned char *dp;
{
  register unsigned char *r;
  register int i;
  
  for(i=number,r= &scan[0][0];i>0;i--,r++,dp++)
    {
      *dp = fmaps[0][*r];
    }
}

/* convert a colour line with map to 8 bits per pixel */
static void c_m_line(dp,number,line)
     int number,line;
     register unsigned char *dp;
{
  register unsigned char *r, *g, *b;
  register int i, col, row;
  
  if(!bwflag)
    {		
      for ( row = line % dith_size, col = x_min % dith_size, i = number, r = &scan[0][0]
	   ,g= &scan[1][0], b= &scan[2][0];
	   i > 0; i--, r++, g++, b++, dp++, col = ((col + 1) % dith_size) )
	{
	  *dp = DMAP(fmaps[0][*r], col, row) +
	    DMAP(fmaps[1][*g], col, row) * 6 +
	      DMAP(fmaps[2][*b], col, row) * 36;
	}
    }
  else
    {
      int red,green,blue;
      for (i = number, r= &scan[0][0], g= &scan[1][0]
	   ,b= &scan[2][0]; i>0;i--,r++,g++,b++,dp++)
	{
	  red = fmaps[0][*r];green=fmaps[1][*g];blue = fmaps[2][*b];
	  *dp = 0.35* red + 0.55* green + 0.1* blue;
	}
    }
}

int rleIdent(fullname, name)
     char *fullname, *name;
{
  ZFILE *rlefile;
  int x_len,y_len;
  int rv;

  debug("rleIdent called\n");
  rlefile = zopen(fullname);
  if(!rlefile)
    {
      perror("rleIdent: Unable to open file");
      return(0);
    }
  debug("rleIdent: Opened file ok\n");
  sv_globals.svfb_fd = rlefile;
  rv = rle_get_setup(&sv_globals);
  debug("rleIdent: got setup ok\n");
  zclose(rlefile);
  debug("rleIdent: closed file ok\n");
  switch(rv)
    {
    case -1:
      return 0;		/* Not rle format */
    case 0:
      /* now figure out the picture type */
      switch(sv_globals.sv_ncolors)
	{
	case 0:
	  perror("rleIdent: no colour channels to display");
	  return(0);
	case 1:
	  x_len = sv_globals.sv_xmax - sv_globals.sv_xmin + 1;
	  y_len = sv_globals.sv_ymax - sv_globals.sv_ymin + 1;
	  fprintf(stderr, "%s is a %dx%d", name, x_len, y_len);
	  switch(sv_globals.sv_ncmap) {
	    case 0:
	      /* black and white, no map */
	      fprintf(stderr, " 8 bit grey scale RLE image with no map\n"); 
	      break;
	    case 1:
	      /* black and white with a map */
	      fprintf(stderr, " 8 bit grey scale RLE image with map\n"); 
	      break;
	    case 3:
	      /* single channel encoded colour with decoding map */
	      fprintf(stderr, " 8 bit color RLE image with color map\n");
	      break;
	    default:
	      perror(" 8 bit RLE image with an illegal color map\n");
	      return 0;
	    }
	  break;
	case 3:
	  x_len = sv_globals.sv_xmax - sv_globals.sv_xmin + 1;
	  y_len = sv_globals.sv_ymax - sv_globals.sv_ymin + 1;
	  fprintf(stderr, "%s is a %dx%d", name, x_len, y_len);
	  switch(sv_globals.sv_ncmap)
	    {
	    case 0:
	      fprintf(stderr, " 24 bit color RLE image with no map\n");
	      break;
	    case 3:
	      fprintf(stderr, " 24 bit color RLE image with colour map\n");
	      break;
	    default:
	      fprintf(stderr, " 24 bit color RLE image with an illegal color map\n");
	      return 0;
	    }
	  break;
	default:
	  x_len = sv_globals.sv_xmax - sv_globals.sv_xmin + 1;
	  y_len = sv_globals.sv_ymax - sv_globals.sv_ymin + 1;
	  fprintf(stderr, "%s is a %dx%d", name, x_len, y_len);
	  fprintf(stderr, " RLE image with an illegal number of color planes\n");
	  return 0;
	}
      return 1;
    default:			/* Some sort of error */
      /*			perror("rleIdent");*/
      return 0;
    }
}

Image *rleLoad(fullname,name,verbose)
     char *fullname,*name;
     unsigned int verbose;
{
  int x_len, y_len;
  int i,j;
  ZFILE *rlefile;
  int ncol;			/* number of colors */
  int depth;
  unsigned char *bufp;
  Image *image;
  unsigned char *buf;
  
  dith_levels = 256;	/* aim for 128 levels of each colour */
  
  debug("rleLoad called\n");
  rlefile = zopen(fullname);
  if(!rlefile)
    {
      perror("rleLoad: Cannot open input file");
      return(NULL);	
    }
  sv_globals.svfb_fd = rlefile;
  debug("rleLoad: About to call get_setup\n");
  if(rle_get_setup( &sv_globals )) {
    zclose(rlefile);
    return(NULL);	
  }
  
  debug("rleLoad: get_setup called ok\n");
  if(iflag == 1)	/* -i flag */
    img_gam = 1.0/img_gam;		/* convert to display gamma */
  
  /* If no image gamma on command line, check comments in file */
  if (!iflag)
    {
      char * v;
      if ( (v = rle_getcom( "image_gamma", &sv_globals )) != NULL )
	{	
	  img_gam = atof( v );
	  /* Protect against bogus information */
	  if ( img_gam == 0.0 )
	    img_gam = 1.0;
	  else
	    img_gam = 1.0 / img_gam;	/* convert to display gamma */
	}
      else if ( (v = rle_getcom( "display_gamma", &sv_globals )) != NULL )
	{
	  img_gam = atof( v );
	  /* Protect */
	  if ( img_gam == 0.0 )
	    img_gam = 1.0;
	}
    }
  
  x_len = sv_globals.sv_xmax - sv_globals.sv_xmin + 1;
  y_len = sv_globals.sv_ymax - sv_globals.sv_ymin + 1;
  
  x_min = sv_globals.sv_xmin;
  y_min = sv_globals.sv_ymin;
  
  /* fix this so that we don't waste space */
  sv_globals.sv_xmax -= sv_globals.sv_xmin;
  sv_globals.sv_xmin = 0;
  
  /* turn off the alpha channel (don't waste time and space)*/
  sv_globals.sv_alpha = 0;
  SV_CLR_BIT(sv_globals,SV_ALPHA);
  
  /* for now, force background clear */
  if(sv_globals.sv_background ==1)	/* danger setting */
    {
      debug("Forcing clear of background\n");
      sv_globals.sv_background = 2;
      if(sv_globals.sv_bg_color==0)	/* if none allocated */
	sv_globals.sv_bg_color = background;	/* use this one */
    }
  
  /* now figure out the picture type */
  switch(sv_globals.sv_ncolors)
    {
    case 0:
      perror("rleLoad: no colour channels to display");
      zclose(rlefile);
      return(NULL);
    case 1:
      switch(sv_globals.sv_ncmap)
	{
	case 0:
	  ptype = BW_NM;	/* black and white, no map */
	  break;
	case 1:
	  ptype = BW_M;	/* black and white with a map */
	  break;
	case 3:
	  ptype = SC_M;	/* single channel encoded colour with decoding map */
	  break;
	default:
	  zclose(rlefile);
	  perror("rleLoad: Illegal number of maps for one colour channel");
	  return(NULL);
	}
      break;
    case 3:
      switch(sv_globals.sv_ncmap)
	{
	case 0:
	  ptype = C_NM;	/* colour, no map */
	  break;
	case 3:
	  ptype = C_M;	/* colour with maps */
	  break;
	default:
	  perror("rleLoad: Illegal number of maps for colour picture");
	  zclose(rlefile);
	  return(NULL);
	}
      break;
    default:
      perror("rleLoad: Illegal number of colour channels");
      zclose(rlefile);
      return(NULL);
    }
  
  if(verbose)
    {
      fprintf(stderr, "%s is a %dx%d",name,x_len,y_len);
      switch(ptype)
	{
	case BW_NM:
	  fprintf(stderr, " 8 bit grey scale RLE image with no map"); 
	  break;
	case BW_M:
	  fprintf(stderr, " 8 bit grey scale RLE image with map"); 
	  break;
	case SC_M:
	  fprintf(stderr, " 8 bit RLE image with colour map");
	  break;
	case C_NM:
	  fprintf(stderr, " 24 bit RLE image with no map (will dither to 8 bits)");
	  break;
	case C_M:
	  fprintf(stderr, " 24 bit RLE image with color map (will dither to 8 bits)");
	  break;
	}
      fprintf(stderr, ", with gamma of %4.2f\n",img_gam);
    }
  znocache(rlefile);
  
  if(ptype==SC_M)
    {	/* don't mess with the image, but change their colour map a bit if needed */
      disp_gam /= img_gam;	/* amount to change their map */
      img_gam = 1.0;		/*  not to the image coming in */
    }
  
  /* get hold of the colour maps, and set to undo their images gamma */
  fmaps = buildmap(&sv_globals,sv_globals.sv_ncolors,img_gam);
  
  /* now we had better sort out the picture data */
  debug("done colour map\n");
  
  /* rle stufff */
  /* Get space for a full colour scan line */
  scan = (unsigned char **) lmalloc( (sv_globals.sv_ncolors +
				      sv_globals.sv_alpha) *
				    sizeof( unsigned char * ) );
  for ( i = 0; i < sv_globals.sv_ncolors + sv_globals.sv_alpha; i++ )
    scan[i] = (unsigned char *)lmalloc(x_len);
  if ( sv_globals.sv_alpha )
    scan++;
  debug("got space for get_row\n");
  
  depth = 8;		/* We always supply 8bit images */
  image = newRGBImage(x_len,y_len,depth);
  image->title = dupString(name);
  debug("got image structure\n");
  
  buf = image->data;
  
  /* If we are going to dither - then create the dither matrix. */
  if(!bwflag && ptype!=SC_M && ptype != BW_NM && ptype != BW_M)
    {
      dith_np2 = 1;		/* allow non-power of 2 dither map size */
      dithermap( 6, disp_gam, colmap, divN, modN, magic );
    }
  
  debug("About to read image in\n");
  bufp = buf + (y_len-1) * x_len;
  for(j=y_len;j>0;j--,bufp -= x_len)
    {
      rle_getrow(&sv_globals,scan);
      switch(ptype)
	{
	case SC_M:
	  memcpy(bufp,&scan[0][0],x_len);
	  break;
	case BW_NM:
	case BW_M:
	  bw_m_line(bufp,x_len);
	  break;
	case C_NM:
	case C_M:
	  c_m_line(bufp,x_len,j);
	  break;
	}
    }
  debug("Image Read in\n");
  
  /* Deal with colour maps */
  /* set up our gamma correction */
  make_gamma(disp_gam,gammamap);	/* we'll need it */
  
  debug("Creating color map\n");
  /* now load an appropriate colour map */
  if(!bwflag && ptype==SC_M)
    {
      /* use their maps  & correct their gamma */
      ncol = 1<<sv_globals.sv_cmaplen;	/* number of entries */
      for(i=0;i<ncol;i++)
	{
	  *(image->rgb.red + i) = gammamap[fmaps[0][i]]<<8;
	  *(image->rgb.green + i) = gammamap[fmaps[1][i]]<<8;
	  *(image->rgb.blue + i) = gammamap[fmaps[2][i]]<<8;
	}
    }
  else if(bwflag || ptype == BW_NM || ptype == BW_M)
    {
      /* load a black and white map (gamma corrected for this display)*/
      ncol = 256;			/* don't know whats been used */
      for(i=0;i<ncol;i++)
	{
	  *(image->rgb.red + i) = 
	    *(image->rgb.green + i) = 
	      *(image->rgb.blue + i) = gammamap[i]<<8;
	}
    }
  else
    {
      /* must be colour, so use dither map (gamma corrected for this display)*/
      ncol = 6*6*6;
      /* Dither map has already been created above */
      for(i = 0; i < ncol; i++)
	{
	  *(image->rgb.red + i) = colmap[i][0]<<8;
	  *(image->rgb.green + i) = colmap[i][1]<<8;
	  *(image->rgb.blue + i) = colmap[i][2]<<8;
	}
    }
  image->rgb.used = ncol;
  
  zclose(rlefile);
  debug("finished\n");
  return(image);
}

