/*****************************************************************************
 *                                                                          
 * This software module was developed by 
 *
 *  Sung gul Ryoo  (SAMSUNG A.I.T)                                                    
 *
 * 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
 *
 *****************************************************************************/

/*****************************************************************************/
/*                                                                           */
/*      Copyright (C) 1998 SAMSUNG A.I.T.  All Rights Reserved.              */
/*                                                                           */
/*****************************************************************************/
 
#include "momusys.h"
#include "mom_vop.h"
#include "vm_config.h"
#include "mom_vol.h"
#include "mom_structs.h"
#include "vm_enc_defs.h"
#include "io_generic.h"
#include "rc.h"
#include "bitmodel.h"
#include "saitlut.h"
#include "sait_rc.h"
#include "text_code.h"
#include "vm_vop_bound.h"
#include "vm_vop_code.h"
#include "alp_code_header.h"
#include "alp_code_mom.h"
#include "mot_util.h"

extern Int   QPfirst[MAX_NUM_VOS][MAX_NUM_VOLS];

/* inserted by SAMSUNG AIT */
Int TC[MAX_NUM_VOS][1000];
Int CC[MAX_NUM_VOS][1000];
Int VC[MAX_NUM_VOS][1000][6];
Int NumMbs[64];
Int VOPHb[32][17];
Int mQP[MAX_NUM_VOS][1000];
Int RefQP[8] = { -4, -2, -1, 0, 1, 2, 3, 4 };
Int InterQP;
/* inserted by SAMSUNG AIT */


/***********************************************************CommentBegin******
 *
 * -- DoAlphaUVPlane -- Downsample the alpha_plane
 *
 * Author :
 *      UPM - Angel Pacheco / Fernando Jaureguizar
 *
 * Created :
 *      30.08.96
 *
 * Purpose :
 *      Downsample the alpha_plane and computes the subsampled alpha plane for
 *      UV data filled with B_TRANSPARENT,B_OPAQUE and B_BOUNDARY data for
 *      each pixel, creating a subsampled alpha plane (UV pixel units) too.
 *
 * Arguments in :
 *      SInt     *alpha_plane,  original alpha plane
 *      Int      w,             original width  (Y or Alpha)
 *      Int      h,             original height (Y or Alpha)
 *
 * Arguments in/out :
 *
 *
 * Arguments out :
 *      SInt     *alpha_sub     subsampled alpha plane (UV pixel units)
 *      SInt     **alpha_plane  subsampled and filled alpha plane (block units)
 *
 * Return values :
 *       1 on success, -1 on error
 *
 * Side effects :
 *
 *
 * Description :
 *    1) Allocates memory for alp_plane
 *    2) Subsamples the alpha plane symply deleting the even lines and cols.
 *    3) Allocates memory for *alp_plane array
 *
 * See also :
 *
 *
 * Modified :
 *
 *      10/22/96: Angel Pacheco
 *                OR between the alpha pixels for subsampling
 *
 ***********************************************************CommentEnd********/
 

Int 
GetMBQuant(Int vo_id, Int mb_num)
{
  return (mQP[vo_id][mb_num]);
}


Int
DoAlphaUVPlane(
      SInt *alpha,     /* <- original alpha plane                            */
      Int w,           /* <- original width  (Y or Alpha)                    */
      Int h,           /* original height (Y or Alpha)                       */
      SInt *alpha_sub, /* subsampled alpha plane (UV pixel units)            */
      SInt **alpha_uv  /* subsampled and filled alpha plane (block units)    */
               )
{
  Int    i, j;
  Int    rows, base;
  Int	 BB_SIZE=8;
 
/* resample the alpha plane */
  rows=w;
  h=h/2;
 
  w/=2;
  for(j=0;j<h;j++)
    {
    base=j*w;
    for(i=0;i<w;i++)
      {
      alpha_sub[base+i]= (alpha[ 2*j   *rows+2*i]+alpha[ 2*j   *rows+2*i+1]+
                          alpha[(2*j+1)*rows+2*i]+alpha[(2*j+1)*rows+2*i+1]) / 4;
      }
    }
 
/* compute the alpha plane, in block units, with the values B_TRANSPARENT,
   B_OPAQUE, B_BOUNDARY if alpha_uv!=NULL (==NULL if this array will not be
   used) */
 
  if(alpha_uv!=NULL)
    {
    if (((*alpha_uv)=(SInt*)malloc(h/BB_SIZE*w/BB_SIZE*sizeof(SInt)))==NULL)
      {
      fprintf(stderr,"DoAlphaUVPlane: malloc failed\n");
      return(-1);
      }
 
    subsamp_alpha(alpha_sub,  w, h, 1/* block-based */, *alpha_uv);
    }
 
  return 1;
}


/* inserted by SAMSUNG AIT */
void compute_VC(
		Image *prev_ori_alpha, 
		Image *alpha_decisions,
		Image *MB_decisions,
		Vop *vop,
		Int vo_id,
		Int vo_type) /* compute Variance Class */
{
	SInt  *Y, *U, *V;
	Float *fY, *fU, *fV;
	Int		width, height;
	Int		x, y, i, j, p1, count;
	Int var=0, s, s2;
	Int variance; 
	/* for alpha data */
	Image * alpha_sub_ima;
	SInt	
				* a_sub,
				* alpha,
				* alpha_uv,
				* alpha_sub;
	SInt	*mb_decisions;
	Int		a_sub_pos, a_w, a_h, h_size, w_size, uv_w_size;

	width = GetImageSizeX(GetVopY(vop));
	height = GetImageSizeY(GetVopY(vop));
	
	count = 0;

	a_sub = (SInt *)GetImageData(alpha_decisions);
	alpha = (SInt *)GetImageData(prev_ori_alpha);
	mb_decisions  = (SInt *)GetImageData(MB_decisions);
	
	a_w = width; a_h = height;
	alpha_sub_ima = AllocImage(a_w/2,a_h/2,SHORT_TYPE);
	alpha_sub = (SInt *)GetImageData(alpha_sub_ima);

	if(DoAlphaUVPlane(alpha, a_w, a_h, alpha_sub, &alpha_uv)!=1)
	{
		fprintf(stderr, "compute_VC : error in DoAlphaUVPlane() \n");
		return ;
	}

	w_size = GetImageSizeX(GetVopY(vop))/16;	/* 16 = MB_SIZE */
	h_size = GetImageSizeY(GetVopY(vop))/16;	/* 16 = MB_SIZE */
	uv_w_size = a_w/8/2;											/* 8 = B_SIZE */
	
	switch (GetImageType(vop->y_chan)) 
	{
		case SHORT_TYPE:
			Y = GetImageIData(vop->y_chan);
			U = GetImageIData(vop->u_chan);
			V = GetImageIData(vop->v_chan);
			for (y=0; y<height; y+=16)
			{
				for(x=0; x<width; x+=16)
				{
					for(p1=0; p1<6; p1++)
					{
						s = s2 = 0;
						for(i=0; i<8; i++)
						{
							for(j=0; j<8; j++)
							{
								switch(p1)
								{
									case 0:
										var = *(Y+width*(y+i) + x+j);
										break;
									case 1:
										var = *(Y+width*(y+i) + x+j+8);
										break;
									case 2:
										var = *(Y+width*(y+i+8) + x+j);
										break;
									case 3:
										var = *(Y+width*(y+i+8) + x+j+8);
										break;
									case 4:
										var = *(U+(width/2)*(y/2+i) + x/2+j);
										break;
									case 5:
										var = *(V+(width/2)*(y/2+i) + x/2+j);
										break;
								}
								s += var;
								s2 += var*var;
							}
						}
						var = (Int)((float)s2/64.0 - ((float)s/64.0)*((float)s/64.0));
						if(vo_type == 0)
						{
							if(var<16)         variance=0;
							else if(var<32)    variance=1;
							else if(var<64)    variance=2;
							else if(var<128)   variance=3;
							else if(var<192)   variance=4;
							else if(var<384)   variance=5;
							else if(var<512)   variance=6;
							else if(var<768)   variance=7;
							else if(var<1024)  variance=8;
							else if(var<1280)  variance=9;
							else if(var<1792)  variance=10;
							else if(var<2048)  variance=11;
							else if(var<3072)  variance=12;
							else if(var<4096)  variance=13;
							else if(var<6154)  variance=14;
							else               variance=15;
						}
						else
						{
							if(var<8)         variance=0;
      				else if(var<16)   variance=1;
      				else if(var<32)   variance=2;
      				else if(var<48)   variance=3;
      				else if(var<64)   variance=4;
      				else if(var<96)   variance=5;
      				else if(var<128)  variance=6;
      				else if(var<160)  variance=7;
      				else if(var<192)  variance=8;
      				else if(var<256)  variance=9;
      				else if(var<320)  variance=10;
      				else if(var<448)  variance=11;
      				else if(var<640)  variance=12;
      				else if(var<896)  variance=13;
      				else if(var<1536) variance=14;
      				else              variance=15;
						}
						VC[vo_id][count][p1] = variance;
					}
      		count++;
				}
			}
			count = 0;
			for(j=0; j<h_size; j++)
			{
				for(i=0; i<w_size; i++)
				{
					if( mb_decisions[j*w_size+i] == 2) /* transparent MB */
					{
						VC[vo_id][count][0] = 16;	
						VC[vo_id][count][1] = 16;	
						VC[vo_id][count][2] = 16;	
						VC[vo_id][count][3] = 16;	
						VC[vo_id][count][4] = 16;	
						VC[vo_id][count][5] = 16;	
					}
					count++;
					
				}
			}
			break;
		case FLOAT_TYPE:
			fY = GetImageFData(vop->y_chan);
			fU = GetImageFData(vop->u_chan);
			fV = GetImageFData(vop->v_chan);
			for (y=0; y<height; y+=16)
			{
				for(x=0; x<width; x+=16)
				{
					for(p1=0; p1<6; p1++)
					{
						s = s2 = 0;
						for(i=0; i<8; i++)
						{
							for(j=0; j<8; j++)
							{
								switch(p1)
								{
									case 0:
										var = *(fY+width*(y+i) + x+j);
										break;
									case 1:
										var = *(fY+width*(y+i) + x+j+8);
										break;
									case 2:
										var = *(fY+width*(y+i+8) + x+j);
										break;
									case 3:
										var = *(fY+width*(y+i+8) + x+j+8);
										break;
									case 4:
										var = *(fU+(width/2)*(y/2+i) + x/2+j);
										break;
									case 5:
										var = *(fV+(width/2)*(y/2+i) + x/2+j);
										break;
								}
								s += var;
								s2 += var*var;
							}
						}
						var = (Int)((float)s2/64.0 - ((float)s/64.0)*((float)s/64.0));
						if(var<16)         variance=0;
						else if(var<32)    variance=1;
						else if(var<64)    variance=2;
						else if(var<128)   variance=3;
						else if(var<192)   variance=4;
						else if(var<384)   variance=5;
						else if(var<512)   variance=6;
						else if(var<768)   variance=7;
						else if(var<1024)  variance=8;
						else if(var<1280)  variance=9;
						else if(var<1792)  variance=10;
						else if(var<2048)  variance=11;
						else if(var<3072)  variance=12;
						else if(var<4096)  variance=13;
						else if(var<6154)  variance=14;
						else               variance=15;
						VC[vo_id][count][p1] = variance;
					}
      		count++;
				}
			}
			count = 0;
			for(j=0; j<h_size; j++)
			{
				for(i=0; i<w_size; i++)
				{
					a_sub_pos = w_size*2*j*2 + i*2;
					if(a_sub[a_sub_pos] == 0) VC[vo_id][count][0] = 16;	/* 0 == B_TRANSPARENT */
					a_sub_pos++;
					if(a_sub[a_sub_pos] == 0) VC[vo_id][count][1] = 16;	/* 0 == B_TRANSPARENT */
					a_sub_pos = w_size*2*(j*2+1) + i*2;
					if(a_sub[a_sub_pos] == 0) VC[vo_id][count][2] = 16;	/* 0 == B_TRANSPARENT */
					a_sub_pos++;
					if(a_sub[a_sub_pos] == 0) VC[vo_id][count][3] = 16;	/* 0 == B_TRANSPARENT */
					a_sub_pos = uv_w_size*j+i;
					if(alpha_uv[a_sub_pos] == 0) VC[vo_id][count][4] = 16;	/* 0 == B_TRANSPARENT */
					if(alpha_uv[a_sub_pos] == 0) VC[vo_id][count][5] = 16;	/* 0 == B_TRANSPARENT */
					count++;
				}
			}
			break;
	  case UCHAR_TYPE:
	    break;

	}
}

void compute_TC(Vop *vop, Int vo_id) /* compute Texture Class */
{
	SInt  *Y, *U, *V;
	Float *fY, *fU, *fV;
	Int		width, height;
	Int		x, y, i, j;
	Int y1, u1, v1, yh, uh, vh, yv, uv, vv;   /* pixel value */
  Int index, count;        /* LUT index */
  Int texture;      /* temporal texture variable */
  Int dh, dv;       /* accumulated horizontal and vertical differences */
  Int ydiff, udiff, vdiff;  /* color difference for each y, u, v channel */
	Float SumTC;

	width = GetImageSizeX(GetVopY(vop));
	height = GetImageSizeY(GetVopY(vop));

	count = 0;
	SumTC = 0;

	switch (GetImageType(vop->y_chan)) 
	{
		case SHORT_TYPE:
			Y = GetImageIData(vop->y_chan);
			U = GetImageIData(vop->u_chan);
			V = GetImageIData(vop->v_chan);
			for (y=0; y<height; y+=16)
			{
				for(x=0; x<width; x+=16)
				{
					dh = 0; dv = 0;
					for(i=0; i<15; i++)
					{
						for(j=0; j<15; j++)
						{
							y1 = *(Y+width*(y+i)+x+j);
							u1 = *(U+((width/2)*((y+i)/2))+(x+j)/2);
							v1 = *(V+((width/2)*((y+i)/2))+(x+j)/2);

          		yh = *(Y+width*(y+i)+x+j+1);
          		uh = *(U+((width/2)*((y+i)/2))+(x+j+1)/2);
          		vh = *(V+((width/2)*((y+i)/2))+(x+j+1)/2);
 
          		yv = *(Y+width*(y+i+1)+x+j);
          		uv = *(U+((width/2)*((y+i+1)/2))+(x+j)/2);
          		vv = *(V+((width/2)*((y+i+1)/2))+(x+j)/2);
 
          		index = (y1/32)*64 + (u1/32)*8 + (v1/32);
 
          		ydiff = abs(y1 - yh);
          		udiff = abs(u1 - uh);
          		vdiff = abs(v1 - vh);
         
          		if( (ydiff > LUT[index][0]) || (udiff > LUT[index][2]) || (vdiff > LUT[index][4]) ) dh++;
         
          		ydiff = abs(y1 - yv);
          		udiff = abs(u1 - uv);
          		vdiff = abs(v1 - vv);
         
          		if( (ydiff > LUT[index][0]) || (udiff > LUT[index][2]) || (vdiff > LUT[index][4]) ) dv++;
        		}
					}
      		texture = dh + dv;
      		if( texture < 64 )          /* 64 */
        		TC[vo_id][count] = 0;
      		else if( texture < 128 )    /* 128 */
        		TC[vo_id][count] = 1;
      		else if( texture < 224 )    /* 224 */
        		TC[vo_id][count] = 2;
      		else
						TC[vo_id][count] = 3;
					/* modified by SAIT 97. 5. 13. */
					TC[vo_id][count] = texture/50;
					if(TC[vo_id][count] > 7) TC[vo_id][count] = 7;
					/* modified by SAIT 97. 5. 13. */
					SumTC += TC[vo_id][count];
      		count++;
				}
			}
			SumTC /= count;
			break;
		case FLOAT_TYPE:
			fY = GetImageFData(vop->y_chan);
			fU = GetImageFData(vop->u_chan);
			fV = GetImageFData(vop->v_chan);
			for (y=0; y<height; y+=16)
			{
				for(x=0; x<width; x+=16)
				{
					dh = 0; dv = 0;
					for(i=0; i<16; i++)
					{
						for(j=0; j<16; j++)
						{
							y1 = *(fY+width*(y+i)+x+j);
							u1 = *(fU+((width/2)*((y+i)/2))+(x+j)/2);
							v1 = *(fV+((width/2)*((y+i)/2))+(x+j)/2);

          		yh = *(fY+width*(y+i)+x+j+1);
          		uh = *(fU+((width/2)*((y+i)/2))+(x+j+1)/2);
          		vh = *(fV+((width/2)*((y+i)/2))+(x+j+1)/2);
 
          		yv = *(fY+width*(y+i+1)+x+j);
          		uv = *(fU+((width/2)*((y+i+1)/2))+(x+j)/2);
          		vv = *(fV+((width/2)*((y+i+1)/2))+(x+j)/2);
 
          		index = (y1/32)*64 + (u1/32)*8 + (v1/32);
 
          		ydiff = abs(y1 - yh);
          		udiff = abs(u1 - uh);
          		vdiff = abs(v1 - vh);
         
          		if( (ydiff > LUT[index][0]) || (udiff > LUT[index][2]) || (vdiff > LUT[index][4]) ) dh++;
         
          		ydiff = abs(y1 - yv);
          		udiff = abs(u1 - uv);
          		vdiff = abs(v1 - vv);
         
          		if( (ydiff > LUT[index][0]) || (udiff > LUT[index][2]) || (vdiff > LUT[index][4]) ) dv++;
        		}
					}
      		texture = dh + dv;
      		if( texture < 64 )          /* 64 */
        		TC[vo_id][count] = 0;
      		else if( texture < 128 )    /* 128 */
        		TC[vo_id][count] = 1;
      		else if( texture < 224 )    /* 224 */
        		TC[vo_id][count] = 2;
      		else
						TC[vo_id][count] = 3;
      		count++;
				}
			}
			break;

	  case UCHAR_TYPE:
	    break;
	}
}

void compute_CC(Vop *vop, Int vo_id) /* compute Color Class */
{
	SInt  *Y, *U, *V;
	Int		width, height;
	Int		x, y, i, j, p1, count;
	Int y1=0, u1=0, v1=0; 			/* pixel value */
	Int index;						/* LUT index  */
	Int color;						/* temporal color variable */
	Int tmpcolor;
	Int dy, du, dv;				/* accumulated horizontal and vertical differences */
	Int dy1, du1, dv1;				/* accumulated horizontal and vertical differences */

	width = GetImageSizeX(GetVopY(vop));
	height = GetImageSizeY(GetVopY(vop));

	count = 0;
	Y = GetImageIData(vop->y_chan);
	U = GetImageIData(vop->u_chan);
	V = GetImageIData(vop->v_chan);
	for (y=0; y<height; y+=16)
	{
		for(x=0; x<width; x+=16)
		{
			CC[vo_id][count] = 20;
			for(p1=0; p1<4; p1++)
      {
        color = 0;
        tmpcolor = 0;
        dy = 0; du = 0; dv = 0;
        for(i=0; i<8; i++)
        {
          for(j=0; j<8; j++)
          {
            switch(p1)
            {
              case 0:
                y1 = *(Y+width*(y+i)+x+j);
                u1 = *(U+((width/2)*((y+i)/2))+(x+j)/2);
                v1 = *(V+((width/2)*((y+i)/2))+(x+j)/2);
                break;
              case 1:
                y1 = *(Y+width*(y+i)+x+j+8);
                u1 = *(U+((width/2)*((y+i)/2))+(x+j+8)/2);
                v1 = *(V+((width/2)*((y+i)/2))+(x+j+8)/2);
                break;
              case 2:
                y1 = *(Y+width*(y+i+8)+x+j);
                u1 = *(U+((width/2)*((y+i+8)/2))+(x+j)/2);
                v1 = *(V+((width/2)*((y+i+8)/2))+(x+j)/2);
                break;
              case 3:
                y1 = *(Y+width*(y+i+8)+x+j+8);
                u1 = *(U+((width/2)*((y+i+8)/2))+(x+j+8)/2);
                v1 = *(V+((width/2)*((y+i+8)/2))+(x+j+8)/2);
                break;
            }
            dy += y1; du += u1; dv += v1;
          }
        }
        dy /= 64;
        du /= 64;
        dv /= 64;
 
        index = (dy/32)*64 + (du/32)*8 + (v1/32);
        dy = LUT[index][0];
        dy1 = LUT[index][1];
        du = LUT[index][2];
        du1 = LUT[index][3];
        dv = LUT[index][4];
        dv1 = LUT[index][5];
 
        color = (int)sqrt(dy*dy1+du*du1+dv*dv1);
        color /= 2;
				if(color < 3) color = 0;
        else if(color > 10) color = 7;
        else color -= 3;
        if(color>7) color = 7;
 
        CC[vo_id][count] = (CC[vo_id][count] > color) ? color : CC[vo_id][count];
			}
      count++;
		}
	}
}


/*
 estimate VOP texture bit from bitmodel 
 and previous reference quantization stepsize
*/
Int estimation(Int vo_id, Int vopcount, Int vo_type)
{
	Int i, n;
	Int qp, bv;
	Int fe;

	fe = 0;
	for (i=0; i<vopcount; i++)
	{		
		qp = mQP[vo_id][i];	/* hvs class */
		for(n=0;n<6;n++)
		{
			bv = VC[vo_id][i][n];	/* variance class */
			fe += BitModelI[qp][bv];
		}
	}
	return fe;
}

/* inserted by SAMSUNG AIT */
Void SetMB_QP(Int vo_id, Int QP, Int Height, Int Width, Int vo_type)
{
	Int i, j;
	Int diff;

	for(i=0; i<Height; i++)
	{
		for(j=0; j<Width; j++)
		{
			if(vo_type == 0)
			{
				if(i==0 && j==0)
				{
					if((RefQP[TC[vo_id][i*Width+j]]+(CC[vo_id][i*Width+j]-4)) > 2) diff = 2;
					else if((RefQP[TC[vo_id][i*Width+j]]+(CC[vo_id][i*Width+j]-4)) < -2) diff = -2;
					else diff = RefQP[TC[vo_id][i*Width+j]]+(CC[vo_id][i*Width+j]-4);
					mQP[vo_id][i*Width+j] = QP+diff;
				}
				else
				{
					mQP[vo_id][i*Width+j] = QP+RefQP[TC[vo_id][i*Width+j]]+(CC[vo_id][i*Width+j]-4);
					if((mQP[vo_id][i*Width+j-1] - mQP[vo_id][i*Width+j]) > 2) mQP[vo_id][i*Width+j] = mQP[vo_id][i*Width+j-1] - 2;
					else if((mQP[vo_id][i*Width+j-1] - mQP[vo_id][i*Width+j]) < -2) mQP[vo_id][i*Width+j] = mQP[vo_id][i*Width+j-1] + 2;
				}
			}
			else
				mQP[vo_id][i*Width+j] = QP+RefQP[TC[vo_id][i*Width+j]]+(CC[vo_id][i*Width+j]-4);
			if(mQP[vo_id][i*Width+j]<1) mQP[vo_id][i*Width+j] = 1;
			else if(mQP[vo_id][i*Width+j]>31) mQP[vo_id][i*Width+j] = 31;
		}
	}	
}

Void SetFirstIntraQPs(Int numVOs, Int QP, Int *Height, Int *Width, Int BitPerFrame, Int shape_bits)
{
	Int TargetBit, EstimatedBit;
	Int Diff, PreDiff;
	Int MinErrorQ=0, i;
	Int vo_id;

	PreDiff = 1000000;

	if(QP == 0)
		TargetBit = BitPerFrame * 9;
	else
		TargetBit = BitPerFrame * 4;

	TargetBit -= shape_bits;

	EstimatedBit = 0;

	for(i=-3; i<36; i++)
	{
		EstimatedBit = 0;
		for(vo_id=0; vo_id<numVOs; vo_id++)
		{
			SetMB_QP(vo_id, i, Height[vo_id]/16, Width[vo_id]/16, 0);
			EstimatedBit += estimation(vo_id,((Height[vo_id]*Width[vo_id])/(16*16)),0);
		}
		Diff = abs(TargetBit - EstimatedBit);
		if(Diff < PreDiff)
		{
			PreDiff = Diff;
			MinErrorQ = i;
		}
	}
	for(vo_id=0; vo_id<numVOs; vo_id++)
	{
		SetMB_QP(vo_id, MinErrorQ, Height[vo_id]/16, Width[vo_id]/16, 0);
		QPfirst[vo_id][0] = mQP[vo_id][0];
	}
	InterQP = MinErrorQ;
}

Void SetFirstIntraQP(Vop *curr, VolConfig *vol_config, Int vo_id, Int vol_id)
{
	PutVopIntraQuantizer(mQP[vo_id][0], curr);
	PutVopQuantizer(mQP[vo_id][0], curr);
	PutVolConfigIntraQuantizer(mQP[vo_id][0], vol_config);
	PutVolConfigQuantizer(mQP[vo_id][0], vol_config);
	QPfirst[vo_id][vol_id] = mQP[vo_id][0];
}

Void SetFirstInterQP(Vop *curr, VolConfig *vol_config, Int vo_id, Int vol_id)
{
	Int num_lines  = (Int) GetImageSizeY(GetVopY(curr));
	Int num_pixels = (int) GetImageSizeX(GetVopY(curr));
	Int MB_width = num_pixels / 16;
	Int MB_height = num_lines / 16;

	compute_TC(curr, vo_id);
	compute_CC(curr, vo_id);

	SetMB_QP(vo_id, InterQP, MB_height, MB_width, 0);

	PutVopQuantizer(mQP[vo_id][0], curr);
	PutVolConfigQuantizer(mQP[vo_id][0], vol_config);
	QPfirst[vo_id][vol_id] = mQP[vo_id][0];
}

Void HVSClassification(Vop *curr, Vop *rec_curr, VolConfig *vol_config, Int vo_id)
{
	Int num_lines  = (Int) GetImageSizeY(GetVopY(curr));
	Int num_pixels = (int) GetImageSizeX(GetVopY(curr));
	SInt *alpha_rec;
	Image *MB_decisions;
	Image *alpha_decisions;
	
	alpha_decisions = AllocImage(num_pixels/8,num_lines/8,SHORT_TYPE);
	MB_decisions  = AllocImage(num_pixels/16, num_lines/16, SHORT_TYPE);

	alpha_rec = (SInt*) GetImageData(GetVopA(rec_curr));
	
	FillMB_decisions(alpha_rec, num_pixels, num_lines, MB_decisions, alpha_decisions);
	
	compute_TC(curr, vo_id);
	compute_CC(curr, vo_id);
	compute_VC(curr->a_chan, alpha_decisions, MB_decisions, curr, vo_id, 0);
}

/***************************************************************************
  GetShapeBits() - Multiple VO rate control module
  inserted by SAMSUNG AIT & Mistubishi 
***************************************************************************/

Void
SetFirstIntraQuantizer(Vol *base_layer,
            VolConfig *layer_config,
            Int frame,
			Float time,
	    Int NumVOs)
{
 
        Vop             *curr_vop_in,
                        *curr_vop_bb,
                        *rec_curr=NULL;
 
        Int             shape, vop_has_content, alpha_th;
	Int		targetbits=0;

	Image         **Shape_stream, **First_stream;

	Int		vo_id, i;
	Int		shape_bits = 0;
	Int		Height[MAX_NUM_VOS], Width[MAX_NUM_VOS];
	Int		QP=0;
 
 
        curr_vop_in = GetVolVop(base_layer);
	/* Set alpha coding threshold */
	alpha_th = GetVolConfigAlphaTh(layer_config);

	for(vo_id=0; vo_id<NumVOs; vo_id++)
	{
		ReadVopGeneric(GetVolConfigY(layer_config),
                        GetVolConfigU(layer_config),
                        GetVolConfigV(layer_config),
                        GetVolConfigA(layer_config),
                        frame,
                        IO_FORMAT,
                        curr_vop_in);
		
		/* Disable shape coding if required */
		shape = GetVolConfigShape(layer_config);
 
		PutVopShape(shape,curr_vop_in);
		if(shape == BINARY)
		{
			PutVopBinaryShape(BINARY,curr_vop_in);

			curr_vop_bb = GetVopBounded(curr_vop_in,1,1,&vop_has_content);
 
			rec_curr = SetUpRecVop(curr_vop_bb);

			Width[vo_id] = GetVopWidth(curr_vop_bb);
			Height[vo_id] = GetVopHeight(curr_vop_bb);
			QP = GetVopIntraQuantizer(curr_vop_bb);


			AllocShapePacket(curr_vop_bb);

			/* Allocate shape stream */
  			First_stream = (Image **)calloc((GetVopWidth(curr_vop_bb)/16)*
                                  (GetVopHeight(curr_vop_bb)/16), sizeof(Image *));
  			Shape_stream = (Image **)calloc((GetVopWidth(curr_vop_bb)/16)*
                                  (GetVopHeight(curr_vop_bb)/16), sizeof(Image *));
  			for(i=0; i<(GetVopWidth(curr_vop_bb)/16)*(GetVopHeight(curr_vop_bb)/16); i++)
    		{
      			First_stream[i] = BitstreamInit();
      			Shape_stream[i] = BitstreamInit();
    		}

        	shape_bits += VopShapeCode(curr_vop_bb,
                              rec_curr,
                              (Vop *)NULL,
                              vo_id,
                              alpha_th,
                              (Image *)NULL,
                              (Image *)NULL,
                              (Image *)NULL,
                              (Image *)NULL,
                              First_stream,
                              Shape_stream);

			/* Free shape stream */
  			for(i=0; i<(GetVopWidth(curr_vop_bb)/16)*(GetVopHeight(curr_vop_bb)/16); i++)
    		{
      			BitstreamFree(First_stream[i]);
      			BitstreamFree(Shape_stream[i]);
    		}
  			free(First_stream);
  			free(Shape_stream);
		}
		else
		{
			curr_vop_bb = GetVopBounded(curr_vop_in,1,1,&vop_has_content);
 
			rec_curr = SetUpRecVop(curr_vop_bb);

			Width[vo_id] = GetVopWidth(curr_vop_bb);
			Height[vo_id] = GetVopHeight(curr_vop_bb);
			QP = GetVopIntraQuantizer(curr_vop_bb);
		}
		HVSClassification(curr_vop_bb, rec_curr, layer_config, vo_id);
		targetbits = (int)((Int)GetVolConfigBitrate(layer_config)/(Float)GetVolConfigFrameRate(layer_config));
	}
	SetFirstIntraQPs(NumVOs, QP, Height, Width, targetbits, shape_bits);
}


