/*****************************************************************
 * flsun.c: FBM Release 1.0 25-Feb-90 Michael Mauldin
 *
 * Copyright (C) 1989,1990 by Michael Mauldin.  Permission is granted
 * to use this file in whole or in part for any purpose, educational,
 * recreational or commercial, provided that this copyright notice
 * is retained unchanged.  This software is available to all free of
 * charge by anonymous FTP and in the UUNET archives.
 *
 * flsun.c: 
 *
 * CONTENTS
 *	read_sun (image, rfile, mstr, mlen)
 *	write_sun (image, wfile)
 *
 * EDITLOG
 *	LastEditDate = Mon Jun 25 00:18:04 1990 - Michael Mauldin
 *	LastFileName = /usr2/mlm/src/misc/fbm/flsun.c
 *
 * HISTORY
 * 25-Jun-90  Michael Mauldin (mlm@cs.cmu.edu) Carnegie Mellon
 *	Package for Release 1.0
 *
 * 20-May-89  Michael Mauldin (mlm) at Carnegie Mellon University
 *	Fixed problem with odd length rows on reading
 *
 * 26-Aug-89  Michael Mauldin (mlm) at Carnegie Mellon University
 *	Beta release (version 0.96) mlm@cs.cmu.edu
 *
 * 12-Nov-88  Michael Mauldin (mlm) at Carnegie-Mellon University
 *	Created.
 *****************************************************************/

# include <stdio.h>
# include <math.h>
# include <ctype.h>
# include "fbm.h"

typedef struct rasterfile {
	long ras_magic;
	long ras_width;
	long ras_height;
	long ras_depth;
	long ras_length;
	long ras_type;
	long ras_maptype;
	long ras_maplength;
} RASHDR;

# define RT_STANDARD	1
# define RMT_NONE	0
# define RMT_EQUAL_RGB	1
# define RMT_RAW	2

# define RED 0
# define GRN 1
# define BLU 2

/****************************************************************
 * write_sun (image, wfile)
 ****************************************************************/

#ifndef lint
static char *fbmid =
"$FBM flsun.c <1.0> 25-Jun-90  (C) 1989,1990 by Michael Mauldin, source \
code available free from MLM@CS.CMU.EDU and from UUNET archives$";
#endif

write_sun (image, wfile)
FBM *image;
FILE *wfile;
{ RASHDR rhdr;
  register int i, j, byte;
  register unsigned char *bmp, *rp, *gp, *bp;
  int width, height, plnlen, clrlen, rowlen, depth, bits;

  if (image->hdr.planes != 1 && image->hdr.planes != 3)
  { fprintf (stderr,
	     "Error, write_sun can only handle images with depth 1 or 3\n");
    return (0);
  }

  if (image->hdr.physbits != 8)
  { fprintf (stderr,
	     "Error, write_sun can only handle 8 physical bits per pixel\n");
    return (0);
  }

  /* This restriction should be fixed */
  if (image->hdr.bits == 1 && (image->hdr.rowlen % 16) != 0)
  { fprintf (stderr,
	"Error, 1 bit deep files must have rowlen (%d) divisible by 16");
    return (0);
  }

  /* Allow for input rowlen to be greater than output rowlen */
  if (image->hdr.bits == 1)
  { bits = 1; }
  else
  { bits = 8; }

# ifdef DEBUG
  fprintf (stderr,
	   "write_sun: [%dx%d] rowlen %d, planes %d, bits %d, physbits %d, using %d\n",
	   image->hdr.cols,
	   image->hdr.rows,
	   image->hdr.rowlen,
	   image->hdr.planes,
	   image->hdr.bits,
	   image->hdr.physbits,
           bits);
# endif

  width = image->hdr.cols;
  height = image->hdr.rows;
  rowlen = image->hdr.rowlen;
  plnlen = image->hdr.plnlen;
  clrlen = image->hdr.clrlen;
  depth = bits * image->hdr.planes;

  /* Initialize Sun raster header */
  rhdr.ras_magic = SUN_MAGIC;
  rhdr.ras_width = width;
  rhdr.ras_height = height;
  rhdr.ras_depth = depth;
  rhdr.ras_length = plnlen * bits / 8;
  rhdr.ras_type = RT_STANDARD;
  rhdr.ras_maptype = depth > 8 ? RMT_RAW : clrlen ? RMT_EQUAL_RGB : RMT_NONE;
  rhdr.ras_maplength = clrlen;

  /* Write rasterfile header - note: use Sun byte order */
  put_long (rhdr.ras_magic, wfile, BIG);
  put_long (rhdr.ras_width, wfile, BIG);
  put_long (rhdr.ras_height, wfile, BIG);
  put_long (rhdr.ras_depth, wfile, BIG);
  put_long (rhdr.ras_length, wfile, BIG);
  put_long (rhdr.ras_type, wfile, BIG);
  put_long (rhdr.ras_maptype, wfile, BIG);
  put_long (rhdr.ras_maplength, wfile, BIG);
  
  /* Dump colormap if need be */
  if (clrlen > 0)
  { fwrite (image->cm, 1, clrlen, wfile); }
  
  /* Write bytes */
  switch (depth)
  { case 24:	rp = &image->bm[0];
		gp = rp + plnlen;
		bp = gp + plnlen;

		for (i=0; i<plnlen; i++)
		{ fputc (*rp++, wfile);
		  fputc (*gp++, wfile);
		  fputc (*bp++, wfile);
		}
		break;

    case 8:	fwrite (image->bm, 1, plnlen, wfile);
		break;

    case 1:	
# ifdef DEBUG
		fprintf (stderr, "Writing Sun 1bit file [%dx%d] rowlen %d\n",
			 width, height, rowlen);
# endif
		for (j=0; j<height; j++)
		{ bmp = &(image->bm[j*rowlen]);
		  byte = 0;

		  for (i=0; i<rowlen; i++)
		  { byte = (byte << 1) | (*bmp++ ? 0 : 1);

		    if ((i & 7) == 7)
		    { fputc (byte, wfile); byte = 0; }
		  }
		}
		break;

    default:	fprintf  (stderr,
		          "Error, write_sun given invalid depth %d bits\n",
			  depth);
		return (0);
  }
  return (1);
}

/****************************************************************
 * read_sun (image, rfile)
 ****************************************************************/

read_sun (image, rfile, mstr, mlen)
FBM *image;
FILE *rfile;
char *mstr;
int mlen;
{ RASHDR rhdr;
  int width, height, plnlen, rowlen, clrlen, res, depth;
  register int i, j, byte;
  register unsigned char *bmp, *rp, *gp, *bp;
  int m1, m2, m3, m4;

  m1 = NEXTMCH(rfile,mstr,mlen) & 0xff;
  m2 = NEXTMCH(rfile,mstr,mlen) & 0xff;
  m3 = NEXTMCH(rfile,mstr,mlen) & 0xff;
  m4 = NEXTMCH(rfile,mstr,mlen) & 0xff;

  rhdr.ras_magic = (m1 << 24) | (m2 << 16) | (m3 << 8)| (m4);

  /* Write rasterfile header - note: use Sun byte order */
  if (rhdr.ras_magic != SUN_MAGIC)
  { fprintf (stderr, "Error, not a Sun raster file (bad magic %08x)\n",
	     rhdr.ras_magic);
    return (0);
  }

  rhdr.ras_width = get_long (rfile, BIG);
  rhdr.ras_height = get_long (rfile, BIG);
  rhdr.ras_depth = get_long (rfile, BIG);
  rhdr.ras_length = get_long (rfile, BIG);
  rhdr.ras_type = get_long (rfile, BIG);
  rhdr.ras_maptype = get_long (rfile, BIG);
  rhdr.ras_maplength = get_long (rfile, BIG);

  /* Check for nonstandard rasterfile formats */
  if (rhdr.ras_type != RT_STANDARD)
  { fprintf (stderr, "Error: rasterfile is not a Sun RT_STANDARD file\n");
    return (0);
  }

  if (rhdr.ras_maplength > 0 && rhdr.ras_maptype != RMT_EQUAL_RGB)
  { fprintf (stderr, "Error: color rasterfile is not RMT_EQUAL_RGB\n");
    return (0);
  }
  
  if (rhdr.ras_maplength == 0 &&
      rhdr.ras_maptype != RMT_NONE &&
      rhdr.ras_maptype != RMT_RAW)
  { fprintf (stderr, "Error: black and white rasterfile is not RMT_NONE\n");
    return (0);
  }

  if (rhdr.ras_depth != 24 && rhdr.ras_depth != 8 && rhdr.ras_depth != 1)
  { fprintf (stderr, "Error, bits per pixel (%d) must be 1, 8 or 24\n", 
	     rhdr.ras_depth);
    return (0);
  }

  /* Initialize and allocate input image */  
  width = rhdr.ras_width;
  height = rhdr.ras_height;
  depth = rhdr.ras_depth;
  clrlen = rhdr.ras_maplength;

  if (depth == 1)
  { rowlen = 16 * ((width + 15) / 16);
    plnlen = rowlen * height;
  }
  else
  { rowlen = width;
    if (rowlen & 1) rowlen++;
    
    plnlen = rowlen * height; /* Corrected 4-6-90 Gary W. Sherwin */
  }

# ifdef DEBUG
  fprintf (stderr, "Reading Sun raster, [%dx%d], rowlen %d, depth %d, clrlen %d\n",
	   width, height, rowlen, depth, clrlen );
# endif
  
  /* Check for consitency between colormap and depth */
  if (depth > 8 && clrlen > 0)
  { fprintf (stderr,
	"Error, input has colormap of length %d, but %d bits per pixel\n",
	clrlen, depth);
    return (0);
  }

  /* Initialize image header */
  image->hdr.cols = width;
  image->hdr.rows = height;
  image->hdr.planes = (depth == 24) ? 3 : 1;
  image->hdr.bits = (depth == 24) ? 8 : depth;
  image->hdr.physbits = 8;
  image->hdr.rowlen = rowlen;
  image->hdr.plnlen = plnlen;
  image->hdr.clrlen = clrlen;
  image->hdr.aspect = 1.0;
  image->hdr.title[0] = '\0';
  image->hdr.credits[0] = '\0';

  /* Allocate space */
  alloc_fbm (image);

  /* Read colormap if need be */
  if (clrlen > 0 && (res = fread (image->cm, 1, clrlen, rfile)) != clrlen) 
  { fprintf (stderr, "Error: couldn't read colormap, read %d of %d bytes\n",
	     res, clrlen);
    return (0);
  }

  /* Read bytes */
  switch (depth)
  { case 24:	rp = &image->bm[0];
		gp = rp + plnlen;
		bp = gp + plnlen;

		for (i=0; i<plnlen && !feof (rfile); i++)
		{ *rp++ = fgetc (rfile);
		  *gp++ = fgetc (rfile);
		  *bp++ = fgetc (rfile);
		}
		
		if (i<plnlen)
		{ fprintf (stderr, "Error: %s %d of %d pixels (%d bytes)\n",
			   "EOF on bitmap after",
			   i, plnlen, plnlen * image->hdr.planes);
		  return (0);
		}
		
		break;

    case 8:	if ((res = fread (image->bm, 1, plnlen, rfile)) != plnlen)
		{ fprintf (stderr,
			   "Error: EOF on bitmap after %d of %d bytes\n",
			   res, plnlen);
		  return (0);
		}
		break;

    case 1:	for (j=0; j<height; j++)
		{ bmp = &(image->bm[j * rowlen]);
    
		  for (i=0; i<rowlen; i++)
		  { if ((i&7) == 0)
		    { if ((byte = fgetc (rfile)) == EOF)
		      { fprintf (stderr,
				 "Error: EOF on bitmap after %d of %d bytes\n",
				 j*rowlen + i, height*rowlen);
			return (0);
		      }
		    }
        
		    *bmp++ = (byte & 0x80) ? BLACK : WHITE;
		    byte <<= 1;
		  }
		}
		break;
    default:	fprintf (stderr, "Invalid depth %d bits\n", depth);
		return (0);
  }

  return (1);
}
