/*****************************************************************
 * 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"*/
#include	"rle.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)
 ****************************************************************/

struct rle_hdr globals;

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;

    globals.ncolors	= planes;
    globals.alpha		= 0;  /* no alpha channel */
    globals.background	= 2;  /* clear background to bg_color */
    globals.xmin		= 0;
    globals.xmax		= cols - 1;
    globals.ymin		= 0;
    globals.ymax		= rows - 1;
    globals.cmaplen	= 0;  /* log2(color_map_length) */
    if (image->hdr.clrlen > 0) {
	globals.ncmap = CMAP_COLORS;

	for (i = 1; i < image->hdr.clrlen / CMAP_COLORS; i <<= 1)
	    globals.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;
	globals.cmap	= colorMap;
    }
    else {
	globals.ncmap	= 0;
	globals.cmap	= (rle_map *)NULL;
    }

    for (channel = 0; channel < planes; channel++)
	RLE_SET_BIT (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 << globals.cmaplen) != image->hdr.clrlen / CMAP_COLORS)
    {
	sprintf (CommentBuf (cp++), "%s=%d",
			 CMAP_COMMENT, image->hdr.clrlen / CMAP_COLORS);
    }

    *cp = (char *)NULL;

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

    globals.rle_file		= wfile;
    globals.dispatch		= RUN_DISPATCH;
  
    rle_put_setup (&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;
	rle_putrow (rowvec, cols, &globals);
    }
    rle_puteof (&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);

    globals.rle_file = rfile;
    switch (rle_get_setup (&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 (globals.alpha) {
	fputs ("read_rle:  discarding alpha channel.\n", stderr);
	RLE_CLR_BIT (globals, RLE_ALPHA);
    }

    image->hdr.cols	= globals.xmax - globals.xmin + 1;
    image->hdr.rows	= rows = globals.ymax - globals.ymin + 1;
    image->hdr.planes	= planes = globals.ncolors;
    image->hdr.bits	= globals.cmaplen ? globals.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 << globals.cmaplen;
    if ((comment = rle_getcom (CMAP_COMMENT, &globals)) != (char *)NULL)
	image->hdr.clrlen = atoi (comment);
    image->hdr.clrlen	*= globals.ncmap;

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

    if ((comment = rle_getcom (TITLE_COMMENT, &globals)) != (char *)NULL)
	(void)strcpy (image->hdr.title, comment);
    else
	image->hdr.title[0] = '\0';
    if ((comment = rle_getcom (CREDITS_COMMENT, &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 / globals.ncmap);
	cp = image->cm;
	colorMap = buildmap (&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 (globals.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 */
	    globals.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",
		     globals.background);
    }

    /* move image to origin */
    globals.xmin	= 0;
    globals.xmax	= image->hdr.cols - 1;
    globals.ymin	= 0;
    globals.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 (&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 */
