/* $Xorg: dfaxg42d.c,v 1.3 2000/08/17 19:46:40 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.
 */
/* $XFree86: xc/lib/lbxutil/image/dfaxg42d.c,v 1.5 2001/01/17 19:43:35 dawes Exp $ */

#include <X11/Xos.h>
#include <X11/Xfuncproto.h>
#include <X11/Xfuncs.h>
#include <stdlib.h>
#include "g3states.h"
#include "lbxfax.h"
#include "lbximage.h"

/*
 * -------------------------------------------------------------------------
 *              FAX G42D decoding for 1 bit images
 * -------------------------------------------------------------------------
 */

static short sp_data, sp_bit;


/*
 * Fetch a byte from the input stream
 */

static unsigned char
fetchByte (unsigned char **inbuf)

{
    unsigned char byte = **inbuf;
    (*inbuf)++;
    return (byte);
}


/*
 * Decode a run of white.
 */

static int
decode_white_run (unsigned char **inbuf)

{
    short state = sp_bit;
    short action;
    int runlen = 0;

    for (;;)
    {
	if (sp_bit == 0)
	{
	nextbyte:
	    sp_data = fetchByte (inbuf);
	}

	action = TIFFFax1DAction[state][sp_data];
	state = TIFFFax1DNextState[state][sp_data];
	if (action == ACT_INCOMP)
	    goto nextbyte;
	if (action == ACT_INVALID)
	    return (G3CODE_INVALID);
	if (action == ACT_EOL)
	    return (G3CODE_EOL);
	sp_bit = state;
	action = RUNLENGTH(action - ACT_WRUNT);
	runlen += action;
	if (action < 64)
	    return (runlen);
    }
}


/*
 * Decode a run of black.
 */

static int
decode_black_run (unsigned char **inbuf)

{
    short state = sp_bit + 8;
    short action;
    int runlen = 0;

    for (;;)
    {
	if (sp_bit == 0)
	{
	nextbyte:
	    sp_data = fetchByte (inbuf);
	}

	action = TIFFFax1DAction[state][sp_data];
	state = TIFFFax1DNextState[state][sp_data];
	if (action == ACT_INCOMP)
	    goto nextbyte;
	if (action == ACT_INVALID)
	    return (G3CODE_INVALID);
	if (action == ACT_EOL)
	    return (G3CODE_EOL);
	sp_bit = state;
	action = RUNLENGTH(action - ACT_BRUNT);
	runlen += action;
	if (action < 64)
	    return (runlen);
	state += 8;
    }
}


/*
 * Return the next uncompressed mode code word.
 */

static int
decode_uncomp_code (unsigned char **inbuf)

{
    short code;

    do {
	if (sp_bit == 0 || sp_bit > 7)
	    sp_data = fetchByte (inbuf);

	code = TIFFFaxUncompAction[sp_bit][sp_data];
	sp_bit = TIFFFaxUncompNextState[sp_bit][sp_data];
    } while (code == ACT_INCOMP);

    return (code);
}


/*
 * Fill a span with ones.
 */

static void
fillspan (char *cp,
	  int x, int count)

{
    static unsigned char masks[] =
        { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };

    if (count <= 0)
	return;

    cp += x>>3;

    if (x &= 7)
    {
	/* align to byte boundary */

	if (count < 8 - x) {
	    *cp++ |= masks[count] >> x;
	    return;
	}

	*cp++ |= 0xff >> x;
	count -= 8 - x;
    }

    while (count >= 8)
    {
	*cp++ = (char)0xff;
	count -= 8;
    }

    *cp |= masks[count];
}


/*
 * Return the next bit in the input stream.  This is
 * used to extract 2D tag values and the color tag
 * at the end of a terminating uncompressed data code.
 */

static int
nextbit (unsigned char **inbuf)

{
    static unsigned char bitMask[8] =
        { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
    int bit;

    if (sp_bit == 0)
	sp_data = fetchByte (inbuf);

    bit = sp_data & bitMask[sp_bit];

    if (++(sp_bit) > 7)
	sp_bit = 0;

    return (bit);
}


static int
DecodeFaxG42D (unsigned char **inbuf,
	       unsigned char *refline,
	       int pixels_per_line,
	       unsigned char *outbuf)

{
    int a0 = -1;
    int b1, b2;
    int run1, run2;		/* for horizontal mode */
    short mode;
    short color = 1;

    do {
	if (sp_bit == 0 || sp_bit > 7)
	    sp_data = fetchByte (inbuf);

	mode = TIFFFax2DMode[sp_bit][sp_data];
	sp_bit = TIFFFax2DNextState[sp_bit][sp_data];

	switch (mode)
	{
	case MODE_NULL:
	    break;

	case MODE_PASS:
	    b2 = LbxImageFindDiff (refline, a0, pixels_per_line, !color);
	    b1 = LbxImageFindDiff (refline, b2, pixels_per_line, color);
	    b2 = LbxImageFindDiff (refline, b1, pixels_per_line, !color);

	    if (color)
	    {
		if (a0 < 0)
		    a0 = 0;
		fillspan ((char *) outbuf, a0, b2 - a0);
	    }

	    a0 = b2;
	    break;

	case MODE_HORIZ:
	    if (color == 1)
	    {
		run1 = decode_white_run (inbuf);
		run2 = decode_black_run (inbuf);
	    }
	    else
	    {
		run1 = decode_black_run (inbuf);
		run2 = decode_white_run (inbuf);
	    }

	    /*
	     * Do the appropriate fill.  Note that we exit this logic with
	     * the same color that we enter with since we do 2 fills.  This
	     * explains the somewhat obscure logic below.
	     */

	    if (a0 < 0)
		a0 = 0;
	    if (a0 + run1 > pixels_per_line)
		run1 = pixels_per_line - a0;
	    if (color)
		fillspan ((char *) outbuf, a0, run1);
	    a0 += run1;
	    if (a0 + run2 > pixels_per_line)
		run2 = pixels_per_line - a0;
	    if (!color)
		fillspan ((char *) outbuf, a0, run2);
	    a0 += run2;
	    break;

	case MODE_VERT_V0:
	case MODE_VERT_VR1:
	case MODE_VERT_VR2:
	case MODE_VERT_VR3:
	case MODE_VERT_VL1:
	case MODE_VERT_VL2:
	case MODE_VERT_VL3:
	    b2 = LbxImageFindDiff (refline, a0, pixels_per_line, !color);
	    b1 = LbxImageFindDiff (refline, b2, pixels_per_line, color);
	    b1 += mode - MODE_VERT_V0;

	    if (color)
	    {
		if (a0 < 0)
		    a0 = 0;
		fillspan ((char *) outbuf, a0, b1 - a0);
	    }

	    color = !color;
	    a0 = b1;
	    break;

	case MODE_UNCOMP:
	    /*
	     * Uncompressed mode: select from the special set of code words.
	     */

	    if (a0 < 0)
		a0 = 0;
	    do
	    {
		mode = decode_uncomp_code (inbuf);
		switch (mode)
		{
		case UNCOMP_RUN1:
		case UNCOMP_RUN2:
		case UNCOMP_RUN3:
		case UNCOMP_RUN4:
		case UNCOMP_RUN5:
		    run1 = mode - UNCOMP_RUN0;
		    fillspan ((char *) outbuf, a0+run1-1, 1);
		    a0 += run1;
		    break;

		case UNCOMP_RUN6:
		    a0 += 5;
		    break;

		case UNCOMP_TRUN0:
		case UNCOMP_TRUN1:
		case UNCOMP_TRUN2:
		case UNCOMP_TRUN3:
		case UNCOMP_TRUN4:

		    run1 = mode - UNCOMP_TRUN0;
		    a0 += run1;
		    color = nextbit (inbuf) ? 0 : 1;
		    break;

		case UNCOMP_INVALID:
		    goto bad;

		case UNCOMP_EOF:
		    return (0);
		}
	    } while (mode < UNCOMP_EXIT);
	    break;

	case MODE_ERROR_1:
	    /* fall thru... */
	case MODE_ERROR:
	    goto bad;

	default:
	    return (0);
	}

    } while (a0 < pixels_per_line);

bad:
    return (a0 >= pixels_per_line);
}


int
LbxImageDecodeFaxG42D (unsigned char *inbuf,
		       unsigned char *outbuf,
		       int image_bytes,
		       int pixels_per_line,
		       int padded_bytes_per_scanline,
		       int reverse_bits)

{
    int bytes_per_scanline = ROUNDUP8 (pixels_per_line);
    unsigned char *refline, *refptr;
    unsigned char *outbuf_start = outbuf;
    int bytes_left = image_bytes;
    int i;

    refline = (unsigned char *) malloc (bytes_per_scanline + 1);
    refptr = refline + 1;

    for (i = 0; i < bytes_per_scanline + 1; i++)
	refline[i] = 0xff;

    bzero (outbuf, image_bytes);

    sp_bit = 0;
    sp_data = 0;

    while (bytes_left > 0)
    {
	if (!DecodeFaxG42D (&inbuf, refptr, pixels_per_line, outbuf))
	    return (0);

	memcpy (refptr, outbuf, bytes_per_scanline);

	outbuf += padded_bytes_per_scanline;
	bytes_left -= padded_bytes_per_scanline;
    }

    free ((char *) refline);

    if (reverse_bits)
	LbxReverseBits (outbuf_start, image_bytes);

    return (outbuf - outbuf_start);
}
