/*
 * 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.
 */
/*
 * pix.c - interface with Alias "pix" format.
 *
 * Author:      Raul Rivero
 *              Mathematics Dept.
 *              University of Oviedo
 * Date:        Mon Jan 6 1992
 * Copyright (c) 1992, Raul Rivero
 *
 */

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

extern int LUGverbose;

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

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

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

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

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, *b;
  int can_read_all;
  byte *buffer, *ptr;
  int filesize;
  int allplanes = 0;
  int internalerror;
  int cmapsize;

  /*
   * Alias "pix" is a bad format to read line per line. If every
   * triplet is different to its next, then we read 4 bytes, 4 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 ).
   */
#ifdef NO_BIGENDIAN
  can_read_all = 0;
#else 
  can_read_all = (handle != stdin);     /* a regular file ? */
  VPRINTF(stderr, "Reading Alias from %s\n",
                  (can_read_all ? "file" : "stdin"));
#endif

  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->magic  = LUGUSED;
  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 */
        internalerror= read_line_alias24(handle, r, b, g, xsize);
      }else {
        /* An image with indexes to cmap */
        internalerror= read_line_alias(handle, r, xsize);
      }
      if (internalerror) {
        /* 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;
}

read_alias_header(handle, header)
FILE *handle;
alias_hdr *header;
{
  byte buffer[10];

  /* Read the header */
  Fread(buffer, 10, 1, handle);

  /* Fill the header structure */
  header->xsize = ( buffer[0] << 8 ) | buffer[1];
  header->ysize = ( buffer[2] << 8 ) | buffer[3];
  header->xinit = ( buffer[4] << 8 ) | buffer[5];
  header->yinit = ( buffer[6] << 8 ) | buffer[7];
  header->depth = ( buffer[8] << 8 ) | buffer[9];

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

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;
}

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;
}

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;
  }
}

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++;
    }
  }
}

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++;
    }
  }
}

write_alias_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_alias( handle, image );

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

}

write_alias(handle, image)
FILE *handle;
bitmap_hdr *image;
{
  register int i;
  byte buffer[10];
  byte *rbuf, *gbuf, *bbuf;

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

  /*
   * 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 */
  buffer[0] = MSB( image->xsize );
  buffer[1] = LSB( image->xsize );
  buffer[2] = MSB( image->ysize );
  buffer[3] = LSB( image->ysize );
  buffer[4] = 0;
  buffer[5] = 0;
  buffer[6] = 0;
  buffer[7] = 0;
  buffer[8] = MSB( image->depth );
  buffer[9] = LSB( image->depth );
  Fwrite( buffer, 10, 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;
  }
}

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