Pillow/src/libImaging/GifDecode.c

304 lines
8.2 KiB
C
Raw Normal View History

2010-07-31 06:52:47 +04:00
/*
* The Python Imaging Library.
* $Id$
*
* a fast, suspendable GIF decoder
*
* history:
2020-05-01 15:08:57 +03:00
* 95-09-03 fl Created
* 95-09-05 fl Fixed sign problem on 16-bit platforms
* 95-09-13 fl Added some storage shortcuts
* 96-03-28 fl Revised API, integrated with PIL
* 96-12-10 fl Added interlace support
* 96-12-16 fl Fixed premature termination bug introduced by last fix
* 97-01-05 fl Don't mess up on bogus configuration
* 97-01-17 fl Don't mess up on very small, interlaced files
* 99-02-07 fl Minor speedups
2010-07-31 06:52:47 +04:00
*
* Copyright (c) Secret Labs AB 1997-99.
* Copyright (c) Fredrik Lundh 1995-97.
*
* See the README file for information on usage and redistribution.
*/
#include "Imaging.h"
#include <stdio.h>
2020-05-01 15:08:57 +03:00
#include <memory.h> /* memcpy() */
2010-07-31 06:52:47 +04:00
#include "Gif.h"
#define NEWLINE(state, context) {\
state->x = 0;\
state->y += context->step;\
while (state->y >= state->ysize)\
2020-05-01 15:08:57 +03:00
switch (context->interlace) {\
case 1:\
context->repeat = state->y = 4;\
context->interlace = 2;\
break;\
case 2:\
context->step = 4;\
context->repeat = state->y = 2;\
context->interlace = 3;\
break;\
case 3:\
context->step = 2;\
context->repeat = state->y = 1;\
context->interlace = 0;\
break;\
default:\
return -1;\
2010-07-31 06:52:47 +04:00
}\
2020-05-11 00:46:12 +03:00
if (state->y < state->ysize) {\
2010-07-31 06:52:47 +04:00
out = im->image8[state->y + state->yoff] + state->xoff;\
2020-05-11 00:46:12 +03:00
}\
2010-07-31 06:52:47 +04:00
}
int
ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_ssize_t bytes)
2010-07-31 06:52:47 +04:00
{
UINT8* p;
UINT8* out;
int c, i;
int thiscode;
GIFDECODERSTATE *context = (GIFDECODERSTATE*) state->context;
UINT8 *ptr = buffer;
if (!state->state) {
2020-05-11 00:46:12 +03:00
/* Initialise state */
if (context->bits < 0 || context->bits > 12) {
state->errcode = IMAGING_CODEC_CONFIG;
return -1;
}
2020-05-11 00:46:12 +03:00
/* Clear code */
context->clear = 1 << context->bits;
2010-07-31 06:52:47 +04:00
2020-05-11 00:46:12 +03:00
/* End code */
context->end = context->clear + 1;
2010-07-31 06:52:47 +04:00
2020-05-11 00:46:12 +03:00
/* Interlace */
if (context->interlace) {
context->interlace = 1;
context->step = context->repeat = 8;
} else {
context->step = 1;
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
state->state = 1;
2010-07-31 06:52:47 +04:00
}
out = im->image8[state->y + state->yoff] + state->xoff + state->x;
for (;;) {
2020-05-01 15:08:57 +03:00
if (state->state == 1) {
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* First free entry in table */
context->next = context->clear + 2;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* Initial code size */
context->codesize = context->bits + 1;
context->codemask = (1 << context->codesize) - 1;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* Buffer pointer. We fill the buffer from right, which
allows us to return all of it in one operation. */
context->bufferindex = GIFBUFFER;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
state->state = 2;
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
if (context->bufferindex < GIFBUFFER) {
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* Return whole buffer in one chunk */
i = GIFBUFFER - context->bufferindex;
p = &context->buffer[context->bufferindex];
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
context->bufferindex = GIFBUFFER;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
} else {
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* Get current symbol */
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
while (context->bitcount < context->codesize) {
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
if (context->blocksize > 0) {
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* Read next byte */
c = *ptr++; bytes--;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
context->blocksize--;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* New bits are shifted in from from the left. */
context->bitbuffer |= (INT32) c << context->bitcount;
context->bitcount += 8;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
} else {
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* New GIF block */
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* We don't start decoding unless we have a full block */
2020-05-10 12:56:36 +03:00
if (bytes < 1) {
2020-05-01 15:08:57 +03:00
return ptr - buffer;
2020-05-10 12:56:36 +03:00
}
2020-05-01 15:08:57 +03:00
c = *ptr;
2020-05-10 12:56:36 +03:00
if (bytes < c+1) {
2020-05-01 15:08:57 +03:00
return ptr - buffer;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
context->blocksize = c;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
ptr++; bytes--;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
}
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* Extract current symbol from bit buffer. */
c = (int) context->bitbuffer & context->codemask;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* Adjust buffer */
context->bitbuffer >>= context->codesize;
context->bitcount -= context->codesize;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* If c is less than "clear", it's a data byte. Otherwise,
it's either clear/end or a code symbol which should be
expanded. */
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
if (c == context->clear) {
2020-05-10 12:56:36 +03:00
if (state->state != 2) {
2020-05-01 15:08:57 +03:00
state->state = 1;
2020-05-10 12:56:36 +03:00
}
2020-05-01 15:08:57 +03:00
continue;
}
2010-07-31 06:52:47 +04:00
2020-05-10 12:56:36 +03:00
if (c == context->end) {
2020-05-01 15:08:57 +03:00
break;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
i = 1;
p = &context->lastdata;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
if (state->state == 2) {
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* First valid symbol after clear; use as is */
if (c > context->clear) {
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
context->lastdata = context->lastcode = c;
state->state = 3;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
} else {
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
thiscode = c;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
if (c > context->next) {
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
if (c == context->next) {
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* c == next is allowed. not sure why. */
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
if (context->bufferindex <= 0) {
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
context->buffer[--context->bufferindex] =
context->lastdata;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
c = context->lastcode;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
while (c >= context->clear) {
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* Copy data string to buffer (beginning from right) */
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
if (context->bufferindex <= 0 || c >= GIFTABLE) {
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
context->buffer[--context->bufferindex] =
context->data[c];
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
c = context->link[c];
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
context->lastdata = c;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
if (context->next < GIFTABLE) {
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* We'll only add this symbol if we have room
for it (take advise, Netscape!) */
context->data[context->next] = c;
context->link[context->next] = context->lastcode;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
if (context->next == context->codemask &&
context->codesize < GIFBITS) {
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* Expand code size */
context->codesize++;
context->codemask = (1 << context->codesize) - 1;
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
context->next++;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
context->lastcode = thiscode;
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
}
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* Copy the bytes into the image */
if (state->y >= state->ysize) {
state->errcode = IMAGING_CODEC_OVERRUN;
return -1;
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* To squeeze some extra pixels out of this loop, we test for
some common cases and handle them separately. */
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* FIXME: should we handle the transparency index in here??? */
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
if (i == 1) {
if (state->x < state->xsize-1) {
/* Single pixel, not at the end of the line. */
*out++ = p[0];
state->x++;
continue;
}
} else if (state->x + i <= state->xsize) {
/* This string fits into current line. */
memcpy(out, p, i);
out += i;
state->x += i;
if (state->x == state->xsize) {
NEWLINE(state, context);
}
continue;
}
2010-07-31 06:52:47 +04:00
2020-05-01 15:08:57 +03:00
/* No shortcut, copy pixel by pixel */
for (c = 0; c < i; c++) {
*out++ = p[c];
if (++state->x >= state->xsize) {
NEWLINE(state, context);
}
}
2010-07-31 06:52:47 +04:00
}
return ptr - buffer;
}