/* $Xorg: epackbits.c,v 1.4 2000/08/17 19:46:41 cpqbld Exp $ */
/*
 * Copyright (c) 1988, 1989, 1990, 1991, 1992 Sam Leffler
 * Copyright (c) 1991, 1992 Silicon Graphics, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and 
 * its documentation for any purpose is hereby granted without fee, provided
 * that (i) the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation, and (ii) the names of
 * Sam Leffler and Silicon Graphics may not be used in any advertising or
 * publicity relating to the software without the specific, prior written
 * permission of Sam Leffler and Silicon Graphics.
 * 
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
 * 
 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
 * OF THIS SOFTWARE.
 */

#include <X11/Xfuncproto.h>
#include "lbximage.h"

/*
 * -------------------------------------------------------------------------
 *              PackBits encoding for 8 bit color images
 * -------------------------------------------------------------------------
 */

#define PutByte(byte,bufptr,bytesLeft) \
{ \
    if (*bytesLeft < 1) {\
         *outbuf = outptr; \
         return (0); \
    } \
    *bufptr++ = byte; \
    (*bytesLeft)--; \
}
    

static int
EncodePackBits (char *inbuf,
		int numPixels,
		char **outbuf,
		int *bytesLeft)

{
    register int pixelsLeft = numPixels;
    register char *outptr = *outbuf;
    register char *lastliteral;
    register int n, b;
    enum { BASE, LITERAL, RUN, LITERAL_RUN } state;

    state = BASE;
    lastliteral = 0;

    while (pixelsLeft > 0)
    {
	/*
	 * Find the longest string of identical bytes.
	 */

	b = *inbuf++, pixelsLeft--, n = 1;
	for (; pixelsLeft > 0 && b == *inbuf; pixelsLeft--, inbuf++)
	    n++;

    again:

	switch (state)
	{
	case BASE:		/* initial state, set run/literal */
	    if (n > 1)
	    {
		state = RUN;

		if (n > 128)
		{
		    PutByte (-127, outptr, bytesLeft);
		    PutByte (b, outptr, bytesLeft);
		    n -= 128;
		    goto again;
		}

		PutByte (-(n-1), outptr, bytesLeft);
		PutByte (b, outptr, bytesLeft);
	    }
	    else
	    {
		lastliteral = outptr;
		PutByte (0, outptr, bytesLeft);
		PutByte (b, outptr, bytesLeft);
		state = LITERAL;
	    }

	    break;

	case LITERAL:		/* last object was literal string */
	    if (n > 1)
	    {
		state = LITERAL_RUN;

		if (n > 128)
		{
		    PutByte (-127, outptr, bytesLeft);
		    PutByte (b, outptr, bytesLeft);
		    n -= 128;
		    goto again;
		}

		    PutByte (-(n-1), outptr, bytesLeft); /* encode run */
		    PutByte (b, outptr, bytesLeft);
	    }
	    else
	    {			/* extend literal */
		if (++(*lastliteral) == 127)
		    state = BASE;
		PutByte (b, outptr, bytesLeft);
	    }

	    break;

	case RUN:		/* last object was run */
	    if (n > 1)
	    {
		if (n > 128)
		{
		    PutByte (-127, outptr, bytesLeft);
		    PutByte (b, outptr, bytesLeft);
		    n -= 128;
		    goto again;
		}
		PutByte (-(n-1), outptr, bytesLeft);
		PutByte (b, outptr, bytesLeft);
	    }
	    else
	    {
		lastliteral = outptr;
		PutByte (0, outptr, bytesLeft);
		PutByte (b, outptr, bytesLeft);
		state = LITERAL;
	    }

	    break;

	case LITERAL_RUN:	/* literal followed by a run */
	    /*
	     * Check to see if previous run should
	     * be converted to a literal, in which
	     * case we convert literal-run-literal
	     * to a single literal.
	     */

	    if (n == 1 && outptr[-2] == (char)-1 && *lastliteral < 126)
	    {
		state = (((*lastliteral) += 2) == 127 ? BASE : LITERAL);
		outptr[-2] = outptr[-1];	/* replicate */
	    }
	    else
		state = RUN;
	    goto again;
	}
    }

    *outbuf = outptr;

    return (1);
}


int
LbxImageEncodePackBits (char *inbuf,
			char *outbuf,
			int outbufSize,
			int format,
			int depth,
			int num_scan_lines,
			int scan_line_size,
			int *bytesCompressed)

{
    char *outbuf_start = outbuf;
    int padded_scan_line_size = (scan_line_size % 4) ?
	scan_line_size + (4 - scan_line_size % 4) : scan_line_size;
    int bytesLeft = outbufSize;

    while (num_scan_lines > 0)
    {
	if (!EncodePackBits (inbuf, scan_line_size, &outbuf, &bytesLeft))
	    return (LBX_IMAGE_COMPRESS_NOT_WORTH_IT);

	inbuf += padded_scan_line_size;
	num_scan_lines--;
    }

    *bytesCompressed = outbuf - outbuf_start;
    return (LBX_IMAGE_COMPRESS_SUCCESS);
}
