/*****************************************************************************
 *
 * This software module was originally developed by
 *
 *   Noel O'Connor (Teltec DCU / ACTS-MoMuSyS)
 *
 * and edited by
 * 
 *   Robert Danielsen (Telenor / ACTS-MoMuSyS)
 *   Michael Frater (UNSW0
 *
 * 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) 1996
 *
 *****************************************************************************/


/***********************************************************HeaderBegin*******
 *                                                                         
 * File:	vm_vop_bound.c
 *
 * Author:	Noel O'Connor Teltec Irl.
 * Created:	3-04-96
 *                                                                         
 * Description: 
 *
 * Notes: 	
 *
 * Modified:	21.04.96 Robert Danielsen: Reformatted. New headers.
 * 		24.10.96 Noel O'Connor: Added SARP for bounding box
 *											extraction
 *              03.01.97 Michael Frater: support for non-8-bit video
 *
 ***********************************************************HeaderEnd*********/

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

#include <stdio.h>
#include "momusys.h"
#include "mom_structs.h"
#include "mom_access.h"
#include "mom_vop.h"
#include "vm_common_defs.h"
#include "vm_vop_bound.h"


/***********************************************************CommentBegin******
 *
 * -- GetVopBounded -- Calculates and extracts the bounding box of this vop
 *
 * Author :		
 *	Noel O'Connor Teltec Irl.
 *
 * Created :		
 *	11-03-96
 *
 * Purpose :		
 *	This function takes as input the vop read from disk and
 * 	calculates and extracts the bounding box of this vop (based
 *	on the vop's alpha plane).
 * 
 * Arguments in : 	
 *	Vop *orig - pointer to original vop
 *	(i.e. vop read from disk),
 *	Int to_nearest_mb  - flag  	( 0 - gives the actual bounded vop )
 *					( 1 - gives the bounded vop extended
 *						to be of size n x 16 )
 *
 * Arguments in/out :	
 *	-
 *
 * Arguments out :	
 *	-
 *
 * Return values :	
 *	Vop *vop_bb - pointer to the bounded vop
 *
 * Side effects :	
 *	Memory is allocated within this function for
 *	the bounded vop
 *
 * Description :	
 *	This function first calculates the vop bounding box co-ordinates
 *	and dimensions. A vop is allocated with these dimensions. The 
 *	required image data is then extracted into this new bounded vop.
 *	Also the alpha plane charateristics (rectangular/arbitrary shape,
 *	binary/grey-level) are determined.
 *
 * See also :
 *	-
 *
 * Modified :		
 *	21.01.97 Added fix for bug VM4-970110-03 by Noel O'Connor
 *
 ***********************************************************CommentEnd********/

Vop *
GetVopBounded(Vop *orig, Int to_nearest_mb, Int even_size, Int *vop_has_content)
{
  Vop *vop_bb;
	
  Image *alpha_image,
    *y_image,
    *u_image,
    *v_image;
	
  SInt *alpha,
    *lum,
    *u,
    *v;
	
  Int x,
    y,
    hor_spat_ref,
    ver_spat_ref,
    i,
    y_ind,
    x_ind,
    width,
    height,
    multiple_of_mb_size,
    inside_alpha_image,
		mod_dim,
    lines,
    pels;	
				
	
  /* Use pointers to vop image data */			
  alpha_image = GetVopA(orig);
  y_image = GetVopY(orig);
  u_image = GetVopU(orig);
  v_image = GetVopV(orig);
	
  /* Use dimensions of alpha plane read from disk */
  x = GetImageSizeX(alpha_image);
  y = GetImageSizeY(alpha_image);
	
  /* Use pointers to YUVa */
  alpha = (SInt *) GetImageData(alpha_image);
  lum = (SInt *) GetImageData(y_image);
  u = (SInt *) GetImageData(u_image);
  v = (SInt *) GetImageData(v_image);
	
			
  /* 
	 *	Call GetBoundingRect() to get the tightest rectangle
	 *		around Vop with the top left co-ordinates modified
	 *		to be even integers 
	 */

	/* Get _tightest_ rectangle */
  multiple_of_mb_size = FALSE;

	/* Don't limit to inside alpha image 			*/
	/* (actually this is only necessary with 	*/
	/* multiple_of_mb_size set to TRUE) 			*/
	inside_alpha_image = FALSE;		

	/* Calc. the bounding box information */
  GetBoundingRect(alpha,x,y,multiple_of_mb_size,
			inside_alpha_image,&hor_spat_ref,&ver_spat_ref,&lines,&pels);

	/* Check for vop content */
	if((pels != 0) && (lines != 0))
		*vop_has_content = TRUE;
	else
		*vop_has_content = FALSE;

	if(*vop_has_content)
	{
		/* Do the Shape Adaptive Region Partitioning if necessary */
		if((hor_spat_ref != 0) || (ver_spat_ref != 0))
			DoSARP(alpha,x,&hor_spat_ref,&ver_spat_ref,&pels,&lines);	

		/* Modify the bounding box width and height to be multiples
				of MB_SIZE */
		if((mod_dim = (pels%MB_SIZE)) != 0)
			pels += (MB_SIZE - mod_dim);
		if((mod_dim = (lines%MB_SIZE)) != 0)
			lines += (MB_SIZE - mod_dim);

		/* Allocate the bounding box */
		vop_bb = AllocVop(pels,lines);
	}
	else   
		vop_bb = SallocVop();
               
  /* Extract image data into this bounded vop */
  width = pels; height = lines;	
   		
  /* Copy data from original vop */
  CopyVopNonImageField(orig,vop_bb);
	
  PutVopHorSpatRef(hor_spat_ref,vop_bb);	/* Spatial ref. of justified 	*/
  PutVopVerSpatRef(ver_spat_ref,vop_bb);	/* vop.	*/
  PutVopWidth(width,vop_bb);		/* Store width and height */
  PutVopHeight(height,vop_bb);		/* as integer multiples of 16 */
  PutVopQuantPrecision(GetVopQuantPrecision(orig), vop_bb);
  PutVopBitsPerPixel(GetVopBitsPerPixel(orig), vop_bb);
	

  /* Extract Y and A image data  */
	
	if (*vop_has_content)
		{
  		i = 0;
  		for(y_ind = ver_spat_ref; y_ind < ver_spat_ref + lines; y_ind++)
    		for(x_ind = hor_spat_ref; x_ind < hor_spat_ref + pels; x_ind++)
      		{
		  if (ValidCoordinate(x_ind,y_ind,x,y)) 
		    {
		      vop_bb->a_chan->f[i] = alpha[x_ind + (y_ind * x)];
		      vop_bb->y_chan->f[i] = lum[x_ind + (y_ind * x)];
		    }
		  
		  i++;
      		}
			
  		/* Extract U and V image data */
  		i = 0;
  		for(y_ind = ver_spat_ref/2; y_ind < (ver_spat_ref/2) + lines/2; y_ind++)
		  for(x_ind = hor_spat_ref/2; x_ind < (hor_spat_ref/2) + pels/2; x_ind++)
		    {
		      if (ValidCoordinate(x_ind,y_ind,x/2,y/2)) 
			{
			  vop_bb->u_chan->f[i] = u[x_ind + (y_ind * (x/2))];
			  vop_bb->v_chan->f[i] = v[x_ind + (y_ind * (x/2))];
			}
		      i++;
		    }
		
		}
	else PutVopArbitraryShape(ARB_SHAPE,vop_bb);
	
  return(vop_bb);
}

/***********************************************************CommentBegin******
 *
 * -- GetBoundingRect -- Obtains the position of the bounding rectangle
 *
 * Author :		
 *	Noel O'Connor Teltec Irl.
 *
 * Created :		
 *	11-03-96
 *
 * Purpose :		
 *	Obtains the position of the bounding rectangle of the shape of a vop.
 * 	Size can includes bottom-left extension to make size MULTIPLE of 16,
 *	and perhaps INSIDE the vop.
 * 
 * Arguments in : 	
 *	SInt	*alpha - pointer to alpha image
 *	Int		vop_width - width of input alpha image
 *	Int		vop_height - height of input alpha image
 *	Int		multiple - when this flag is set the function
 *				returns a BB which is a multiple of MB_SIZE
 *				Otherwise it returns the _exact_ BB.
 *	Int		inside - bounding rectangle inside the alpha plane
 *
 * Arguments in/out :	
 *	-
 *
 * Arguments out :	
 *	Int		*br_x0 - BB horizontal coordinate
 *	Int		*br_y0 - BB vertical coordinate
 *	Int		*br_lines - row size of BB
 *	Int		*br_pels - column size of BB
 *
 * Return values :	
 *	-
 *
 * Side effects :	
 *	-
 *
 * Description :	
 *	-
 *
 * See also :
 *	-
 *
 * Modified :		
 *	
 *
 ***********************************************************CommentEnd********/

Void
GetBoundingRect(
     SInt       *alpha,      /* alpha plane data   */
     Int         vop_width,   /* alpha plane width  */
     Int         vop_height,  /* alpha plane height */
     Int         multiple,    /* 1: dimensions multiple of 16 */
     Int         inside,      /* bounding rectangle (BB) inside 
     				 the alpha plane */
     Int         *br_x0,  	  /* BB horizontal coordinate */
     Int         *br_y0,      /* BB vertical coordinate */
     Int         *br_lines,   /* rows of the BB */
     Int         *br_pels     /* columns of the BB */
     )   
{
#define BIGVALUE 10000
  Int i,j,
    x_min=  BIGVALUE,
    x_max=    -1,
    y_min=  BIGVALUE,
    y_max=    -1;
  
  Int base,kk;


  for(j=0;j<vop_height;j++)
    {
      base=j*vop_width;

      for(i=0;i<vop_width;i++)
	{
	  if(alpha[base+i]!=0)
	    {
	      x_min=(i<x_min)?i:x_min;
	      x_max=(i>x_max)?i:x_max;
	      y_min=(j<y_min)?j:y_min;
	      y_max=(j>y_max)?j:y_max;
	    }
	}
    }

	x_min -= x_min%2;
	y_min -= y_min%2;
	
  if (!((x_min==BIGVALUE)||(x_max==-1)||(y_min==BIGVALUE)||(y_max==-1)))
    {
      *br_x0 = x_min;
      *br_y0 = y_min;
      *br_pels = x_max - x_min +1 ;
      *br_lines  = y_max - y_min +1 ;
     
      /* multiples of 16 */
      if(multiple==1) 
	{
	  if((kk=(*br_lines)%MB_SIZE)!=0)
	    *br_lines+=(MB_SIZE-kk);
	  if((kk=(*br_pels)%MB_SIZE)!=0)
	    *br_pels+=(MB_SIZE-kk);
	}
     
      /* check the bottom side */
      if (inside==1)
	{
	  if ((*br_x0+*br_pels)>vop_width) 
	    {
	      /* fprintf(stderr,"\nGetBoundingRect: fixing right bound!\n\n"); */
	      *br_x0= vop_width - *br_pels ;
	    }
	  /* check the right side */
	  if ((*br_y0+*br_lines)>vop_height)  
	    { 
	      /* fprintf(stderr,"\nGetBoundingRect: fixing bottom bound!\n\n"); */
	      *br_y0= vop_height - *br_lines ;
	    } 
	}
      /* check if position of upper-left corner is inside the VOP */
      if( (*br_x0 <0) || (*br_y0 <0) )
	{
          fprintf(stderr,
		  "\nGetBoundingRect: UNABLE TO FIX POSITION-DIMENSIONS!\n\n");
          fprintf(stderr,
		  "\n                 SETTING br_x0=br_y0=br_pels=br_lines=0 !\n\n");
          *br_x0 = *br_y0 = *br_pels = *br_lines = 0;
	}

    }
  else
    {
      *br_x0 = *br_y0 = *br_pels = *br_lines  = 0;
      fprintf(stderr,"GetBoundingRect: alpha plane without any object!!\n");
    }

 
}/*GetBoundingRect*/


/***********************************************************CommentBegin******
 *
 * -- DoSARP -- Intelligent VOP formation as per VM 4.0 (Shape Adaptive Region
 *							Partitioning)
 *
 * Author :		
 *	Noel O'Connor Teltec Irl.
 *
 * Created :		
 *	11-03-96
 *
 * Purpose :		
 * 	Having found the tightest rectangle for the VOP (with top left coordinates 
 *	suitably adjusted) it is necessary to find the optimal top left co-ordinates
 *	for the bounding box which ensure that the minimum number of macroblocks are 
 *	coded.
 * 
 * Arguments in : 	
 *	SInt 	*alpha - pointer to alpha pixel data (unbounded)
 *	
 * Arguments in/out :	
 *	Int 	*hor_spat_ref - initially x co-ord. of tightest rect. (suitably adjusted)
 *	Int		*ver_spat_ref - initially y co-ord. of tightest rect. (suitably adjusted)
 *	Int		*vop_width - initially width of tightest rectangle
 *	Int		*vop_height - initially height of tightest rectangle
 *
 * Arguments out :	
 *	-
 *
 * Return values :	
 *	none
 *
 * Side effects :	
 *	-
 *
 * Description :	
 *	This code is based on the source code distributed by Gwang Hoon Park of Hyundai
 *	(11/10/96)
 *
 * See also :
 *	-
 *
 * Modified :		
 *	
 *
 ***********************************************************CommentEnd********/
Void
DoSARP(SInt *alpha,					/* Pointer to alpha plane image dat */
				Int dim_x,					/* X dimension of alpha plane on disk */
				Int *hor_spat_ref,	/* current hor. spatial reference */
				Int *ver_spat_ref,	/* current ver. spatial referance */
				Int *vop_width,			/* current bb width */
				Int *vop_height)		/* current bb height */
{
	Int		N = MB_SIZE,
				search_step = 2,
				min_block_number = 9999,
				block_number,
				GS_x, GS_y,
				SARP_grid_x=0, SARP_grid_y=0,
				move_SARP_grid_x, move_SARP_grid_y,
				start_x, start_y,
				x,y,xx,yy,
				check;

	/* Get starting co-ordinates for loop */ 
	start_x = (*hor_spat_ref>=0 && *hor_spat_ref<N) ? -(*hor_spat_ref) : -(N-2);
	start_y = (*ver_spat_ref>=0 && *ver_spat_ref<N) ? -(*ver_spat_ref) : -(N-2);
	
	/* Offset for location of tightest rectangle in alpha image */
	start_x += (*hor_spat_ref);
	start_y += (*ver_spat_ref);

	/* For each control point */	
	for(GS_y = start_y; GS_y <= (*ver_spat_ref); GS_y += search_step)
		for(GS_x = start_x; GS_x <= (*hor_spat_ref); GS_x += search_step)
		{
			block_number = 0;
			
			/* For each MB in bb defined by control point */
			for(y = GS_y; y< GS_y + ((*vop_height)+N); y+=N)
				for(x = GS_x; x< GS_x + ((*vop_width)+N); x+=N)
				{
					check = 0;
					      
					/* for upper and lower side boundary pixels of each macroblock*/
				
      		for ( yy = 0; yy < N; yy+=(N-1)) 
					{
        		if (check) break;
        		for ( xx = 0; xx < N; xx++)
          		if((x+xx >= (*hor_spat_ref)) && (y+yy >= (*ver_spat_ref))
								 	&& (x+xx < ((*hor_spat_ref) + (*vop_width))) 
									&& (y+yy < ((*ver_spat_ref) + (*vop_height))))
             		if (alpha[((y+yy) * dim_x) + (x+xx)]) 
								{
                 		block_number ++;
                 		check ++;
                 		break;
                }
         	}
				
      		/* for left and right side boundary pixels of each macroblock*/
				
       		if(check==0) 
					{
         		for ( xx = 0; xx < N; xx+=(N-1)) 
						{
           		if (check) break;
           		for ( yy = 1; yy < N-1; yy++)
             		if((x+xx >= (*hor_spat_ref)) && (y+yy >= (*ver_spat_ref))
									&& (x+xx < ((*hor_spat_ref) + (*vop_width))) 
									&& (y+yy < ((*ver_spat_ref) + (*vop_height))))
                if (alpha[((y+yy) * dim_x) + (x+xx)]) 
								{	
                    block_number ++;
                    check ++;
                    break;
                }
            }
          }
   			}

			if(block_number <= min_block_number)
			{
				SARP_grid_x = GS_x;	
				SARP_grid_y = GS_y;
				min_block_number = block_number;

			}
		}

	/* Calculate relative shift of co-ord. for new bb */
	move_SARP_grid_x = (*hor_spat_ref) - SARP_grid_x;
	move_SARP_grid_y = (*ver_spat_ref) - SARP_grid_y;	

	/* Return bb co-ordinates based on minimum */		
	(*hor_spat_ref) = SARP_grid_x;
	(*ver_spat_ref) = SARP_grid_y;
	(*vop_width) += move_SARP_grid_x;
	(*vop_height) += move_SARP_grid_y;
	
	return;
}
 
