/* picture.c,v 1.1.1.1 1995/02/27 07:38:44 explorer Exp */

/*
 * 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".
 *
 */

#include "rayshade.h"
#include "picture.h"
#include "viewing.h"
#include "options.h"
#include "stats.h"
#include "zbuf.h"

#ifdef URT
unsigned char **outptr;		/* Output buffer */
static int count_rle_rows _PROTO((rle_hdr *));
#endif

unsigned char correct _PROTO((Float));

/*
 * Convert floating-point (0.-1.) to unsigned char (0-255), with no gamma
 * correction.
 */
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 (Options.framenum != Options.startframe) {
    /*
     * We've been here before;
     * write a new header and return.
     */
    rle_put_setup(&rle_dflt_hdr);
    return;
  }
  /*
   * 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);
    /*
     * Append "linefeed" to last line in outfile.
     */
    if (Screen.miny > rle_dflt_hdr.ymin)
      rle_skiprow(&rle_dflt_hdr, 1);
  } else {
    /*
     * Starting image from scatch.
     */
    if (Options.imgname) {
      Options.pictfile = fopen(Options.imgname, "w");
      if (Options.pictfile == (FILE *)NULL)
	RLerror(RL_ABORT,"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 (Options.zbufprint) {
    z_buffer.outfile = Options.imgname ?
      strsave (Options.imgname) : NULL;
    z_buffer.argv = argv;
  }
  
  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_ABORT,
	    "No partially-completed image file specified.\n");
  
  /*
   * Open image and read RLE header.
   */
  Options.pictfile = fopen(Options.imgname, "r");
  if (Options.pictfile == (FILE *)NULL) {
    fprintf(stderr,
	    "Cannot open image file \"%s\".  Creating new file.\n",
	    Options.imgname);
    Options.appending = FALSE;
    return;
  }
  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.
   */
  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;
{
  int y, ynext;
  
  for (y = hdr->ymin - 1; (ynext = rle_getskip(hdr)) != 32768; y = ynext)
    ;
  
  /*
   * If we have not reached physical EOF, then we must be at logical EOF
   * which means the image is complete regardless of the amount of
   * scanline data present.
   */
  if (!feof(hdr->rle_file))
    y = hdr->ymax;
  
  return y - hdr->ymin + 1;
}

/*
 * 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);
}

/*
 * End the frame.
 */
void
PictureFrameEnd()
{
  rle_puteof(&rle_dflt_hdr);
}

/*
 * Close image file.
 */
void
PictureEnd()
{
  (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_ABORT, "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((int)CORRECT(buf[i].r), Options.pictfile);
    (void)fputc((int)CORRECT(buf[i].g), Options.pictfile);
    (void)fputc((int)CORRECT(buf[i].b), Options.pictfile);
  }
  (void)fflush(Options.pictfile);
}

void
PictureFrameEnd()
{
  /*
   * Don't do anything -- generic format has no end-of-image marker.
   */
}

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

#endif /* URT */

#ifdef NSPARALLEL
unsigned char *nsp_outbuf;

void
PictureBufferWrite(buf)
     Pixel *buf;
{
  register int i;
  NSBuffer *bp;
  
  bp = GetBuffer();
  
  bp->rlen = Screen.xsize;
  bp->glen = Screen.xsize;
  bp->blen = Screen.xsize;
  bp->alen = Screen.xsize;
  
  if (bp->rlen && bp->r == NULL)
    bp->r = Malloc(bp->rlen);
  if (bp->glen && bp->g == NULL)
    bp->g = Malloc(bp->glen);
  if (bp->blen && bp->b == NULL)
    bp->b = Malloc(bp->blen);
  if (bp->alen && bp->a == NULL)
    bp->a = Malloc(bp->alen);
  if (bp->zlen && bp->z == NULL)
    bp->z = Malloc(bp->zlen);
  
  for (i = 0; i < bp->rlen; i++) {
    bp->r[i] = CORRECT(buf[i].r);
    bp->g[i] = CORRECT(buf[i].g);
    bp->b[i] = CORRECT(buf[i].b);
    bp->a[i] = correct(buf[i].alpha);
  }
  
  NetSendPictureLine(Options.tcpfd, bp);
}

void
PictureBufferStart()
{
  NetSendPictureStart(Options.tcpfd);
}

void
PictureBufferEnd()
{
  NetSendPictureEnd(Options.tcpfd);
}

/*
 * a modified write routine for preprocessed data.
 */
void
PictureWritePreprocessedLine(bp)
     NSBuffer *bp;
{
  register int i;
  
  for(i = 0; i < Screen.xsize; i++) {
    outptr[0][i]  = bp->r[i];
    outptr[1][i]  = bp->g[i];
    outptr[2][i]  = bp->b[i];
    outptr[-1][i] = bp->a[i];
  }
  rle_putrow(outptr, Screen.xsize, &rle_dflt_hdr);
}

#endif /* NSPARALLEL */
