/*
 *	THIS ROUTINE IS PART OF THE CLEMENTINE PDS FILE READER PROGRAM.
 *	IT WAS WRITTEN BY ACT CORP. IN DIRECT SUPPORT TO THE
 *	CLEMENTINE (DSPSE) PROGRAM.
 *
 *	IF YOU FIND A PROBLEM OR MAKE ANY CHANGES TO THIS CODE PLEASE CONTACT
 *	Dr. Erick Malaret at ACT Corp.
 *			tel: (703) 742-0294
 *			     (703) 683-7431
 *                       email:	nrlvax.nrl.navy.mil
 *
 *
 */
/*
	Program to calculate the Discrete Cosine Transform of a 8x8 block of data
*/

#include 
#ifdef __BORLANDC__
#include 
#else
#include 
#endif
#include 
#include 
#include "jpeg_c.h"

#define TRUE	1
#define FALSE	0

extern int	OPTIMIZE;
extern int	SLOWDECOMP;
long		*DCTHist[64];
float		*Rn[64];
float	Q[64];
float	C[64], Ct[64];
float	qtable[64];
float	U[64];
short	ULS[64];
int	zzseq[] = {0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,
			48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,
			22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,
			47,55,62,63};

void core(void);
void getDCTHist(BitStream *bs, long rows, long cols);
void getRn(void);
void initC(float *m, short N);
void transp(float *out, float *in, short N);
void matmult1(float *out, float *in1, float *in2, short N);

/****************************************************************************/
void decomp(BitStream *bs, CHARH *Image, long rows, long cols)
{
	short	i,j,k,indexi,indexip8,indexj,indexjp8;
	float	temp;
	float	V1[64];
	long	rowsleft, colsleft, icols;
	short	Done, Diff, Pred;

	/*********************** Image Decompression *********************/
	Done = FALSE;
	rowsleft = rows;
	colsleft = cols;
	indexj = 0;
	indexi = 0;
	Pred = 0;

	while ( !Done ) {
		/* Decode block data */
		decode(ULS,bs);
		Diff = ULS[0] + Pred;
		Pred = Diff;
		ULS[0] = Diff;

		/* Calculate Inverse Discrete Cosine Transform */
		/*dequantize(U,ULS1,pt_qtable);  64 multiplications */
		for (i=0; i<64; i++) {
			if ( OPTIMIZE ) {
				temp = Rn[i][ULS[i]];
				if ( SLOWDECOMP )
					U[zzseq[i]] = temp * Q[i];
				else
					U[zzseq[i]] = temp * qtable[i];
			} else {
				if ( SLOWDECOMP )
					U[zzseq[i]] = ULS[i] * Q[i];
				else
					U[zzseq[i]] = ULS[i] * qtable[i];
			}
		}

		if ( SLOWDECOMP ) {
			matmult1(V1,Ct,U,8);
			matmult1(U,V1,C,8);
		} else {
			core();
		}

		/*ilevelshift(U);*/
		for (i=0; i<64; i++) {
			U[i] += 128.0;
			U[i] = floor( U[i] + 0.5 );
			if ( U[i] > 255.0 ) U[i] = 255.0;
			else if ( U[i] < 0.0 ) U[i] = 0.0;
			else;
		}

		indexip8 = indexi + 8;
		indexjp8 = indexj + 8;

		if ( (rowsleft > 8) && (colsleft > 8) ) {

			for (i=indexi, k=0; i < indexip8; i++)
				for (j=indexj, icols=i*cols; j < indexjp8; j++, k++)
					Image[icols+j] = (char)U[k];

			indexj += 8;
			colsleft -= 8;
		}
		else if ( (rowsleft > 8) && (colsleft <= 8) ) {

			for (i=indexi, k=0; i < indexip8; i++) {
				for (j=indexj, icols=i*cols; j < cols; j++, k++)
					Image[icols+j] = (char)U[k];
				k += (8 - colsleft);
			}

			indexj = 0;
			indexi += 8;
			rowsleft -= 8;
			colsleft = cols;
		}
		else if ( (rowsleft <= 8) && (colsleft > 8) ) {

			for (i=indexi, k=0; i < rows; i++)
				for (j=indexj, icols=i*cols; j < indexjp8; j++, k++)
					Image[icols+j] = (char)U[k];

			indexj += 8;
			colsleft -= 8;
		}
		else {

			for (i=indexi, k=0; i < rows; i++) {
				for (j=indexj, icols=i*cols; j < cols; j++, k++)
					Image[icols+j] = (char)U[k];
				k += (8 - colsleft);
			}

			Done = TRUE;
		}
	}
}

void getDCTHist(BitStream *bs, long rows, long cols)
{
	short	Pred, i;
	long	nblocks;

	nblocks = (rows * cols) / 64;
	Pred = 0;

	while ( nblocks ) {
		/* Decode block data */
		decode(ULS,bs);
		ULS[0] += Pred;
		Pred = ULS[0];

		for (i=0; i<64; i++) DCTHist[i][ULS[i]]++;

		nblocks--;
	}
}

void getRn()
{
	short	i, j;
	float	lb, ub, a, b, num, den, phi;
	float	m, pa, pap, pb, pbp, pm, pmp;

	for (i=0; i<64; i++) {
		Rn[i][-256] = (float)-256;
		Rn[i][256] = (float)256;
/*
		for (j=-255; j<256; j++) {
			if ( DCTHist[i][j] > 0 ) {
				m  = ((float)j)*Q[i];
				pm = DCTHist[i][j];
				pmp = m*pm;

				a = ((float)j - 0.5)*Q[i];
				phi = 0.5;
				pa = phi*DCTHist[i][j] + (1.0-phi)*DCTHist[i][j-1];
				pap = ((float)j*pa - (1.0-phi)*DCTHist[i][j-1])*Q[i];

				b = ((float)j + 0.5)*Q[i];
				phi = 0.5;
				pb = phi*DCTHist[i][j+1] + (1.0-phi)*DCTHist[i][j];
				pbp = ((float)j*pb + phi*DCTHist[i][j+1])*Q[i];

				num = (m-a)*(pap+pmp)+(b-m)*(pbp+pmp);
				den = (m-a)*(pa+pm)+(b-m)*(pb+pm);
				Rn[i][j] = (num/den)/Q[i];
			} else {
				Rn[i][j] = (float)j;
			}
		}
*/
		for (j=-255; j<256; j++) {
			if ( DCTHist[i][j] > 0 ) {
				lb = ((float)j - 0.5)*Q[i];
				ub = ((float)j + 0.5)*Q[i];

				m  = ((float)j)*Q[i];
				pm = DCTHist[i][j];
				pmp = m*pm;

				a = ceil(lb);
				phi = a/Q[i] - (float)(j-1);
				pa = phi*DCTHist[i][j] + (1.0-phi)*DCTHist[i][j-1];
				pap = ((float)j*pa - (1.0-phi)*DCTHist[i][j-1])*Q[i];

				b = ceil(ub);
				phi = b/Q[i] - (float)j;
				pb = phi*DCTHist[i][j+1] + (1.0-phi)*DCTHist[i][j];
				pbp = ((float)j*pb + phi*DCTHist[i][j+1])*Q[i];

				num = (m-a)*(pap+pmp)+(b-m)*(pbp+pmp);
				den = (m-a)*(pa+pm)+(b-m)*(pb+pm);
				Rn[i][j] = (num/den)/Q[i];
			} else {
				Rn[i][j] = (float)j;
			}
		}
	}
}

#define PI 3.141592653589793

void initC(float *m, short N)
{
	short     i, j, k;

	for (i=0, k=0; i