aboutsummaryrefslogtreecommitdiff
path: root/vendor/x11iraf/ximtool/gifio.c
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/x11iraf/ximtool/gifio.c')
-rw-r--r--vendor/x11iraf/ximtool/gifio.c1325
1 files changed, 1325 insertions, 0 deletions
diff --git a/vendor/x11iraf/ximtool/gifio.c b/vendor/x11iraf/ximtool/gifio.c
new file mode 100644
index 00000000..de81085b
--- /dev/null
+++ b/vendor/x11iraf/ximtool/gifio.c
@@ -0,0 +1,1325 @@
+/*
+ * GIFIO.C -- Routines to load and save simple GIF files.
+ *
+ * ival = isGIF (fname)
+ * loadGIF (fname, pixels, pixtype, w,h, r,g,b, ncolors)
+ * writeGIF (fp, pixels, pixtype, w,h, r,g,b, ncolors, gray)
+
+ * LoadGIF(fname, numcols) - loads a GIF file
+ * WriteGIF(fp, pic, w, h, rmap, gmap, bmap, numcols)
+ *
+ * isGIF -- returns nonzero if the named file is a GIF file.
+ * loadGIF -- reads a GIF file and returns the decoded pixel array and gray-
+ * scale 8 bit colormap. The caller is responsible for freeing
+ * the pixels buffer.
+ * writeGIF -- performs the converse operation, writing the given pixel array
+ * and colormap to the output GIF file.
+ *
+ * Based on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>.A
+ * Lempel-Zim compression based on "compress".
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+
+/* a code_int must be able to hold 2**GIFBITS values of type int, and also -1
+ */
+typedef int code_int;
+typedef long int count_int;
+typedef unsigned char byte;
+#ifndef AIXV3
+#ifndef OSF1
+typedef unsigned char uchar;
+#endif
+#endif
+
+#define MAXCOLORS 256
+#define TRUE 1
+#define FALSE 0
+
+#define CM_RED 0
+#define CM_GREEN 1
+#define CM_BLUE 2
+
+#define GIFBITS 12
+
+#define INTERLACE 0x40
+#define LOCALCOLORMAP 0x80
+
+#define BitSet(byte, bit) (((byte) & (bit)) == (bit))
+#define ReadOK(file,buffer,len) (fread(buffer, len, 1, file) != 0)
+#define LM_to_uint(a,b) (((b)<<8)|(a))
+
+static struct {
+ unsigned int Width;
+ unsigned int Height;
+ unsigned int BitPixel;
+ unsigned int ColorResolution;
+ unsigned int Background;
+ unsigned int AspectRatio;
+} GifScreen;
+
+static struct {
+ int transparent;
+ int delayTime;
+ int inputFlag;
+ int disposal;
+} Gif89 = {
+ -1, -1, -1, 0 };
+
+
+
+/* MONO returns total intensity of r,g,b components */
+#define MONO(rd,gn,bl) (((rd)*11 + (gn)*16 + (bl)*5) >> 5) /*.33R+ .5G+ .17B*/
+
+
+/* Function prototypes */
+#ifdef __STDC__
+
+#include <stddef.h>
+#include <stdlib.h>
+
+static int colorstobpp (int colors);
+static int GetPixel (int x, int y);
+static void BumpPixel (void);
+static int GIFNextPixel ();
+static void GIFEncode (FILE* fp, int GWidth, int GHeight, int GInterlace,
+ int Background, int BitsPerPixel, int Red[], int Green[], int Blue[]);
+static void Putword (int w, FILE* fp);
+static void compress (int init_bits, FILE* outfile);
+static void output (code_int code);
+static void cl_block (void);
+static void cl_hash (count_int hsize);
+static void writeerr (void);
+static void char_init (void);
+static void char_out (int c);
+static void flush_char (void);
+
+static char *ReadGIF (FILE *fd, int imageNumber, uchar **pix, int *nx, int *ny,
+ uchar *r, uchar *g, uchar *b, int *ncolors);
+static int ReadColorMap (FILE *fd, int number, uchar *r, uchar *g, uchar *b );
+static int DoExtension (FILE *fd, int label );
+static int GetDataBlock (FILE *fd, uchar *buf );
+static int GetCode (FILE *fd, int code_size, int flag );
+static int LWZReadByte (FILE *fd, int flag, int input_code_size );
+static char *ReadImage (FILE *fd, int len, int height,
+ int interlace, int ignore, uchar **image );
+
+#else
+static int colorstobpp(), GIFNextPixel();
+static void BumpPixel (), GIFEncode (), Putword (), compress ();
+static void output (), cl_block (), cl_hash (), writeerr (), char_init ();
+static void char_out (), flush_char ();
+
+static char *ReadGIF (), *ReadImage ();
+static int ReadColorMap (), DoExtension (), GetDataBlock ();
+static int GetCode (), LWZReadByte ();
+#endif
+
+static byte *pixels;
+static int nrows, ncols;
+static char *errstr = NULL;
+
+
+/* ----------------
+ * Public routines.
+ * ----------------*/
+
+
+/* loadGIF - Load a GIF file.
+ */
+
+char *
+loadGIF (fname, pix, nx, ny, r,g,b, ncolors, gray)
+char *fname; /* input filename */
+uchar **pix; /* output pixels */
+int *nx, *ny; /* dimensions */
+uchar *r, *g, *b; /* colormap */
+int *ncolors; /* number of colors */
+int gray; /* read as a grayscale? */
+{
+ register FILE *fd;
+ register int i, imageNumber = 1;
+
+ if ((fd = fopen(fname, "rb")) == NULL)
+ return "Unable to open FITS file";
+
+ for (i=0; i < 256; i++)
+ r[i] = g[i] = b[i] = 0;
+
+ errstr = ReadGIF(fd, imageNumber, pix, nx, ny, r, g, b, ncolors);
+
+ /* If we requested grayscale reset the colormap and pixels. */
+ if (gray)
+ for (i=0; i < 256; i++)
+ r[i] = g[i] = b[i] = MONO(r[i],g[i],b[i]);
+
+ fclose (fd);
+ return (errstr);
+}
+
+
+/* writeGIF -- Write a GIF image.
+ */
+
+int
+writeGIF (fp, image, w, h, rmap, gmap, bmap, numcols, gray)
+FILE *fp; /* output file descriptor */
+byte *image; /* image pixels */
+int w, h; /* image dimensions */
+byte *rmap, *gmap, *bmap; /* colormap */
+int numcols; /* number of colors */
+int gray; /* save as grayscale? */
+{
+ register int i, bpp, npix;
+ int r[MAXCOLORS], g[MAXCOLORS], b[MAXCOLORS];
+
+ /* Change type of the colormap. */
+ if (gray)
+ for (i = 0; i < MAXCOLORS; i++)
+ r[i] = g[i] = b[i] = (int) MONO(rmap[i],gmap[i],bmap[i]);
+ else
+ for (i = 0; i < MAXCOLORS; i++) {
+ r[i] = (int) rmap[i];
+ g[i] = (int) gmap[i];
+ b[i] = (int) bmap[i];
+ }
+ bpp = colorstobpp (numcols);
+
+ /* Load the pixel buffer */
+ npix = w * h;
+ ncols = w;
+ nrows = h;
+ pixels = (byte *) malloc (npix * sizeof(byte));
+ for (i = 0; i < npix; i++)
+ pixels[i] = image[i];
+
+ /* All set, write it out. */
+ GIFEncode (fp, w, h, 0, 0, bpp, r, g, b);
+
+ /* Clean up. */
+ free ((byte *)pixels);
+}
+
+
+/* IsGIF -- Test a file to see if it is a GIF file.
+ */
+int
+isGIF (fname)
+char *fname; /* input filename */
+{
+ register FILE *fp;
+ int value = 0;
+ char tag[5];
+
+ if (fp = fopen (fname, "r")) {
+ fread (tag, sizeof(char), 4, fp);
+ if (strncmp ("GIF8", tag, 4) == 0)
+ value = 1;
+ fclose (fp);
+ }
+ return value;
+}
+
+
+/* getGIFHdr -- Get some set of header information for the GUI.
+ */
+
+char *
+getGIFHdr (fname)
+char *fname;
+{
+ FILE *fp;
+ char *line;
+ uchar buf[16], version[4];
+ int nx, ny, ncolors;
+
+
+ /* Open the image. */
+ fp = fopen (fname, "r");
+ if (!fp)
+ return NULL;
+
+ /* Get the version. */
+ if (!ReadOK(fp, buf, 6) || strncmp(buf, "GIF", 3) != 0)
+ return NULL;
+ strncpy(version, buf + 3, 3);
+ version[3] = '\0';
+
+
+ /* Read the image header. */
+ if (!ReadOK(fp, buf, 7))
+ return NULL;
+ nx = LM_to_uint(buf[0], buf[1]);
+ ny = LM_to_uint(buf[2], buf[3]);
+ ncolors = 2 << (buf[4] & 0x07);
+
+ /* Format the description. */
+ line = (char *) malloc (80);
+ sprintf (line, "%-16.16s 8 %5dx%-5d GIF%s Image (%d colors)",
+ fname, nx, ny, version, ncolors);
+
+ fclose (fp);
+ return (line);
+}
+
+
+
+/* ------------------
+ * Private Procedures
+ * ------------------*/
+
+
+/* COLORSTOBPP -- Convert the numbers of colors we're writing to a
+ * bits-per-pixel value.
+ */
+static int
+colorstobpp (colors)
+int colors;
+{
+ int bpp;
+
+ if (colors <= 2)
+ bpp = 1;
+ else if (colors <= 4)
+ bpp = 2;
+ else if (colors <= 8)
+ bpp = 3;
+ else if (colors <= 16)
+ bpp = 4;
+ else if (colors <= 32)
+ bpp = 5;
+ else if (colors <= 64)
+ bpp = 6;
+ else if (colors <= 128)
+ bpp = 7;
+ else if (colors <= 256)
+ bpp = 8;
+ else
+ perror ("can't happen");
+
+ return bpp;
+}
+
+
+
+/*----------------------
+ * GIF Input Procedures
+ *----------------------*/
+
+
+/* +-------------------------------------------------------------------+
+ * | Copyright 1990, David Koblas. |
+ * | Permission to use, copy, modify, and distribute this software |
+ * | and its documentation for any purpose and without fee is hereby |
+ * | granted, provided that the above copyright notice appear in all |
+ * | copies and that both that copyright notice and this permission |
+ * | notice appear in supporting documentation. This software is |
+ * | provided "as is" without express or implied warranty. |
+ * +-------------------------------------------------------------------+
+ */
+
+
+static char *
+ReadGIF(fd, imageNumber, pix, nx, ny, r, g, b, ncolors)
+FILE *fd;
+int imageNumber;
+uchar **pix;
+int *nx, *ny;
+uchar *r, *g, *b;
+int *ncolors;
+{
+ uchar buf[16];
+ uchar c;
+ int useGlobalColormap;
+ int bitPixel;
+ int imageCount = 0;
+ char version[4];
+
+ if (!ReadOK(fd, buf, 6))
+ return "error reading magic number";
+
+ if (strncmp(buf, "GIF", 3) != 0)
+ return "not a GIF file";
+
+ strncpy(version, buf + 3, 3);
+ version[3] = '\0';
+
+ if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0))
+ return "bad version number, not '87a' or '89a'";
+
+ if (!ReadOK(fd, buf, 7))
+ return "failed to read screen descriptor";
+
+ *nx = GifScreen.Width = LM_to_uint(buf[0], buf[1]);
+ *ny = GifScreen.Height = LM_to_uint(buf[2], buf[3]);
+ *ncolors = GifScreen.BitPixel = 2 << (buf[4] & 0x07);
+ GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
+ GifScreen.Background = buf[5];
+ GifScreen.AspectRatio = buf[6];
+
+ if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */
+ if (ReadColorMap(fd, *ncolors, r, g, b))
+ return "error reading global colormap";
+ }
+
+ if (GifScreen.AspectRatio != 0 && GifScreen.AspectRatio != 49) {
+ float r;
+ r = ( (float) GifScreen.AspectRatio + 15.0 ) / 64.0;
+ }
+
+ for (; ; ) {
+ if (!ReadOK(fd, &c, 1))
+ return "EOF / read error on image data";
+
+ if (c == ';') { /* GIF terminator */
+ if (imageCount < imageNumber)
+ return "requested image number not found";
+ return;
+ }
+
+ if (c == '!') { /* Extension */
+ if (!ReadOK(fd, &c, 1))
+ return "OF / read error on extention function code";
+ DoExtension(fd, c);
+ continue;
+ }
+
+ if (c != ',') /* Not a valid start character */
+ continue;
+
+ ++imageCount;
+
+ if (!ReadOK(fd, buf, 9))
+ return "couldn't read left/top/width/height";
+
+ useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP);
+
+ bitPixel = 1 << ((buf[8] & 0x07) + 1);
+
+ if (!useGlobalColormap) {
+ if (ReadColorMap(fd, bitPixel, r, g, b))
+ return "error reading local colormap";
+ return (ReadImage(fd, LM_to_uint(buf[4], buf[5]),
+ LM_to_uint(buf[6], buf[7]), BitSet(buf[8], INTERLACE),
+ imageCount != imageNumber, pix ));
+ } else {
+ return (ReadImage(fd, LM_to_uint(buf[4], buf[5]),
+ LM_to_uint(buf[6], buf[7]), BitSet(buf[8], INTERLACE),
+ imageCount != imageNumber, pix ));
+ }
+
+ }
+}
+
+
+static int
+ReadColorMap(fd, number, r, g, b)
+FILE *fd;
+int number;
+uchar *r, *g, *b;
+{
+ int i;
+ uchar rgb[3];
+
+ for (i = 0; i < number; ++i) {
+ if (!ReadOK(fd, rgb, sizeof(rgb)))
+ return TRUE;
+
+ r[i] = rgb[0] ;
+ g[i] = rgb[1] ;
+ b[i] = rgb[2] ;
+ }
+ return FALSE;
+}
+
+
+static int
+DoExtension(fd, label)
+FILE *fd;
+int label;
+{
+ static char buf[256];
+ char *str;
+
+ switch (label) {
+ case 0x01: /* Plain Text Extension */
+ str = "Plain Text Extension";
+ break;
+ case 0xff: /* Application Extension */
+ str = "Application Extension";
+ break;
+ case 0xfe: /* Comment Extension */
+ str = "Comment Extension";
+ while (GetDataBlock(fd, (uchar * ) buf) != 0)
+ ;
+ return FALSE;
+ case 0xf9: /* Graphic Control Extension */
+ str = "Graphic Control Extension";
+ (void) GetDataBlock(fd, (uchar * ) buf);
+ Gif89.disposal = (buf[0] >> 2) & 0x7;
+ Gif89.inputFlag = (buf[0] >> 1) & 0x1;
+ Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
+ if ((buf[0] & 0x1) != 0)
+ Gif89.transparent = buf[3];
+
+ while (GetDataBlock(fd, (uchar * ) buf) != 0)
+ ;
+ return FALSE;
+ default:
+ str = buf;
+ sprintf(buf, "UNKNOWN (0x%02x)", label);
+ break;
+ }
+
+ while (GetDataBlock(fd, (uchar * ) buf) != 0) ;
+
+ return FALSE;
+}
+
+
+static int ZeroDataBlock = FALSE;
+
+static int
+GetDataBlock(fd, buf)
+FILE *fd;
+uchar *buf;
+{
+ uchar count;
+
+ if (!ReadOK(fd, &count, 1))
+ return - 1;
+
+ ZeroDataBlock = count == 0;
+
+ if ((count != 0) && (!ReadOK(fd, buf, count)))
+ return - 1;
+
+ return count;
+}
+
+
+static int
+GetCode(fd, code_size, flag)
+FILE *fd;
+int code_size;
+int flag;
+{
+ static uchar buf[280];
+ static int curbit, lastbit, done, last_byte;
+ int i, j, ret;
+ uchar count;
+
+ if (flag) {
+ curbit = 0;
+ lastbit = 0;
+ done = FALSE;
+ return 0;
+ }
+
+ if ( (curbit + code_size) >= lastbit) {
+ if (done)
+ return - 1;
+ buf[0] = buf[last_byte-2];
+ buf[1] = buf[last_byte-1];
+
+ if ((count = GetDataBlock(fd, &buf[2])) == 0)
+ done = TRUE;
+
+ last_byte = 2 + count;
+ curbit = (curbit - lastbit) + 16;
+ lastbit = (2 + count) * 8 ;
+ }
+
+ ret = 0;
+ for (i = curbit, j = 0; j < code_size; ++i, ++j)
+ ret |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
+
+ curbit += code_size;
+
+ return ret;
+}
+
+
+static int
+LWZReadByte(fd, flag, input_code_size)
+FILE *fd;
+int flag;
+int input_code_size;
+{
+ static int fresh = FALSE;
+ int code, incode;
+ static int code_size, set_code_size;
+ static int max_code, max_code_size;
+ static int firstcode, oldcode;
+ static int clear_code, end_code;
+ static int table[2][(1<< GIFBITS)];
+ static int stack[(1<<(GIFBITS))*2], *sp;
+ register int i;
+
+ if (flag) {
+ set_code_size = input_code_size;
+ code_size = set_code_size + 1;
+ clear_code = 1 << set_code_size ;
+ end_code = clear_code + 1;
+ max_code_size = 2 * clear_code;
+ max_code = clear_code + 2;
+
+ GetCode(fd, 0, TRUE);
+
+ fresh = TRUE;
+
+ for (i = 0; i < clear_code; ++i) {
+ table[0][i] = 0;
+ table[1][i] = i;
+ }
+ for (; i < (1 << GIFBITS); ++i)
+ table[0][i] = table[1][0] = 0;
+
+ sp = stack;
+
+ return 0;
+ } else if (fresh) {
+ fresh = FALSE;
+ do {
+ firstcode = oldcode =
+ GetCode(fd, code_size, FALSE);
+ } while (firstcode == clear_code);
+ return firstcode;
+ }
+
+ if (sp > stack)
+ return * --sp;
+
+ while ((code = GetCode(fd, code_size, FALSE)) >= 0) {
+ if (code == clear_code) {
+ for (i = 0; i < clear_code; ++i) {
+ table[0][i] = 0;
+ table[1][i] = i;
+ }
+ for (; i < (1 << GIFBITS); ++i)
+ table[0][i] = table[1][i] = 0;
+ code_size = set_code_size + 1;
+ max_code_size = 2 * clear_code;
+ max_code = clear_code + 2;
+ sp = stack;
+ firstcode = oldcode =
+ GetCode(fd, code_size, FALSE);
+ return firstcode;
+ } else if (code == end_code) {
+ int count;
+ uchar buf[260];
+
+ if (ZeroDataBlock)
+ return - 2;
+
+ while ((count = GetDataBlock(fd, buf)) > 0)
+ ;
+
+ return - 2;
+ }
+
+ incode = code;
+
+ if (code >= max_code) {
+ *sp++ = firstcode;
+ code = oldcode;
+ }
+
+ while (code >= clear_code) {
+ *sp++ = table[1][code];
+ code = table[0][code];
+ }
+
+ *sp++ = firstcode = table[1][code];
+
+ if ((code = max_code) < (1 << GIFBITS)) {
+ table[0][code] = oldcode;
+ table[1][code] = firstcode;
+ ++max_code;
+ if ((max_code >= max_code_size) &&
+ (max_code_size < (1 << GIFBITS))) {
+ max_code_size *= 2;
+ ++code_size;
+ }
+ }
+
+ oldcode = incode;
+
+ if (sp > stack)
+ return * --sp;
+ }
+ return code;
+}
+
+
+static char *
+ReadImage(fd, len, height, interlace, ignore, image)
+FILE *fd;
+int len, height;
+int interlace, ignore;
+uchar **image;
+{
+ uchar c;
+ int v;
+ int xpos = 0, ypos = 0, pass = 0;
+
+ /* Initialize the Compression routines */
+ if (!ReadOK(fd, &c, 1))
+ return "EOF / read error on image data";
+
+ if (LWZReadByte(fd, TRUE, c) < 0)
+ return "error reading image";
+
+ /* If this is an "uninteresting picture" ignore it. */
+ if (ignore) {
+ while (LWZReadByte(fd, FALSE, c) >= 0)
+ ;
+ return;
+ }
+
+ if ((*image = (uchar *)malloc(len * height)) == NULL)
+ return "couldn't alloc space for image";
+
+ while ((v = LWZReadByte(fd, FALSE, c)) >= 0 ) {
+ (*image)[ypos * len + xpos] = v;
+
+ ++xpos;
+ if (xpos == len) {
+ xpos = 0;
+ if (interlace) {
+ switch (pass) {
+ case 0:
+ case 1:
+ ypos += 8;
+ break;
+ case 2:
+ ypos += 4;
+ break;
+ case 3:
+ ypos += 2;
+ break;
+ }
+
+ if (ypos >= height) {
+ ++pass;
+ switch (pass) {
+ case 1:
+ ypos = 4;
+ break;
+ case 2:
+ ypos = 2;
+ break;
+ case 3:
+ ypos = 1;
+ break;
+ default:
+ goto fini;
+ }
+ }
+ } else {
+ ++ypos;
+ }
+ }
+ if (ypos >= height)
+ break;
+ }
+
+fini:
+ return NULL;
+}
+
+
+
+/*-----------------------
+ * GIF Output Procedures
+ *-----------------------*/
+
+/*****************************************************************************
+ *
+ * GIFENCODE.C - GIF Image compression interface
+ *
+ * GIFEncode (FName, GHeight, GWidth, GInterlace, Background,
+ * BitsPerPixel, Red, Green, Blue)
+ *
+ *****************************************************************************/
+
+#define TRUE 1
+#define FALSE 0
+
+static int Width, Height;
+static int curx, cury;
+static long CountDown;
+static int Pass = 0;
+static int Interlace;
+
+
+
+static void
+GIFEncode (fp, GWidth, GHeight, GInterlace, Background,
+BitsPerPixel, Red, Green, Blue)
+
+FILE*fp;
+int GWidth, GHeight;
+int GInterlace;
+int Background;
+int BitsPerPixel;
+int Red[], Green[], Blue[];
+{
+ int B;
+ int RWidth, RHeight;
+ int LeftOfs, TopOfs;
+ int Resolution;
+ int ColorMapSize;
+ int InitCodeSize;
+ int i;
+
+ Interlace = GInterlace;
+
+ ColorMapSize = 1 << BitsPerPixel;
+
+ RWidth = Width = GWidth;
+ RHeight = Height = GHeight;
+ LeftOfs = TopOfs = 0;
+
+ Resolution = BitsPerPixel;
+
+ /* Calculate number of bits we are expecting */
+ CountDown = (long)Width * (long)Height;
+
+ /* Indicate which pass we are on (if interlace) */
+ Pass = 0;
+
+ /* The initial code size */
+ if (BitsPerPixel <= 1)
+ InitCodeSize = 2;
+ else
+ InitCodeSize = BitsPerPixel;
+
+ /* Set up the current x and y position */
+ curx = cury = 0;
+
+ /* Write the Magic header */
+ fwrite ("GIF87a", 1, 6, fp);
+
+ /* Write out the screen width and height */
+ Putword (RWidth, fp);
+ Putword (RHeight, fp);
+
+ /* Indicate that there is a global colour map */
+ B = 0x80; /* Yes, there is a color map */
+
+ /* OR in the resolution */
+ B |= (Resolution - 1) << 5;
+
+ /* OR in the Bits per Pixel */
+ B |= (BitsPerPixel - 1);
+
+ /* Write it out */
+ fputc (B, fp);
+
+ /* Write out the Background colour */
+ fputc (Background, fp);
+
+ /* Byte of 0's (future expansion) */
+ fputc (0, fp);
+
+ /* Write out the Global Colour Map */
+ for (i = 0; i < ColorMapSize; ++i) {
+ fputc (Red[i], fp);
+ fputc (Green[i], fp);
+ fputc (Blue[i], fp);
+ }
+
+ /* Write an Image separator */
+ fputc (',', fp);
+
+ /* Write the Image header */
+ Putword (LeftOfs, fp);
+ Putword (TopOfs, fp);
+ Putword (Width, fp);
+ Putword (Height, fp);
+
+ /* Write out whether or not the image is interlaced */
+ if (Interlace)
+ fputc (0x40, fp);
+ else
+ fputc (0x00, fp);
+
+ /* Write out the initial code size */
+ fputc (InitCodeSize, fp);
+
+ /* Go and actually compress the data */
+ compress (InitCodeSize + 1, fp);
+
+ /* Write out a Zero-length packet (to end the series) */
+ fputc (0, fp);
+
+ /* Write the GIF file terminator */
+ fputc (';', fp);
+}
+
+
+/* Bump the 'curx' and 'cury' to point to the next pixel
+ */
+static void
+BumpPixel()
+{
+ /* Bump the current X position */
+ ++curx;
+
+ /* If we are at the end of a scan line, set curx back to the beginning
+ * If we are interlaced, bump the cury to the appropriate spot,
+ * otherwise, just increment it.
+ */
+ if (curx == Width) {
+ curx = 0;
+
+ if (!Interlace)
+ ++cury;
+ else {
+ switch (Pass) {
+ case 0:
+ cury += 8;
+ if (cury >= Height) {
+ ++Pass;
+ cury = 4;
+ }
+ break;
+ case 1:
+ cury += 8;
+ if (cury >= Height) {
+ ++Pass;
+ cury = 2;
+ }
+ break;
+ case 2:
+ cury += 4;
+ if (cury >= Height) {
+ ++Pass;
+ cury = 1;
+ }
+ break;
+ case 3:
+ cury += 2;
+ break;
+ }
+ }
+ }
+}
+
+
+/* Return the next pixel from the image
+ */
+static int
+GIFNextPixel ()
+{
+ int r;
+
+ if (CountDown == 0)
+ return EOF;
+
+ --CountDown;
+ r = (int) pixels[ cury * ncols + curx ] ;
+ BumpPixel();
+ return r;
+}
+
+
+/* Write out a word to the GIF file
+ */
+static void
+Putword (w, fp)
+int w;
+FILE*fp;
+{
+ fputc (w & 0xff, fp);
+ fputc ((w / 256) & 0xff, fp);
+}
+
+
+/***************************************************************************
+ *
+ * GIFCOMPR.C - GIF Image compression routines
+ *
+ * Lempel-Ziv compression based on 'compress'. GIF modifications by
+ * David Rowley (mgardi@watdcsu.waterloo.edu)
+ *
+ ***************************************************************************/
+
+/*
+ * General DEFINEs
+ */
+
+#define GIFBITS 12
+#define HSIZE 5003 /* 80% occupancy */
+
+#ifdef NO_UCHAR
+typedef char char_type;
+#else /*NO_UCHAR*/
+typedef unsigned char char_type;
+#endif /*NO_UCHAR*/
+
+/*
+ *
+ * GIF Image compression - modified 'compress'
+ *
+ * Based on: compress.c - File compression ala IEEE Computer, June 1984.
+ *
+ * By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
+ * Jim McKie (decvax!mcvax!jim)
+ * Steve Davies (decvax!vax135!petsd!peora!srd)
+ * Ken Turkowski (decvax!decwrl!turtlevax!ken)
+ * James A. Woods (decvax!ihnp4!ames!jaw)
+ * Joe Orost (decvax!vax135!petsd!joe)
+ *
+ */
+#include <ctype.h>
+
+#define ARGVAL() (*++(*argv) || (--argc && *++argv))
+
+static int n_bits; /* number of bits/code */
+static int maxbits = GIFBITS; /* user settable max # bits/code */
+static code_int maxcode; /* maximum code, given n_bits */
+ /* should NEVER generate this code */
+static code_int maxmaxcode = (code_int) 1 << GIFBITS;
+#ifdef COMPATIBLE /* But wrong! */
+# define MAXCODE(n_bits) ((code_int) 1 << (n_bits) - 1)
+#else /*COMPATIBLE*/
+# define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1)
+#endif /*COMPATIBLE*/
+
+static count_int htab [HSIZE];
+static unsigned short codetab [HSIZE];
+#define HashTabOf(i) htab[i]
+#define CodeTabOf(i) codetab[i]
+
+/*static*/ code_int hsize = HSIZE; /* for dynamic table sizing */
+
+/* To save much memory, we overlay the table used by compress() with those
+ * used by decompress(). The tab_prefix table is the same size and type
+ * as the codetab. The tab_suffix table needs 2**GIFBITS characters. We
+ * get this from the beginning of htab. The output stack uses the rest
+ * of htab, and contains characters. There is plenty of room for any
+ * possible stack (stack used to be 8000 characters).
+ */
+
+#define tab_prefixof(i) CodeTabOf(i)
+#define tab_suffixof(i) ((char_type*)(htab))[i]
+#define de_stack ((char_type*)&tab_suffixof((code_int)1<<GIFBITS))
+
+static code_int free_ent = 0; /* first unused entry */
+
+/*
+ * block compression parameters -- after all codes are used up,
+ * and compression rate changes, start over.
+ */
+static int clear_flg = 0;
+
+static int offset;
+static long int in_count = 1; /* length of input */
+static long int out_count = 0; /* # of codes output (for debugging) */
+
+/*
+ * compress stdin to stdout
+ *
+ * Algorithm: use open addressing double hashing (no chaining) on the
+ * prefix code / next character combination. We do a variant of Knuth's
+ * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
+ * secondary probe. Here, the modular division first probe is gives way
+ * to a faster exclusive-or manipulation. Also do block compression with
+ * an adaptive reset, whereby the code table is cleared when the compression
+ * ratio decreases, but after the table fills. The variable-length output
+ * codes are re-sized at this point, and a special CLEAR code is generated
+ * for the decompressor. Late addition: construct the table according to
+ * file size for noticeable speed improvement on small files. Please direct
+ * questions about this implementation to ames!jaw.
+ */
+
+static int g_init_bits;
+static FILE *g_outfile;
+
+static int ClearCode;
+static int EOFCode;
+
+static unsigned long cur_accum = 0;
+static int cur_bits = 0;
+static unsigned long masks[] = {
+ 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
+ 0x001F, 0x003F, 0x007F, 0x00FF,
+ 0x01FF, 0x03FF, 0x07FF, 0x0FFF,
+ 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
+
+static int a_count; /* Number of characters so far in this 'packet' */
+static char accum[ 256 ]; /* Define the storage for the packet accumulator */
+
+static void
+compress (init_bits, outfile)
+int init_bits;
+FILE*outfile;
+{
+ register long fcode;
+ register code_int i /* = 0 */;
+ register int c;
+ register code_int ent;
+ register code_int disp;
+ register code_int hsize_reg;
+ register int hshift;
+
+ /*
+ * Set up the globals: g_init_bits - initial number of bits
+ * g_outfile - pointer to output file
+ */
+ g_init_bits = init_bits;
+ g_outfile = outfile;
+
+ /*
+ * Set up the necessary values
+ */
+ offset = 0;
+ out_count = 0;
+ clear_flg = 0;
+ cur_accum = 0;
+ cur_bits = 0;
+ in_count = 1;
+ maxbits = GIFBITS;
+ maxcode = MAXCODE(n_bits = g_init_bits);
+
+ ClearCode = (1 << (init_bits - 1));
+ EOFCode = ClearCode + 1;
+ free_ent = ClearCode + 2;
+
+ char_init();
+ for (i=0; i<HSIZE; i++) {
+ htab[i] = 0;
+ codetab[i] = 0;
+ }
+
+ ent = GIFNextPixel ();
+
+ hshift = 0;
+ for (fcode = (long) hsize; fcode < 65536L; fcode *= 2L)
+ ++hshift;
+ hshift = 8 - hshift; /* set hash code range bound */
+
+ hsize_reg = hsize;
+ cl_hash ((count_int) hsize_reg); /* clear hash table */
+
+ output ((code_int)ClearCode);
+
+ while ((c = GIFNextPixel ()) != EOF) {
+
+ ++in_count;
+
+ fcode = (long) (((long) c << maxbits) + ent);
+ i = (((code_int)c << hshift) ^ ent); /* xor hashing */
+
+ if (HashTabOf (i) == fcode) {
+ ent = CodeTabOf (i);
+ continue;
+ } else if ((long)HashTabOf (i) < 0) /* empty slot */
+ goto nomatch;
+ disp = hsize_reg - i; /* secondary hash (after G. Knott) */
+ if (i == 0)
+ disp = 1;
+probe:
+ if ((i -= disp) < 0)
+ i += hsize_reg;
+
+ if (HashTabOf (i) == fcode) {
+ ent = CodeTabOf (i);
+ continue;
+ }
+ if ((long)HashTabOf (i) > 0)
+ goto probe;
+nomatch:
+ output ((code_int) ent);
+ ++out_count;
+ ent = c;
+ if (free_ent < maxmaxcode) { /* } */
+ CodeTabOf (i) = free_ent++; /* code -> hashtable */
+ HashTabOf (i) = fcode;
+ } else
+ cl_block();
+ }
+
+ /*
+ * Put out the final code.
+ */
+ output ((code_int)ent);
+ ++out_count;
+ output ((code_int) EOFCode);
+}
+
+/*****************************************************************
+ * TAG (output)
+ *
+ * Output the given code.
+ * Inputs:
+ * code: A n_bits-bit integer. If == -1, then EOF. This assumes
+ * that n_bits =< (long)wordsize - 1.
+ * Outputs:
+ * Outputs code to the file.
+ * Assumptions:
+ * Chars are 8 bits long.
+ * Algorithm:
+ * Maintain a GIFBITS character long buffer (so that 8 codes will
+ * fit in it exactly). Use the VAX insv instruction to insert each
+ * code in turn. When the buffer fills up empty it and start over.
+ */
+
+
+static void
+output (code)
+code_int code;
+{
+ cur_accum &= masks[ cur_bits ];
+
+ if (cur_bits > 0)
+ cur_accum |= ((long)code << cur_bits);
+ else
+ cur_accum = code;
+
+ cur_bits += n_bits;
+
+ while (cur_bits >= 8) {
+ char_out ((unsigned int)(cur_accum & 0xff));
+ cur_accum >>= 8;
+ cur_bits -= 8;
+ }
+
+ /*
+ * If the next entry is going to be too big for the code size,
+ * then increase it, if possible.
+ */
+ if (free_ent > maxcode || clear_flg) {
+
+ if (clear_flg) {
+ maxcode = MAXCODE (n_bits = g_init_bits);
+ clear_flg = 0;
+ } else {
+ ++n_bits;
+ if (n_bits == maxbits)
+ maxcode = maxmaxcode;
+ else
+ maxcode = MAXCODE(n_bits);
+ }
+ }
+
+ if (code == EOFCode) {
+ /* At EOF, write the rest of the buffer. */
+ while (cur_bits > 0) {
+ char_out ((unsigned int)(cur_accum & 0xff));
+ cur_accum >>= 8;
+ cur_bits -= 8;
+ }
+ flush_char();
+ fflush (g_outfile);
+ if (ferror (g_outfile))
+ writeerr();
+ }
+}
+
+
+/*
+ * Clear out the hash table
+ */
+static void
+cl_block () /* table clear for block compress */
+{
+
+ cl_hash ((count_int) hsize);
+ free_ent = ClearCode + 2;
+ clear_flg = 1;
+
+ output ((code_int)ClearCode);
+}
+
+static void
+cl_hash(hsize) /* reset code table */
+register count_int hsize;
+{
+
+ register count_int *htab_p = htab + hsize;
+
+ register long i;
+ register long m1 = -1;
+
+ i = hsize - 16;
+ do { /* might use Sys V memset(3) here */
+ *(htab_p - 16) = m1;
+ *(htab_p - 15) = m1;
+ *(htab_p - 14) = m1;
+ *(htab_p - 13) = m1;
+ *(htab_p - 12) = m1;
+ *(htab_p - 11) = m1;
+ *(htab_p - 10) = m1;
+ *(htab_p - 9) = m1;
+ *(htab_p - 8) = m1;
+ *(htab_p - 7) = m1;
+ *(htab_p - 6) = m1;
+ *(htab_p - 5) = m1;
+ *(htab_p - 4) = m1;
+ *(htab_p - 3) = m1;
+ *(htab_p - 2) = m1;
+ *(htab_p - 1) = m1;
+ htab_p -= 16;
+ } while ((i -= 16) >= 0);
+
+ for (i += 16; i > 0; --i)
+ *--htab_p = m1;
+}
+
+static void
+writeerr()
+{
+ perror ("error writing output file");
+}
+
+
+/******************************************************************************
+ *
+ * GIF Specific routines
+ *
+ ******************************************************************************/
+
+
+/* Set up the 'byte output' routine
+ */
+static void
+char_init()
+{
+ register int i;
+
+ a_count = 0;
+ for (i=0; i<256; i++)
+ accum[i] = 0;
+}
+
+
+/*
+ * Add a character to the end of the current packet, and if it is 254
+ * characters, flush the packet to disk.
+ */
+static void
+char_out (c)
+int c;
+{
+ accum[ a_count++ ] = c;
+ if (a_count >= 254)
+ flush_char();
+}
+
+/*
+ * Flush the packet to disk, and reset the accumulator
+ */
+static void
+flush_char()
+{
+ if (a_count > 0) {
+ fputc (a_count, g_outfile);
+ fwrite (accum, 1, a_count, g_outfile);
+ a_count = 0;
+ }
+}