/*
 * 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.
 */
/*
 * rletoalias.c - Convert Utah's rle images to Alias "pix" format
 *
 * Author:      Raul Rivero
 *              Mathematics Dept.
 *              University of Oviedo
 * Date:        Mon Dec 30 1991
 * Copyright (c) 1991, Raul Rivero
 *
 */
#ifndef lint
static char rcs_id[] = "$Header: /l/spencer/src/urt/cnv/RCS/rletoalias.c,v 3.0.1.1 1992/04/30 13:58:47 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
#define PSEUDOCOLOR             1
#define DIRECTCOLOR             2
#define TRUECOLOR               3
#define GRAYSCALE               4

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

typedef byte color_map[3];

/*
 * the flag for verbose.
 */
int verbose= 0;
/*
 * RLE file header (here for command name and file name).
 */
rle_hdr the_hdr;

#ifdef USE_PROTOTYPES
static void read_rle( FILE *, bitmap_hdr *);
static void write_alias( FILE *, bitmap_hdr *);
static void error( int );
static void rlecmap_to_bitmapcmap( rle_hdr *, bitmap_hdr * );
static void code_alias24( byte *, byte *, byte *, int, FILE * );
static char *Malloc( long int );
#else
static void read_rle(), write_alias(), error(), rlecmap_to_bitmapcmap();
static void code_alias24();
static char *Malloc();
#endif

/*****************************************************************
 * TAG( main )
 *
 * Usage: rletoalias [-v] [-o outfile] [infile]
 *
 * Inputs:
 *	-v:		Verbose switch.
 *      infile:		Input a color RLE file. Stdin used if
 *                      not specified.
 * Outputs:
 *      outfile:	Output Alias pix 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 = stdout;
  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 );

  /*
   * Open rle file.
   */
  rle_names( &the_hdr, cmd_name( argv ), inname, 0 );
  infile = rle_open_f(the_hdr.cmd, inname, "r");

  /*
   * Translate Utah's rle format to our bitmap.
   */
  read_rle(infile, &bitmap);
  fclose(infile);    /* we finish with this file */

  outfile = rle_open_f(cmd_name(argv), outname, "w");
  /* An output file name ? */
  rle_names( &the_hdr, cmd_name( argv ), outname, 0 );
  VPRINTF(stderr, "Writing to file %s\n", the_hdr.file_name);
  outfile= rle_open_f(the_hdr.cmd, outname, "w");

  write_alias(outfile, &bitmap);
  fclose(outfile);            /* it is not necesary, but ... */

  exit(0);
}

static void
read_rle(handle, image)
FILE *handle;
bitmap_hdr *image;
{
  register int i, j;
  rle_pixel **row;
  byte *r, *g = 0, *b = 0;
  int totalsize;
  color_map *map = 0;
  int type = 0;
  int two_lines;
  int offset_last;

  /*
   * Read the file's configuration.
   */
  the_hdr.rle_file = handle;
  rle_get_setup_ok( &the_hdr, NULL, NULL );

  /*
   * Fill our bitmap.
   */
  image->xsize = the_hdr.xmax - the_hdr.xmin + 1;
  image->ysize = the_hdr.ymax - the_hdr.ymin + 1;
  image->depth = ( the_hdr.ncolors < 3 ? the_hdr.cmaplen : 24 );
  image->colors = ( 1 << image->depth );
  totalsize= image->xsize * image->ysize;
  offset_last = totalsize - image->xsize;
  two_lines = 2 * image->xsize;
  VPRINTF(stderr, "Image size: %dx%d\n", image->xsize, image->ysize);
  VPRINTF(stderr, "Image depth: %d\n", image->depth);
  VPRINTF(stderr, "%s colormap\n", 
                  (the_hdr.ncmap ? "With" : "Without"));

  /*
   * Check the coherence and image type.
   */
  VPRINTF(stderr, "Image type ");
  switch ( the_hdr.ncolors ) {
    case  1: switch ( the_hdr.ncmap ) {
               case  0: type = GRAYSCALE;       /* 8 planes, no cmap */
                        VPRINTF(stderr, "GRAYSCALE\n");
                        break;
               case  3: type = PSEUDOCOLOR;     /* 8 planes, cmap */
                        VPRINTF(stderr, "PSEUDOCOLOR\n");
                        break;
               default: VPRINTF(stderr, "unkown\n");
                        error(6);
                        break;
             }
             break;
    case  3: switch ( the_hdr.ncmap ) {
               case  0: type = DIRECTCOLOR;     /* 24 planes, no cmap */
                        VPRINTF(stderr, "DIRECTCOLOR\n");
                        break;
               case  3: type = TRUECOLOR;       /* 24 planes, cmap */
                        VPRINTF(stderr, "TRUECOLOR\n");
                        break;
               default: VPRINTF(stderr, "unkown\n");
                        error(6);
                        break;
             }

             break;
    default: error(6);
             VPRINTF(stderr, "unkown\n");
             break;
  }

  /*
   * Allocate some memory.
   */
  if (rle_row_alloc(&the_hdr, &row) < 0)
    error(2);
  if (image->depth > 8 || type == GRAYSCALE) {
    /*
     * 24 planes => we need three components
     * GRAYSCALE use 8 planes but it's defined like 24 planes
     */
    r= image->r = (byte *) Malloc(totalsize);
    g= image->g = (byte *) Malloc(totalsize);
    b= image->b = (byte *) Malloc(totalsize);
    if ( type == TRUECOLOR) {
      image->colors = 256;      /* well, a trap to rlecmap_to_bit... */
      rlecmap_to_bitmapcmap(&the_hdr, image);
      map = (color_map *) image->cmap;	/* will be more easy use it */
    }
  }else {
    /* 8 planes => one component and cmap */
    r= image->r = (byte *) Malloc(totalsize);
    /* Convert rle cmap to bitmap cmap */
    rlecmap_to_bitmapcmap(&the_hdr, image);
    map = (color_map *) image->cmap;	/* will be more easy use it */
  }

  /*
   * Read the input image and convert it to a simple bitmap.
   * RLE writes lines in reverse order, so we point to last
   * line.
   */
  VPRINTF(stderr, "Uncompressing RLE file\n");
  r += offset_last;
  g += offset_last;
  b += offset_last;
  for (j= 0; j< image->ysize; j++) {
    rle_getrow(&the_hdr, row);
    switch ( type ) {
      case GRAYSCALE  :
      case DIRECTCOLOR: for (i= 0; i< image->xsize; i++) {
                          *r++ = row[0][i];
                          *g++ = row[1][i];
                          *b++ = row[2][i];
                        }
                        break;
      case PSEUDOCOLOR: for (i= 0; i<image->xsize; i++) {
                          *r++ = row[0][i];
                        }
                        break;
      case TRUECOLOR  : for (i= 0; i< image->xsize; i++) {
                          *r++ = map[row[0][i]][0];
                          *g++ = map[row[1][i]][1];
                          *b++ = map[row[2][i]][i];
                        }
                        break;
      default         : error(6);
                        break;
    }
    /*
     * Pointers to next byte of current line, so we substract
     * two lines.
     */
    r -= two_lines;
    g -= two_lines;
    b -= two_lines;
  }

  /*
   * TRUECOLOR has map of colors, but we'll not use it.
   */
  if ( type == TRUECOLOR )
    free(image->cmap);
}

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

  /* Allocate memory */
  ptr= bitmap->cmap= (byte *) Malloc(bitmap->colors * 3);

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

static void
write_alias(handle, image)
FILE *handle;
bitmap_hdr *image;
{
  register int i;
  alias_hdr alias;
  byte *rbuf, *gbuf, *bbuf;

  /*
   * Alias 8 bits files are b&w ( no cmap ), so Alias
   * don't support really a 8 bits file.
   */
  if (image->depth <= 8) {
    fprintf(stderr, "Bitmap with 8 planes\n");
    error(99);
  }

  VPRINTF(stderr, "Writing Alias file\n");
  /* Fill the Alias header and write it */
  alias.yinit = 0;
  alias.xinit = 0;
  alias.xsize = image->xsize;
  alias.ysize = image->ysize;
  alias.depth = image->depth;
  Fwrite(&alias, sizeof(alias_hdr), 1, handle);

  /* Set the pointers to buffers */
  rbuf = image->r;
  gbuf = image->g;
  bbuf = image->b;

  /* Compress each line */
  for (i= 0; i< image->ysize; i++) {
    /*
     * Alias "pix" format do a compression method on
     * each raster line.
     */
    code_alias24(rbuf, gbuf, bbuf, image->xsize, handle);
    /* Move pointers to next lines */
    rbuf += image->xsize;
    gbuf += image->xsize;
    bbuf += image->xsize;
  }
}

static void
code_alias24(rbuf, gbuf, bbuf, xmax, handle)
byte *rbuf, *gbuf, *bbuf;
int xmax;
FILE *handle;
{
  byte r, g, b;
  unsigned int number= 0;
  int repeat= 1;
  static byte buf[5120];
  byte *bufptr, *end;

  bufptr= buf;
  end= rbuf + xmax;
  r= *rbuf++; g= *gbuf++; b= *bbuf++;

  while (rbuf < end)
    /*
     * While we have the same pattern ( and < 255 ), we continue
     * skipping bytes ( pixels ).
     */
    if (r== *rbuf && g== *gbuf && b== *bbuf && repeat < 255) {
      repeat++;
      rbuf++; gbuf++; bbuf++;
    }else {
      /*
       * A different triple or repeat == 255 was found, then we add
       * this to the output buffer.
       */
      *bufptr++ = repeat;
      *bufptr++ = b;
      *bufptr++ = g;
      *bufptr++ = r;
      number += 4;
      repeat  = 1;
      r= *rbuf++; g= *gbuf++; b= *bbuf++;
    }

  /*
   * We need add the last triplet.
   */
  *bufptr++ = repeat;
  *bufptr++ = b;
  *bufptr++ = g;
  *bufptr++ = r;
  number += 4;
  /*
   * And ... write it !
   */
  Fwrite(buf, number, 1, handle);
}

static void
error(code)
int code;
{
  fprintf(stderr, "%s: ", the_hdr.cmd);
  switch (code) {
    case  0: fprintf(stderr, "Usage:  %s [-v] [-o outfile] [infile] \n", 
                     the_hdr.cmd);
             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, "Incorrect # of planes or # of colors\n");
             break;
    case 99: fprintf(stderr, "Not ready\n");
             break;
    default: fprintf(stderr, "Unknow erro 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;
}

