/*****************************************************************
 * flrle.c: FBM Release 1.0 25-Feb-90 Michael Mauldin
 *
 * flrle.c: Library routines for reading and Writing Utah RLE
 *	    raster toolkit format.
 *
 * CONTENTS
 *	read_rle (image, rfile, mstr, mlen)
 *	write_rle (image, wfile)
 *
 * EDITLOG
 *	LastEditDate = Mon Jun 25 00:05:13 1990 - Michael Mauldin
 *	LastFileName = /usr2/mlm/src/misc/fbm/flrle.c
 *
 * HISTORY
 * 25-Jun-90  Michael Mauldin (mlm@cs.cmu.edu) Carnegie Mellon
 *	Package for Release 1.0
 *
 * 13-Aug-89  Paul Milazzo (milazzo@diamond.bbn.com) BBN
 *	Created.
 *****************************************************************/

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

#ifndef lint
static char *fbmid =
"$FBM flrle.c <1.0> 25-Jun-90  by Paul Milazzo, source code available \
free from MLM@CS.CMU.EDU and from UUNET archives$";
#endif

#ifdef RLE

#include	"svfb_global.h"

#define		CMAP_COLORS		3

#define		TITLE_COMMENT		"TITLE"
#define		CREDITS_COMMENT		"CREDITS"
#define		ASPECT_COMMENT		"aspect_ratio"
#define		CMAP_COMMENT		"color_map_length"
#define		MAX_COMMENTS		4
#define		MAX_LABEL_LENGTH	(sizeof (CMAP_COMMENT))

#define		TRUE			1
#define		FALSE			0

static char	*CommentBuf (bpp)
char		**bpp;
{
    char	*bp;

    if ((bp = (char *)malloc (FBM_MAX_TITLE + MAX_LABEL_LENGTH)) ==
	(char *)NULL)
    {
	perror ("Can't allocate space for RLE comment");
	exit (1);
    }

    *bpp = bp;

    return bp;
}

/****************************************************************
 * write_rle (image, wfile)
 ****************************************************************/

write_rle	(image, wfile)
FBM		*image;
FILE		*wfile;
{
    rle_pixel	**rowvec;
    char	*comments[MAX_COMMENTS + 1];
    char	**cp = comments;
    rle_map	*colorMap = (rle_map *)NULL;
    int		channel;
    int		i;
    int		j;
    int		rows;
    int		cols;
    int		planes;
    int		rowlen;
    int		plnlen;

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

    rows = image->hdr.rows;
    cols = image->hdr.cols;
    planes = image->hdr.planes;
    rowlen = image->hdr.rowlen;
    plnlen = image->hdr.plnlen;

    sv_globals.sv_ncolors	= planes;
    sv_globals.sv_alpha		= 0;  /* no alpha channel */
    sv_globals.sv_background	= 2;  /* clear background to sv_bg_color */
    sv_globals.sv_xmin		= 0;
    sv_globals.sv_xmax		= cols - 1;
    sv_globals.sv_ymin		= 0;
    sv_globals.sv_ymax		= rows - 1;
    sv_globals.sv_cmaplen	= 0;  /* log2(color_map_length) */
    if (image->hdr.clrlen > 0) {
	sv_globals.sv_ncmap = CMAP_COLORS;

	for (i = 1; i < image->hdr.clrlen / CMAP_COLORS; i <<= 1)
	    sv_globals.sv_cmaplen++;

	if ((colorMap = (rle_map *)malloc(image->hdr.clrlen*sizeof(rle_map))) ==
	    (rle_map *)NULL)
	{
	    perror ("write_rle:  can't allocate RLE color map");
	    return 0;
	}
	for (i = 0; i < image->hdr.clrlen; i++)
	    colorMap[i] = (rle_map)image->cm[i] << 8;
	sv_globals.sv_cmap	= colorMap;
    }
    else {
	sv_globals.sv_ncmap	= 0;
	sv_globals.sv_cmap	= (rle_map *)NULL;
    }

    for (channel = 0; channel < planes; channel++)
	SV_SET_BIT (sv_globals, channel);

    if (*image->hdr.title != '\0')
	sprintf (CommentBuf (cp++), "%s=%s",
			 TITLE_COMMENT, image->hdr.title);

    if (*image->hdr.credits != '\0')
	sprintf (CommentBuf (cp++), "%s=%s",
			 CREDITS_COMMENT, image->hdr.credits);

    if (image->hdr.aspect != 1.0)
	sprintf (CommentBuf (cp++), "%s=%g",
			 ASPECT_COMMENT, image->hdr.aspect);

    /*
     *  If the color map length is not a power of two, put the actual length
     *  in a comment.
     */
    if (image->hdr.clrlen > 0 &&
	(1 << sv_globals.sv_cmaplen) != image->hdr.clrlen / CMAP_COLORS)
    {
	sprintf (CommentBuf (cp++), "%s=%d",
			 CMAP_COMMENT, image->hdr.clrlen / CMAP_COLORS);
    }

    *cp = (char *)NULL;

    sv_globals.sv_comments = cp > comments ? comments : (char **)NULL;

    sv_globals.svfb_fd		= wfile;
  
    sv_setup (RUN_DISPATCH, &sv_globals);

    if ((rowvec = (unsigned char **)malloc (planes*sizeof(unsigned char *))) ==
	(unsigned char **)NULL)
    {
	perror ("write_rle:  can't allocate row indirection vectors");
	return 0;
    }

    for (j = rows - 1; j >= 0; --j) {
	for (channel = 0; channel < planes; channel ++)
	    rowvec[channel] = image->bm + j * rowlen + channel * plnlen;
	sv_putrow (rowvec, cols, &sv_globals);
    }
    sv_puteof (&sv_globals);

    free (rowvec);
    while (cp > comments)
	free (*--cp);
    if (colorMap != (rle_map *)NULL)
	free (colorMap);

    return 1;
}

/****************************************************************
 * read_rle (image, rfile)
 ****************************************************************/

read_rle (image, rfile, mstr, mlen)
FBM	*image;
FILE	*rfile;
char	*mstr;
int	mlen;
{
    rle_pixel		**colorMap;
    rle_pixel		**rowvec;
    unsigned char	*cp;
    char		*comment;
    int			j;
    int			channel;
    int			rows;
    int			planes;
    int			rowlen;
    int			plnlen;
    int			mapEntries;
    int			clearRow;

    /* must put the magic number back so the setup code can read it */
    while (mlen--)
	(void)ungetc (*mstr++, rfile);

    sv_globals.svfb_fd = rfile;
    switch (rle_get_setup (&sv_globals)) {
	case 0:
	    break;	/* success */
	case -1:
	    fputs ("read_rle:  input is not a Utah RLE file.\n", stderr);
	    /* fall through... */
	case -2:
	    return 0;	/* sv_get_setup already printed an error message */
	case -3:
	    fputs ("read_rle:  input file is empty.\n", stderr);
	    return 0;
	case -4:
	    fputs ("read_rle:  end-of-file encountered while reading RLE header.\n",
		   stderr);
	    return 0;
	default:
	    fputs ("read_rle:  rle_get_setup returned something strange!\n",
		   stderr);
    }

    if (sv_globals.sv_alpha) {
	fputs ("read_rle:  discarding alpha channel.\n", stderr);
	SV_CLR_BIT (sv_globals, SV_ALPHA);
    }

    image->hdr.cols	= sv_globals.sv_xmax - sv_globals.sv_xmin + 1;
    image->hdr.rows	= rows = sv_globals.sv_ymax - sv_globals.sv_ymin + 1;
    image->hdr.planes	= planes = sv_globals.sv_ncolors;
    image->hdr.bits	= sv_globals.sv_cmaplen ? sv_globals.sv_cmaplen : 8;
    image->hdr.physbits	= 8;
    image->hdr.rowlen	= rowlen = image->hdr.cols;
    image->hdr.plnlen	= plnlen = image->hdr.rows * image->hdr.rowlen;

    image->hdr.clrlen	= 1 << sv_globals.sv_cmaplen;
    if ((comment = rle_getcom (CMAP_COMMENT, &sv_globals)) != (char *)NULL)
	image->hdr.clrlen = atoi (comment);
    image->hdr.clrlen	*= sv_globals.sv_ncmap;

    if ((comment = rle_getcom (ASPECT_COMMENT, &sv_globals)) != (char *)NULL)
	image->hdr.aspect = atof (comment);
    else
	image->hdr.aspect = 1.0;

    if ((comment = rle_getcom (TITLE_COMMENT, &sv_globals)) != (char *)NULL)
	(void)strcpy (image->hdr.title, comment);
    else
	image->hdr.title[0] = '\0';
    if ((comment = rle_getcom (CREDITS_COMMENT, &sv_globals)) != (char *)NULL)
	(void)strcpy (image->hdr.credits, comment);
    else
	image->hdr.credits[0] = '\0';

    image->cm = (unsigned char *)NULL;
    image->bm = (unsigned char *)NULL;
    alloc_fbm (image);

    if (image->hdr.clrlen > 0) {
	mapEntries = (image->hdr.clrlen / sv_globals.sv_ncmap);
	cp = image->cm;
	colorMap = buildmap (&sv_globals, CMAP_COLORS, 1.0);
	for (channel = 0; channel < CMAP_COLORS; channel++) {
	    for (j = 0; j < mapEntries; j++)
		*cp++ = colorMap[channel][j];
	    free (colorMap[channel]);
	}
	free (colorMap);
	image->hdr.clrlen = mapEntries * CMAP_COLORS; /* renormalize clrlen */
    }

    switch (sv_globals.sv_background) {
	case 0:		/* no background color was saved */
	    clearRow = TRUE;	/* manually clear rows to 0 */
	    break;
	case 1:		/* don't clear to the background color */
	    sv_globals.sv_background = 2;  /* force automatic clearing */
	    /* fall through... */
	case 2:		/* clear to the background color */
	    clearRow = FALSE;
	    break;
	default:
	    fprintf (stderr, "read_rle:  unknown background flag '%d'.\n",
		     sv_globals.sv_background);
    }

    /* move image to origin */
    sv_globals.sv_xmin	= 0;
    sv_globals.sv_xmax	= image->hdr.cols - 1;
    sv_globals.sv_ymin	= 0;
    sv_globals.sv_ymax	= image->hdr.rows - 1;

    if ((rowvec = (unsigned char **)malloc (planes*sizeof(unsigned char *))) ==
	(unsigned char **)NULL)
    {
	perror ("write_rle:  can't allocate row indirection vectors");
	return 0;
    }

    for (j = rows - 1; j >= 0; --j) {
	for (channel = 0; channel < planes; channel ++) {
	    rowvec[channel] = image->bm + j * rowlen + channel * plnlen;
	    if (clearRow)
		bzero ((char *)rowvec[channel], rowlen);
	}
	rle_getrow (&sv_globals, rowvec);
    }
    free (rowvec);

    return 1;
}

#else /* ! RLE */

/*ARGSUSED*/
write_rle	(image, wfile)
FBM		*image;
FILE		*wfile;
{
    fputs ("RLE support was omitted at compile time.\n", stderr);
}

/*ARGSUSED*/
read_rle (image, rfile, mstr, mlen)
FBM	*image;
FILE	*rfile;
char	*mstr;
int	mlen;
{
    write_rle (image, rfile);
}

#endif /* RLE */
