/*
 * xvsunras.c - load routine for 'sun rasterfile' format pictures
 *
 * LoadSunRas(fname, numcols)  -  loads a PM pic, does 24to8 code if nec.
 * WriteSunRas(fp, pic, w, h, r,g,b, numcols, style)
 * WriteRaw(fp, pic, w, h, r,g,b, numcols, style)
 *
 * This file written by Dave Heath (heath@cs.jhu.edu)
 * fixBGR() added by Ken Rossman (ken@shibuya.cc.columbia.edu)
 */


#include "xv.h"
#include "sunras.h"

static int  SunRasError();
static int  rle_read();
static void flipl();
static void SunRas1to8();
static void SunRas8to1();
static int  write_sun_long();
static void fixBGR();


/*******************************************/
int LoadSunRas(fname,nc)
     char *fname;
     int   nc;
/*******************************************/
{
  FILE	*fp;
  int	 linesize,lsize,csize,isize,flipit,i,w,h,d,rv;
  byte	 *image, *line;
  struct rasterfile sunheader;

  rv = 0;

  /* read in the Sun Rasterfile picture */
  fp=fopen(fname,READ_BINARY);
  if (!fp) return( SunRasError("unable to open file") );

  flipit = 0;
  fread(&sunheader,sizeof(struct rasterfile),1,fp);
  if (sunheader.ras_magic != RAS_MAGIC)
  {
    flipl( (byte *) &sunheader.ras_magic);
    if (sunheader.ras_magic == RAS_MAGIC) flipit = 1;
    else flipl( (byte *) &sunheader.ras_magic);
  }
  if (sunheader.ras_magic != RAS_MAGIC) 
    return( SunRasError("not a Sun rasterfile") );

  if (flipit) {
    flipl((byte *) &sunheader.ras_width);
    flipl((byte *) &sunheader.ras_height);
    flipl((byte *) &sunheader.ras_depth);
    flipl((byte *) &sunheader.ras_length);
    flipl((byte *) &sunheader.ras_type);
    flipl((byte *) &sunheader.ras_maptype);
    flipl((byte *) &sunheader.ras_maplength);
    }

  /* make sure that the input picture can be dealt with */
  if (sunheader.ras_depth != 1 &&
      sunheader.ras_depth != 8 &&
      sunheader.ras_depth != 24 &&
      sunheader.ras_depth != 32) {
    fprintf (stderr, "Sun rasterfile image has depth %d\n", 
	     sunheader.ras_depth);
    fprintf (stderr, "Depths supported are 1, 8, 24, and 32\n");
    return 1;
  }

  if (sunheader.ras_type != RT_OLD &&
      sunheader.ras_type != RT_STANDARD &&
      sunheader.ras_type != RT_BYTE_ENCODED &&
      sunheader.ras_type != RT_FORMAT_RGB) {
    fprintf (stderr, "Sun rasterfile of unsupported type %d\n",
	     sunheader.ras_type);
    return 1;
  }

  if (sunheader.ras_maptype != RMT_RAW &&
      sunheader.ras_maptype != RMT_NONE &&
      sunheader.ras_maptype != RMT_EQUAL_RGB) {
    fprintf (stderr, "Sun rasterfile colormap of unsupported type %d\n",
	     sunheader.ras_maptype);
    return 1;
  }

  w = sunheader.ras_width;
  h = sunheader.ras_height;
  d = sunheader.ras_depth;
  isize = sunheader.ras_length ?
	  sunheader.ras_length :
	  (w * h * d) / 8;
  csize = (sunheader.ras_maptype == RMT_NONE) ? 0 : sunheader.ras_maplength;

  /* compute length of the output (xv-format) image */
  lsize = w * h;     
  if (d == 24 || d == 32) lsize = lsize * 3;

  linesize = w * d;
  /* if ((linesize % 48) && d == 24) linesize += (48 - (linesize % 48)); */
  if (linesize % 16) linesize += (16 - (linesize % 16));
  linesize /= 8;

  if (DEBUG)
  {
    fprintf(stderr,"%s: LoadSunRas() - loading a %dx%d pic, %d planes\n",
	    cmd, w, h,
	    d);
    fprintf (stderr, 
       "type %d, maptype %d, isize %d, csize %d, lsize %d, linesize %d\n",
	     sunheader.ras_type, sunheader.ras_maptype,
	     isize, csize, lsize, linesize);
  }


  SetDirRButt(F_FORMAT, F_SUNRAS);
  if (d==1) SetDirRButt(F_COLORS, F_BWDITHER);
       else SetDirRButt(F_COLORS, F_FULLCOLOR);

  SetISTR(ISTR_FORMAT,"Sun %s rasterfile.  (%d plane%s)  (%d bytes)",
	  sunheader.ras_type == RT_BYTE_ENCODED ? "rle" : "standard",
	  d,
	  d == 1 ? "" : "s",
	  sizeof (struct rasterfile) + csize + isize);

  sprintf(formatStr, "%dx%d Sun Rasterfile.",w,h);

  /* read in the colormap, if any */
  if (sunheader.ras_maptype == RMT_EQUAL_RGB && csize)
  {
    fread (r, sizeof (byte), sunheader.ras_maplength/3, fp);
    fread (g, sizeof (byte), sunheader.ras_maplength/3, fp);
    fread (b, sizeof (byte), sunheader.ras_maplength/3, fp);

   /* for (i = 0; i < sunheader.ras_maplength/3; i++)
    {
      r[i] = getc (fp);
      g[i] = getc (fp);
      b[i] = getc (fp);
    } */
  }
  else if (sunheader.ras_maptype == RMT_RAW && csize)
  {
    /* we don't know how to handle raw colormap, ignore */
    fseek (fp, (long) csize, 1);
  }

  else {  /* no colormap, make one up */
    if (sunheader.ras_depth == 1) {
      r[0] = g[0] = b[0] = 0;
      r[1] = g[1] = b[1] = 255;
    }

    else if (sunheader.ras_depth == 8) {
      for (i = 0; i < 256; i++)
	r[i] = g[i]  = b[i] = i;
    }
  }


  /* allocate memory for picture and read it in */
  /* note we may slightly overallocate here (if image is padded) */
  image = (byte *) malloc (lsize);
  line = (byte *) malloc (linesize);
  if (image == NULL || line == NULL)
    FatalError("Can't allocate memory for image\n");

  for (i = 0; i < h; i++) {
    if ((i&0x1f) == 0) WaitCursor();
    if (sunheader.ras_type == RT_BYTE_ENCODED) {
      if (rle_read (line, 1, linesize, fp, (i==0)) != linesize) break;
	/* return (SunRasError ("rle file read error")); */
    }

    else {
      if (fread (line, 1, linesize, fp) != linesize)
	return (SunRasError ("file read error"));
    }

    switch (d) {
    case 1:  SunRas1to8 (image + w * i, line, w);	break;
    case 8:  memcpy (image + w * i, line, w);		break;
    case 24: memcpy (image + w * i * 3, line, w * 3); break;
    case 32: {
               int k;
               byte *ip, *op;
	       ip = line;
	       op = (byte *) (image + w * i * 3);
	       for (k = 0; k<w; k++) {
		 *ip++;           /* skip 'alpha' */
		 *op++ = *ip++;   /* red   */
		 *op++ = *ip++;   /* green */
		 *op++ = *ip++;   /* blue  */
	       }
	     }
    }
  }

  if (fp != stdin) fclose (fp);
  if (DEBUG) fprintf(stderr,"Sun ras: image loaded!\n");

  if (d == 24 || d == 32) {
    if (sunheader.ras_type != RT_FORMAT_RGB) fixBGR(image,w,h);
    rv = Conv24to8 (image, w, h, nc);
    free (image);
    return (rv);
  }

  else {
    pic = image;
    pWIDE = w;
    pHIGH = h;
    return (0);
  }
}


/*****************************/
static int rle_read (ptr, size, nitems, fp, init)
byte *ptr;
int size, nitems,init;
FILE *fp;
{
  static int count, ch;
  int readbytes, c, read;

  if (init) { count = ch = 0; }

  readbytes = size * nitems;
  for (read = 0; read < readbytes; read++) {
    if (count) {
      *ptr++ = (byte) ch;
      count--;
    }

    else {
      c = getc(fp);
      if (c == EOF) break;

      if (c == RAS_RLE) {   /* 0x80 */
	count = getc(fp);
	if (count == EOF) break;

	if (count < 0) count &= 0xff;
	if (count == 0) *ptr++ = c;
        else {
          if ((ch = getc(fp)) == EOF) break;
          *ptr++ = ch;
        }
      }
      else *ptr++ = c;
    }
  }

  return (read/size);
}


/*****************************/
static int SunRasError(st)
char *st;
{
  SetISTR(ISTR_WARNING,"LoadSunRas() - %s",st);
  Warning();
  return -1;
}


/*****************************/
static void flipl(p)
     byte *p;
{
  byte t;
  t = p[0];  p[0]=p[3];  p[3] = t;
  t = p[1];  p[1]=p[2];  p[2] = t;
}


static void SunRas1to8 (dest, src, len)
byte *dest, *src;
int len;
{
  int i, b;
  int c;

  for (i = 0, b = -1; i < len; i++) {
    if (b < 0) {
      b = 7;
      c = ~*src++;
    }
    *dest++ = ((c >> (b--)) & 1);
  }
}



static void SunRas8to1 (dest, src, len)
byte *dest, *src;
int len;
{
  int i, b;
  int c;

  for (c = b = i = 0; i < len; i++) {
    c <<= 1;
    c |= (*src++ ? 0 : 1);
    if (b++ == 7) {
      *dest++ = c;
      b = c = 0;
    }
  }
  if (b) *dest = c;
}




/*******************************************/
int WriteSunRas(fp,pic,w,h,rmap,gmap,bmap,numcols,colorstyle,userle)
FILE *fp;
byte *pic;
int   w,h;
byte *rmap, *gmap, *bmap;
int   numcols, colorstyle, userle;
{
  /* writes a sun rasterfile to the already open stream
     writes either 8-bit or 1-bit (never 24)
     currently will not write rle files

     biggest problem w/ rle file: should we compute
     image size first (nicer) or go back and write it
     in when we are done (kludgy)?
   */

  struct rasterfile sunheader;
  int linesize, i, color, d, y;
  byte *line;

  if (colorstyle != 2 && numcols > 2) d = 8;
  else d = 1;

  linesize = w;
  if (d == 1) {
    if (linesize % 8) linesize += (8 - linesize % 8);
    linesize /= 8;
  }

  if (linesize % 2) linesize++;
  line = (byte *) malloc (linesize);
  if (!line) {
    SetISTR(ISTR_WARNING, "Can't allocate memory for save!\n");
    return (1);
  }

  if (DEBUG)
    fprintf (stderr,
	     "WriteSunRas: d %d, linesize %d numcols %d\n",
	     d, linesize, numcols);

  /* set up the header */
  sunheader.ras_magic	  = RAS_MAGIC;
  sunheader.ras_width	  = w;
  sunheader.ras_height	  = h;
  sunheader.ras_depth	  = d;
  sunheader.ras_length	  = linesize * h;
  sunheader.ras_type	  = RT_STANDARD;
  sunheader.ras_maptype   = (d == 1) ? RMT_NONE : RMT_EQUAL_RGB;
  sunheader.ras_maplength = (d == 1) ? 0 : 3 * numcols;

  write_sun_long (sunheader.ras_magic	 , fp);
  write_sun_long (sunheader.ras_width	 , fp);
  write_sun_long (sunheader.ras_height	 , fp);
  write_sun_long (sunheader.ras_depth	 , fp);
  write_sun_long (sunheader.ras_length	 , fp);
  write_sun_long (sunheader.ras_type	 , fp);
  write_sun_long (sunheader.ras_maptype  , fp);
  write_sun_long (sunheader.ras_maplength, fp);

  /* write the colormap */
  if (d > 1)
    if (colorstyle == 1)  /* grayscale */
      for (color = 0; color < 3; color++)
	for (i = 0; i < numcols; i++)
	  putc (MONO(rmap[i],gmap[i],bmap[i]), fp);
    else {
      fwrite (rmap, sizeof (byte), numcols, fp);
      fwrite (gmap, sizeof (byte), numcols, fp);
      fwrite (bmap, sizeof (byte), numcols, fp);
    }

  /* write the image */
  line[linesize-1] = 0;
  for (y = 0; y < h; y++) {
    if ((y&0x1f) == 0) WaitCursor();

    if (d > 1)
      memcpy (line, pic + y * w, w);
    else
      SunRas8to1 (line, pic + y * w, w);

    if (fwrite (line, sizeof (byte), linesize, fp) != linesize) {
      SetISTR(ISTR_WARNING, "Write failed during save!\n");
      free (line);
      return (2);
    }
  }

  free (line);
  return (0);
}


/* write a long word in sun byte-order
   returns 0 for success, EOF for failure
 */
static int write_sun_long (l, fp)
long l;
FILE *fp;
{
    char c;

    c = ((l >> 24) & 0xff);
    if (putc (c, fp) == EOF) return (EOF);
    c = ((l >> 16) & 0xff);
    if (putc (c, fp) == EOF) return (EOF);
    c = ((l >> 8) & 0xff);
    if (putc (c, fp) == EOF) return (EOF);
    c = (l & 0xff);
    if (putc (c, fp) == EOF) return (EOF);
    return (0);
}




/* kr3 - fix up BGR order SUN 24-bit rasters to be RGB order */
static void fixBGR(img,w,h)
unsigned char *img;
int w,h;
{
  int i,npixels;
  unsigned char tmp;

  npixels = w*h;
  for (i=0; i<npixels; i++) {
    tmp = img[0];                   /* swap red and blue channels */
    img[0] = img[2];
    img[2] = tmp;
    img += 3;                       /* bump to next pixel */
  }
}

