[comp.sources.mac] GIF Image Compression Routines

SXWRR@ALASKA.BITNET (Reed Rector) (05/13/89)

[GIF Image Compression Routines]

Well, I finally got around to it...  This set of 'C' routines will
create a GIF file from image data.  It is a SHAR type archive, so if
you don't have a UNIX machine, you will have to de-archive it by hand
(pretty straigtforward process).

        -Reed

---
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#
#	gifcompr.c
#	gifencod.c
#
# This archive created: Fri May 12 20:26:46 1989
# By:	Roger L. Long (bytebug@dhw68k.cts.com)
export PATH; PATH=/bin:$PATH
echo shar: extracting "'gifcompr.c'" '(11182 characters)'
if test -f 'gifcompr.c'
then
	echo shar: will not over-write existing file "'gifcompr.c'"
else
sed 's/^X//' << \SHAR_EOF > 'gifcompr.c'
X/***************************************************************************
X *
X *  GIFENCOD.C       - GIF Image compression routines
X *
X *  Lempel-Ziv compression based on 'compress'.  GIF modifications by
X *  David Rowley (mgardi@watdcsu.waterloo.edu)
X *
X ***************************************************************************/
X
X/*
X * General DEFINEs
X */
X#define min(a,b)        ((a>b) ? b : a)
X
X#define BITS    12
X#define MSDOS   1
X
X#define HSIZE  5003            /* 80% occupancy */
X
X/*
X * Pointer to function returning an int
X */
Xtypedef int (* ifunptr)();
X
X/*
X * a code_int must be able to hold 2**BITS values of type int, and also -1
X */
Xtypedef int             code_int;
X
X#ifdef SIGNED_COMPARE_SLOW
Xtypedef unsigned long int count_int;
Xtypedef unsigned short int count_short;
X#else
Xtypedef long int          count_int;
X#endif
X
X#ifdef NO_UCHAR
X typedef char   char_type;
X#else
X typedef        unsigned char   char_type;
X#endif /* UCHAR */
X
X/*
X *
X * GIF Image compression - modified 'compress'
X *
X * Based on: compress.c - File compression ala IEEE Computer, June 1984.
X *
X * By Authors:  Spencer W. Thomas       (decvax!harpo!utah-cs!utah-gr!thomas)
X *              Jim McKie               (decvax!mcvax!jim)
X *              Steve Davies            (decvax!vax135!petsd!peora!srd)
X *              Ken Turkowski           (decvax!decwrl!turtlevax!ken)
X *              James A. Woods          (decvax!ihnp4!ames!jaw)
X *              Joe Orost               (decvax!vax135!petsd!joe)
X *
X */
X#include <stdio.h>
X#include <ctype.h>
X#include <signal.h>
X
X#define ARGVAL() (*++(*argv) || (--argc && *++argv))
X
Xstatic int n_bits;                        /* number of bits/code */
Xstatic int maxbits = BITS;                /* user settable max # bits/code */
Xstatic code_int maxcode;                  /* maximum code, given n_bits */
Xstatic code_int maxmaxcode = (code_int)1 << BITS; /* should NEVER generate this
Xcode */
X#ifdef COMPATIBLE               /* But wrong! */
X# define MAXCODE(n_bits)        ((code_int) 1 << (n_bits) - 1)
X#else
X# define MAXCODE(n_bits)        (((code_int) 1 << (n_bits)) - 1)
X#endif /* COMPATIBLE */
X
Xstatic count_int htab [HSIZE];
Xstatic unsigned short codetab [HSIZE];
X#define HashTabOf(i)       htab[i]
X#define CodeTabOf(i)    codetab[i]
X
Xstatic code_int hsize = HSIZE;                 /* for dynamic table sizing */
Xstatic count_int fsize;
X
X/*
X * To save much memory, we overlay the table used by compress() with those
X * used by decompress().  The tab_prefix table is the same size and type
X * as the codetab.  The tab_suffix table needs 2**BITS characters.  We
X * get this from the beginning of htab.  The output stack uses the rest
X * of htab, and contains characters.  There is plenty of room for any
X * possible stack (stack used to be 8000 characters).
X */
X
X#define tab_prefixof(i) CodeTabOf(i)
X#define tab_suffixof(i)        ((char_type *)(htab))[i]
X#define de_stack               ((char_type *)&tab_suffixof((code_int)1<<BITS))
X
Xstatic code_int free_ent = 0;                  /* first unused entry */
Xstatic int exit_stat = 0;
X
X/*
X * block compression parameters -- after all codes are used up,
X * and compression rate changes, start over.
X */
Xstatic int clear_flg = 0;
X
Xstatic int offset;
Xstatic long int in_count = 1;            /* length of input */
Xstatic long int out_count = 0;           /* # of codes output (for debugging) */
X
X/*
X * compress stdin to stdout
X *
X * Algorithm:  use open addressing double hashing (no chaining) on the
X * prefix code / next character combination.  We do a variant of Knuth's
X * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
X * secondary probe.  Here, the modular division first probe is gives way
X * to a faster exclusive-or manipulation.  Also do block compression with
X * an adaptive reset, whereby the code table is cleared when the compression
X * ratio decreases, but after the table fills.  The variable-length output
X * codes are re-sized at this point, and a special CLEAR code is generated
X * for the decompressor.  Late addition:  construct the table according to
X * file size for noticeable speed improvement on small files.  Please direct
X * questions about this implementation to ames!jaw.
X */
X
Xstatic int g_init_bits;
Xstatic FILE *g_outfile;
X
Xstatic int ClearCode;
Xstatic int EOFCode;
X
Xcompress( init_bits, outfile, ReadValue )
Xint init_bits;
XFILE *outfile;
Xifunptr ReadValue;
X{
X    register long fcode;
X    register code_int i = 0;
X    register int c;
X    register code_int ent;
X    register code_int disp;
X    register code_int hsize_reg;
X    register int hshift;
X
X    /*
X     * Set up the globals:  g_init_bits - initial number of bits
X     *                      g_outfile   - pointer to output file
X     */
X    g_init_bits = init_bits;
X    g_outfile = outfile;
X
X    /*
X     * Set up the necessary values
X     */
X    offset = 0;
X    out_count = 0;
X    clear_flg = 0;
X    in_count = 1;
X    maxcode = MAXCODE(n_bits = g_init_bits);
X
X    ClearCode = (1 << (init_bits - 1));
X    EOFCode = ClearCode + 1;
X    free_ent = ClearCode + 2;
X
X    char_init();
X
X    ent = GIFNextPixel( ReadValue );
X
X    hshift = 0;
X    for ( fcode = (long) hsize;  fcode < 65536L; fcode *= 2L )
X        hshift++;
X    hshift = 8 - hshift;                /* set hash code range bound */
X
X    hsize_reg = hsize;
X    cl_hash( (count_int) hsize_reg);            /* clear hash table */
X
X    output( (code_int)ClearCode );
X
X#ifdef SIGNED_COMPARE_SLOW
X    while ( (c = GIFNextPixel( ReadValue )) != (unsigned) EOF ) {
X#else
X    while ( (c = GIFNextPixel( ReadValue )) != EOF ) {
X#endif
X
X        in_count++;
X
X        fcode = (long) (((long) c << maxbits) + ent);
X        i = (((code_int)c << hshift) ~ ent);    /* xor hashing */
X
X        if ( HashTabOf (i) == fcode ) {
X            ent = CodeTabOf (i);
X            continue;
X        } else if ( (long)HashTabOf (i) < 0 )      /* empty slot */
X            goto nomatch;
X        disp = hsize_reg - i;           /* secondary hash (after G. Knott) */
X        if ( i == 0 )
X            disp = 1;
Xprobe:
X        if ( (i -= disp) < 0 )
X            i += hsize_reg;
X
X        if ( HashTabOf (i) == fcode ) {
X            ent = CodeTabOf (i);
X            continue;
X        }
X        if ( (long)HashTabOf (i) > 0 )
X            goto probe;
Xnomatch:
X        output ( (code_int) ent );
X        out_count++;
X        ent = c;
X#ifdef SIGNED_COMPARE_SLOW
X        if ( (unsigned) free_ent < (unsigned) maxmaxcode) {
X#else
X        if ( free_ent < maxmaxcode ) {
X#endif
X            CodeTabOf (i) = free_ent++; /* code -> hashtable */
X            HashTabOf (i) = fcode;
X        } else
X                cl_block();
X    }
X    /*
X     * Put out the final code.
X     */
X    output( (code_int)ent );
X    out_count++;
X    output( (code_int) EOFCode );
X
X    return;
X}
X
X/*****************************************************************
X * TAG( output )
X *
X * Output the given code.
X * Inputs:
X *      code:   A n_bits-bit integer.  If == -1, then EOF.  This assumes
X *              that n_bits =< (long)wordsize - 1.
X * Outputs:
X *      Outputs code to the file.
X * Assumptions:
X *      Chars are 8 bits long.
X * Algorithm:
X *      Maintain a BITS character long buffer (so that 8 codes will
X * fit in it exactly).  Use the VAX insv instruction to insert each
X * code in turn.  When the buffer fills up empty it and start over.
X */
X
Xstatic unsigned long cur_accum = 0;
Xstatic int  cur_bits = 0;
X
Xstatic
Xunsigned long masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
X                                  0x001F, 0x003F, 0x007F, 0x00FF,
X                                  0x01FF, 0x03FF, 0x07FF, 0x0FFF,
X                                  0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
X
Xstatic
Xoutput( code )
Xcode_int  code;
X{
X    cur_accum &= masks[ cur_bits ];
X
X    if( cur_bits > 0 )
X        cur_accum |= ((long)code << cur_bits);
X    else
X        cur_accum = code;
X
X    cur_bits += n_bits;
X
X    while( cur_bits >= 8 ) {
X        char_out( (unsigned int)(cur_accum & 0xff) );
X        cur_accum >>= 8;
X        cur_bits -= 8;
X    }
X
X    /*
X     * If the next entry is going to be too big for the code size,
X     * then increase it, if possible.
X     */
X   if ( free_ent > maxcode || clear_flg ) {
X
X            if( clear_flg ) {
X
X                maxcode = MAXCODE (n_bits = g_init_bits);
X                clear_flg = 0;
X
X            } else {
X
X                n_bits++;
X                if ( n_bits == maxbits )
X                    maxcode = maxmaxcode;
X                else
X                    maxcode = MAXCODE(n_bits);
X            }
X        }
X
X    if( code == EOFCode ) {
X        /*
X         * At EOF, write the rest of the buffer.
X         */
X        while( cur_bits > 0 ) {
X                char_out( (unsigned int)(cur_accum & 0xff) );
X                cur_accum >>= 8;
X                cur_bits -= 8;
X        }
X
X        flush_char();
X
X        fflush( g_outfile );
X
X        if( ferror( g_outfile ) )
X                writeerr();
X    }
X}
X
X/*
X * Clear out the hash table
X */
Xstatic
Xcl_block ()             /* table clear for block compress */
X{
X
X        cl_hash ( (count_int) hsize );
X        free_ent = ClearCode + 2;
X        clear_flg = 1;
X
X        output( (code_int)ClearCode );
X}
X
Xstatic
Xcl_hash(hsize)          /* reset code table */
Xregister count_int hsize;
X{
X
X        register count_int *htab_p = htab+hsize;
X
X        register long i;
X        register long m1 = -1;
X
X        i = hsize - 16;
X        do {                            /* might use Sys V memset(3) here */
X                *(htab_p-16) = m1;
X                *(htab_p-15) = m1;
X                *(htab_p-14) = m1;
X                *(htab_p-13) = m1;
X                *(htab_p-12) = m1;
X                *(htab_p-11) = m1;
X                *(htab_p-10) = m1;
X                *(htab_p-9) = m1;
X                *(htab_p-8) = m1;
X                *(htab_p-7) = m1;
X                *(htab_p-6) = m1;
X                *(htab_p-5) = m1;
X                *(htab_p-4) = m1;
X                *(htab_p-3) = m1;
X                *(htab_p-2) = m1;
X                *(htab_p-1) = m1;
X                htab_p -= 16;
X        } while ((i -= 16) >= 0);
X
X        for ( i += 16; i > 0; i-- )
X                *--htab_p = m1;
X}
X
Xstatic
Xwriteerr()
X{
X        printf( "error writing output file\n" );
X        exit(1);
X}
X
X/******************************************************************************
X *
X * GIF Specific routines
X *
X ******************************************************************************/
X
X/*
X * Number of characters so far in this 'packet'
X */
Xstatic int a_count;
X
X/*
X * Set up the 'byte output' routine
X */
Xstatic
Xchar_init()
X{
X        a_count = 0;
X}
X
X/*
X * Define the storage for the packet accumulator
X */
Xstatic char accum[ 256 ];
X
X/*
X * Add a character to the end of the current packet, and if it is 254
X * characters, flush the packet to disk.
X */
Xstatic
Xchar_out( c )
Xint c;
X{
X        accum[ a_count++ ] = c;
X        if( a_count >= 254 )
X                flush_char();
X}
X
X/*
X * Flush the packet to disk, and reset the accumulator
X */
Xstatic
Xflush_char()
X{
X        if( a_count > 0 ) {
X                fputc( a_count, g_outfile );
X                fwrite( accum, 1, a_count, g_outfile );
X                a_count = 0;
X        }
X}
X
X/* The End */
SHAR_EOF
if test 11182 -ne "`wc -c < 'gifcompr.c'`"
then
	echo shar: error transmitting "'gifcompr.c'" '(should have been 11182 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'gifencod.c'" '(5916 characters)'
if test -f 'gifencod.c'
then
	echo shar: will not over-write existing file "'gifencod.c'"
else
sed 's/^X//' << \SHAR_EOF > 'gifencod.c'
X
X/*****************************************************************************
X *
X * GIFENCODE.C    - GIF Image compression interface
X *
X * GIFEncode( FName, GHeight, GWidth, GInterlace, Background,
X *            BitsPerPixel, Red, Green, Blue, GetPixel )
X *
X *****************************************************************************/
X
X#include <stdio.h>
X
X/*
X * Pointer to function returning an int
X */
Xtypedef int (* ifunptr)();
X
X#define TRUE 1
X#define FALSE 0
X
Xstatic int Width, Height;
Xstatic int curx, cury;
Xstatic long CountDown;
Xstatic int Pass = 0;
Xstatic int Interlace;
X
X/*
X * Bump the 'curx' and 'cury' to point to the next pixel
X */
Xstatic
XBumpPixel()
X{
X        /*
X         * Bump the current X position
X         */
X        curx++;
X
X        /*
X         * If we are at the end of a scan line, set curx back to the beginning
X         * If we are interlaced, bump the cury to the appropriate spot,
X         * otherwise, just increment it.
X         */
X        if( curx == Width ) {
X                curx = 0;
X
X                if( !Interlace )
X                        cury++;
X                else {
X                     switch( Pass ) {
X
X                       case 0:
X                          cury += 8;
X                          if( cury >= Height ) {
X                                Pass++;
X                                cury = 4;
X                          }
X                          break;
X
X                       case 1:
X                          cury += 8;
X                          if( cury >= Height ) {
X                                Pass++;
X                                cury = 2;
X                          }
X                          break;
X
X                       case 2:
X                          cury += 4;
X                          if( cury >= Height ) {
X                             Pass++;
X                             cury = 1;
X                          }
X                          break;
X
X                       case 3:
X                          cury += 2;
X                          break;
X                        }
X                }
X        }
X}
X
X/*
X * Return the next pixel from the image
X */
XGIFNextPixel( getpixel )
Xifunptr getpixel;
X{
X        int r;
X
X        if( CountDown == 0 )
X                return EOF;
X
X        CountDown--;
X
X        r = ( * getpixel )( curx, cury );
X
X        BumpPixel();
X
X        return r;
X}
X
X/* public */
X
XGIFEncode( FName, GWidth, GHeight, GInterlace, Background,
X           BitsPerPixel, Red, Green, Blue, GetPixel )
X
Xchar *FName;
Xint GWidth, GHeight;
Xint GInterlace;
Xint Background;
Xint BitsPerPixel;
Xint Red[], Green[], Blue[];
Xifunptr GetPixel;
X
X{
X        FILE *fp;
X        int B;
X        int RWidth, RHeight;
X        int LeftOfs, TopOfs;
X        int Resolution;
X        int ColorMapSize;
X        int InitCodeSize;
X        int i;
X
X        Interlace = GInterlace;
X
X        ColorMapSize = 1 << BitsPerPixel;
X
X        RWidth = Width = GWidth;
X        RHeight = Height = GHeight;
X        LeftOfs = TopOfs = 0;
X
X        Resolution = BitsPerPixel;
X
X        /*
X         * Calculate number of bits we are expecting
X         */
X        CountDown = (long)Width * (long)Height;
X
X        /*
X         * Indicate which pass we are on (if interlace)
X         */
X        Pass = 0;
X
X        /*
X         * The initial code size
X         */
X        if( BitsPerPixel <= 1 )
X                InitCodeSize = 2;
X        else
X                InitCodeSize = BitsPerPixel;
X
X        /*
X         * Set up the current x and y position
X         */
X        curx = cury = 0;
X
X        /*
X         * Open the GIF file for binary write
X         */
X        fp = fopen( FName, "wb" );
X
X        if( fp == (FILE *)0 ) {
X                printf( "error: could not open output file\n" );
X                exit(1);
X        }
X
X        /*
X         * Write the Magic header
X         */
X        fwrite( "GIF87a", 1, 6, fp );
X
X        /*
X         * Write out the screen width and height
X         */
X        Putword( RWidth, fp );
X        Putword( RHeight, fp );
X
X        /*
X         * Indicate that there is a global colour map
X         */
X        B = 0x80;       /* Yes, there is a color map */
X
X        /*
X         * OR in the resolution
X         */
X        B |= (Resolution - 1) << 5;
X
X        /*
X         * OR in the Bits per Pixel
X         */
X        B |= (BitsPerPixel - 1);
X
X        /*
X         * Write it out
X         */
X        fputc( B, fp );
X
X        /*
X         * Write out the Background colour
X         */
X        fputc( Background, fp );
X
X        /*
X         * Byte of 0's (future expansion)
X         */
X        fputc( 0, fp );
X
X        /*
X         * Write out the Global Colour Map
X         */
X        for( i=0; i<ColorMapSize; i++ ) {
X                fputc( Red[i], fp );
X                fputc( Green[i], fp );
X                fputc( Blue[i], fp );
X        }
X
X        /*
X         * Write an Image separator
X         */
X        fputc( ',', fp );
X
X        /*
X         * Write the Image header
X         */
X
X        Putword( LeftOfs, fp );
X        Putword( TopOfs, fp );
X        Putword( Width, fp );
X        Putword( Height, fp );
X
X        /*
X         * Write out whether or not the image is interlaced
X         */
X        if( Interlace )
X                fputc( 0x40, fp );
X        else
X                fputc( 0x00, fp );
X
X        /*
X         * Write out the initial code size
X         */
X        fputc( InitCodeSize, fp );
X
X        /*
X         * Go and actually compress the data
X         */
X        compress( InitCodeSize+1, fp, GetPixel );
X
X        /*
X         * Write out a Zero-length packet (to end the series)
X         */
X        fputc( 0, fp );
X
X        /*
X         * Write the GIF file terminator
X         */
X        fputc( ';', fp );
X
X        /*
X         * And close the file
X         */
X        fclose( fp );
X
X}
X
X/*
X * Write out a word to the GIF file
X */
Xstatic
XPutword( w, fp )
Xint w;
XFILE *fp;
X{
X        fputc( w & 0xff, fp );
X        fputc( (w / 256) & 0xff, fp );
X}
X
SHAR_EOF
if test 5916 -ne "`wc -c < 'gifencod.c'`"
then
	echo shar: error transmitting "'gifencod.c'" '(should have been 5916 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0
---