/* * The Python Imaging Library. * $Id$ * * encoder for uncompressed GIF data * * history: * 97-01-05 fl created (writes uncompressed data) * 97-08-27 fl fixed off-by-one error in buffer size test * 98-07-09 fl added interlace write support * 99-02-07 fl rewritten, now uses a run-length encoding strategy * 99-02-08 fl improved run-length encoding for long runs * * Copyright (c) Secret Labs AB 1997-99. * Copyright (c) Fredrik Lundh 1997. * * See the README file for information on usage and redistribution. */ #include "Imaging.h" #include "Gif.h" /* codes from 0 to 255 are literals */ #define CLEAR_CODE 256 #define EOF_CODE 257 #define FIRST_CODE 258 #define LAST_CODE 511 enum { INIT, ENCODE, ENCODE_EOF, FLUSH, EXIT }; /* to make things a little less complicated, we use a simple output queue to hold completed blocks. the following inlined function adds a byte to the current block. it allocates a new block if necessary. */ static inline int emit(GIFENCODERSTATE *context, int byte) { /* write a byte to the output buffer */ if (!context->block || context->block->size == 255) { GIFENCODERBLOCK* block; /* no room in the current block (or no current block); allocate a new one */ /* add current block to end of flush queue */ if (context->block) { block = context->flush; while (block && block->next) { block = block->next; } if (block) { block->next = context->block; } else { context->flush = context->block; } } /* get a new block */ if (context->free) { block = context->free; context->free = NULL; } else { /* malloc check ok, small constant allocation */ block = malloc(sizeof(GIFENCODERBLOCK)); if (!block) { return 0; } } block->size = 0; block->next = NULL; context->block = block; } /* write new byte to block */ context->block->data[context->block->size++] = byte; return 1; } /* write a code word to the current block. this is a macro to make sure it's inlined on all platforms */ #define EMIT(code) {\ context->bitbuffer |= ((INT32) (code)) << context->bitcount;\ context->bitcount += 9;\ while (context->bitcount >= 8) {\ if (!emit(context, (UINT8) context->bitbuffer)) {\ state->errcode = IMAGING_CODEC_MEMORY;\ return 0;\ }\ context->bitbuffer >>= 8;\ context->bitcount -= 8;\ }\ } /* write a run. we use a combination of literals and combinations of literals. this can give quite decent compression for images with long stretches of identical pixels. but remember: if you want really good compression, use another file format. */ #define EMIT_RUN(label) {\ label:\ while (context->count > 0) {\ int run = 2;\ EMIT(context->last);\ context->count--;\ if (state->count++ == LAST_CODE) {\ EMIT(CLEAR_CODE);\ state->count = FIRST_CODE;\ goto label;\ }\ while (context->count >= run) {\ EMIT(state->count - 1);\ context->count -= run;\ run++;\ if (state->count++ == LAST_CODE) {\ EMIT(CLEAR_CODE);\ state->count = FIRST_CODE;\ goto label;\ }\ }\ if (context->count > 1) {\ EMIT(state->count - 1 - (run - context->count));\ context->count = 0;\ if (state->count++ == LAST_CODE) {\ EMIT(CLEAR_CODE);\ state->count = FIRST_CODE;\ }\ break;\ }\ }\ } int ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes) { UINT8* ptr; int this; GIFENCODERBLOCK* block; GIFENCODERSTATE *context = (GIFENCODERSTATE*) state->context; if (!state->state) { /* place a clear code in the output buffer */ context->bitbuffer = CLEAR_CODE; context->bitcount = 9; state->count = FIRST_CODE; if (context->interlace) { context->interlace = 1; context->step = 8; } else { context->step = 1; } context->last = -1; /* sanity check */ if (state->xsize <= 0 || state->ysize <= 0) { state->state = ENCODE_EOF; } } ptr = buf; for (;;) switch (state->state) { case INIT: case ENCODE: /* identify and store a run of pixels */ if (state->x == 0 || state->x >= state->xsize) { if (!context->interlace && state->y >= state->ysize) { state->state = ENCODE_EOF; break; } if (context->flush) { state->state = FLUSH; break; } /* get another line of data */ state->shuffle( state->buffer, (UINT8*) im->image[state->y + state->yoff] + state->xoff * im->pixelsize, state->xsize ); state->x = 0; if (state->state == INIT) { /* preload the run-length buffer and get going */ context->last = state->buffer[0]; context->count = state->x = 1; state->state = ENCODE; } /* step forward, according to the interlace settings */ state->y += context->step; while (context->interlace && state->y >= state->ysize) switch (context->interlace) { case 1: state->y = 4; context->interlace = 2; break; case 2: context->step = 4; state->y = 2; context->interlace = 3; break; case 3: context->step = 2; state->y = 1; context->interlace = 0; break; default: /* just make sure we don't loop forever */ context->interlace = 0; } } this = state->buffer[state->x++]; if (this == context->last) { context->count++; } else { EMIT_RUN(label1); context->last = this; context->count = 1; } break; case ENCODE_EOF: /* write the final run */ EMIT_RUN(label2); /* write an end of image marker */ EMIT(EOF_CODE); /* empty the bit buffer */ while (context->bitcount > 0) { if (!emit(context, (UINT8) context->bitbuffer)) { state->errcode = IMAGING_CODEC_MEMORY; return 0; } context->bitbuffer >>= 8; context->bitcount -= 8; } /* flush the last block, and exit */ if (context->block) { GIFENCODERBLOCK* block; block = context->flush; while (block && block->next) { block = block->next; } if (block) { block->next = context->block; } else { context->flush = context->block; } context->block = NULL; } state->state = EXIT; /* fall through... */ case EXIT: case FLUSH: while (context->flush) { /* get a block from the flush queue */ block = context->flush; if (block->size > 0) { /* make sure it fits into the output buffer */ if (bytes < block->size+1) { return ptr - buf; } ptr[0] = block->size; memcpy(ptr+1, block->data, block->size); ptr += block->size+1; bytes -= block->size+1; } context->flush = block->next; if (context->free) { free(context->free); } context->free = block; } if (state->state == EXIT) { /* this was the last block! */ if (context->free) { free(context->free); } state->errcode = IMAGING_CODEC_END; return ptr - buf; } state->state = ENCODE; break; } }