mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 01:47:47 +03:00 
			
		
		
		
	find * -type f "-(" -name "*.bdf" -o -name "*.c" -o -name "*.h" -o -name "*.py" -o -name "*.rst" -o -name "*.txt" "-)" -exec sed -e "s/[[:space:]]*$//" -i {} \;
		
	
			
		
			
				
	
	
		
			320 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			320 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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 {
 | 
						|
            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;
 | 
						|
        }
 | 
						|
}
 |