/*
 * picture.c
 *
 * Copyright (C) 1989, 1991, Craig E. Kolb, Rod G. Bogart
 * All rights reserved.
 *
 * This software may be freely copied, modified, and redistributed
 * provided that this copyright notice is preserved on all copies.
 *
 * You may not distribute this software, in whole or in part, as part of
 * any commercial product without the express consent of the authors.
 *
 * There is no warranty or other guarantee of fitness of this software
 * for any purpose.  It is provided solely "as is".
 *
 * $Id$
 *
 * $Log$
 */
#include "rayshade.h"
#include "picture.h"
#include "viewing.h"
#include "options.h"
#include "stats.h"

#ifdef URT
unsigned char **outptr;		/* Output buffer */
#endif

/*
 * Convert floating-point (0.-1.) to unsigned char (0-255), with no gamma
 * correction.
 */
static unsigned char
correct(x)
Float x;
{
	/*
	 * Truncate values < 0 or > 1.
	 */
	if (x < 0)
		return 0;
	if (x > 1.)
		return 255;
	return (unsigned char)(x * 255.);
}

#ifdef URT
/*
 * Open image file and write RLE header.
 */
void
PictureStart(argv)
char **argv;
{
	char gammacom[40];

	/*
	 * If Appending, then we know that outfile is valid, 'cause
	 * we've already read its header.
	 */
	if (Options.appending) {
		Options.pictfile = fopen(Options.imgname, "a");
		if (Options.pictfile == (FILE *)0)
			RLerror(RL_PANIC, "Cannot append to %s?!\n",
					Options.imgname);
		rle_dflt_hdr.rle_file = Options.pictfile;
		rle_put_init(&rle_dflt_hdr);
	} else {
		/*
		 * Starting image from scatch.
		 */
		if (Options.imgname) {
			Options.pictfile = fopen(Options.imgname, "w");
			if (Options.pictfile == (FILE *)NULL)
				RLerror(RL_PANIC,"Cannot open %s for writing.",
					Options.imgname);
		} else
			Options.pictfile = stdout;

		rle_dflt_hdr.xmax = Screen.maxx;
		rle_dflt_hdr.ymax = Screen.maxy;
		rle_dflt_hdr.xmin = Screen.minx;
		rle_dflt_hdr.ymin = Screen.miny;
		rle_dflt_hdr.alpha = Options.alpha;
		if (Options.alpha)
			RLE_SET_BIT(rle_dflt_hdr, RLE_ALPHA);
		if (Options.exp_output) {
			RLE_SET_BIT(rle_dflt_hdr, RLE_BLUE + 1);
			rle_dflt_hdr.ncolors = 4;
			rle_putcom("exponential_data", &rle_dflt_hdr);
		}
		else
			rle_dflt_hdr.ncolors = 3;
		/*
	 	 * Document image gamma in RLE comment area.
		 * Options.gamma has been inverted.
	 	 */
		(void)sprintf(gammacom, "display_gamma=%g", 1./Options.gamma);
		rle_putcom(gammacom, &rle_dflt_hdr);
		/*
	 	 * Document command line in RLE history.
	 	 */
		rle_addhist(argv, (rle_hdr *)0, &rle_dflt_hdr);
		rle_dflt_hdr.rle_file = Options.pictfile;
		rle_put_setup(&rle_dflt_hdr);
		/*
	 	 * Flush the header.  If we don't, and LINDA forks off
	 	 * a bunch of workers, strange things will happen (they'll
	 	 * all flush the buffer when they die, and you end up with
	 	 * lots of headers at the end of the file).
	 	 */
		(void)fflush(rle_dflt_hdr.rle_file);
	}

	if (rle_row_alloc(&rle_dflt_hdr, &outptr) < 0)
		RLerror(RL_PANIC, "Unable to allocate image memory.\n");
}

/*
 * Read RLE header to which we are appending in order determine
 * old resolution, window location, and the like.
 */
void
PictureSetWindow()
{
	if (Options.imgname == (char *)NULL)
		RLerror(RL_PANIC,
			"No partially-completed image file specified.\n");

	/*
	 * Open image and read RLE header.
	 */
	Options.pictfile = fopen(Options.imgname, "r");
	rle_dflt_hdr.rle_file = Options.pictfile;
	rle_get_setup_ok(&rle_dflt_hdr, "rayshade", Options.imgname);

	/*
	 * If user specified a window that does not match what's in
	 * the header, complain.
	 */
	if (Screen.minx != UNSET && Screen.minx != rle_dflt_hdr.xmin ||
	    Screen.miny != UNSET && Screen.miny != rle_dflt_hdr.ymin ||
	    Screen.maxx != UNSET && Screen.maxx != rle_dflt_hdr.xmax ||
	    Screen.maxy != UNSET && Screen.maxy != rle_dflt_hdr.ymax)
		RLerror(RL_ADVISE, "Image window: %d - %d, %d - %d.\n",
			rle_dflt_hdr.xmin, rle_dflt_hdr.xmax,
			rle_dflt_hdr.ymin, rle_dflt_hdr.ymax);
	/*
	 * Set window.
	 */
	Screen.minx = rle_dflt_hdr.xmin;
	Screen.miny = rle_dflt_hdr.ymin;
	Screen.maxx = rle_dflt_hdr.xmax;
	Screen.maxy = rle_dflt_hdr.ymax;

	/*
	 * Set alpha.  Warn the user if the alpha option doesn't reflect
	 * what's already been rendered.
	 */
	if (Options.alpha != rle_dflt_hdr.alpha)
		RLerror(RL_WARN, "Image %s %s an alpha channel.\n",
			Options.imgname,
			rle_dflt_hdr.alpha ? "has" : "does not have");

	Options.alpha = rle_dflt_hdr.alpha;

	/*
	 * Determine number of scanlines written to file.  Starting y
	 * (miny) is incremented by this number.
	 */
	Screen.miny += count_rle_rows(&rle_dflt_hdr);
	if (Screen.miny >= Screen.maxy) {
		fprintf(stderr, "\"%s\" is a complete image.\n",
			Options.imgname);
		exit(0);
	}
	fprintf(Stats.fstats,"Continuing \"%s\" at scanline #%d.\n",
		Options.imgname, Screen.miny);
	(void)fclose(Options.pictfile);
}

static int
count_rle_rows( hdr )
rle_hdr *hdr;
{
	rle_op **raw;
	int **nraw, y, ynext;

	if (rle_raw_alloc( hdr, &raw, &nraw ) < 0)  {
		RLerror(RL_PANIC,
			"Unable to allocate memory in count_rle_rows.\n");
	}

	y = 0;
	while ((ynext = rle_getraw( hdr, raw, nraw )) != 32768) {
		y = ynext+1;
		rle_freeraw( hdr, raw, nraw );
	}

	/* Free memory. */
	rle_raw_free( hdr, raw, nraw );

	return y;
}

/*
 * Write a scanline of output.
 * "buf" is an array of Color structures of size Screen.xsize.  Each color
 * component is normalized to [0, 1.].
 */
void
PictureWriteLine(buf)
Pixel *buf;
{
	register int i, chan;
	float floats[3];
	rle_pixel pixels[4];

	for(i = 0; i < Screen.xsize; i++) {
		if (!Options.exp_output) {
			/*
			 * Scale colors to fit unsigned char and check for
			 * over/underflow.
			 */
			outptr[0][i] = CORRECT(buf[i].r);
			outptr[1][i] = CORRECT(buf[i].g);
			outptr[2][i] = CORRECT(buf[i].b);
		} else {
			/*
			 * Convert 3 floats to 4 unsigned chars for
			 * 'exponential_data' RLE file.
			 */
			floats[0] = GAMMACORRECT(buf[i].r);
			floats[1] = GAMMACORRECT(buf[i].g);
			floats[2] = GAMMACORRECT(buf[i].b);
			float_to_exp( 3, floats, pixels );
			for (chan = 0; chan <= 3; chan++)
				outptr[chan][i] = pixels[chan];
		}
		if (Options.alpha)
			/*
			 * Don't gamma correct alpha channel.
			 */
			outptr[-1][i] = correct(buf[i].alpha);
	}
	rle_putrow(outptr, Screen.xsize, &rle_dflt_hdr);
}

/*
 * Close image file.
 */
void
PictureEnd()
{
	rle_puteof(&rle_dflt_hdr);
	(void)fclose(Options.pictfile);
}

#else /* !URT */
void
PictureStart(argv)
char **argv;
{
	if (Options.imgname) {
		Options.pictfile = fopen(Options.imgname, "w");
		if (Options.pictfile == (FILE *)NULL)
			RLerror(RL_PANIC, "Cannot open %s for writing.",
				Options.imgname);
	} else
		Options.pictfile = stdout;

	fprintf(Options.pictfile,"%d %d\n",Screen.xsize, Screen.ysize);

	(void)fflush(Options.pictfile);
}

void
PictureWriteLine(buf)
Pixel *buf;
{
	register int i;

	for (i = 0; i < Screen.xsize; i++) {
		(void)fputc(CORRECT(buf[i].r), Options.pictfile);
		(void)fputc(CORRECT(buf[i].g), Options.pictfile);
		(void)fputc(CORRECT(buf[i].b), Options.pictfile);
	}
	(void)fflush(Options.pictfile);
}

void
PictureEnd()
{
	(void)fclose(Options.pictfile);
}

#endif /* URT */
