/*
 * jcmarker.c
 *
 * Copyright (C) 1991-1995, Thomas G. Lane.
 * This file is part of the Independent JPEG Group's software.
 * For conditions of distribution and use, see the accompanying README file.
 *
 * This file contains routines to write JPEG datastream markers.
 */

#define JPEG_INTERNALS
#include "jinclude.h"
#include "jpeglib.h"


typedef enum {          /* JPEG marker codes */
	M_SOF0  = 0xc0,
	M_SOF1  = 0xc1,
	M_SOF2  = 0xc2,
	M_SOF3  = 0xc3,

	M_SOF5  = 0xc5,
	M_SOF6  = 0xc6,
	M_SOF7  = 0xc7,

	M_JPG   = 0xc8,
	M_SOF9  = 0xc9,
	M_SOF10 = 0xca,
	M_SOF11 = 0xcb,

	M_SOF13 = 0xcd,
	M_SOF14 = 0xce,
	M_SOF15 = 0xcf,

	M_DHT   = 0xc4,

	M_DAC   = 0xcc,

	M_RST0  = 0xd0,
	M_RST1  = 0xd1,
	M_RST2  = 0xd2,
	M_RST3  = 0xd3,
	M_RST4  = 0xd4,
	M_RST5  = 0xd5,
	M_RST6  = 0xd6,
	M_RST7  = 0xd7,

	M_SOI   = 0xd8,
	M_EOI   = 0xd9,
	M_SOS   = 0xda,
	M_DQT   = 0xdb,
	M_DNL   = 0xdc,
	M_DRI   = 0xdd,
	M_DHP   = 0xde,
	M_EXP   = 0xdf,

	M_APP0  = 0xe0,
	M_APP1  = 0xe1,
	M_APP2  = 0xe2,
	M_APP3  = 0xe3,
	M_APP4  = 0xe4,
	M_APP5  = 0xe5,
	M_APP6  = 0xe6,
	M_APP7  = 0xe7,
	M_APP8  = 0xe8,
	M_APP9  = 0xe9,
	M_APP10 = 0xea,
	M_APP11 = 0xeb,
	M_APP12 = 0xec,
	M_APP13 = 0xed,
	M_APP14 = 0xee,
	M_APP15 = 0xef,

	M_JPG0  = 0xf0,
	M_JPG13 = 0xfd,
	M_COM   = 0xfe,

	M_TEM   = 0x01,

	M_ERROR = 0x100
} JPEG_MARKER;


/*
 * Basic output routines.
 *
 * Note that we do not support suspension while writing a marker.
 * Therefore, an application using suspension must ensure that there is
 * enough buffer space for the initial markers (typ. 600-700 bytes) before
 * calling jpeg_start_compress, and enough space to write the trailing EOI
 * (a few bytes) before calling jpeg_finish_compress.  Multipass compression
 * modes are not supported at all with suspension, so those two are the only
 * points where markers will be written.
 */

LOCAL void
emit_byte( j_compress_ptr cinfo, int val ) {
/* Emit a byte */
	struct jpeg_destination_mgr * dest = cinfo->dest;

	*( dest->next_output_byte )++ = (JOCTET) val;
	if ( --dest->free_in_buffer == 0 ) {
		if ( !( *dest->empty_output_buffer )( cinfo ) ) {
			ERREXIT( cinfo, JERR_CANT_SUSPEND );
		}
	}
}


LOCAL void
emit_marker( j_compress_ptr cinfo, JPEG_MARKER mark ) {
/* Emit a marker code */
	emit_byte( cinfo, 0xFF );
	emit_byte( cinfo, (int) mark );
}


LOCAL void
emit_2bytes( j_compress_ptr cinfo, int value ) {
/* Emit a 2-byte integer; these are always MSB first in JPEG files */
	emit_byte( cinfo, ( value >> 8 ) & 0xFF );
	emit_byte( cinfo, value & 0xFF );
}


/*
 * Routines to write specific marker types.
 */

LOCAL int
emit_dqt( j_compress_ptr cinfo, int index ) {
/* Emit a DQT marker */
/* Returns the precision used (0 = 8bits, 1 = 16bits) for baseline checking */
	JQUANT_TBL * qtbl = cinfo->quant_tbl_ptrs[index];
	int prec;
	int i;

	if ( qtbl == NULL ) {
		ERREXIT1( cinfo, JERR_NO_QUANT_TABLE, index );
	}

	prec = 0;
	for ( i = 0; i < DCTSIZE2; i++ ) {
		if ( qtbl->quantval[i] > 255 ) {
			prec = 1;
		}
	}

	if ( !qtbl->sent_table ) {
		emit_marker( cinfo, M_DQT );

		emit_2bytes( cinfo, prec ? DCTSIZE2 * 2 + 1 + 2 : DCTSIZE2 + 1 + 2 );

		emit_byte( cinfo, index + ( prec << 4 ) );

		for ( i = 0; i < DCTSIZE2; i++ ) {
			if ( prec ) {
				emit_byte( cinfo, qtbl->quantval[i] >> 8 );
			}
			emit_byte( cinfo, qtbl->quantval[i] & 0xFF );
		}

		qtbl->sent_table = TRUE;
	}

	return prec;
}


LOCAL void
emit_dht( j_compress_ptr cinfo, int index, boolean is_ac ) {
/* Emit a DHT marker */
	JHUFF_TBL * htbl;
	int length, i;

	if ( is_ac ) {
		htbl = cinfo->ac_huff_tbl_ptrs[index];
		index += 0x10;  /* output index has AC bit set */
	} else {
		htbl = cinfo->dc_huff_tbl_ptrs[index];
	}

	if ( htbl == NULL ) {
		ERREXIT1( cinfo, JERR_NO_HUFF_TABLE, index );
	}

	if ( !htbl->sent_table ) {
		emit_marker( cinfo, M_DHT );

		length = 0;
		for ( i = 1; i <= 16; i++ )
			length += htbl->bits[i];

		emit_2bytes( cinfo, length + 2 + 1 + 16 );
		emit_byte( cinfo, index );

		for ( i = 1; i <= 16; i++ )
			emit_byte( cinfo, htbl->bits[i] );

		for ( i = 0; i < length; i++ )
			emit_byte( cinfo, htbl->huffval[i] );

		htbl->sent_table = TRUE;
	}
}


LOCAL void
emit_dac( j_compress_ptr cinfo ) {
/* Emit a DAC marker */
/* Since the useful info is so small, we want to emit all the tables in */
/* one DAC marker.  Therefore this routine does its own scan of the table. */
#ifdef C_ARITH_CODING_SUPPORTED
	char dc_in_use[NUM_ARITH_TBLS];
	char ac_in_use[NUM_ARITH_TBLS];
	int length, i;
	jpeg_component_info *compptr;

	for ( i = 0; i < NUM_ARITH_TBLS; i++ )
		dc_in_use[i] = ac_in_use[i] = 0;

	for ( i = 0; i < cinfo->comps_in_scan; i++ ) {
		compptr = cinfo->cur_comp_info[i];
		dc_in_use[compptr->dc_tbl_no] = 1;
		ac_in_use[compptr->ac_tbl_no] = 1;
	}

	length = 0;
	for ( i = 0; i < NUM_ARITH_TBLS; i++ )
		length += dc_in_use[i] + ac_in_use[i];

	emit_marker( cinfo, M_DAC );

	emit_2bytes( cinfo, length * 2 + 2 );

	for ( i = 0; i < NUM_ARITH_TBLS; i++ ) {
		if ( dc_in_use[i] ) {
			emit_byte( cinfo, i );
			emit_byte( cinfo, cinfo->arith_dc_L[i] + ( cinfo->arith_dc_U[i] << 4 ) );
		}
		if ( ac_in_use[i] ) {
			emit_byte( cinfo, i + 0x10 );
			emit_byte( cinfo, cinfo->arith_ac_K[i] );
		}
	}
#endif /* C_ARITH_CODING_SUPPORTED */
}


LOCAL void
emit_dri( j_compress_ptr cinfo ) {
/* Emit a DRI marker */
	emit_marker( cinfo, M_DRI );

	emit_2bytes( cinfo, 4 ); /* fixed length */

	emit_2bytes( cinfo, (int) cinfo->restart_interval );
}


LOCAL void
emit_sof( j_compress_ptr cinfo, JPEG_MARKER code ) {
/* Emit a SOF marker */
	int ci;
	jpeg_component_info *compptr;

	emit_marker( cinfo, code );

	emit_2bytes( cinfo, 3 * cinfo->num_components + 2 + 5 + 1 ); /* length */

	/* Make sure image isn't bigger than SOF field can handle */
	if ( (long) cinfo->image_height > 65535L ||
		 (long) cinfo->image_width > 65535L ) {
		ERREXIT1( cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) 65535 );
	}

	emit_byte( cinfo, cinfo->data_precision );
	emit_2bytes( cinfo, (int) cinfo->image_height );
	emit_2bytes( cinfo, (int) cinfo->image_width );

	emit_byte( cinfo, cinfo->num_components );

	for ( ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
		  ci++, compptr++ ) {
		emit_byte( cinfo, compptr->component_id );
		emit_byte( cinfo, ( compptr->h_samp_factor << 4 ) + compptr->v_samp_factor );
		emit_byte( cinfo, compptr->quant_tbl_no );
	}
}


LOCAL void
emit_sos( j_compress_ptr cinfo ) {
/* Emit a SOS marker */
	int i, td, ta;
	jpeg_component_info *compptr;

	emit_marker( cinfo, M_SOS );

	emit_2bytes( cinfo, 2 * cinfo->comps_in_scan + 2 + 1 + 3 ); /* length */

	emit_byte( cinfo, cinfo->comps_in_scan );

	for ( i = 0; i < cinfo->comps_in_scan; i++ ) {
		compptr = cinfo->cur_comp_info[i];
		emit_byte( cinfo, compptr->component_id );
		td = compptr->dc_tbl_no;
		ta = compptr->ac_tbl_no;
		if ( cinfo->progressive_mode ) {
			/* Progressive mode: only DC or only AC tables are used in one scan;
			 * furthermore, Huffman coding of DC refinement uses no table at all.
			 * We emit 0 for unused field(s); this is recommended by the P&M text
			 * but does not seem to be specified in the standard.
			 */
			if ( cinfo->Ss == 0 ) {
				ta = 0; /* DC scan */
				if ( cinfo->Ah != 0 && !cinfo->arith_code ) {
					td = 0; /* no DC table either */
				}
			} else {
				td = 0; /* AC scan */
			}
		}
		emit_byte( cinfo, ( td << 4 ) + ta );
	}

	emit_byte( cinfo, cinfo->Ss );
	emit_byte( cinfo, cinfo->Se );
	emit_byte( cinfo, ( cinfo->Ah << 4 ) + cinfo->Al );
}


LOCAL void
emit_jfif_app0( j_compress_ptr cinfo ) {
/* Emit a JFIF-compliant APP0 marker */
/*
 * Length of APP0 block	(2 bytes)
 * Block ID			(4 bytes - ASCII "JFIF")
 * Zero byte			(1 byte to terminate the ID string)
 * Version Major, Minor	(2 bytes - 0x01, 0x01)
 * Units			(1 byte - 0x00 = none, 0x01 = inch, 0x02 = cm)
 * Xdpu			(2 bytes - dots per unit horizontal)
 * Ydpu			(2 bytes - dots per unit vertical)
 * Thumbnail X size		(1 byte)
 * Thumbnail Y size		(1 byte)
 */

	emit_marker( cinfo, M_APP0 );

	emit_2bytes( cinfo, 2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1 ); /* length */

	emit_byte( cinfo, 0x4A ); /* Identifier: ASCII "JFIF" */
	emit_byte( cinfo, 0x46 );
	emit_byte( cinfo, 0x49 );
	emit_byte( cinfo, 0x46 );
	emit_byte( cinfo, 0 );
	/* We currently emit version code 1.01 since we use no 1.02 features.
	 * This may avoid complaints from some older decoders.
	 */
	emit_byte( cinfo, 1 );  /* Major version */
	emit_byte( cinfo, 1 );  /* Minor version */
	emit_byte( cinfo, cinfo->density_unit ); /* Pixel size information */
	emit_2bytes( cinfo, (int) cinfo->X_density );
	emit_2bytes( cinfo, (int) cinfo->Y_density );
	emit_byte( cinfo, 0 );  /* No thumbnail image */
	emit_byte( cinfo, 0 );
}


LOCAL void
emit_adobe_app14( j_compress_ptr cinfo ) {
/* Emit an Adobe APP14 marker */
/*
 * Length of APP14 block	(2 bytes)
 * Block ID			(5 bytes - ASCII "Adobe")
 * Version Number		(2 bytes - currently 100)
 * Flags0			(2 bytes - currently 0)
 * Flags1			(2 bytes - currently 0)
 * Color transform		(1 byte)
 *
 * Although Adobe TN 5116 mentions Version = 101, all the Adobe files
 * now in circulation seem to use Version = 100, so that's what we write.
 *
 * We write the color transform byte as 1 if the JPEG color space is
 * YCbCr, 2 if it's YCCK, 0 otherwise.  Adobe's definition has to do with
 * whether the encoder performed a transformation, which is pretty useless.
 */

	emit_marker( cinfo, M_APP14 );

	emit_2bytes( cinfo, 2 + 5 + 2 + 2 + 2 + 1 ); /* length */

	emit_byte( cinfo, 0x41 ); /* Identifier: ASCII "Adobe" */
	emit_byte( cinfo, 0x64 );
	emit_byte( cinfo, 0x6F );
	emit_byte( cinfo, 0x62 );
	emit_byte( cinfo, 0x65 );
	emit_2bytes( cinfo, 100 ); /* Version */
	emit_2bytes( cinfo, 0 ); /* Flags0 */
	emit_2bytes( cinfo, 0 ); /* Flags1 */
	switch ( cinfo->jpeg_color_space ) {
	case JCS_YCbCr:
		emit_byte( cinfo, 1 ); /* Color transform = 1 */
		break;
	case JCS_YCCK:
		emit_byte( cinfo, 2 ); /* Color transform = 2 */
		break;
	default:
		emit_byte( cinfo, 0 ); /* Color transform = 0 */
		break;
	}
}


/*
 * This routine is exported for possible use by applications.
 * The intended use is to emit COM or APPn markers after calling
 * jpeg_start_compress() and before the first jpeg_write_scanlines() call
 * (hence, after write_file_header but before write_frame_header).
 * Other uses are not guaranteed to produce desirable results.
 */

METHODDEF void
write_any_marker( j_compress_ptr cinfo, int marker,
				  const JOCTET *dataptr, unsigned int datalen ) {
/* Emit an arbitrary marker with parameters */
	if ( datalen <= (unsigned int) 65533 ) { /* safety check */
		emit_marker( cinfo, (JPEG_MARKER) marker );

		emit_2bytes( cinfo, (int) ( datalen + 2 ) ); /* total length */

		while ( datalen-- ) {
			emit_byte( cinfo, *dataptr );
			dataptr++;
		}
	}
}


/*
 * Write datastream header.
 * This consists of an SOI and optional APPn markers.
 * We recommend use of the JFIF marker, but not the Adobe marker,
 * when using YCbCr or grayscale data.  The JFIF marker should NOT
 * be used for any other JPEG colorspace.  The Adobe marker is helpful
 * to distinguish RGB, CMYK, and YCCK colorspaces.
 * Note that an application can write additional header markers after
 * jpeg_start_compress returns.
 */

METHODDEF void
write_file_header( j_compress_ptr cinfo ) {
	emit_marker( cinfo, M_SOI ); /* first the SOI */

	if ( cinfo->write_JFIF_header ) { /* next an optional JFIF APP0 */
		emit_jfif_app0( cinfo );
	}
	if ( cinfo->write_Adobe_marker ) { /* next an optional Adobe APP14 */
		emit_adobe_app14( cinfo );
	}
}


/*
 * Write frame header.
 * This consists of DQT and SOFn markers.
 * Note that we do not emit the SOF until we have emitted the DQT(s).
 * This avoids compatibility problems with incorrect implementations that
 * try to error-check the quant table numbers as soon as they see the SOF.
 */

METHODDEF void
write_frame_header( j_compress_ptr cinfo ) {
	int ci, prec;
	boolean is_baseline;
	jpeg_component_info *compptr;

	/* Emit DQT for each quantization table.
	 * Note that emit_dqt() suppresses any duplicate tables.
	 */
	prec = 0;
	for ( ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
		  ci++, compptr++ ) {
		prec += emit_dqt( cinfo, compptr->quant_tbl_no );
	}
	/* now prec is nonzero iff there are any 16-bit quant tables. */

	/* Check for a non-baseline specification.
	 * Note we assume that Huffman table numbers won't be changed later.
	 */
	if ( cinfo->arith_code || cinfo->progressive_mode ||
		 cinfo->data_precision != 8 ) {
		is_baseline = FALSE;
	} else {
		is_baseline = TRUE;
		for ( ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
			  ci++, compptr++ ) {
			if ( compptr->dc_tbl_no > 1 || compptr->ac_tbl_no > 1 ) {
				is_baseline = FALSE;
			}
		}
		if ( prec && is_baseline ) {
			is_baseline = FALSE;
			/* If it's baseline except for quantizer size, warn the user */
			TRACEMS( cinfo, 0, JTRC_16BIT_TABLES );
		}
	}

	/* Emit the proper SOF marker */
	if ( cinfo->arith_code ) {
		emit_sof( cinfo, M_SOF9 ); /* SOF code for arithmetic coding */
	} else {
		if ( cinfo->progressive_mode ) {
			emit_sof( cinfo, M_SOF2 ); /* SOF code for progressive Huffman */
		} else if ( is_baseline )                                                                       {
			emit_sof( cinfo, M_SOF0 ); /* SOF code for baseline implementation */
		} else {
			emit_sof( cinfo, M_SOF1 ); /* SOF code for non-baseline Huffman file */
		}
	}
}


/*
 * Write scan header.
 * This consists of DHT or DAC markers, optional DRI, and SOS.
 * Compressed data will be written following the SOS.
 */

METHODDEF void
write_scan_header( j_compress_ptr cinfo ) {
	int i;
	jpeg_component_info *compptr;

	if ( cinfo->arith_code ) {
		/* Emit arith conditioning info.  We may have some duplication
		 * if the file has multiple scans, but it's so small it's hardly
		 * worth worrying about.
		 */
		emit_dac( cinfo );
	} else {
		/* Emit Huffman tables.
		 * Note that emit_dht() suppresses any duplicate tables.
		 */
		for ( i = 0; i < cinfo->comps_in_scan; i++ ) {
			compptr = cinfo->cur_comp_info[i];
			if ( cinfo->progressive_mode ) {
				/* Progressive mode: only DC or only AC tables are used in one scan */
				if ( cinfo->Ss == 0 ) {
					if ( cinfo->Ah == 0 ) { /* DC needs no table for refinement scan */
						emit_dht( cinfo, compptr->dc_tbl_no, FALSE );
					}
				} else {
					emit_dht( cinfo, compptr->ac_tbl_no, TRUE );
				}
			} else {
				/* Sequential mode: need both DC and AC tables */
				emit_dht( cinfo, compptr->dc_tbl_no, FALSE );
				emit_dht( cinfo, compptr->ac_tbl_no, TRUE );
			}
		}
	}

	/* Emit DRI if required --- note that DRI value could change for each scan.
	 * If it doesn't, a tiny amount of space is wasted in multiple-scan files.
	 * We assume DRI will never be nonzero for one scan and zero for a later one.
	 */
	if ( cinfo->restart_interval ) {
		emit_dri( cinfo );
	}

	emit_sos( cinfo );
}


/*
 * Write datastream trailer.
 */

METHODDEF void
write_file_trailer( j_compress_ptr cinfo ) {
	emit_marker( cinfo, M_EOI );
}


/*
 * Write an abbreviated table-specification datastream.
 * This consists of SOI, DQT and DHT tables, and EOI.
 * Any table that is defined and not marked sent_table = TRUE will be
 * emitted.  Note that all tables will be marked sent_table = TRUE at exit.
 */

METHODDEF void
write_tables_only( j_compress_ptr cinfo ) {
	int i;

	emit_marker( cinfo, M_SOI );

	for ( i = 0; i < NUM_QUANT_TBLS; i++ ) {
		if ( cinfo->quant_tbl_ptrs[i] != NULL ) {
			(void) emit_dqt( cinfo, i );
		}
	}

	if ( !cinfo->arith_code ) {
		for ( i = 0; i < NUM_HUFF_TBLS; i++ ) {
			if ( cinfo->dc_huff_tbl_ptrs[i] != NULL ) {
				emit_dht( cinfo, i, FALSE );
			}
			if ( cinfo->ac_huff_tbl_ptrs[i] != NULL ) {
				emit_dht( cinfo, i, TRUE );
			}
		}
	}

	emit_marker( cinfo, M_EOI );
}


/*
 * Initialize the marker writer module.
 */

GLOBAL void
jinit_marker_writer( j_compress_ptr cinfo ) {
	/* Create the subobject */
	cinfo->marker = (struct jpeg_marker_writer *)
				( *cinfo->mem->alloc_small ) ( (j_common_ptr) cinfo, JPOOL_IMAGE,
											   SIZEOF( struct jpeg_marker_writer ) );
	/* Initialize method pointers */
	cinfo->marker->write_any_marker = write_any_marker;
	cinfo->marker->write_file_header = write_file_header;
	cinfo->marker->write_frame_header = write_frame_header;
	cinfo->marker->write_scan_header = write_scan_header;
	cinfo->marker->write_file_trailer = write_file_trailer;
	cinfo->marker->write_tables_only = write_tables_only;
}
