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