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"
|
|
|
|
|
2021-01-03 06:17:51 +03:00
|
|
|
#define NEWLINE(state, context) \
|
|
|
|
{ \
|
|
|
|
state->x = 0; \
|
|
|
|
state->y += context->step; \
|
|
|
|
while (state->y >= state->ysize) 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; \
|
|
|
|
} \
|
|
|
|
if (state->y < state->ysize) { \
|
|
|
|
out = im->image8[state->y + state->yoff] + state->xoff; \
|
|
|
|
} \
|
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
int
|
2021-01-03 06:17:51 +03:00
|
|
|
ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8 *buffer, Py_ssize_t bytes) {
|
|
|
|
UINT8 *p;
|
|
|
|
UINT8 *out;
|
2010-07-31 06:52:47 +04:00
|
|
|
int c, i;
|
|
|
|
int thiscode;
|
2021-01-03 06:17:51 +03:00
|
|
|
GIFDECODERSTATE *context = (GIFDECODERSTATE *)state->context;
|
2010-07-31 06:52:47 +04:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2013-07-01 02:42:19 +04:00
|
|
|
|
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) {
|
|
|
|
/* 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) {
|
|
|
|
/* 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 {
|
|
|
|
/* Get current symbol */
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2020-05-01 15:08:57 +03:00
|
|
|
while (context->bitcount < context->codesize) {
|
|
|
|
if (context->blocksize > 0) {
|
|
|
|
/* Read next byte */
|
2021-01-03 06:17:51 +03:00
|
|
|
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. */
|
2021-01-03 06:17:51 +03:00
|
|
|
context->bitbuffer |= (INT32)c << context->bitcount;
|
2020-05-01 15:08:57 +03:00
|
|
|
context->bitcount += 8;
|
2010-07-31 06:52:47 +04:00
|
|
|
|
2020-05-01 15:08:57 +03:00
|
|
|
} else {
|
|
|
|
/* 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;
|
2021-01-03 06:17:51 +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
|
|
|
|
2021-01-03 06:17:51 +03:00
|
|
|
ptr++;
|
|
|
|
bytes--;
|
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. */
|
2021-01-03 06:17:51 +03:00
|
|
|
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) {
|
|
|
|
/* 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 {
|
|
|
|
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) {
|
|
|
|
/* 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
|
|
|
|
2021-01-03 06:17:51 +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
|
|
|
while (c >= context->clear) {
|
|
|
|
/* 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
|
|
|
|
2021-01-03 06:17:51 +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) {
|
|
|
|
/* We'll only add this symbol if we have room
|
2021-03-16 14:19:48 +03:00
|
|
|
for it (take the advice, Netscape!) */
|
2020-05-01 15:08:57 +03:00
|
|
|
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 &&
|
2021-01-03 06:17:51 +03:00
|
|
|
context->codesize < GIFBITS) {
|
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
|
|
|
context->lastcode = thiscode;
|
|
|
|
}
|
|
|
|
}
|
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
|
|
|
|
2022-03-22 12:28:49 +03:00
|
|
|
/* This cannot be used if there is transparency */
|
|
|
|
if (context->transparency == -1) {
|
|
|
|
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);
|
|
|
|
}
|
2020-05-01 15:08:57 +03:00
|
|
|
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++) {
|
2022-03-22 12:28:49 +03:00
|
|
|
if (p[c] != context->transparency) {
|
|
|
|
*out = p[c];
|
|
|
|
}
|
|
|
|
out++;
|
2020-05-01 15:08:57 +03:00
|
|
|
if (++state->x >= state->xsize) {
|
|
|
|
NEWLINE(state, context);
|
|
|
|
}
|
|
|
|
}
|
2010-07-31 06:52:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return ptr - buffer;
|
|
|
|
}
|