mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-25 05:01:26 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			321 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			321 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 {
 | |
|             /* 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;
 | |
|         }
 | |
| }
 |