/******************************************************************************
 *                                                                          
 * This software module was originally developed by 
 *
 * Noel Brady (TELTEC IRELAND / ACTS-MoMuSys).                  
 *
 * in the course of development of the MPEG-4 Video (ISO/IEC 14496-2) standard.
 * This software module is an implementation of a part of one or more MPEG-4 
 * Video (ISO/IEC 14496-2) tools as specified by the MPEG-4 Video (ISO/IEC 
 * 14496-2) standard. 
 *
 * ISO/IEC gives users of the MPEG-4 Video (ISO/IEC 14496-2) standard free 
 * license to this software module or modifications thereof for use in hardware
 * or software products claiming conformance to the MPEG-4 Video (ISO/IEC 
 * 14496-2) standard. 
 *
 * Those intending to use this software module in hardware or software products
 * are advised that its use may infringe existing patents. The original 
 * developer of this software module and his/her company, the subsequent 
 * editors and their companies, and ISO/IEC have no liability for use of this 
 * software module or modifications thereof in an implementation. Copyright is 
 * not released for non MPEG-4 Video (ISO/IEC 14496-2) standard conforming 
 * products. 
 *
 * ACTS-MoMuSys partners retain full right to use the code for his/her own 
 * purpose, assign or donate the code to a third party and to inhibit third 
 * parties from using the code for non MPEG-4 Video (ISO/IEC 14496-2) standard
 * conforming products. This copyright notice must be included in all copies or
 * derivative works. 
 *
 * Copyright (c) 1997
 *
 *****************************************************************************/


/***********************************************************HeaderBegin*******
 *                                                                         
 * File:	alp_code_cae.c
 *
 * Author:	Noel Brady Teltec Irl.
 * Created:	11-04-97
 *                                                                         
 * Description: Contains functions used to implement block-based CAE
 *							coding of binary alpha blocks.
 *
 *
 *                                 
 ***********************************************************HeaderEnd*********/

/************************    INCLUDE FILES    ********************************/

	#include <math.h>
	#include "momusys.h"
	#include "mom_structs.h"
	#include "mom_image.h"
	#include "mom_vop.h"
	#include "mot_util.h"
	#include "alp_common_cae.h"
	#include "alp_code_cae.h"
	#include "alp_code_header.h"
	#include "alp_common_util.h"
	#include "bin_ar_code.h"
	#include "alp_common_def.h"

	/* probability tables for CAE */

	#include "cae_intra.h"
	#include "cae_inter.h"

/***********************************************************CommentBegin******
 *
 * -- CAE_MB -- Encodes a binary alpha block using CAE.
 *
 * Author :		
 *	Noel Brady Teltec Irl.
 *
 * Created :		
 *	11-04-97
 * 
 * Arguments: 	
 *		alpha_smb 		: alpha macroblock to be coded (subsampled)
 *    alpha_mb_comp : prediction for alpha macroblock
 *		x,y						: address of macroblock in pixels
 *		ox,oy				: the differential spatial reference between
 *										current VOP and previous VOP
 *		rec_alpha 		: current reconstructed alpha map
 *		prev_alpha 		: previous reconstructed alpha map
 *    motA_x,motA_y : the motion field for shape
 *		shape_inter 	: flag to tell encoder whether inter coding 
 *										is applicable (always = 1, for P/B-VOPs)
 *		amvbits 			: number of bits used for coding the shape MV  of the MB
 *		send_cr				: =0, CR is assumed equal to 1, 
 *									: =1, CR is transmitted in the bitstream.
 * 		mb_type 			: shape coding mode for the macroblock
 *		shape_stream 	: bitstream produced
 *
 * Return values :	
 *
 * Side effects :	
 *	-
 *
 * Description :	A binary alpha block will be coded using cr,st, and bac.
 *								These bits are added to the <shape_stream>. Decisions
 *								on transposition and INTRA/INTER are made within.
 *
 * See also :
 *
 ***********************************************************CommentEnd********/

Int
CAE_MB( Vop *ref_vop,
	Image *bordered_smb_data, /* SAIT */
				UChar *alpha_mb_comp,
				Int		x,
				Int		y,
				Int ox,
				Int oy,
				Image	*rec_alpha,
				Image	*prev_alpha,
				Image *alpha_rec_packet,
				Image	*motA_x,
				Image	*motA_y,
        Int shape_inter,
				Int mvda,
       	Int amvbits,
				Int send_cr,
       	Int *mb_type,
       	UChar *shape_stream,
				Int vo_id,
				Int vol_id,
				Int error_res_disable
)
{
	/* SAIT begin */
  	/*****comment out
	Image *top_border = AllocImage(MB_SIZE+4,2,SHORT_TYPE),
				*left_border = AllocImage(2,MB_SIZE,SHORT_TYPE),
				*bordered_smb,
				*bordered_smb_v,
				*alpha_smb_mc=NULL,
				*alpha_smb_mc_v=NULL,
				*top_sborder,
				*left_sborder,
				*tmp;
  	******/
	Image	*bordered_smb_v,
    		*alpha_smb_mc=NULL,
    		*alpha_smb_mc_v=NULL;
 	/* SAIT end */
	
  	UChar
		alpha_smb_comp[MB_SIZE*MB_SIZE];
  
  	UChar h_stream[1024], v_stream[1024];
  	UChar tmp_stream[1024];

  	Int
    		ii,jj,
    		mb_s=MB_SIZE,
    		h_bits,h_bits1,h_bits2,
    		v_bits,v_bits1,v_bits2,
    		h_offset,v_offset,
    		offset,
    		h_mb_type,v_mb_type,
    		k,
    		best_bits,
		mb_x,mb_y,
		mvx,mvy,
		tmp_mb_type;

	SInt *p;

  	h_offset = v_offset = 0;
  	offset = amvbits+1;
  	for (k=0;k<1024;k++) v_stream[k]=h_stream[k]=tmp_stream[k]=0; 

	if (shape_inter) 
		offset =
			amvbits+1+GetModeCodeLength(5+mvda,x/MB_SIZE,y/MB_SIZE,ref_vop)
				-GetModeCodeLength(2,x/MB_SIZE,y/MB_SIZE,ref_vop);

  	h_mb_type = v_mb_type = *mb_type;

  	if(*mb_type==2) mb_s = MB_SIZE;
  	else if(*mb_type==3) mb_s = MB_SIZE>>1;
  	else if(*mb_type==4) mb_s = MB_SIZE>>2;

	/* Get all the data for the bordered block to be coded */

	/* SAIT begin */
	/************** comment out
	bordered_smb = AllocImage(mb_s+4,mb_s+4,SHORT_TYPE);

	if (error_res_disable)
		{
			if (y>1) GetSubImage(rec_alpha,top_border,x-2,y-2);
			else SetConstantImage(top_border,0);
			if (x>1) GetSubImage(rec_alpha,left_border,x-2,y);
			else SetConstantImage(left_border,0);
		}
	else
		{
			if (y>1) GetSubImage(alpha_rec_packet,top_border,x-2,y-2);
			else SetConstantImage(top_border,0);
			if (x>1) GetSubImage(alpha_rec_packet,left_border,x-2,y);
			else SetConstantImage(left_border,0);
		}		
			
	BinariseImage(top_border);
	BinariseImage(left_border);

	if (mb_s != MB_SIZE) {
		top_sborder=TopBorderDecimate(top_border,MB_SIZE/mb_s);
		left_sborder=LeftBorderDecimate(left_border,MB_SIZE/mb_s);

		PutSubImage(bordered_smb,top_sborder,0,0);
		PutSubImage(bordered_smb,left_sborder,0,2);
	
		FreeImage(top_sborder);
		FreeImage(left_sborder);
	}
	else {
		PutSubImage(bordered_smb,top_border,0,0);
		PutSubImage(bordered_smb,left_border,0,2);
	}
	FreeImage(top_border);
	FreeImage(left_border);


	p = (SInt *) GetImageData(bordered_smb) 
					+ 2*GetImageSizeX(bordered_smb)
					+ 2;
  	for( ii=0; ii<mb_s; ii++) 
    		for( jj=0; jj<mb_s; jj++) {
      			p[GetImageSizeX(bordered_smb)*ii+jj] = (SInt) alpha_smb[ii*mb_s+jj];
    	}

  	for( ii=0; ii<mb_s; ii++, p+=GetImageSizeX(bordered_smb)-mb_s ) 
		for( jj=0; jj<mb_s; jj++, p++ ) {
    			*p = (SInt) alpha_smb[ii*mb_s+jj];
  		}

	tmp = AllocImage(2,1,SHORT_TYPE);
	GetSubImage(bordered_smb,tmp,0,mb_s+1);
	PutSubImage(bordered_smb,tmp,0,mb_s+2);
	PutSubImage(bordered_smb,tmp,0,mb_s+3);
	FreeImage(tmp);
	*********************/
	/* SAIT end */

	/* TBD: Get the data for the MC bordered block */

	if (shape_inter) {
		mb_x = x/MB_SIZE; mb_y = y/MB_SIZE;
		p = (SInt *) GetImageData(motA_x) 
					+ mb_y*GetImageSizeX(motA_x)
					+ mb_x;
		mvx = *p;
		p = (SInt *) GetImageData(motA_y) 
					+ mb_y*GetImageSizeX(motA_y)
					+ mb_x;
		mvy = *p;
	
		alpha_smb_mc = AllocImage(mb_s+4,mb_s+4,SHORT_TYPE);

		GetBorderedMC(prev_alpha,x+ox,y+oy,mvx,mvy,MB_SIZE/mb_s,alpha_smb_mc); 

#ifndef _VM7_FILTER_
   		if(mb_s < MB_SIZE)
		{
			DownSampling( alpha_mb_comp, alpha_smb_comp, MB_SIZE, MB_SIZE, MB_SIZE, mb_s );
		} 
		else if(mb_s==MB_SIZE)
		{
			for(ii=0;ii<MB_SIZE*MB_SIZE;ii++) alpha_smb_comp[ii]=alpha_mb_comp[ii];
		} 
		else 
		{
       			 fprintf(stderr,"MB_SIZE=%d mb_s=%d in CAE_MB()\n", MB_SIZE,mb_s);
        		exit(0);
		}
#else
  		ChangeSamplingRate( alpha_mb_comp, alpha_smb_comp, MB_SIZE, MB_SIZE,
                       MB_SIZE, mb_s );
#endif

		p = (SInt *) GetImageData(alpha_smb_mc)+2*GetImageSizeX(alpha_smb_mc)+2;
  		for( ii=0; ii<mb_s; ii++,p+=4) 
			for( jj=0; jj<mb_s; jj++, p++ ) {
    				*p = (SInt) f1bit(alpha_smb_comp[ii*mb_s+jj]);
  			}
	}

	/* TBD: transpose all the data */

	/* SAIT begin */
	/******* comment out 
	bordered_smb_v = AllocSameImage(bordered_smb);
	TransposeImage(bordered_smb,bordered_smb_v);
	*******************/
  	bordered_smb_v = AllocSameImage(bordered_smb_data);
  	TransposeImage(bordered_smb_data,bordered_smb_v);
	/* SAIT end */

	if (shape_inter) {
		alpha_smb_mc_v = AllocSameImage(alpha_smb_mc);
		TransposeImage(alpha_smb_mc,alpha_smb_mc_v);
	}
	
	tmp_mb_type = *mb_type;

  	if(shape_inter)
	{

		/* (horizontal) code INTRA and INTER and choose the best one */
		/* SAIT begin */
		/************* comment out 
	   	h_bits1 = ShapeCodingInterCAE( bordered_smb, alpha_smb_mc, 
						*mb_type, tmp_stream, 1 , send_cr,
						error_res_disable);

      		h_bits2 = ShapeCodingCAE( bordered_smb, *mb_type, 
						h_stream, 1, send_cr,
						error_res_disable );
		***************************/
      		h_bits1 = ShapeCodingInterCAE( bordered_smb_data, alpha_smb_mc,
                                    		*mb_type, tmp_stream, 1 , send_cr,
				    		error_res_disable);
      		h_bits2 = ShapeCodingCAE( bordered_smb_data, *mb_type,
               			                h_stream, 1, send_cr,
						error_res_disable );
		/* SAIT end */


      		if( (h_bits1+offset) < h_bits2 ) {
        		h_bits = h_bits1;
        		h_offset = offset;
        		h_mb_type = 5;
        		for( k=0; k<1024; k++ ) h_stream[k] = tmp_stream[k];
      		} else {
        		h_bits = h_bits2;
        		h_offset = 0;
        		h_mb_type = *mb_type;
      		}
	} 
	else 
	{
		/*SAIT begin */
		/******comment out
		h_bits = ShapeCodingCAE( bordered_smb, *mb_type, h_stream, 1, 
					send_cr, error_res_disable);
		******************/
      		h_bits = ShapeCodingCAE( bordered_smb_data, *mb_type,
               		                h_stream, 1, send_cr,
					error_res_disable);
		/*SAIT end */

	}
				

	if(shape_inter)
	{

		/* (vertical) code INTRA and INTER and choose the best one */
      		v_bits1 = ShapeCodingInterCAE( bordered_smb_v, alpha_smb_mc_v, 
					*mb_type, tmp_stream, 0, send_cr,
					error_res_disable );

		v_bits2 = ShapeCodingCAE( bordered_smb_v, *mb_type, 
					v_stream, 0, send_cr, error_res_disable);

      		if( (v_bits1+offset) < v_bits2 ) {
				v_bits = v_bits1;
				v_offset = offset;
				v_mb_type = 5;
				for( k=0; k<1024; k++ ) v_stream[k] = tmp_stream[k];
      		} else {
				v_bits = v_bits2;
				v_offset = 0;
				v_mb_type = *mb_type;
      		}
	} 
	else
	{
 		v_bits = ShapeCodingCAE( bordered_smb_v, *mb_type, v_stream, 0, 
					send_cr, error_res_disable);
	}		


	/* choose the best representation */

	if((h_bits+h_offset)<(v_bits+v_offset))
	{
			best_bits=h_bits;
			for( k=0; k<512; k++ ) shape_stream[k] = h_stream[k];
			*mb_type = h_mb_type;
	}
	else if((h_bits+h_offset)>(v_bits+v_offset))
	{
			best_bits=v_bits;
			for( k=0; k<512; k++ ) shape_stream[k] = v_stream[k];
			*mb_type = v_mb_type;
	}
	else
	{
			best_bits=h_bits;
			for( k=0; k<512; k++ ) shape_stream[k] = h_stream[k];
			*mb_type = h_mb_type;
	}
  
	if( best_bits > 511 ) 
	{
    			fprintf(stderr,"WARNING:best_bits=%d\n",(int)best_bits); 
	}

	/* SAIT begin */
	/****** comment out
	FreeImage(bordered_smb);
	*****************/
	FreeImage(bordered_smb_data);
	/* SAIT end */

	FreeImage(bordered_smb_v);
	if (shape_inter) {	
		FreeImage(alpha_smb_mc);
		FreeImage(alpha_smb_mc_v);
	}
	
  	return best_bits;
}


/***********************************************************CommentBegin******
 *
 * -- ShapeCodingCAE -- Encodes a binary alpha block using intraCAE.
 *
 * Author :		
 *	Noel Brady Teltec Irl.
 *
 * Created :		
 *	11-04-97
 * 
 * Arguments: 	
 *		alpha_smb 		: alpha macroblock to be coded (subsampled)
 * 		mb_type 			: shape coding mode for the macroblock
 *		bitstream_shape 	: bitstream produced
 *		st						: binary flag specifying whether alpha_smb has
 *											been transposed.
 *		send_cr				: flag which allows CR to be sent.
 *
 * Return values :	
 *
 * Side effects :	
 *	-
 *
 * Description :	A binary alpha block will be coded using cr,st, and bac.
 *								These bits are added to the <bitstream_shape>. 
 *
 * See also :
 *
 ***********************************************************CommentEnd********/


Int ShapeCodingCAE(
		Image *alpha_smb,       				/* in     */
		Int mb_type,                    /* in     */
		UChar *bitstream_shape, /*    out */
		Int           st,
		Int send_cr,
		Int error_res_disable
		)
{
	Image *bitstream;

	Int 	cr=0,
				ret_mb=0,
				i;

	SInt 	*p;

	/* get the parameters for coding */

  if( mb_type == 2 ) cr = 0;         
	if( mb_type == 3 ) cr = 1;
  if( mb_type == 4 ) cr = 2;

	/* do the coding (CR|ST|BAC)*/

	bitstream = BitstreamInit();

	if (send_cr) 
	{
	switch (cr) 
		{
			case 0: BitstreamPutBits(bitstream,0,1);
							break;
			case 1: BitstreamPutBits(bitstream,2,2);
							break;
			case 2:	BitstreamPutBits(bitstream,3,2);
							break;
			default: break;
		}
	}

	BitstreamPutBits(bitstream,st,1);

	ret_mb = CAE_AlphaIntra(alpha_smb, error_res_disable, bitstream);	

	p = (SInt *) GetImageData(bitstream);
	for (i=0;i<ret_mb;i++,p++) bitstream_shape[i] = *p;
	FreeImage(bitstream);

  return ret_mb;
}


/***********************************************************CommentBegin******
 *
 * -- ShapeCodingInterCAE -- Encodes a binary alpha block using interCAE.
 *
 * Author :		
 *	Noel Brady Teltec Irl.
 *
 * Created :		
 *	11-04-97
 * 
 * Arguments: 	
 *		alpha_smb 		: alpha macroblock to be coded (subsampled)
 *		ref_alpha_smb 		: MC alpha macroblock (subsampled)
 * 		mb_type 			: shape coding mode for the macroblock
 *		bitstream_shape 	: bitstream produced
 *		st						: binary flag specifying whether alpha_smb has
 *											been transposed.
 *		send_cr				: flag which allows CR to be sent.
 *
 * Return values :	
 *
 * Side effects :	
 *	-
 *
 * Description :	A binary alpha block will be coded using cr,st, and bac.
 *								These bits are added to the <bitstream_shape>. 
 *
 * See also :
 *
 ***********************************************************CommentEnd********/

Int ShapeCodingInterCAE(
                     Image *alpha_smb,       					/* in     */
                     Image *ref_alpha_smb,    					/* in     */
                     Int mb_type,                    	/* in     */
                     UChar *bitstream_shape, 	/*    out */
                     Int           st,
											Int send_cr,
											Int error_res_disable
                     )
{
	Image *bitstream;

	Int cr=0,
			ret_mb=0,
			i;

	SInt *p;

	/* get the parameters for coding */
  if( mb_type == 2 ) cr = 0;         
	if( mb_type == 3 ) cr = 1;
  if( mb_type == 4 ) cr = 2;


	/* do the coding (CR|ST|BAC)*/
	bitstream = BitstreamInit();

	if (send_cr)
  {
	switch (cr) 
		{
			case 0: BitstreamPutBits(bitstream,0,1);
							break;
			case 1: BitstreamPutBits(bitstream,2,2);
							break;
			case 2:	BitstreamPutBits(bitstream,3,2);
							break;
			default: break;
		}
	}

	BitstreamPutBits(bitstream,st,1);

	ret_mb = CAE_AlphaInter(alpha_smb, ref_alpha_smb, 
														error_res_disable, bitstream);	

	p = (SInt *) GetImageData(bitstream);
	for (i=0;i<ret_mb;i++,p++) bitstream_shape[i] = *p;
	FreeImage(bitstream);

	return ret_mb;			
}


/* Intra CAE applied to a bordered binary alpha block */

Int CAE_AlphaIntra(Image *image, Int error_res_disable, Image *bitstream)
{
	USInt q;

	ArCoder	ar_coder; 

	Int		width = GetImageSizeX(image),
				height = GetImageSizeY(image);

	Int 	i,j,
				context,
				bit;

	SInt 	*p_image = (SInt *) GetImageData(image);

	StartArCoder(&ar_coder,error_res_disable); 
	for (i=2; i<height-2; i++)
		{
	 		for (j=2; j<width-2; j++) 
				{
					bit = *(p_image+i*width+j);
					context = GetContextIntra(p_image,j,i,width-3,2,width);
					q = intra_prob[context];
					ArCodeSymbol(bit,q,&ar_coder,bitstream);
    		}
		}

	StopArCoder(&ar_coder,bitstream); 

	return GetImageSizeX(bitstream);
}

/* Inter CAE applied to a bordered binary alpha block */

Int CAE_AlphaInter(Image *image, Image *mc_image, Int error_res_disable, 
													Image *bitstream)
{
	USInt q;

	ArCoder	ar_coder; 

	Int		width = GetImageSizeX(image),
				height = GetImageSizeY(image);

	Int 	i,j,
				context,
				bit;

	SInt 	*p_image = (SInt *) GetImageData(image),
				*p_mc_image = (SInt *) GetImageData(mc_image);

	StartArCoder(&ar_coder, error_res_disable); 
	for (i=2; i<height-2; i++)
		{
	 		for (j=2; j<width-2; j++) 
				{
					bit = *(p_image+i*width+j);
					context = GetContextInter(p_image,p_mc_image,j,i,width-3,2,width);
					q = inter_prob[context];
					ArCodeSymbol(bit,q,&ar_coder,bitstream);
    		}
		}

	StopArCoder(&ar_coder,bitstream); 

	return GetImageSizeX(bitstream);
}


