/*
 * The Python Imaging Library.
 * $Id$
 *
 * encoder for PCX data
 *
 * history:
 * 99-02-07 fl	created
 *
 * Copyright (c) Fredrik Lundh 1999.
 * Copyright (c) Secret Labs AB 1999.
 *
 * See the README file for information on usage and redistribution.
 */


#include "Imaging.h"

enum { INIT, FETCH, ENCODE };

/* we're reusing "ystep" to store the last value */
#define LAST ystep

int
ImagingPcxEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
{
    UINT8* ptr;
    int this;

    ptr = buf;

    if (!state->state) {

        /* sanity check */
        if (state->xsize <= 0 || state->ysize <= 0) {
            state->errcode = IMAGING_CODEC_END;
            return 0;
        }

	state->bytes = (state->xsize*state->bits + 7) / 8;
        state->state = FETCH;

    }

    for (;;)

        switch (state->state) {
        case FETCH:

            /* get a line of data */
            if (state->y >= state->ysize) {
                state->errcode = IMAGING_CODEC_END;
                return ptr - buf;
            }

            state->shuffle(state->buffer,
                           (UINT8*) im->image[state->y + state->yoff] +
                           state->xoff * im->pixelsize, state->xsize);

            state->y++;

            state->count = 1;
            state->LAST = state->buffer[0];

            state->x = 1;

            state->state = ENCODE;
            /* fall through */

        case ENCODE:

            /* compress this line */

            /* when we arrive here, "count" contains the number of
               bytes having the value of "LAST" that we've already
               seen */

            while (state->x < state->bytes) {

                if (state->count == 63) {

                    /* this run is full; flush it */
                    if (bytes < 2)
                        return ptr - buf;
                    *ptr++ = 0xff;
                    *ptr++ = state->LAST;
                    bytes -= 2;

                    state->count = 0;

                }

                this = state->buffer[state->x];

                if (this == state->LAST) {

                    /* extend the current run */
                    state->x++;
                    state->count++;

                } else {

                    /* start a new run */
                    if (state->count == 1 && (state->LAST < 0xc0)) {
                        if (bytes < 1)
                            return ptr - buf;
                        *ptr++ = state->LAST;
                        bytes--;
                    } else {
                        if (state->count > 0) {
                            if (bytes < 2)
                                return ptr - buf;
                            *ptr++ = 0xc0 | state->count;
                            *ptr++ = state->LAST;
                            bytes -= 2;
                        }
                    }

                    state->LAST = this;
                    state->count = 1;

                    state->x++;

                }
            }

            /* end of line; flush the current run */
            if (state->count == 1 && (state->LAST < 0xc0)) {
                if (bytes < 1)
                    return ptr - buf;
                *ptr++ = state->LAST;
                bytes--;
            } else {
                if (state->count > 0) {
                    if (bytes < 2)
                        return ptr - buf;
                    *ptr++ = 0xc0 | state->count;
                    *ptr++ = state->LAST;
                    bytes -= 2;
                }
            }

            /* read next line */
            state->state = FETCH;
            break;

        }
}