Pillow/src/libImaging/GifEncode.c

323 lines
11 KiB
C
Raw Normal View History

2010-07-31 06:52:47 +04:00
/*
* The Python Imaging Library.
* $Id$
*
* encoder for uncompressed GIF data
*
* history:
2020-05-01 15:08:57 +03:00
* 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
2010-07-31 06:52:47 +04:00
*
* 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
2021-01-03 06:17:51 +03:00
emit(GIFENCODERSTATE *context, int byte) {
2010-07-31 06:52:47 +04:00
/* write a byte to the output buffer */
if (!context->block || context->block->size == 255) {
2021-01-03 06:17:51 +03:00
GIFENCODERBLOCK *block;
2010-07-31 06:52:47 +04:00
/* no room in the current block (or no current block);
allocate a new one */
2010-07-31 06:52:47 +04:00
/* add current block to end of flush queue */
if (context->block) {
block = context->flush;
2020-05-10 12:56:36 +03:00
while (block && block->next) {
2010-07-31 06:52:47 +04:00
block = block->next;
2020-05-10 12:56:36 +03:00
}
if (block) {
2010-07-31 06:52:47 +04:00
block->next = context->block;
2020-05-10 12:56:36 +03:00
} else {
2010-07-31 06:52:47 +04:00
context->flush = context->block;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
/* get a new block */
if (context->free) {
block = context->free;
context->free = NULL;
} else {
2016-03-16 14:47:18 +03:00
/* malloc check ok, small constant allocation */
2010-07-31 06:52:47 +04:00
block = malloc(sizeof(GIFENCODERBLOCK));
2020-05-10 12:56:36 +03:00
if (!block) {
2010-07-31 06:52:47 +04:00
return 0;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
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 */
2021-01-03 06:17:51 +03:00
#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; \
} \
}
2010-07-31 06:52:47 +04:00
/* 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. */
2021-01-03 06:17:51 +03:00
#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; \
} \
} \
}
2010-07-31 06:52:47 +04:00
int
2021-01-03 06:17:51 +03:00
ImagingGifEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
UINT8 *ptr;
2010-07-31 06:52:47 +04:00
int this;
2021-01-03 06:17:51 +03:00
GIFENCODERBLOCK *block;
GIFENCODERSTATE *context = (GIFENCODERSTATE *)state->context;
2010-07-31 06:52:47 +04:00
if (!state->state) {
2020-05-01 15:08:57 +03:00
/* place a clear code in the output buffer */
context->bitbuffer = CLEAR_CODE;
context->bitcount = 9;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
state->count = FIRST_CODE;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
if (context->interlace) {
context->interlace = 1;
context->step = 8;
2020-05-10 12:56:36 +03:00
} else {
2020-05-01 15:08:57 +03:00
context->step = 1;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
context->last = -1;
/* sanity check */
2020-05-10 12:56:36 +03:00
if (state->xsize <= 0 || state->ysize <= 0) {
2010-07-31 06:52:47 +04:00
state->state = ENCODE_EOF;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
ptr = buf;
2021-01-03 06:17:51 +03:00
for (;;) switch (state->state) {
2020-05-01 15:08:57 +03:00
case INIT:
case ENCODE:
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* identify and store a run of pixels */
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
if (state->x == 0 || state->x >= state->xsize) {
if (!context->interlace && state->y >= state->ysize) {
state->state = ENCODE_EOF;
break;
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
if (context->flush) {
state->state = FLUSH;
break;
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* get another line of data */
state->shuffle(
state->buffer,
2021-01-03 06:17:51 +03:00
(UINT8 *)im->image[state->y + state->yoff] +
state->xoff * im->pixelsize,
state->xsize);
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
state->x = 0;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
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;
2010-07-31 06:52:47 +04:00
}
2020-05-01 15:08:57 +03:00
/* 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;
}
}
/* Potential special case for xsize==1 */
if (state->x < state->xsize) {
this = state->buffer[state->x++];
} else {
EMIT_RUN(label0);
break;
}
2020-05-01 15:08:57 +03:00
2020-05-10 12:56:36 +03:00
if (this == context->last) {
2020-05-01 15:08:57 +03:00
context->count++;
2020-05-10 12:56:36 +03:00
} else {
2020-05-01 15:08:57 +03:00
EMIT_RUN(label1);
context->last = this;
context->count = 1;
}
break;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
case ENCODE_EOF:
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* write the final run */
EMIT_RUN(label2);
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* write an end of image marker */
EMIT(EOF_CODE);
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* empty the bit buffer */
while (context->bitcount > 0) {
2021-01-03 06:17:51 +03:00
if (!emit(context, (UINT8)context->bitbuffer)) {
2020-05-01 15:08:57 +03:00
state->errcode = IMAGING_CODEC_MEMORY;
return 0;
}
context->bitbuffer >>= 8;
context->bitcount -= 8;
2010-07-31 06:52:47 +04:00
}
2020-05-01 15:08:57 +03:00
/* flush the last block, and exit */
if (context->block) {
2021-01-03 06:17:51 +03:00
GIFENCODERBLOCK *block;
2020-05-01 15:08:57 +03:00
block = context->flush;
2020-05-10 12:56:36 +03:00
while (block && block->next) {
2020-05-01 15:08:57 +03:00
block = block->next;
2020-05-10 12:56:36 +03:00
}
if (block) {
2020-05-01 15:08:57 +03:00
block->next = context->block;
2020-05-10 12:56:36 +03:00
} else {
2020-05-01 15:08:57 +03:00
context->flush = context->block;
2020-05-10 12:56:36 +03:00
}
2020-05-01 15:08:57 +03:00
context->block = NULL;
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
state->state = EXIT;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* fall through... */
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
case EXIT:
case FLUSH:
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
while (context->flush) {
/* get a block from the flush queue */
block = context->flush;
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
if (block->size > 0) {
/* make sure it fits into the output buffer */
if (bytes < block->size + 1) {
return ptr - buf;
2020-05-01 15:08:57 +03:00
}
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
ptr[0] = block->size;
memcpy(ptr + 1, block->data, block->size);
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
ptr += block->size + 1;
bytes -= block->size + 1;
}
context->flush = block->next;
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
if (context->free) {
free(context->free);
2020-05-01 15:08:57 +03:00
}
2021-01-03 06:17:51 +03:00
context->free = block;
}
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
if (state->state == EXIT) {
/* this was the last block! */
if (context->free) {
free(context->free);
2020-05-01 15:08:57 +03:00
}
2021-01-03 06:17:51 +03:00
state->errcode = IMAGING_CODEC_END;
return ptr - buf;
}
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
state->state = ENCODE;
break;
}
2010-07-31 06:52:47 +04:00
}