/*
 * xvrle.c - load routine for rle (Utah Raster Toolkit) format pictures
 *
 * LoadRLE(fname, numcols)  -  loads an RLE file
 */

/*
 * Copyright 1989, 1990, 1991, 1992 by John Bradley and
 *                       The University of Pennsylvania
 *
 * Permission to use, copy, and distribute for non-commercial purposes,
 * is hereby granted without fee, providing that the above copyright
 * notice appear in all copies and that both the copyright notice and this
 * permission notice appear in supporting documentation.
 *
 * The software may be modified for your own purposes, but modified versions
 * may not be distributed.
 *
 * This software is provided "as is" without any expressed or implied warranty.
 *
 * The author may be contacted via:
 *    US Mail:   John Bradley
 *               GRASP Lab, Room 301C
 *               3401 Walnut St.
 *               Philadelphia, PA  19104
 *
 *    Phone:     (215) 898-8813
 *    EMail:     bradley@cis.upenn.edu
 */


#include "xv.h"


/* Definitions needed to parse RLE format */
/*   snarfed from rle_code.h, part of the Utah Raster Toolkit */
#define     LONG_OP             0x40
#define     RSkipLinesOp        1
#define     RSetColorOp         2
#define     RSkipPixelsOp       3
#define     RByteDataOp         5
#define     RRunDataOp          6
#define     REOFOp              7

#define     H_CLEARFIRST        0x1   /* clear framebuffer flag */
#define     H_NO_BACKGROUND     0x2   /* if set, no bg color supplied */
#define     H_ALPHA             0x4   /* if set, alpha channel (-1) present */
#define     H_COMMENT           0x8   /* if set, comments present */


#define GETINT(fp) (c=getc(fp), c1=getc(fp), (c1<<8) + c )

static void read_rle();
static int RLEerr();



/*******************************************/
int LoadRLE(fname,nc)
     char *fname;
     int   nc;
/*******************************************/
{
  FILE  *fp;
  int    c, c1, i, j, k;
  byte   bgcol[256];
  byte   maps[3][256];
  int    xpos, ypos, w, h, flags, ncolors, pixelbits, ncmap, cmaplen;
  int    cmtlen;
  byte  *img;
  long filesize;


  /* open the stream */
  fp=fopen(fname,READ_BINARY);
  if (!fp) return 1;
  

  /* figure out the file size */
  fseek(fp, 0L, 2);
  filesize = ftell(fp);
  fseek(fp, 0L, 0);


  /* read the magic number */
  c = getc(fp);  c1 = getc(fp);
  if ((c != 0x52) || (c1 != 0xcc))
    return(RLEerr("unrecognized magic number"));

  xpos       = GETINT(fp);    /* read rest of header info */
  ypos       = GETINT(fp);
  w          = GETINT(fp);
  h          = GETINT(fp);
  flags      = getc(fp);
  ncolors    = getc(fp);
  pixelbits  = getc(fp);
  ncmap      = getc(fp);
  c          = getc(fp);
  cmaplen = (1L << c);

  if (DEBUG) {
    fprintf(stderr,"RLE: %dx%d image at %d,%d\n", w, h, xpos, ypos);
    fprintf(stderr,"flags: 0x%02x  (%s%s%s%s)\n",
	    flags, 
	    (flags & H_CLEARFIRST)    ? "CLEARFIRST " : "",
	    (flags & H_NO_BACKGROUND) ? "NO_BG " : "",
	    (flags & H_ALPHA)         ? "ALPHA " : "",
	    (flags & H_COMMENT)       ? "COMMENT" : "");

    fprintf(stderr, "%d bands, %d pixelbits, %d cmap bands, %d cmap entries\n",
	    ncolors, pixelbits, ncmap, cmaplen);
  }  

  if (DEBUG) fprintf(stderr, "background value: ");
  for (i=0; i<ncolors; i++) {
    bgcol[i] = getc(fp);
    if (DEBUG) fprintf(stderr, "0x%02x ", bgcol[i]);
  }
  if (DEBUG) fprintf(stderr,"\n");

  if ((ncolors % 2) == 0) getc(fp);     /* get on a word boundary */

  /* read colormap(s) */
  for (i=0; i<ncmap; i++) {
    for (j = 0; j<cmaplen; j++) {
      k = GETINT(fp);
      if (i<3) maps[i][j] = k>>8;
    }
  }

  if (DEBUG) {
    if (ncmap) {
      fprintf(stderr, "Colormap:\n");
      for (i=0; i<cmaplen; i++) {
	fprintf(stderr,"(");
	for (j=0; (j<ncmap && j<3); j++) {
	  fprintf(stderr, "%02x ", maps[j][i]);
	}
	fprintf(stderr,")  ");
      }
      fprintf(stderr,"\n\n");
    }
    else fprintf(stderr,"No colormap\n");
  }


  /* read (skip over, actually) the comments, if any */
  if (flags & H_COMMENT) {
    cmtlen = GETINT(fp);
    if (cmtlen) {
      if (DEBUG) fprintf(stderr,"Comment: (%d bytes) '", cmtlen);
      for (i=0; i<cmtlen; i++) {
	c = getc(fp);
	if (DEBUG) fprintf(stderr,"%c",c);
      }
      if (cmtlen % 2) getc(fp);    /* get on a word boundary */
      if (DEBUG) fprintf(stderr,"'\n\n");
    }
  }


  if (ferror(fp)) 
    return RLEerr("EOF reached while reading RLE header.\n");


  /*
   * Acceptable cases:
   *   ncolors = 1, 3, or >3 (extra planes ignored)
   *   pixelbits = 8
   *   ncmap = 0    (interpreted as TrueColor/TrueGray)
   *           1    (TrueColor/TrueGray with a gamma curve)
   *    3 | ncolors (TrueColor with three gamma curves)
   */

  if (ncolors == 0 || ncolors == 2) 
    return RLEerr("Unsupported number of channels in RLE file.\n");

  if (pixelbits != 8)
    return RLEerr("Only 8-bit pixels supported in RLE files.\n");

  if (ncmap==0 || ncmap==1 || (ncmap == 3 && ncolors >=3)) { /* ok */ }
  else return RLEerr("Invalid # of colormap channels in RLE file.\n");

  if (w<1 || h<1) 
    return RLEerr("Bogus size in RLE header.\n");



  /* allocate image memory */
  if (ncolors == 1) img = (byte *) calloc(w * h, 1);
               else img = (byte *) calloc(w * h * 3, 1);
  if (!img) return RLEerr("LoadRLE(): Unable to allocate image data.\n");


  /* set background, if necessary */
  if ((flags & H_CLEARFIRST) && ((~flags) & H_NO_BACKGROUND)) {
    byte *ip;
    if (ncolors == 1) {
      for (i=0, ip=img; i<w*h; i++, ip++) *ip = bgcol[0];
    }
    else {
      for (i=0, ip=img; i<w*h; i++) 
	for (j=0; j<3; j++, ip++) *ip = bgcol[j];
    }
  }


  read_rle(fp, img, w, h, ncolors, ncmap);


  /* apply colormap transformation to image */
  if (ncmap) {
    byte *ip;
    int   imagelen, cmask;
    imagelen = (ncolors==1) ? w*h : w*h*3;
    cmask = (cmaplen-1);

    if (ncmap == 1) {
      for (i=0, ip=img; i<imagelen; i++, ip++) *ip = maps[0][*ip & cmask];
    }

    else if (ncmap == 3) {
      for (i=0, ip=img; i<w*h; i++) {
	*ip = maps[0][*ip & cmask];   ip++;
	*ip = maps[1][*ip & cmask];   ip++;
	*ip = maps[2][*ip & cmask];   ip++;
      }
    }
  }


  /* finally, convert into XV internal format */
  if (ncolors == 1) {     /* grayscale */
    pic = img;  pWIDE = w;  pHIGH = h;
    SetISTR(ISTR_FORMAT, "Greyscale RLE.  (%ld bytes)", filesize);
    sprintf(formatStr, "%dx%d RLE.",w, h);
    SetDirRButt(F_COLORS, F_GREYSCALE);

    for (i=0; i<256; i++) r[i] = g[i] = b[i] = i;  /* gray cmap */
  }

  else {                  /* true color */
    SetISTR(ISTR_FORMAT, "Color RLE.  (%ld bytes)", filesize);
    sprintf(formatStr, "%dx%d RLE.",w, h);
    SetDirRButt(F_COLORS, F_FULLCOLOR);

    i = Conv24to8(img, w, h, nc);
    free(img);
    return i;
  }

  return 0;
}


/*******************************************/
static void read_rle(fp, img, w, h, ncolors, ncmap)
     FILE *fp;
     byte *img;
     int   w, h, ncolors, ncmap;
{
  int posx, posy, plane, bperpix, i, pixval, skipcalls;
  int opcode, operand, done, c, c1;    
  byte *ip;

  posx = posy = plane = done = skipcalls = 0;
  if (ncolors == 1) bperpix = 1;
               else bperpix = 3;


  while (!done && (opcode=getc(fp)) != EOF) {
    switch (opcode & 0x3f) {
    case RSkipLinesOp:
      if (opcode & LONG_OP) { getc(fp);  operand = GETINT(fp); }
      else operand = getc(fp);
      posx = 0;
      posy += operand;
      skipcalls++;
      if ((skipcalls & 0x7f)==0) WaitCursor();
      break;


    case RSetColorOp:
      operand = getc(fp);
      plane = operand;
      posx = 0;
      break;


    case RSkipPixelsOp:
      if (opcode & LONG_OP) { getc(fp);  operand = GETINT(fp); }
      else operand = getc(fp);
      
      posx += operand;
      break;


    case RByteDataOp:
      if (opcode & LONG_OP) { getc(fp);  operand = GETINT(fp); }
      else operand = getc(fp);

      ip = img + ((h-posy-1) * w*bperpix) + posx*bperpix + plane;
      operand++;

      for (i=0; i<operand; i++, ip+=bperpix) {
	c = getc(fp);
	if (plane<ncolors && posy<h && (posx+i < w)) *ip = c;
      }
      
      if (operand & 1) getc(fp);  /* word boundary */
      posx += operand;
      break;


    case RRunDataOp:
      if (opcode & LONG_OP) { getc(fp);  operand = GETINT(fp); }
      else operand = getc(fp);

      pixval = getc(fp);  getc(fp);
      operand++;

      ip = img + ((h-posy-1) * w*bperpix) + posx*bperpix + plane;

      for (i=0; i<operand; i++, ip+=bperpix) {
	if (plane<ncolors && posy<h && (posx+i < w)) *ip = pixval;
      }
      
    /*  if (operand & 1) getc(fp);  /* word boundary */
      posx += operand;
      break;

    case REOFOp:
      done = 1;
      break;
    }
  }
}


/*******************************************/
static int RLEerr(st)
char *st;
{
  SetISTR(ISTR_WARNING,st);
  return 1;
}

