/*  18 Jun 12.  WJS							*/
/*	Do big/little endian determination at runtime			*/
/*  22 May 08.  WJS							*/
/*	Add includes for stdlib.h & string.h to get rid of compiler	*/
/*    warnings.                                      			*/
/*	Replace a call to free that had 2 args(!)			*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define byte unsigned char
#define ushort unsigned short
#define ulong unsigned long

ushort assign_ushort (v)
ushort v;
/*	Swap bytes if architecture bigendian.  Replaces compile-time stuff	*/
/*	saved at bottom								*/
{
  union short_and_char {
    short i;
    char c;	/* "littleendian" indicator - points to least sig byte of i	*/
		/* (which is 1) if arch is littleendian; to most sig byte 	*/
		/* (which is 0) otherwise					*/
  };
  static union short_and_char u = {1};	/* Must be 1 - see above comments	*/
  return (u.c) ? v : (v>>8) + (v<<8);
}

void putgifheader(file,xsize,ysize)
FILE *file;
int xsize;
int ysize;
{
  struct {
    byte        signature[3];   /* magic number == 'GIF' */
    byte        version[3];     /* version # '87a' or '89a' */
    ushort      width;          /* screen width */
    ushort      height;         /* screen height */

    /*  struct  {                bit structure of flags */
    /*  unsigned globalcolor:1;  global color table flag - MSB*/
#define globalcolor_shift 7
    /*  unsigned colorres:3;     bits/color */
#define colorres_shift 4
    /*  unsigned sort:1;                color table sorted */
#define sort_shift 3
    /*  unsigned colorsize:3;    2^colorsize bytes in color table -LSB */
#define colorsize_shift 0
    /*  } flags;                */

    byte        flags;
    byte        background;     /* background color index */
    byte        aspect;         /* pixel aspect ratio */
    /* ratio = (aspect + 15) / 64 */
  } header;

  struct {
    byte red,green,blue;
  } palette_entry;

  memcpy(header.signature, "GIF", 3);
  memcpy(header.version, "87a", 3);

/*	Sample of replaced code used w/compile-time endian choice	*/
/*	  assign_ushort(header.width, xsize);				*/
  header.width = assign_ushort(xsize);
  header.height = assign_ushort(ysize);

  /*    header.flags.globalcolor = TRUE;        */
  /*    header.flags.colorres = depth-1;        */
  /*    header.flags.sort = FALSE;              */
  /*    header.flags.colorsize = depth-1;       */
  header.flags = 0xf0;
  header.background = 0;
  header.aspect = 0;

  if ( fwrite(&header, 1, 13, file) < 13 ) {
    fprintf(stderr,"Error in GIF file\n");
    exit(1);
  };

  palette_entry.red=255;
  palette_entry.green=255;
  palette_entry.blue=255;
  if ( fwrite(&palette_entry, 1, 3, file) < 3 ) {
    fprintf(stderr,"Error in GIF file\n");
    exit(1);
  };

  palette_entry.red=0;
  palette_entry.green=0;
  palette_entry.blue=0;
  if ( fwrite(&palette_entry, 1, 3, file) < 3 ) {
    fprintf(stderr,"Error in GIF file\n");
    exit(1);
  };

}

void putimageheader(file,xsize,ysize)
FILE *file;
int xsize;
int ysize;
{
  struct  {
    ushort      left_pos;       /* image left pos (pixels) */
    ushort      top_pos;        /* image top  pos (pixels) */
    ushort      width;          /* image width    (pixels) */
    ushort      height;         /* image height   (pixels) */

    /*  struct  {               */
    /*  unsigned localcolor:1;   local color table flag */
    /*  unsigned interlace:1;    image interlaced  0=no */
    /*  unsigned sort:1;         color table sorted 0=no*/
    /*  unsigned resv:2;        */
    /*  unsigned localsize:3;    2^localsize+1 = color table size */
    /*  } flags;                */

    byte        flags;
  } header_desc;

  header_desc.left_pos = 0;
  header_desc.top_pos = 0;
  header_desc.width = assign_ushort(xsize);
  header_desc.height = assign_ushort(ysize);
  header_desc.flags = 0;
  fputc(0x2c,file);             /* start with separator */
  if ( fwrite(&header_desc, 1, 9, file) < 9 ) {
    fprintf(stderr,"Error in GIF file\n");
    exit(1);
  };
}

/********************************************************/
/* LZW routines are based on:                           */
/* Dr. Dobbs Journal --- Oct. 1989.                     */
/* Article on LZW Data Compression by Mark R. Nelson    */
/********************************************************/

#define MAX_BITS 12             /* this is max for GIF. */

#define TABLE_SIZE 5123         /* this is max for 12-bit codes */
#define TABLE_HASH_SHIFT 2      /* size < 4095 + (4095 >> shift) */
#define uint unsigned int

/* State of LZW encoder */
typedef struct code_entry_s {
        int     code_value;
        ushort  prefix_code;
        byte    append_character;
} code_entry;
typedef struct lzw_encoder_s {
        int     bits;
        ushort  Max_Code;
        ushort  Clear_code;
        ushort  next_code;
        FILE    *file;
        code_entry      *table;
        ushort  string_code;
        /* State of output buffer */
        byte    output_bit_buffer;
        int     output_bit_count;       /* # of valid low-order bits */
                                        /* (between 0 and 7) in buffer */
        uint    byte_count;
        byte    gif_buffer[260];
} lzw_encoder;

/* Initialize LZW encoder */
void lzw_set_bits();
void lzw_reset();
int
lzw_init(pe, bits, file)
register lzw_encoder *pe;
int bits;
FILE *file;
{       lzw_set_bits(pe, bits);
        pe->Clear_code = (1 << bits);
        pe->file = file;
        pe->byte_count = 1;
        pe->output_bit_count = 0;
        pe->output_bit_buffer = 0;
        pe->table = (code_entry *)malloc(TABLE_SIZE*sizeof(code_entry));

        if ( pe->table == 0 )
                {fprintf(stderr,"Can't allocate buffer\n");
                 exit(1);
                 };     /* can't allocate buffers */

        lzw_reset(pe);
        pe->string_code = 0;
        return 0;
}
/* Establish the width of the code in bits */
void
lzw_set_bits(pe, bits)
register lzw_encoder *pe;
int bits;
{       pe->bits = bits;
        pe->Max_Code = (1 << (bits+1)) - 1;
}
/* Reset the encoding table */
 void
lzw_reset(pe)
register lzw_encoder *pe;
{       int index;
        for ( index = 0; index < TABLE_SIZE; index++ )
                pe->table[index].code_value = -1;
        pe->next_code = pe->Clear_code + 2;
}

/* Put out (data) of length (bits) to GIF buffer */
void
lzw_putc(pe, data)
register lzw_encoder *pe;
uint data;
{       int bits = pe->bits + 1;        /* output width */
        ulong buffer = pe->output_bit_buffer | ((ulong)data << pe->output_bit_count);
        pe->output_bit_count += bits;
        while ( pe->output_bit_count >= 8 )
        {       /* putc(output_bit_buffer >> 24, file); */
                pe->gif_buffer[pe->byte_count] = (byte)buffer;  /* low byte */
                buffer >>= 8;
                pe->output_bit_count -= 8;
                pe->byte_count++;
                if ( pe->byte_count == 256 )
                {       pe->byte_count = 1;
                        pe->gif_buffer[0] = 255;  /* byte count for block */
                        fwrite(pe->gif_buffer, 1, 256, pe->file);
                }
        }
        pe->output_bit_buffer = (byte)buffer;
}

/* Finish encoding, and flush the buffers. */
 void
lzw_finish(pe)
register lzw_encoder *pe;
{       lzw_putc(pe, pe->string_code);  /* output last code */
        lzw_putc(pe, pe->Clear_code+1); /* output eof code */
        lzw_putc(pe, 0);        /* force out last code */
        if ( pe->byte_count != 1 )
        {       pe->gif_buffer[0] = pe->byte_count;
                fwrite(pe->gif_buffer, 1, pe->byte_count+1, pe->file);
        }
}

/* Terminate LZW encoder. */
 void
lzw_exit(pe)
register lzw_encoder *pe;
{       free((char *)pe->table);
}

/* Get the next (depth) bits from the pixel buffer. */
/* Note that 8 % depth == 0. */
/* Free variables: bits_left, bit_buffer, next, depth, depth_mask. */
#define lzw_getc()\
 (bits_left =\
  (bits_left == 0 ?\
   (bit_buffer = *(next++), 8 - (depth)) :\
   bits_left - (depth)),\
  (bit_buffer >> bits_left) & (depth_mask))

/* Output 1 row of data in GIF (LZW) format. */
void
lzw(from, end, pe, depth)
byte *from;
byte *end;
register lzw_encoder *pe;
int depth;
{       int bits_left = 0;
        uint bit_buffer;
        byte *next = from;
        uint depth_mask = (1 << depth) - 1;

        if ( pe->next_code == (pe->Clear_code + 2))     /* first time through */
        {       pe->string_code = lzw_getc();
        }

        while ( next < end || bits_left >= depth )
        {       uint    data = lzw_getc();  /* actually only a byte */

                /* Hash to find a match for the prefix+char */
                /* string in the string table */

                ushort  hash_prefix = pe->string_code;
                int     index = (data << 4) ^ hash_prefix;
                int     hash_offset;
                register code_entry *pce;

                index += index >> TABLE_HASH_SHIFT;
                if ( index == 0 )
                        hash_offset = 1;
                else
                        hash_offset = TABLE_SIZE - index;

                while ( 1 )
                {       pce = &pe->table[index];
                        if ( pce->code_value == -1 )
                                break;
                        if ( pce->prefix_code == hash_prefix &&
                             pce->append_character == data )
                                break;
                        index -= hash_offset;
                        if ( index < 0 )
                                index += TABLE_SIZE;
                }
                if ( pce->code_value != -1 )
                        pe->string_code = pce->code_value;
                else
                {       /* Make a new entry */
                        pce->code_value = pe->next_code++;
                        pce->prefix_code = pe->string_code;
                        pce->append_character = data;

                        lzw_putc(pe, pe->string_code);

                        if ( pe->next_code > (pe->Max_Code + 1) )
                        {       /* Increment the width of the code */
                                if ( pe->bits+1 >= MAX_BITS )
                                {       /* output clear code first*/
                                        lzw_putc(pe, pe->Clear_code);
                                        pe->bits = (depth == 1 ? 2 : depth);
                                        lzw_reset(pe);
                                }
                                else
                                        pe->bits++;
                                lzw_set_bits(pe, pe->bits);
                        }
                        pe->string_code = data;
                }
        }
}


int putbitmap(file,bitmap,xsize,ysize)
     FILE *file;
     unsigned char bitmap[];
     int xsize;
     int ysize;
{
  int depth=1;
  int raster=xsize/8;
  ushort height=ysize;
  ushort gif_width = raster * (8 / depth); /* decoders want the width */
  /* on a byte boundary */
  byte *row = (byte *)malloc(raster * 2);
  byte *end = row + raster;
  lzw_encoder encoder;
  int y;

  if(lzw_init(&encoder,2,file)<0)exit(1);
  putgifheader(file,xsize,ysize);
  putimageheader(file,xsize,ysize);

  fputc(encoder.bits, file);            /* start with code size */

  lzw_putc(&encoder, encoder.Clear_code);       /* output clear code first*/

  /* Dump the contents of the image. */
  for ( y = 0; y < height; y++ )
    {   memcpy(row, bitmap+raster*y, raster);
        lzw(row, end, &encoder, depth);
      }

  lzw_finish(&encoder);
/* odd fix
  fputc(0, file);
*/
  lzw_exit(&encoder);
/*  Replace the commented-out free below.  My guess is that the         */
/*  raster*2 was the number of bytes to be freed.  Wonder how old THAT  */
/*  code is ... or if we're supposed to have our own free routine       */
/*      free((char *)row, raster * 2);                                  */
  free((char *)row);
  fputc(0,file);
  fputc(0x3b, file);  /* EOF */
  return 0;
}

/**************/
/*	Compile-time "endian" code replaced by runtime assign_ushort	*/
/*	function.  I assume that the first if/else was "original",	*/
/*	requiring the builder to edit this code to set the arch_is_	*/
/*	I assume we replaced this w/the IRIX/ULTRIX/? determination,	*/
/*	since those are JGOFS switches, set up by build-env(.pl) and	*/
/*	propagated to the make files.  Not only is the association of	*/
/*	endian-ism with OS'es suspect (but newer hardware can run	*/
/*	"bi-endian" w/the OS making the choice), but we didn't bother	*/
/*	to spec the JGOFS env var which spec'd the IRIX/ULTRIX/? choice	*/
/*	Doing job at runtime should address all of this ...		*/
/*					WJS  Jun 12			*/

/*
#  define arch_is_big_endian 1
#if arch_is_big_endian
#  define assign_ushort(a,v) a = ((v) >> 8) + ((v) << 8)
#else
#  define assign_ushort(a,v) a = (v)
#endif
*/

/*
#if IRIX || ULTRIX
#  define assign_ushort(a,v) a = (v)
#else
#  define assign_ushort(a,v) a = ((v) >> 8) + ((v) << 8)
#endif
*/
/**************/
