/*
 * This software is copyrighted as noted below.  It may be freely copied,
 * modified, and redistributed, provided that the copyright notice is
 * preserved on all copies.
 *
 * There is no warranty or other guarantee of fitness for this software,
 * it is provided solely "as is".  Bug reports or fixes may be sent
 * to the author, who may or may not act on them as he desires.
 *
 * You may not include this software in a program or other software product
 * without supplying the source, or without informing the end-user that the
 * source is available for no extra charge.
 *
 * If you modify this software, you should include a notice giving the
 * name of the person performing the modification, the date of modification,
 * and the reason for such modification.
 */
/*
 * rla.c - interface with Wavefront's RLA format.
 *
 * Author:      Raul Rivero
 *              Mathematics Dept.
 *              University of Oviedo
 * Date:        Sat Jan 4 1992
 * Copyright (c) 1992, Raul Rivero
 *
 */

#include <lug.h>
#include <lugfnts.h>

#define no_complete()   { Error( 18 );   \
                          free(raux);    \
                          free(gaux);    \
                          free(baux);    \
                          free(offsets); \
                          free(ptr);     \
                          return 1; }

extern int LUGverbose;

read_rla_file( name, bitmap )
char *name;
bitmap_hdr *bitmap;
{
  FILE *handle;

  /* Open the file descriptor */
  if ( name != NULL )
    handle = Fopen( name, "rb" );
  else error( 20 );

  /* Read the bitmap */
  read_rla( handle, bitmap );
  rm_compress();

  /* Close the file */
  Fclose( handle );
}

read_rla(handle, image)
FILE *handle;
bitmap_hdr *image;
{
  register int i, j;
  RLA_HEADER rla;
  int totalsize;
  byte *r, *g, *b;
  int *offsets;
  short no_bytes;
  byte *ptr;
  byte *raux, *gaux, *baux;
  byte *rptr, *gptr, *bptr;

  /*
   * Read the RLA's header.
   */
  Fread(&rla, 740, 1, handle);
  image->magic = LUGUSED;
  image->xsize = rla.window.right - rla.window.left + 1;
  image->ysize = rla.window.top - rla.window.bottom + 1;
  image->depth = 24;
  image->colors = ( 1 << image->depth );
  totalsize= image->xsize * image->ysize;

  if ( image->xsize < 1 || image->xsize > 2559 ||
       image->ysize < 1 || image->ysize > 2559 )
    error(5);

  /*
   * Allocate for bitmap components.
   */
  r = image->r = (byte *) Malloc(totalsize);
  g = image->g = (byte *) Malloc(totalsize);
  b = image->b = (byte *) Malloc(totalsize);

  /* Some space to unpack scanlines */
  raux = (byte *) Malloc(image->xsize);
  gaux = (byte *) Malloc(image->xsize);
  baux = (byte *) Malloc(image->xsize);
  ptr  = (byte *) Malloc(2560);

  /* Allocate space for offsets */
  offsets = (int *) Malloc(image->ysize*sizeof(int));
  Fread(offsets, sizeof(int), image->ysize, handle);

  for (i = rla.window.top; i >= rla.window.bottom; i--) {
    if ( i > rla.act_window.top || i < rla.act_window.bottom ) {
      /* Outlines => zeros ( done with Malloc ) */
      r += image->xsize;
      g += image->xsize;
      b += image->xsize;
    }else {
      /*
       * Set pointer where offsets table says.
       */
      if (fseek(handle, (long) offsets[i - rla.act_window.bottom], 0))
        no_complete();
      /*
       * Uncode R channel.
       */
      Fread(&no_bytes, sizeof(short), 1, handle);
      Fread(ptr, (int) no_bytes, 1, handle);
      decodeRLA(ptr, raux, no_bytes);
      /*
       * Uncode G channel.
       */
      Fread(&no_bytes, sizeof(short), 1, handle);
      Fread(ptr, (int) no_bytes, 1, handle);
      decodeRLA(ptr, gaux, no_bytes);
      /*
       * Uncode B channel.
       */
      Fread(&no_bytes, sizeof(short), 1, handle);
      Fread(ptr, (int) no_bytes, 1, handle);
      decodeRLA(ptr, baux, no_bytes);

      /*
       * If you wanna you do the same task with matte ( but I
       * don't mind that channel ).
       */

      /*
       * We uncode the active window, now we fill the real window
       * with these datas and aditional zeros.
       */
      rptr= raux;
      gptr= gaux;
      bptr= baux;
      for (j = rla.window.left; j <= rla.window.right; j++) {
        if ( j < rla.act_window.left || j > rla.act_window.right ) {
          /* Outlines => zeros ( done with Malloc ) */
          *r++, *g++, *b++;
         }else {
           *r++ = *rptr++;
           *g++ = *gptr++;
           *b++ = *bptr++;
         }
      }
    }
  }

  /*
   * Free memory.
   */
  free(raux);
  free(gaux);
  free(baux);
  free(offsets);
  free(ptr);

  /* no errors */
  return 0;
}

decodeRLA(buf_in, buf_out, len)
byte    *buf_in;
byte    *buf_out;
int     len;
{
   int  ct;
   byte *end;

   end = buf_out + len;
   while(len > 0) {
      ct = *buf_in++;
      len--;
      if (ct < 128) {
        /* repeat pixel value ct+1 times */
         while (ct-- >= 0)
            *buf_out++ = *buf_in;
         buf_in++;
         len--;
      }
      else {
        /* copy ct unencoded values */
         for (ct = 256-ct; ct-- > 0; len--)
            *buf_out++ = *buf_in++;
      }
      /* if ( buf_out > end )
        error(6);*/
   }
}

write_rla_file( name, image )
char *name;
bitmap_hdr *image;
{
  FILE *handle;

  /* Open the file descriptor */
  if ( name != NULL )
    handle = Fopen( name, "wb" );
  else handle = stdout;

  /* Write the bitmap */
  write_rla( handle, image );

  /* Close the file */
  Fclose( handle );

}

write_rla(handle, image)
FILE *handle;
bitmap_hdr *image;
{
  register int i;
  RLA_HEADER header;
  byte *matte;
  int *offsets;
  byte *r, *g, *b;
  byte buffer[2560];
  short no_bytes;

  if ( image->magic != LUGUSED )
    error( 19 );

  /* Fill RLA's header with zeros ( and save later work ) */
  bzero(&header, 740);

  VPRINTF(stderr, "Writing RLA's header\n");
  /* Fill RLA's header ( with datas ) */
  header.window.left   = 0;
  header.window.bottom = 0;
  header.window.right  = image->xsize - 1;
  header.window.top    = image->ysize - 1;
  /* Sorry, but I don't define the active window */
  header.act_window.left   = 0;
  header.act_window.bottom = 0;
  header.act_window.right  = image->xsize - 1;
  header.act_window.top    = image->ysize - 1;
  /* Three channels, RGB */
  header.num_chan = 3;
  /* One matte channel, puff ! */
  header.num_matte = 1;
  /* Other definitions */
  header.frame = 1;
  strcpy( header.gamma, "2.2" );
  strcpy( header.red_pri  , "0.670 0.330" );
  strcpy( header.green_pri, "0.210 0.710" );
  strcpy( header.blue_pri , "0.140 0.080" );
  strcpy( header.white_pt , "0.310 0.316" );
  strcpy( header.name, "unknown" );
  strcpy( header.desc, "converted from LUG library" );
  gethostname( header.machine, 32 );
  strcpy( header.user, cuserid(NULL) );
  strcpy( header.aspect, "user defined" );
  strcpy( header.chan, "rgb" );

  /*
   *  Write the RLA's header.
   */
  fwrite(&header, 740, 1, handle), fflush(handle);

  /*
   * Allocate memory for offset buffer ( and a simulated
   * scanline matte table ).
   */
  offsets = (int  *) Malloc( image->ysize * sizeof(int) );
  matte = (byte *) Malloc( image->xsize );

  /*
   * We need write the ( incorrect ) offsets now, so that ...
   */
  fwrite(offsets, image->ysize, sizeof(int), handle);

  VPRINTF( stderr, "Compressing scanline info\n");
  r = image->r;
  g = image->g;
  b = image->b;
  for ( i = 0; i < image->ysize; i++ ) {
#ifdef DEBUG
    VPRINTF( stderr, "Line: %4d\r", i);
    VFLUSH( stderr );
#endif
    /* Store the file position */
    offsets[image->ysize-i-1]= (int) ftell(handle);

    /*
     * R channel.
     */
    no_bytes = encodeRLA(r, buffer, image->xsize);
    Fwrite( &no_bytes, sizeof(short), 1, handle );
    Fwrite( buffer, 1, no_bytes, handle );
    r += image->xsize;
    /*
     * G channel.
     */
    no_bytes = encodeRLA(g, buffer, image->xsize);
    Fwrite( &no_bytes, sizeof(short), 1, handle );
    Fwrite( buffer, 1, no_bytes, handle );
    g += image->xsize;
    /*
     * B channel.
     */
    no_bytes = encodeRLA(b, buffer, image->xsize);
    Fwrite( &no_bytes, sizeof(short), 1, handle );
    Fwrite( buffer, 1, no_bytes, handle );
    b += image->xsize;
    /*
     * Matte channel ( always the same but ... ).
     */
    no_bytes = encodeRLA(matte, buffer, image->xsize);
    Fwrite( &no_bytes, sizeof(short), 1, handle );
    Fwrite( buffer, 1, no_bytes, handle );
  }

  VPRINTF( stderr, "Rewriting offsets\n");
  /*
   * Well, now we have the correct offsets and we need
   * write them, so ...
   */
  fflush(handle);
  fseek(handle, 740, 0);
  fwrite(offsets, image->ysize, sizeof(int), handle);

  /*
   * Free memory.
   */
  free(matte);
  free(offsets);
}

encodeRLA(c_in, c_out, width)
byte *c_in;
byte *c_out;
int  width;
{
  int len;
  int ct;

  len = 0;
  while ( width > 0 ) {
    if ( width > 1 && c_in[0] == c_in[1] ) {
      for ( ct = 2; ct < width; ct++ ) {
        if ( c_in[ct] != c_in[ct-1] )
          break;
        if (ct >= 127)
          break;
      }

      /* write out count */
      *c_out++ = ct - 1;
      len++;

      /* write out value */
      *c_out++ = *c_in;
      len++;
      width -= ct;
      c_in += ct;
    }else {
      for ( ct = 1; ct < width; ct++ ) {
        if ( width - ct > 1 && c_in[ct] == c_in[ct+1] )
          break;
        if (ct >= 127)
          break;
      }

      /* write out count */
      *c_out++ = 256 - ct;
      len++;

      /* copy string of pixels */
      for ( ; ct-- > 0; len++, width-- )
        *c_out++ = *c_in++;
    }
  }

  return len;
}

