//-----------------------------------------------------------------------------
// Copyright © 2003 - Philip Howard - All rights reserved
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//-----------------------------------------------------------------------------
// package	libh/angif
// homepage	http://libh.slashusr.org/
//-----------------------------------------------------------------------------
// author	Philip Howard
// email	libh at ipal dot org
// homepage	http://phil.ipal.org/
//-----------------------------------------------------------------------------
// This library is "patent free" in that it contains no code to implement
// the LZW compression, which is covered by patent number 4,558,302 owned
// by Unisys.
//
// GIF is a service mark, property of CompuServe, Inc.
//
// The GIF standard is obsolete and is being deprecated in favor of the
// PNG (Portable Network Graphics) standard.
//-----------------------------------------------------------------------------

#include "angif_lib.h"

__PROTO_BEGIN__
//-----------------------------------------------------------------------------
// function	angif_put_index
//
// purpose	Output an index mode image array to a stream target.
//
// arguments	1 (angif_stream *)	stream target
//		2 (angif_image *)	image object
//
// returns	(int) 0 successful completion
//		(int) <0 error indicated by return value and errno
//-----------------------------------------------------------------------------
int
angif_put_index (
    angif_stream *	stream
    ,
    angif_image *	image
    )
__PROTO_END__
{
    //-----------------------------------------------------------------------------
    // This array defines the interlacing starting point and row increment.
    // Starting points are accessed from offset 3 downward to 1.
    // Row increments are accessed from offset 5 downward to 2.
    //-----------------------------------------------------------------------------
    static const unsigned char	interlace_table	[6]	= { 1, 1, 2, 4, 8, 8 };

    unsigned char	buffer	[1024]	;// The buffer where data blocks are built.

    long *		color_ptr	;

    unsigned char *	buf_ptr		;// Points to the current position in the data block buffer.
    unsigned char *	buf_end		;// Points to the end of the data block buffer for comparison.
    unsigned char *	img_end		;// Points to the end of the image data for comparison.
    unsigned char *	row_ptr		;// Points to the beginning of the current row.
    unsigned char *	row_end		;// Points to the end of the current row for comparison.
    unsigned char *	pix_ptr		;// Points to the current pixel being output.

    unsigned long	acc_data	;// Accumulate bits of the output data codes here.

    unsigned int	reset_code	;// The special code assigned to clear the decoder hash.
    unsigned int	end_code	;// The special code assigned to indicate the end of data.
    unsigned int	code_bits	;// The number of bits for codes.
    unsigned int	interlace	;// The current interlace group (4..1) or 0 for non-interlace.
    unsigned int	pixel_mask	;// Bit mask for pixel indexes to limit range to given bits.
    unsigned int	code_limit	;// ??
    unsigned int	code_count	;// ??

    int			acc_bits	;// How many bits are currently in the accum variable.
    int			color_count	;// Count of actual colors in table
    int			color_size	;
    int			ct_size		;// Color table size indicator (0..7 for 2^(n+1))
    int			rc		;


    //-- Check arguments.
    if ( ! stream ) return ANGIF_ERROR_ARG_STREAM;
    if ( ! image  ) return ANGIF_ERROR_ARG_IMAGE;

    //-- Make sure we have index data.
    if ( ! image->data_index ) return ANGIF_ERROR_NO_INDEX;

    //-- Calculate the color table size indicator.
    ct_size = 0;
    if ( image->color_table ) {
	color_count = image->color_count - 1;
	if ( color_count < 1 || color_count > 255 ) return ANGIF_ERROR_COLOR_COUNT;
	while ( (color_count >>= 1) ) ++ ct_size;
    }

    //-- Setup buffer pointer.
    buf_ptr = buffer;

    //-- Output a graphic control extension if needed.
    if ( image->delay_time || image->disposal || image->user_input || image->trans_flag ) {
	* (buf_ptr ++) = 0x21;	// extension introducer
	* (buf_ptr ++) = 0xf9;	// graphic control label
	* (buf_ptr ++) = 0x04;	// block size
	* (buf_ptr ++) = 0 +
	    ( 0x1c & ( image->disposal   << 2 ) ) +
	    ( 0x02 & ( image->user_input << 1 ) ) +
	    ( 0x01 &   image->trans_flag        );
	* (buf_ptr ++) = image->delay_time;
	* (buf_ptr ++) = image->delay_time >> 8;
	* (buf_ptr ++) = image->trans_index;
	* (buf_ptr ++) = 0;	// block terminator
    }

    //-- Output the image descriptor.
    * (buf_ptr ++) = 0x2c;	// image separator
    * (buf_ptr ++) = image->image_left;
    * (buf_ptr ++) = image->image_left >> 8;
    * (buf_ptr ++) = image->image_top;
    * (buf_ptr ++) = image->image_top >> 8;
    * (buf_ptr ++) = image->image_width;
    * (buf_ptr ++) = image->image_width >> 8;
    * (buf_ptr ++) = image->image_height;
    * (buf_ptr ++) = image->image_height >> 8;
    * (buf_ptr ++) =
	  ( image->color_table ? 0x80 : 0x00 ) +
	  ( 0x40 & ( image->interlace << 6 ) ) +
	  ( 0x20 & ( image->ct_sort   << 5 ) ) +
	  ( 0x07 &   ct_size                 );

    //-- If the local color table is provided, output it.
    if ( image->color_table ) {
	color_count = image->color_count;
	color_ptr   = image->color_table;
	color_size  = 2 << ct_size;

	//-- Output as many color table elements as exist.
	while ( color_count -- ) {
	    * (buf_ptr ++) = * color_ptr >> 16;
	    * (buf_ptr ++) = * color_ptr >> 8;
	    * (buf_ptr ++) = * color_ptr;
	    ++ color_ptr;
	    -- color_size;
	}

	//-- Pad the output color table to a power of 2 elements.
	while ( color_size -- ) {
	    * (buf_ptr ++) = 0;
	    * (buf_ptr ++) = 0;
	    * (buf_ptr ++) = 0;
	}
    }

    //-- Flush the buffer.
    if ( (rc = angif_write( stream, buffer, (size_t) ( buf_ptr - buffer ) )) < 0 ) return rc;

    //-----------------------------------------------------------------------------
    // Now we output literal, uncompressed, pixel data codes.
    //-----------------------------------------------------------------------------

    //-- Get number of bits in each code
    code_bits = ct_size + 1;
    if ( code_bits < 2 ) code_bits = 2;

    //-- Calculate the reset code (256 for 8 bits, 64 for 6).
    reset_code = 1 << code_bits;

    //-- Calculate the end code (257 for 8 bits, 65 for 6).
    end_code = reset_code + 1;

    //-- Calculate a mask to extract pixel bits (255 for 8 bits, 63 for 6).
    pixel_mask = reset_code - 1;

    //-- Calculate the limit on number of codes (254 for 8 bits, 62 for 6).
    code_limit = pixel_mask - 1;

    //-- Setup pointers for indexing the image data.
    row_ptr = image->data_index;
    pix_ptr = row_ptr;
    img_end = row_ptr + ( image->image_width * image->image_height );
    row_end = row_ptr + image->image_width;

    //-- Start the interlacing index if interlacing is enabled.
    interlace = image->interlace ? 4 : 0;

    //-- Initialize the code counter to force an initial clear code.
    code_count = 1;

    //-- Output the minimum code length.
    buffer[0] = code_bits;
    angif_write( stream, buffer, 1 );

    //-- Need an extra bit for reset code and end code.
    ++ code_bits;

    //-- Ready the data accumulator empty.
    acc_data = 0;
    acc_bits = 0;

    //-- Initialize the output buffer to empty, leaving space for the length code.
    buf_ptr = buffer + 1;
    buf_end = buffer + 256;


    //-----------------------------------------------------------------------------
    // Do main loop through all pixels and rows.
    //-----------------------------------------------------------------------------
    for (;;) {
	//-- Count codes and check for a needed reset.
	if ( (-- code_count) == 0 ) {
	    //-- Append reset code to accumulator.
	    acc_data |= reset_code << acc_bits;
	    acc_bits += code_bits;
	    code_count = code_limit;
	}

	//-- Append current pixel into accumulator.
	acc_data |= ( pixel_mask & (unsigned long) (* pix_ptr) ) << acc_bits;
	acc_bits += code_bits;

	//-- Flush accumulator of whole octets.
	while ( acc_bits >= 8 ) {
	    //-- Store one octet from acculator and remove it.
	    * (buf_ptr ++) = acc_data;
	    acc_data >>= 8;
	    acc_bits -= 8;

	    //-- If buffer is full, output it.
	    if ( buf_ptr == buf_end ) {
		* buffer = (unsigned int) 255;
		if ( (rc = angif_write( stream, buffer, (size_t) 256 )) < 0 ) return rc;
		buf_ptr = buffer + 1;
	    }
	}

	//-- Check for end of row.
	if ( (++ pix_ptr) == row_end ) {
	    //-- Get next row, considering possible interlacing.
	    row_ptr += image->image_width * interlace_table[ 1 + interlace ];

	    //-- Check for end of image.
	    if ( row_ptr >= img_end ) {
		//-- If no interlacing or end of interlacing, break out.
		if ( interlace < 2 ) break;

		//-- Restart image with different interlacing.
		-- interlace;
		row_ptr = image->data_index + interlace_table[ interlace ];
	    }

	    //-- Recalculate end of row pointer.
	    row_end = row_ptr + image->image_width;
	}
    }


    //-----------------------------------------------------------------------------
    // Finish with ending code and ending segment.
    //-----------------------------------------------------------------------------

    //-- Count codes and check for a needed reset.
    if ( (-- code_count) == 0 ) {
	//-- Append reset code to accumulator.
	acc_data |= reset_code << acc_bits;
	acc_bits += code_bits;
	code_count = code_limit;
    }

    //-- Append the end code into accumulator.
    acc_data |= end_code << acc_bits;
    acc_bits += code_bits;

    //-- Pad accumulator to get the last octet out.
    acc_bits += 7;

    //-- Flush accumulator of whole octets.
    while ( acc_bits >= 8 ) {
	//-- Store one octet from acculator and remove it.
	* (buf_ptr ++) = acc_data;
	acc_data >>= 8;
	acc_bits -= 8;

	//-- If buffer is full, output it.
	if ( buf_ptr == buf_end ) {
	    * buffer = (unsigned int) 255;
	    if ( (rc = angif_write( stream, buffer, (size_t) 256 )) < 0 ) return rc;
	    buf_ptr = buffer + 1;
	}
    }

    //-- If buffer has any data, finish it.
    if ( ( buf_ptr - buffer ) > 1 ) {
	* buffer = (unsigned int) ( ( buf_ptr - buffer ) - 1 );
	if ( (rc = angif_write( stream, buffer, (size_t) ( buf_ptr - buffer ) )) < 0 ) return rc;
    }

    //-- End the image data with a null data segment.
    buffer[0] = 0;
    if ( (rc = angif_write( stream, buffer, (size_t) 1 )) < 0 ) return rc;

    //-- Return with successful completion.
    return 0;
}

