/*
 * 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 <stdio.h>
#include <memory.h> /* 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, Py_ssize_t 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 > 12) {
            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 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 the advice, 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. */

        /* 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);
                }
                continue;
            }
        }

        /* No shortcut, copy pixel by pixel */
        for (c = 0; c < i; c++) {
            if (p[c] != context->transparency) {
                *out = p[c];
            }
            out++;
            if (++state->x >= state->xsize) {
                NEWLINE(state, context);
            }
        }
    }

    return ptr - buffer;
}
