123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- /*
- * 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;
- }
- }
|