/*
 * 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.
 */
/*
 * aliastorle.c - Convert Alias "pix" format to Utah's rle images
 *
 * Author:      Raul Rivero
 *              Mathematics Dept.
 *              University of Oviedo
 * Date:        Fri Dec 27 1991
 * Copyright (c) 1991, Raul Rivero
 *
 */
#ifndef lint
static char rcs_id[] = "$Header: /l/spencer/src/urt/cnv/RCS/aliastorle.c,v 3.0.1.1 1992/04/29 20:01:12 spencer Exp $";
#endif
/*
  aliastorle()			Tag the file.
*/

#include <stdio.h>
#include <math.h>
#include "rle.h"

#define byte                    unsigned char
#define Fread(p, s, n, f)       if ( fread(p, s, n, f) != n ) error(3)
#define Fwrite(p, s, n, f)      if ( fwrite(p, s, n, f) != n ) error(4)
#define VPRINTF                 if (verbose) fprintf

/*
 * Alias header.
 */
typedef struct {
        short xsize, ysize;
        short xinit, yinit;
        short depth;
} alias_hdr;

/*
 * I use a intermediate format ( a simple bitmap ) with
 * this format ...
 */
typedef struct {
        int xsize, ysize;       /* sizes */
        int depth;              /* # of colors */
        int colors;             /* # of colors */
        byte *r, *g, *b;        /* components or bitmap (planes < 8) */
        byte *cmap;             /* cmap if planes < 8 */
} bitmap_hdr;

/*
 * Prototypes ( only what is necesary ).
 */
#ifdef USE_PROTOTYPES
static char *Malloc(long int);
static char *read_file(FILE *, int *);
static long filelen(FILE *);
static int read_alias(FILE *, bitmap_hdr *);
static void write_rle(FILE *, bitmap_hdr *);
static void read_alias_header(FILE *, alias_hdr *);
static void create_alias_cmap(bitmap_hdr *);
static void uncode_alias24(byte *, byte *, byte *, byte *, byte *);
static void uncode_alias(byte *, byte *, byte *);
static int read_line_alias24(FILE *, byte *, byte *, byte *, int);
static int read_line_alias(FILE *, byte *, int);
static void error( int );
#else
static char *Malloc();
static char *read_file();
static long filelen();
static int read_alias();
static void write_rle(), read_alias_header();
static void create_alias_cmap(), uncode_alias24(), uncode_alias();
static int read_line_alias24(), read_line_alias();
static void error();
#endif


/*
 * The flag for verbose.
 */
int verbose= 0;
/*
 * RLE file header (put here so can be initialized with cmd and file names).
 */
rle_hdr the_hdr;

/*****************************************************************
 * TAG( main )
 *
 * Usage: aliastorle [-v] [-o outfile] [infile]
 *
 * Inputs:
 *	-v:		Verbose switch.
 *      infile:         Input Alias pix file. Stdin used if not
 *                      specified.
 * Outputs:
 *      outfile:        Output a RLE file.  Stdout used if not
 *                      specified.
 * Assumptions:
 *      [None]
 * Algorithm:
 *      [None]
 */

void
main( argc, argv )
int argc;
char **argv;
{
  char *inname  = NULL;
  char *outname = NULL;
  FILE *infile;
  FILE *outfile;
  int oflag = 0;
  bitmap_hdr bitmap;

  the_hdr = *rle_hdr_init( (rle_hdr *)NULL );
  /*
   * Get options
   */
  if ( !scanargs( argc, argv, "% v%- o%-outfile!s infile%s",
                  &verbose, &oflag, &outname, &inname ))
    exit( 1 );

  /* An input file name ? */
  rle_names( &the_hdr, cmd_name( argv ), inname, 0 );
  infile= rle_open_f(the_hdr.cmd, inname, "r");


  /*
   * Translate Alias "pix" format into my bitmap.
   */
  read_alias(infile, &bitmap);
  rle_close_f(infile);    /* We finish with this file */

  /*
   * We have a bitmap with the image, so we open the
   * new rle file and write it.
   */
  outfile = rle_open_f(cmd_name(argv), outname, "w");
  write_rle(outfile, &bitmap);
  fclose(outfile);            /* it is not necesary, but ... */

  exit(0);
}

static int
read_alias(handle, image)
FILE *handle;
bitmap_hdr *image;
{
  register int i;
  alias_hdr header;
  int total_size;
  register int xsize;
  byte *end;
  byte *r, *g = 0, *b = 0;
  int can_read_all;
  byte *buffer = 0, *ptr = 0;
  int filesize;
  int allplanes = 0;
  int error;
  int cmapsize;

  /*
   * Alias "pix" is a bad format to read line per line. If every
   * triplet is different to its next, then we read 2 bytes, 2 bytes ...
   * ... and read a file of 1 Mb. with this form ... hmmmmmm !!!
   * This is necesary if we are reading from stdin, but if we have
   * a file, we'll read the file into memory ( one access to disk ).
   */
  can_read_all = (handle != stdin);     /* a regular file ? */
  VPRINTF(stderr, "Reading Alias from %s\n", 
                  (can_read_all ? "file" : "stdin"));

  if (can_read_all) {
    /*
     * We are using a regular file ( not a pipe ), so we can
     * read it into memory.
     */
    ptr= buffer= (byte *) read_file(handle, &filesize);
    VPRINTF(stderr, "File size: %d\n", filesize);
    /* Copy the header */
    bcopy(ptr, &header, sizeof(alias_hdr));
    ptr += sizeof(alias_hdr);           /* skip the header */
  }else {
    /*
     * We are using stdin so ...
     */
    /* We need the header of Alias pix */
    VPRINTF(stderr, "Reading Alias header\n");
    read_alias_header(handle, &header);
  }

  /* Size in pixels */
  total_size = header.xsize * header.ysize;

  /* Fill our header */
  image->xsize  = header.xsize;
  image->ysize  = header.ysize;
  image->depth  = header.depth;
  image->colors = 1 << image->depth;
  allplanes = ( image->depth > 8 );     /* an image with 24 planes ? */
  VPRINTF(stderr, "Image size: %dx%d\n", image->xsize, image->ysize);
  VPRINTF(stderr, "Depth: %d\n", image->depth);

  /* Get some memory */
  if ( allplanes ) {
    /*
     * We have a image with 24 planes, so we need three buffers to
     * store it.
     */
    r= image->r = (byte *) Malloc(total_size);
    g= image->g = (byte *) Malloc(total_size);
    b= image->b = (byte *) Malloc(total_size);
  }else {
    /*
     * The image has less than 256 colors, so we use one plane,
     * for the bitmap, and the cmap.
     */
    r= image->r = (byte *) Malloc(total_size);
    cmapsize= image->colors * 3;        /* size in bytes */
    image->cmap = (byte *) Malloc(cmapsize);
    /* Creating the cmap from file */
    VPRINTF(stderr, "Creating cmap\n");
    create_alias_cmap(image);
  }

  /*
   * We are ready to uncompress Alias file.
   */
  VPRINTF(stderr, "Uncompressing Alias file\n");
  if (can_read_all) {
    /*
     * We have the file into memory so we uncode it directly.
     */
    end = r + total_size;       /* the end of main buffer */
    if ( allplanes )
      uncode_alias24(ptr, r, g, b, end);
    else uncode_alias(ptr, r, end);
    /*
     * We have read the file and uncompressed, so we can
     * free the memory ( and exit ).
     */
    free(buffer);
  }else {
    /*
     * We'll read each line from Alias file ( stdin ) until we
     * fill our buffers or we get an error.
     */
    xsize = image->xsize;
    for (i= 0; i< image->ysize; i++) {
      /*
       * Read and decode the Alias raster information, two cases:
       * with planes > 8 => a triplet (RGB), else a index.
       */
      if ( allplanes ) {
        /* An image with 24 planes */
        error= read_line_alias24(handle, r, b, g, xsize);
      }else {
        /* An image with indexes to cmap */
        error= read_line_alias(handle, r, xsize);
      }
      if (error) {
        /* An error, so we exit */
        fprintf(stderr, "Error while reading line %d\n", i);
        return 1;
      }else {
        /* A line has been read, so we increment theirs pointers */
        r += xsize;
        if ( allplanes ) {
          g += xsize;
          b += xsize;
        }
      }
    }
  } /* end of reading from stdin */

  /* Finish with no problems */
  return 0;
}

static void
read_alias_header(handle, header)
FILE *handle;
alias_hdr *header;
{
  /* Read the header */
  Fread(header, sizeof(alias_hdr), 1, handle);

  /* Check some things */
  if ( header->xsize < 1 || header -> xsize > 2560 ||
       header->ysize < 1 || header -> ysize > 2560 )
    error(5);
  if ( header->depth > 24 || header->depth < 1 )
    error(5);
}

static int
read_line_alias24(handle, r, g, b, size)
FILE *handle;
byte *r, *g, *b;
register int size;
{
  register int i;
  register int count = 0;
  byte *end;
  byte buffer[4];

  end = r + size;
  while (count < size) {
    /*
     * Alias code format:  <repeat>BGR => 4 bytes.
     */
    if ( !fread(buffer, 4, 1, handle) )         /* read next buffer */
      return 1;   /* hey !, an EOF here ?. Hmmmm, this is an error ! */

    count += buffer[0];                 /* # of repetitions */
    /* repeat 'buffer[0]' times these triplet */
    for (i= 0; i< buffer[0]; i++) {
      *r++ = buffer[3];
      *g++ = buffer[1];
      *b++ = buffer[2];
    }
    if ( r > end )
      error(6);         /* uncompress more bytes than size */
  }

  /* No problems */
  return 0;
}

static int
read_line_alias(handle, r, size)
FILE *handle;
byte *r;
register int size;
{
  register int i;
  register int count = 0;
  byte *end;
  byte buffer[2];

  end = r + size;
  while (count < size) {
    /*
     * Alias code format:  <repeat><index> => 2 bytes.
     */
    if ( !fread(buffer, 2, 1, handle) )         /* read next buffer */
      return 1;   /* hey !, an EOF here ?. Hmmmm, this is an error ! */

    count += buffer[0];                 /* # of repetitions */
    /* repeat 'buffer[0]' times these index */
    for (i= 0; i< buffer[0]; i++) {
      *r++ = buffer[1];
    }
    if ( r > end )
      error(6);         /* uncompress more bytes than size */
  }

  /* No problems */
  return 0;
}

static void
create_alias_cmap(image)
bitmap_hdr *image;
{
  register int i;
  byte *ptr;

  /*
   * Alias 8 bits files are b&w, so ...
   */
  ptr = (byte *) image->cmap;
  for (i= 0; i< image->colors; i++) {
    *ptr++ = i;
    *ptr++ = i;
    *ptr++ = i;
  }
}

static void
bitmapcmap_to_rlecmap(bitmap, rle)
bitmap_hdr *bitmap;
rle_hdr *rle;
{
  register int i;
  rle_map *rch, *gch, *bch;
  byte *ptr;

  /* Allocate memory */
  rle->cmap= (rle_map *) Malloc(bitmap->colors * 3 * sizeof(rle_map));

  /*
   * We'll use 3 ptrs, first to R channel, second to G channel, ...
   */
  ptr = bitmap->cmap;
  rch = rle->cmap;
  gch = &(rle->cmap[bitmap->colors]);
  bch = &(rle->cmap[2 * bitmap->colors]);
  for (i= 0; i< bitmap->colors; i++) {
    *rch++ = (*ptr++ << 8);
    *gch++ = (*ptr++ << 8);
    *bch++ = (*ptr++ << 8);
  }
}

static void
uncode_alias24(ptr, rbuf, gbuf, bbuf, end)
byte *ptr;
byte *rbuf, *gbuf, *bbuf;
byte *end;
{
  register int i;
  byte r, g, b;

  while ( rbuf < end ) {        /* while not end of buffer */
    if ( (i= *ptr++) > 1 ) {            /* how mary repetitions ? */
      b= *ptr++;
      g= *ptr++;
      r= *ptr++;
      while( i-- ) {    /* copy these triplet 'i' times */
        *rbuf++ = r;
        *gbuf++ = g;
        *bbuf++ = b;
      }
    }else {                     /* else, copy these triplet */
      *bbuf++ = *ptr++;
      *gbuf++ = *ptr++;
      *rbuf++ = *ptr++;
    }
  }
}

static void
uncode_alias(ptr, rbuf, end)
byte *ptr;
byte *rbuf;
byte *end;
{
  register int i;
  byte r;

  while ( rbuf < end ) {        /* while not end of buffer */
    if ( (i= *ptr++) > 1 ) {            /* how mary repetitions ? */
      r= *ptr++;
      while( i-- ) {    /* copy these index 'i' times */
        *rbuf++ = r;
      }
    }else {                     /* else, copy these index */
      *rbuf++ = *ptr++;
    }
  }
}

static void
write_rle(handle, image)
FILE *handle;
bitmap_hdr *image;
{
  register int i, j;
  rle_pixel **row;
  byte *r;
  int offset_last;

  VPRINTF(stderr, "Writing RLE file\n");
  /*
   * Fill the rle header with our ( little ) information.
   */
  the_hdr.rle_file = handle;
  the_hdr.xmin = 0;
  the_hdr.ymin = 0;
  the_hdr.xmax = image->xsize - 1;
  the_hdr.ymax = image->ysize - 1;
  if (image->depth > 8) {
    the_hdr.ncolors = 3;            /* 24 planes */
    the_hdr.ncmap = 0;
  }else {
    the_hdr.ncolors = 1;
    the_hdr.ncmap = 3;
    the_hdr.cmaplen = image->depth;
    /* Convert our cmap to rle cmap */
    bitmapcmap_to_rlecmap(image, &the_hdr);
  }
  the_hdr.alpha = 0;           /* we don't use alpha channels */

  /* Write the header */
  rle_put_setup(&the_hdr);

  /*
   * RLE write raster lines in reverse order, so we'll put
   * pointers to the last line.
   */
  offset_last = image->xsize * image->ysize - image->xsize;

  VPRINTF(stderr, "Compressing RLE lines\n");
  if (image->depth > 8) {
    /*
     * 24 planes, rle_putrow functions use a buffer which contains
     * a line, so we need do it.
     */
    /* Allocate some memory */
    row = (rle_pixel **)Malloc( 3 * sizeof(rle_pixel *) );
    if ( row == 0 )
      error(2);

    /* Pointers to last line */
    row[0] = image->r + offset_last;
    row[1] = image->g + offset_last;
    row[2] = image->b + offset_last;

    /* Write each line */
    for (i= 0; i< image->ysize; i++) {
      rle_putrow(row, image->xsize, &the_hdr);
      /*
       * Back up to the previous line.
       */
      for ( j = 0; j < 3; j++ )
	row[0] -= image->xsize;
    }
    /* free rle row buffer */
    free(row);

  }else {
    /*
     * A image with cmap.
     */
    /* if a simple plane => stored on R component */
    r = image->r + offset_last;      
    for (i= 0; i< image->ysize; i++) {
      rle_putrow(&r, image->xsize, &the_hdr);
      r -= image->xsize;
    }
  }

  /*
   * Put an EOF into the RLE file ( and THE END ! )
   */
  rle_puteof(&the_hdr);
}

static void
error(code)
int code;
{
  fprintf(stderr, "%s: ", the_hdr.cmd);
  switch (code) {
    case  0:
             break;
    case  1: fprintf(stderr, "Cannot open file\n");
             break;
    case  2: fprintf(stderr, "Out of memory\n");
             break;
    case  3: fprintf(stderr, "Error while reading input file\n");
             break;
    case  4: fprintf(stderr, "Error while writing output file\n");
             break;
    case  5: fprintf(stderr, "Input file is not an Alias pix\n");
             break;
    case  6: fprintf(stderr, "File corrupt ( uncompress too bytes )\n");
             break;
    case 99: fprintf(stderr, "Not ready\n");
             break;
    default: fprintf(stderr, "Unknow error code (%d)\n", code);
             break;
  }
  exit(1);
}

/*
 * My standard functions.
 */

static char *Malloc(size)
long int size;
{
  char *ptr;

  if ((ptr = (char *) malloc(size)) == NULL)
    error(2);

  /*
   * Usually compilers fill buffers with zeros,
   * but ...
   */
  bzero( ptr, size );
  return ptr;
}


static char *read_file(handle, bytes)
FILE *handle;
int *bytes;
{
  char *buffer;

  /* Get size of file and allocate memory */
  *bytes= (int) filelen(handle);
  if ( *bytes > 0 )		/* Ok, it's a regular file. */
  {
    buffer= (char *) Malloc(*bytes);

    /* Read it */
    Fread(buffer, (int) (*bytes), 1, handle);
  }
  else				/* Oops!  It's a pipe. */
  {
    int n = 0, bufsize = 0;
    /* Read in chunks of BUFSIZ. */
    buffer = Malloc( BUFSIZ );
    while ( (n = fread( buffer + bufsize, 1, BUFSIZ, handle )) == BUFSIZ )
    {
      bufsize += BUFSIZ;
      buffer = realloc( buffer, bufsize + BUFSIZ );
      RLE_CHECK_ALLOC( the_hdr.cmd, buffer, "input image" );
    }
    if ( n >= 0 )
      n += bufsize;
    else
      n = bufsize;
  }

  /* Return the buffer */
  return buffer;
}

static long filelen(handle)
FILE *handle;
{
  long current_pos;
  long len;

  /* Save current position */
  current_pos= ftell(handle);

  /* Get len of file */
  fseek(handle, 0, 2);
  len= ftell(handle);

  /* Restore position */
  fseek(handle, current_pos, 0);

  return len;
}


