#include #include #include #include #include "gd.h" /* Code drawn from ppmtogif.c, from the pbmplus package ** ** Based on GIFENCOD by David Rowley . A ** Lempel-Zim compression based on "compress". ** ** Modified by Marcel Wijkstra ** ** Copyright (C) 1989 by Jef Poskanzer. ** ** 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. ** ** The Graphics Interchange Format(c) is the Copyright property of ** CompuServe Incorporated. GIF(sm) is a Service Mark property of ** CompuServe Incorporated. */ /* * a code_int must be able to hold 2**GIFBITS values of type int, and also -1 */ typedef int code_int; #ifdef SIGNED_COMPARE_SLOW typedef unsigned long int count_int; typedef unsigned short int count_short; #else /*SIGNED_COMPARE_SLOW*/ typedef long int count_int; #endif /*SIGNED_COMPARE_SLOW*/ static int colorstobpp(int colors); static void BumpPixel (void); static int GIFNextPixel (gdImagePtr im); static void GIFEncode (gdIOCtx *fp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im); /*static void Putword (int w, gdIOCtx *fp); */ static void compress (int init_bits, gdIOCtx *outfile, gdImagePtr im); static void output (code_int code); static void cl_block (void); static void cl_hash (register count_int hsize); static void char_init (void); static void char_out (int c); static void flush_char (void); /* Allows for reuse */ static void init_statics(void); void gdImageLzwCtx(gdImagePtr im, gdIOCtx *out) { int interlace, transparent, BitsPerPixel; interlace = im->interlace; transparent = im->transparent; BitsPerPixel = colorstobpp(im->colorsTotal); /* Clear any old values in statics strewn through the GIF code */ init_statics(); /* All set, let's do it. */ GIFEncode( out, im->sx, im->sy, interlace, 0, transparent, BitsPerPixel, im->red, im->green, im->blue, im); } void gdImageLzw(gdImagePtr im, FILE *outFile) { gdIOCtx *out = gdNewFileCtx(outFile); gdImageLzwCtx(im, out); out->gd_free(out); } void* gdImageLzwPtr(gdImagePtr im, int *size) { void *rv; gdIOCtx *out = gdNewDynamicCtx(2048, NULL); gdImageLzwCtx(im, out); rv = gdDPExtractData(out,size); out->gd_free(out); return rv; } static int colorstobpp(int colors) { int bpp = 0; 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; return bpp; } /***************************************************************************** * * GIFENCODE.C - GIF Image compression interface * * GIFEncode( FName, GHeight, GWidth, GInterlace, Background, Transparent, * BitsPerPixel, Red, Green, Blue, gdImagePtr ) * *****************************************************************************/ #define TRUE 1 #define FALSE 0 static int Width, Height; static int curx, cury; static long CountDown; static int Pass = 0; static int Interlace; /* * Bump the 'curx' and 'cury' to point to the next pixel */ static void BumpPixel(void) { /* * 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(gdImagePtr im) { int r; if( CountDown == 0 ) return EOF; --CountDown; r = gdImageGetPixel(im, curx, cury); BumpPixel(); return r; } /* public */ static void GIFEncode(gdIOCtx *fp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im) { 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 */ gdPutBuf( Transparent < 0 ? "GIF87a" : "GIF89a", 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 */ gdPutC( B, fp ); /* * Write out the Background colour */ gdPutC( Background, fp ); /* * Byte of 0's (future expansion) */ gdPutC( 0, fp ); /* * Write out the Global Colour Map */ for( i=0; i= 0 ) { gdPutC( '!', fp ); gdPutC( 0xf9, fp ); gdPutC( 4, fp ); gdPutC( 1, fp ); gdPutC( 0, fp ); gdPutC( 0, fp ); gdPutC( (unsigned char) Transparent, fp ); gdPutC( 0, fp ); } /* * Write an Image separator */ gdPutC( ',', 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 ) gdPutC( 0x40, fp ); else gdPutC( 0x00, fp ); /* * Write out the initial code size */ gdPutC( InitCodeSize, fp ); /* * Go and actually compress the data */ compress( InitCodeSize+1, fp, im ); /* * Write out a Zero-length packet (to end the series) */ gdPutC( 0, fp ); /* * Write the GIF file terminator */ gdPutC( ';', fp ); } /* */ /* * Write out a word to the GIF file */ /* */ /*static void */ /*Putword(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 #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 */ static code_int maxmaxcode = (code_int)1 << GIFBITS; /* should NEVER generate this code */ #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< 0 ) goto probe; nomatch: output ( (code_int) ent ); ++out_count; ent = c; #ifdef SIGNED_COMPARE_SLOW if ( (unsigned) free_ent < (unsigned) maxmaxcode) { #else /*SIGNED_COMPARE_SLOW*/ if ( free_ent < maxmaxcode ) { /* } */ #endif /*SIGNED_COMPARE_SLOW*/ 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 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 void output(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 ) ) */ /* return; */ } } /* * Clear out the hash table */ static void cl_block (void) /* 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(register count_int hsize) /* reset code table */ { 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; } /****************************************************************************** * * GIF Specific routines * ******************************************************************************/ /* * Number of characters so far in this 'packet' */ static int a_count; /* * Set up the 'byte output' routine */ static void char_init(void) { a_count = 0; } /* * Define the storage for the packet accumulator */ static char accum[ 256 ]; /* * 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(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(void) { if( a_count > 0 ) { gdPutC( a_count, g_outfile ); gdPutBuf( accum, a_count, g_outfile ); a_count = 0; } } static void init_statics(void) { /* Some of these are properly initialized later. What I'm doing here is making sure code that depends on C's initialization of statics doesn't break when the code gets called more than once. */ Width = 0; Height = 0; curx = 0; cury = 0; CountDown = 0; Pass = 0; Interlace = 0; a_count = 0; cur_accum = 0; cur_bits = 0; g_init_bits = 0; g_outfile = 0; ClearCode = 0; EOFCode = 0; free_ent = 0; clear_flg = 0; offset = 0; in_count = 1; out_count = 0; hsize = HSIZE; n_bits = 0; maxbits = GIFBITS; maxcode = 0; maxmaxcode = (code_int)1 << GIFBITS; } /* +-------------------------------------------------------------------+ */ /* | Copyright 1990, 1991, 1993, David Koblas. (koblas@netcom.com) | */ /* | 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. | */ /* +-------------------------------------------------------------------+ */