/* Copyright (C) 1992 Aladdin Enterprises.  All rights reserved.

This file is part of Ghostscript.

Ghostscript is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
to anyone for the consequences of using it or for whether it serves any
particular purpose or works at all, unless he says so in writing.  Refer
to the Ghostscript General Public License for full details.

Everyone is granted permission to copy, modify and redistribute
Ghostscript, but only under the conditions described in the Ghostscript
General Public License.  A copy of this license is supposed to have been
given to you along with Ghostscript so you can know your rights and
responsibilities.  It should be in a file named COPYING.  Among other
things, the copyright notice and this notice must be preserved on all
copies.  */

/* scfe.c */
/* CCITTFax encoding filter */
#include "stdio_.h"	/* includes std.h */
#include "memory_.h"
#include "gdebug.h"
#include "scf.h"
#include "stream.h"
#include "sbits.h"

/* Imported procedures */
extern int s_filter_write_flush(P1(stream *));

/* ------ Macros and support routines ------ */

/* Put a code onto the target stream. */
/* Relevant invariants: 0 <= bits_left <= bits_size; only the leftmost */
/* (bits_size - bits_left) bits of bits contain valid data. */

#define bits_size (arch_sizeof_int == 2 ? 16 : 32)

#ifdef DEBUG
#  define p_c(rp)\
    (gs_debug['w'] ?\
     (dprintf2("[w]0x%x,%d\n", (rp)->code, (rp)->code_length), 0) : 0)
#else
#  define p_c(rp) 0
#endif

#define cf_put_code(s, rp)\
  (p_c(rp),\
   ((s->bits_left -= (rp)->code_length) >= 0 ?\
    s->bits += (rp)->code << s->bits_left :\
    cf_put_code_out(s, (rp)->code)))

private uint
cf_put_code_out(register stream *s, uint code)
{	int left = s->bits_left;
	const uint cw = s->bits + (code >> -left);
	stream *strm = s->strm;
#if bits_size > 16
#  define cb3 ((byte)(cw >> 24))
#  define cb2 ((byte)(cw >> 16))
#endif
#define cb1 ((byte)(cw >> 8))
#define cb0 ((byte)cw)
	if ( s->reverse_bits )
	{
#if bits_size > 16
		sputc(strm, sbits_reverse_bits[cb3]);
		sputc(strm, sbits_reverse_bits[cb2]);
#endif
		sputc(strm, sbits_reverse_bits[cb1]);
		sputc(strm, sbits_reverse_bits[cb0]);
	}
	else
	{
#if bits_size > 16
		sputc(strm, cb3);
		sputc(strm, cb2);
#  undef cb3
#  undef cb2
#endif
		sputc(strm, cb1);
		sputc(strm, cb0);
	}
#undef cb1
#undef cb0
	return (s->bits = code << (s->bits_left = left + bits_size));
}

/* Put a run onto the output stream. */

#define cf_put_run(s, lenv, tt, mut)\
{	const cfe_run _ds *rp;\
	if ( lenv >= 64 )\
	{	rp = &mut[lenv >> 6];\
		cf_put_code(s, rp);\
		lenv &= 63;\
	}\
	rp = &tt[lenv];\
	cf_put_code(s, rp);\
}

#define cf_put_white_run(s, lenv)\
  cf_put_run(s, lenv, cf_white_termination, cf_white_make_up)

#define cf_put_black_run(s, lenv)\
  cf_put_run(s, lenv, cf_black_termination, cf_black_make_up)

/* ------ Stream procedures ------ */
	  
/*
 * For the 2-D encoding modes, we leave the previous complete scan line
 * at the beginning of the buffer, and start the new data after it.
 */

/* Initialize CCITTFaxEncode filter */
void
s_CFE_init(register stream *s, CCITTFax_state *pcfs)
{	s->state.cf = *pcfs;
	if ( s->state.cf.K != 0 )
	{	/* Clear the initial reference line for 2-D encoding. */
		s->cptr = s->cbuf + s->state.cf.raster - 1;
		memset(s->cbuf, (s->state.cf.BlackIs1 ? 0xff : 0), s->state.cf.raster);
		s->state.cf.k_left = -1;
	}
	s->state.cf.k_left = max(s->state.cf.K, 0);
	s->bits = 0;
	s->bits_left = bits_size;
}

/* Flush the buffer */
private int cf_encode_1d(P2(stream *, byte *));
private int cf_encode_2d(P3(stream *, byte *, byte *));
private int
s_CFE_write_buf(register stream *s)
{	int raster = s->state.cf.raster;
	byte *prev = s->cbuf;
	byte *p = (s->state.cf.K ? prev + raster : prev);
	byte *limit = s->cptr;
	byte end_mask = 1 << (-s->state.cf.Columns & 7);
	uint count;
	/* Do complete scan lines. */
	while ( (count = limit - p + 1) >= raster )
	{	/* Ensure that the scan line ends with a polarity change. */
		/* This may involve saving and restoring one byte beyond */
		/* the scan line. */
		byte *next = p + raster;
		byte save_next = *next;
		int code;
		if ( end_mask == 1 )		/* set following byte */
			*next = (next[-1] & 1) - 1;
		else if ( next[-1] & end_mask )	/* clear lower bits */
			next[-1] &= -end_mask;
		else				/* set lower bits */
			next[-1] |= end_mask - 1;
		if ( s->state.cf.EncodedByteAlign )
			s->bits_left &= ~7;
		if ( s->state.cf.K > 0 )
		{	/* Group 3, mixed encoding */
			if ( --(s->state.cf.k_left) )
			{	/* Use 2-D encoding */
				if ( s->state.cf.EndOfLine )
					cf_put_code(s, &cf2_run_eol_2d);
				code = cf_encode_2d(s, p, prev);
			}
			else
			{	/* Use 1-D encoding */
				if ( s->state.cf.EndOfLine )
					cf_put_code(s, &cf2_run_eol_1d);
				code = cf_encode_1d(s, p);
				s->state.cf.k_left = s->state.cf.K;
			}
		}
		else
		{	/* Uniform encoding */
			if ( s->state.cf.EndOfLine )
				cf_put_code(s, &cf_run_eol);
			code = (s->state.cf.K == 0 ? cf_encode_1d(s, p) :
				cf_encode_2d(s, p, prev));
		}
		*next = save_next;
		if ( code )
		{	s->end_status = code;
			break;
		}
		prev = p;
		p = next;
	}
	/* Move any remaining partial scan line (and, if 2-D encoding */
	/* is a possibility, the last full scan line) to the beginning */
	/* of the buffer. */
	if ( s->state.cf.K )
		count += raster, p -= raster;
	memcpy(s->cbuf, p, count);
	s->cptr = s->cbuf - 1 + count;
	return 0;
}

/* Encode a 1-D scan line. */
private int
cf_encode_1d(stream *s, register byte *p)
{	byte invert = (s->state.cf.BlackIs1 ? 0 : 0xff);
	register uint count;
	uint end_count = s->state.cf.Columns & 7;
	register uint data = *p++ ^ invert;
	for ( count = s->state.cf.raster << 3; count != end_count; )
	{	int white, black;
		/* Parse a white run. */
		white = count;
		skip_white_pixels(data, p, count, invert, w0);
		white -= count;
		cf_put_white_run(s, white);
		if ( count == end_count ) break;
		/* Parse a black run. */
		black = count;
		skip_black_pixels(data, p, count, invert, b0, b1);
		black -= count;
		cf_put_black_run(s, black);
	}
	return 0;
}

/* Encode a 2-D scan line. */
private int
cf_encode_2d(stream *s, byte *p, byte *prev_p)
{	byte invert = (s->state.cf.BlackIs1 ? 0 : 0xff);
	byte invert_white = invert;
	register uint count;
	uint end_count = s->state.cf.Columns & 7;
	register uint data = *p++ ^ invert;
	int dist = prev_p - p;
	for ( count = s->state.cf.raster << 3; count != end_count; )
	{	/* If invert == invert_white, white and black have their */
		/* correct meanings; if invert == ~invert_white, */
		/* black and white are interchanged. */
		uint a0, a1, b1;
		uint prev_count = count;
		byte prev_data;
		int diff;
		static const byte count_bit[8] =
			{ 0x80, 1, 2, 4, 8, 0x10, 0x20, 0x40 };
		prev_p = p + dist;
		prev_data = prev_p[-1] ^ invert;
		/* Find the a1 and b1 transitions. */
		a0 = count;
		skip_white_pixels(data, p, count, invert, w00);
		a1 = count;
		if ( (prev_data & count_bit[prev_count & 7]) )
		{	/* Look for changing white first. */
			skip_black_pixels(prev_data, prev_p, prev_count, invert, b01, b11);
		}
		if ( prev_count != end_count )
		{	skip_white_pixels(prev_data, prev_p, prev_count, invert, w01);
		}
		b1 = prev_count;
		/* In all the comparisons below, remember that count */
		/* runs downward, not upward, so the comparisons are */
		/* reversed. */
		if ( b1 >= a1 + 2 )
		{	/* Could be a pass mode.  Find b2. */
			if ( prev_count != end_count )
			{	skip_black_pixels(prev_data, prev_p,
						 prev_count, invert, b02, b12);
			}
			if ( prev_count > a1 )
			{	/* Use pass mode. */
				cf_put_code(s, &cf2_run_pass);
				count = prev_count;
				p = prev_p - dist;
				data = p[-1] ^ invert;
				continue;
			}
		}
		/* Check for vertical coding. */
		diff = a1 - b1;		/* i.e., logical b1 - a1 */
		if ( diff <= 3 && diff >= -3 )
		{	/* Use vertical coding. */
			cf_put_code(s, &cf2_run_vertical[diff + 3]);
			invert = ~invert;	/* a1 polarity changes */
			continue;
		}
		/* No luck, use horizontal coding. */
		cf_put_code(s, &cf2_run_horizontal);
		if ( count != end_count )
		{	skip_black_pixels(data, p, count, invert, b03, b13);	/* find a2 */
		}
		a0 -= a1;
		a1 -= count;
		if ( invert == invert_white )
		{	cf_put_white_run(s, a0);
			cf_put_black_run(s, a1);
		}
		else
		{	cf_put_black_run(s, a0);
			cf_put_white_run(s, a1);
		}
	}
	return 0;
}

/* Close the stream */
private int
s_CFE_close(register stream *s)
{	int code = s_CFE_write_buf(s);
	if ( code == ERRC ) return code;
	if ( s->state.cf.EndOfBlock )
	{	int i = (s->state.cf.K < 0 ? 2 : 6);
		const cfe_run _ds *rp =
			(s->state.cf.K > 0 ? &cf2_run_eol_1d : &cf_run_eol);
		while ( --i >= 0 )
			cf_put_code(s, rp);
	}
	/* Force out the last byte or bytes. */
	while ( s->bits_left < bits_size )
	{	stream *strm = s->strm;
		byte c = (byte)(s->bits >> (bits_size - 8));
		if ( s->reverse_bits )
			c = sbits_reverse_bits[c];
		sputc(strm, c);
		s->bits <<= 8;
		s->bits_left += 8;
	}
	return s_std_close(s);
}

/* Stream procedures */
const stream_procs s_CFE_procs =
   {	s_std_noavailable, NULL, s_filter_write_flush, s_CFE_close,
	NULL, s_CFE_write_buf
   };
