/* * The Python Imaging Library. * $Id$ * * a fast, suspendable GIF decoder * * history: * 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 * * 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 #include /* memcpy() */ #include "Gif.h" #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;\ } int ImagingGifDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes) { UINT8* p; UINT8* out; int c, i; int thiscode; GIFDECODERSTATE *context = (GIFDECODERSTATE*) state->context; UINT8 *ptr = buffer; if (!state->state) { /* Initialise state */ if (context->bits < 0 || context->bits > 8) { state->errcode = IMAGING_CODEC_CONFIG; return -1; } /* Clear code */ context->clear = 1 << context->bits; /* End code */ context->end = context->clear + 1; /* Interlace */ if (context->interlace) { context->interlace = 1; context->step = context->repeat = 8; } else context->step = 1; state->state = 1; } out = im->image8[state->y + state->yoff] + state->xoff + state->x; for (;;) { if (state->state == 1) { /* First free entry in table */ context->next = context->clear + 2; /* Initial code size */ context->codesize = context->bits + 1; context->codemask = (1 << context->codesize) - 1; /* Buffer pointer. We fill the buffer from right, which allows us to return all of it in one operation. */ context->bufferindex = GIFBUFFER; state->state = 2; } if (context->bufferindex < GIFBUFFER) { /* Return whole buffer in one chunk */ i = GIFBUFFER - context->bufferindex; p = &context->buffer[context->bufferindex]; context->bufferindex = GIFBUFFER; } else { /* Get current symbol */ while (context->bitcount < context->codesize) { if (context->blocksize > 0) { /* Read next byte */ c = *ptr++; bytes--; context->blocksize--; /* New bits are shifted in from from the left. */ context->bitbuffer |= (INT32) c << context->bitcount; context->bitcount += 8; } else { /* New GIF block */ /* We don't start decoding unless we have a full block */ if (bytes < 1) return ptr - buffer; c = *ptr; if (bytes < c+1) return ptr - buffer; context->blocksize = c; ptr++; bytes--; } } /* Extract current symbol from bit buffer. */ c = (int) context->bitbuffer & context->codemask; /* Adjust buffer */ context->bitbuffer >>= context->codesize; context->bitcount -= context->codesize; /* 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. */ if (c == context->clear) { if (state->state != 2) state->state = 1; continue; } if (c == context->end) break; i = 1; p = &context->lastdata; if (state->state == 2) { /* First valid symbol after clear; use as is */ if (c > context->clear) { state->errcode = IMAGING_CODEC_BROKEN; return -1; } context->lastdata = context->lastcode = c; state->state = 3; } else { thiscode = c; if (c > context->next) { state->errcode = IMAGING_CODEC_BROKEN; return -1; } if (c == context->next) { /* c == next is allowed. not sure why. */ if (context->bufferindex <= 0) { state->errcode = IMAGING_CODEC_BROKEN; return -1; } context->buffer[--context->bufferindex] = context->lastdata; c = context->lastcode; } while (c >= context->clear) { /* Copy data string to buffer (beginning from right) */ if (context->bufferindex <= 0 || c >= GIFTABLE) { state->errcode = IMAGING_CODEC_BROKEN; return -1; } context->buffer[--context->bufferindex] = context->data[c]; c = context->link[c]; } context->lastdata = c; if (context->next < GIFTABLE) { /* 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; if (context->next == context->codemask && context->codesize < GIFBITS) { /* Expand code size */ context->codesize++; context->codemask = (1 << context->codesize) - 1; } context->next++; } context->lastcode = thiscode; } } /* Copy the bytes into the image */ if (state->y >= state->ysize) { state->errcode = IMAGING_CODEC_OVERRUN; return -1; } /* To squeeze some extra pixels out of this loop, we test for some common cases and handle them separately. */ /* FIXME: should we handle the transparency index in here??? */ 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; } /* No shortcut, copy pixel by pixel */ for (c = 0; c < i; c++) { *out++ = p[c]; if (++state->x >= state->xsize) { NEWLINE(state, context); } } } return ptr - buffer; }