/* * GIF encoder. * * The Graphics Interchange Format(c) is the Copyright property of * CompuServe Incorporated. GIF(sm) is a Service Mark property of * CompuServe Incorporated." * */ #include #include #include struct bitstream { int words_filled; /* Completed words */ int bit_off; /* bits written to current word */ int (*flush)(void *,int,char *); void *flush_arg; unsigned long buffer[65]; }; static int add_bits ( struct bitstream *bs, int value, int size ); static int compress_data ( struct bitstream *bs, int code_size, unsigned char *data, int length ); /*****************************************************************************/ /* */ int write_gif_header ( int width, int height, /* Size of screen */ int gct_size, /* Number of entries in color table */ unsigned char *gct, /* Color table 3 bytes/entry (r,g,b)*/ int background, /* Background color pixel index*/ int (*output)(void*, int, char *), /* output function */ void *outarg ) /* user-arg for output routine*/ { int size, count; #ifdef __ALPHA #pragma nomember_alignment #endif struct gif_header { char signature[6]; /* GIF87a */ short width, height; /* little-endian */ int flags; /* <0..2> gct_size (2**(val+1)). */ /* <3> color table sorted */ /* <4..6> out device clr res (7)*/ /* <7> Global color table if set */ } header; #ifdef __ALPHA #pragma member_alignment #endif /* * Build gif header in structure. */ strcpy ( header.signature, "GIF87a" ); header.width = width; header.height = height; header.flags = 0; /* ASSUME 2^(0+1) = 2 BITS */ header.flags = header.flags | (background*256) | 0x00080; for ( size = 2; size < gct_size; size = size * 2 ) header.flags++; /* * Send header and color table using caller-supplied output function. */ count = output( outarg, 13, (char *) &header ); count = output( outarg, gct_size*3, (char *) gct ); return count; } /*****************************************************************************/ /* */ int write_gif_image ( int width, int height, /* Size of image */ int max_pixel, /* Highest pixel value in image */ unsigned char *image, /* Image data, 1 byte/pixel */ int interlace, /* Only set if you've interlaced image*/ int (*output)(void*, int, char *), /* User-supplied output function */ void *outarg ) /* user-arg for output function */ { int size, status, count, bits; #ifdef __ALPHA #pragma nomember_alignment #endif struct { char align_fill; char separator; short left, top; /* x, y offset of origin */ short width, height; /* size of image */ int flags; /* <0..2> pixel depth */ /* <3..5> mbz */ /* <6> Interlace flag */ /* <7> local color table */ } header; #ifdef __ALPHA #pragma member_alignment #endif int prefix, clear, eoi; struct bitstream bs; char trailer[2] = { '\0', ';' }; /* * Construct image header and write it, assume no local color table. */ header.separator = ','; header.left = header.top = 0; header.width = width; header.height = height; header.flags = 0; /* depth of 2 */ if ( interlace ) header.flags |= 64; count = output (outarg,10, (char *) &header.separator ); /* * Determine number of bits needed to hold max_pixel, minimum 2, and output. */ for ( bits = 2, size = 4; size < max_pixel; size = size * 2 ) bits++; status = output ( outarg, 1, (char *) &bits ); /* * Initilize structure used for packing variable sized bits fields. * Include poitner to output function so add_bits can flush accumulated * data as needed. */ bs.words_filled = 0; bs.bit_off = 0; bs.flush = output; bs.flush_arg = outarg; bs.buffer[0] = 0; /* * Compress the data, when done flush any remaining data in bs buffer. */ status = compress_data ( &bs, bits, image, width * height ); count = bs.words_filled*4 + (bs.bit_off+7)/8; /* include whole byte */ if ( count > 0 ) { status = output ( outarg, 1, (char *) &count ); status = output ( outarg, count, (char *) bs.buffer ); } /* * Send final 0 byte to end image data and ';' to end gif. */ status = output ( outarg, 2, trailer ); return status; } static unsigned bmask[32] = { 0, 1, 3, 7, 15, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 0x03fff, 0x07fff, 0x0ffff, 0x01ffff, 0x03ffff, 0x07ffff, 0x0fffff, 0x01fffff, 0x03ffff, 0x07ffff, 0x0ffffff, 0x01ffffff, 0x03fffff, 0x07fffff, 0x0ffffff, 0x1fffffff, 0x3ffffff, 0x7ffffff, 0xffffffff }; static int add_bits ( struct bitstream *bs, int value, int size ) { int cur, pos, off, ov_bits, status; unsigned low; cur = bs->words_filled; off = bs->bit_off; if ( off + size <= 32 ) { /* * All fits in current longword of buffer. */ low = (value &bmask[size]); bs->buffer[cur] |= (low << off); bs->bit_off = off + size; } else { /* * value straddles two longwords. */ ov_bits = size + off - 32; low = (value & bmask[size-ov_bits]); bs->buffer[cur++] |= (low< 62 ) { unsigned char bcount; bcount = cur*4; status = bs->flush ( bs->flush_arg, 1, (char *)&bcount ); status = bs->flush ( bs->flush_arg, cur*4, (char *)bs->buffer ); cur = 0; } low = value; low = (low>>(size-ov_bits)); bs->buffer[cur] = (low & bmask[ov_bits]); bs->bit_off = ov_bits; } bs->words_filled = cur; return cur; } struct code_t_entry { short prefix, code, next_hash; }; struct lz_state { int code_size; /* Number of bits of input size */ int clear; /* value of clear code (2^code_size) */ int eoi; /* End-of-info code (clear+1); */ int out_size; int max_code; /* hight entry in code_table */ int max_hash; #define HASH_BITS 18 #define HASH_TABLE_SIZE 262144 int hash_shift; /* shift for charval in hash func */ struct code_t_entry table[4096]; /* code table */ short hash_table[262144]; }; typedef struct lz_state *lzptr; static int initialize_lz_state ( lzptr lz, int code_size ) { int i, max_code, max_hash; struct code_t_entry *table; short *hash; lz->code_size = code_size; lz->clear = 1; for ( i = 0; i < code_size; i++ ) lz->clear *= 2; lz->eoi = lz->clear+1; lz->out_size = code_size + 1; lz->max_code = max_code = lz->eoi; max_hash = HASH_TABLE_SIZE; lz->hash_shift = 12; while ( (code_size+lz->hash_shift) > HASH_BITS) { lz->hash_shift = lz->hash_shift - 1; } max_hash = bmask[code_size+lz->hash_shift]; lz->max_hash = max_hash; /* * Initialize the tables. */ table = lz->table; for ( i = 0; i <= max_code; i++ ) { table[i].prefix = -1; table[i].code = i; table[i].next_hash = 0; } hash = lz->hash_table; for ( i = 0; i < max_hash; i++ ) hash[i] = 0; return 1; } static int compress_data ( struct bitstream *bs, int code_size, unsigned char *data, int length ) { int stack[4100], *stackptr; int code, prefix, current, new, prev_code, out_size, i, hashval; int code_limit; unsigned char *dptr, *data_end; struct code_t_entry *table; short *hash_table; struct lz_state lz; data_end = &data[length]; dptr = data; initialize_lz_state ( &lz, code_size ); table = lz.table; hash_table = lz.hash_table; out_size = lz.out_size; code_limit = bmask[out_size]; add_bits ( bs, lz.clear, out_size ); prefix = *dptr++; while ( dptr < data_end ) { current = *dptr++; /* Find prefix//current table entry */ hashval = (lz.max_hash&((current< code_limit ) { out_size++; code_limit = code_limit*2 + 1; } if ( new > 4092 ) { /* Reset so we don't overflow */ add_bits ( bs, current, out_size ); add_bits ( bs, lz.clear, out_size ); current = prefix = lz.clear; initialize_lz_state ( &lz, code_size ); printf("Reset code table, out_size %d\n", lz.out_size); out_size = lz.out_size; code_limit = bmask[out_size]; } else { lz.table[new].prefix = prefix; lz.table[new].code = current; lz.table[new].next_hash = hash_table[hashval]; lz.hash_table[hashval] = new; /* prefix for next value */ } prefix = current; } } /* * Flush remaining to output. */ add_bits ( bs, prefix, out_size ); if ( dptr >= data_end ) { add_bits ( bs, lz.eoi, out_size ); return 1; } return 1; }