/*********************************************************************************

Copyright(c) 2007 Analog Devices, Inc. All Rights Reserved. 

This software is proprietary and confidential.  By using this software you agree
to the terms of the associated Analog Devices License Agreement.  

Description:
        Contains functions to process video buffers
				
*********************************************************************************/

/*****************************************************************************\

  Includes:

\*****************************************************************************/

#include <drivers/adi_dev.h>

#include <devicecontrol.h>



/****************************************************************************
*
*	  Defines
*
****************************************************************************/

/*To make sure that you are bounding your inputs in the range of 0 & 255*/
#define CLIP8BIT(x) ((unsigned int) x <= 255 ? x : (x < 0 ? 0: 255))

/*****************************************************************************\

  Declarations:

\*****************************************************************************/

tRGBConversion RGBParams, *pRGBParams;

/*****************************************************************************\

  Externs:

\*****************************************************************************/
extern void _rgb_conv(tRGBConversion* pRGBConvert);   

/****************************************************************************
*
*	  Function prototypes
*
****************************************************************************/

void convert_yuv420_to_rgb24(
						u8 *yuv_buf, 					/* input image buffer	*/
						u8 *rgb_buf, 					/* rgb image buffer		*/
						int decoded_width, 	            /* decoded image width 	*/
						int decoded_height);	        /* decoded image height	*/

void remove_artifacts(
						u8 *rgb_buf, 
						int decoded_width,   	        /* decoded image width 	*/
						int decoded_height,	            /* decoded image height	*/
						int encoded_width, 	            /* ip image width 		*/
						int encoded_height);	        /* ip image height 		*/

void fill_lcd_black_rgb(						
						u8 *rgb_buf, 					/* rgb buffer       	*/
						int decoded_width,   	        /* decoded image width 	*/
						int decoded_height);	        /* decoded image height	*/

void post_process_video(	
						u8 *ptr_src_yuv, 				/* input image buffer	*/
						u8 *ptr_src_yuv422,				/* input image buffer	*/						
						u8 *ptr_dest_rgb, 				/* rgb image buffer		*/
						int decoded_image_width, 		/* decoded image width 	*/
						int decoded_image_height,		/* decoded image height	*/ 
						int actual_image_width,			/* actual image width 	*/
						int actual_image_height);		/* actual image height 	*/
						
void convert_YUVPlanar_422(
						u8 *ptr_src_yuv,                /* input image buffer	*/ 	
						u8 *ptr_src_yuv422,             /* input image buffer	*/				
						int decoded_image_width,        /* decoded image width 	*/
						int decoded_image_height,       /* decoded image height	*/
						int actual_image_width,         /* actual image width 	*/
						int actual_image_height);       /* actual image height 	*/						
						
void fill_black_422(u8 *pYUV422);						
						

						
/***************************************************************************

	Function:		post_process_video

	Description:	processes video frame for rgb output.  

****************************************************************************/
void post_process_video(	
						u8 *ptr_src_yuv, 				/* input image buffer	*/
						u8 *ptr_src_yuv422, 				/* input image buffer	*/
						u8 *ptr_dest_rgb, 				/* rgb image buffer		*/
						int decoded_image_width, 		/* decoded image width 	*/
						int decoded_image_height,		/* decoded image height	*/ 
						int actual_image_width,			/* actual image width 	*/
						int actual_image_height)		/* actual image height 	*/
{
	
	pRGBParams = &RGBParams;	
	
#if defined(__ADSP_KOOKABURRA__)  


	/* If image is smaller than LCD then fill background with BLACK RGB */
	if(decoded_image_width < LCD_WIDTH || decoded_image_height < LCD_HEIGHT) 
	{
		fill_lcd_black_rgb(ptr_dest_rgb, decoded_image_width, decoded_image_height);
	}
#if 1
	/* Configure parameters for RGB conversion */
	/* Pointer to Y buffer */
	pRGBParams->pY   		= ptr_src_yuv;
	/* Pointer to U buffer */
	pRGBParams->pU   		= ptr_src_yuv + decoded_image_width*decoded_image_height;
	/* Pointer to V buffer */
	pRGBParams->pV   		= ptr_src_yuv + decoded_image_width*decoded_image_height*5/4;
	/* Pointer to RGB buffer */
	pRGBParams->pRGB 		= ptr_dest_rgb;
	/* JPEG decoded width */
	pRGBParams->ImageWidth 	= decoded_image_width;
	/* JPEG decoded height */
	pRGBParams->ImageHeight	= decoded_image_height;
	/* Offset skip to next Y line */
	pRGBParams->Y_Offset	= 0;
	/* Offset skip to next U line */
	pRGBParams->UV_Offset	= 0;
	/* Offset skip to next RGB line */
	pRGBParams->RGB_Offset  = 0;
	/* LCD width */
	pRGBParams->LCDWidth  	= LCD_WIDTH;
	
	/* Image needs to be cropped if it is bigger than LCD size, extra YUV data discarded*/
	if(decoded_image_width >= LCD_WIDTH)
	{
		/* Skip over unwanted Y samples */
		pRGBParams->Y_Offset	= decoded_image_width - LCD_WIDTH + decoded_image_width;
		/* Skip over unwanted UV samples */
		pRGBParams->UV_Offset	= (decoded_image_width - LCD_WIDTH)/2;
		/* Offset needed to skip to every second RGB line */
		pRGBParams->RGB_Offset  = LCD_WIDTH*3;	
	}
	
	/* Image is smaller than LCD, RGB buffer requires Offset pointers */
	else
	{
		/* Centre LCD Image */
		pRGBParams->pRGB 		= ptr_dest_rgb + ((LCD_HEIGHT - decoded_image_height)/2*LCD_WIDTH)*3 + (LCD_WIDTH - decoded_image_width)/2*3;
		/* Skip every 2nd line of Y samples */
		pRGBParams->Y_Offset	= decoded_image_width;
		/* Offset needed to skip to second RGB line  and the no-imaged RGB pixels*/
		pRGBParams->RGB_Offset  = LCD_WIDTH*3 + (LCD_WIDTH - decoded_image_width)*3;	
	}
	
	if(decoded_image_height > LCD_HEIGHT)
  	{
  		pRGBParams->ImageHeight	= LCD_HEIGHT;
  	} 	
  	
  	/* YUV to RGB Conversion */ 
	_rgb_conv(pRGBParams);
	
#endif

#if 0

	// Covert from YUV Planar to RGB888 format
	convert_yuv420_to_rgb24(ptr_src_yuv, ptr_dest_rgb, decoded_image_width, decoded_image_height);

#endif

	/* sometimes JPEG decoder produces artifacts due to macro block size alignment */
	if(decoded_image_width != actual_image_width || decoded_image_height != actual_image_height)
		remove_artifacts(ptr_dest_rgb, decoded_image_width, decoded_image_height,actual_image_width,actual_image_height); 	
		
#endif
						
	
#if defined(__ADSP_MOAB__)        					

	// Fill YUV422 Buffer with BLACK background  if image is smaller than LCD */
	if(decoded_image_width < LCD_WIDTH || decoded_image_height < LCD_HEIGHT) 
		fill_black_422(ptr_src_yuv422);
	
	// Covert from YUV Planar to YUV 422 format
	convert_YUVPlanar_422(ptr_src_yuv, ptr_src_yuv422, decoded_image_width, decoded_image_height,actual_image_width,actual_image_height);
        					
#endif								
												
}						
/***************************************************************************

	Function:		convert_yuv420_to_rgb24

	Description:	Converts YUV 420 Planar to RGB888.  

****************************************************************************/

section ("MPP_L1_code")
void convert_yuv420_to_rgb24(
						u8 *yuv_buf, 					/* input image buffer	*/
						u8 *rgb_buf, 					/* rgb image buffer		*/
						int decoded_width, 				/* decoded image width 	*/
						int decoded_height)				/* decoded image height	*/

{
    u8  *rgbbuf_line1, *rgbbuf_line2;
    unsigned char start_y_ptr, start_u_ptr, start_v_ptr;
    
    //unsigned int *yuvbuf_line1,*yuvbuf_line2;
    u8 *yuvbuf_line1,*yuvbuf_line2;
    int r,g,b, r_temp, g_temp, b_temp;
    int  y,u,v;
    int ip_width, ip_height, height_offset, width_offset, offset;
    unsigned int height_idx =0,width_idx =0;
    unsigned int buf_size;
    
    u8 * u_ptr;
    u8 * v_ptr;

	/* Buffer size */
	buf_size = decoded_width*decoded_height;
  

	/* Determine if Width / Height of decoded image is larger or 	*/
	/* smaller than the LCD output Width / Height				*/
	/* Calculate centre offset if image is smaller than the LCD */
	/* and needs to be centred									*/
	
	ip_width = decoded_width;
	ip_height = decoded_height;
	
	if(decoded_width >= LCD_WIDTH)
	{
  		decoded_width = LCD_WIDTH;
  		width_offset = 0;
	}
	else
	{
		width_offset = (LCD_WIDTH - decoded_width)/2;
	}
		
  	if(decoded_height >= LCD_HEIGHT)
  	{
  		decoded_height = LCD_HEIGHT;
  		height_offset = 0; 
  	} 		
	else
	{
		height_offset = (LCD_HEIGHT - decoded_height)/2;
	}
	
	offset = (height_offset * LCD_WIDTH + width_offset)*3;
	
 	for (height_idx =0; height_idx<decoded_height; height_idx+=2)
  	{
  		/* first line start address of Y */
    	yuvbuf_line1 = yuv_buf +  height_idx*ip_width;
    	/* second line start address Y */
		yuvbuf_line2 = yuvbuf_line1 + ip_width;
  		
		/* Start address of U */
  		u_ptr = yuv_buf + buf_size + height_idx*ip_width/4;
  		/* Start address of V */
    	v_ptr = u_ptr +      buf_size/4; 

    	/* first line start address of RGB */    				
		rgbbuf_line1 = rgb_buf + height_idx*LCD_WIDTH*3 + offset;
    	/* second line start address of RGB */    						
 		rgbbuf_line2 = rgbbuf_line1 + LCD_WIDTH*3;

		/* for loop for YUV to RGB conversion */
  		for (width_idx =0; width_idx<decoded_width;width_idx+=2)

    	{
    		
    		/* fetch yuv values line 1 */
        	start_y_ptr = *yuvbuf_line1++;
        	start_u_ptr = *u_ptr++;
            start_v_ptr = *v_ptr++;

            /* pre-scale calculation */
            y = (start_y_ptr -16)*298;
			u = start_u_ptr - 128;
            v = start_v_ptr - 128;
            
      		/* rgb conversion */
            r_temp = 409*v + 128;
            g_temp = 100*u + 208*v - 128;
            b_temp = 516*u;

            r =(y + r_temp)>>8;
            g =(y - g_temp)>>8;
            b =(y + b_temp)>>8;

            /* Clip and store */
            *rgbbuf_line1++ = CLIP8BIT(r);
            *rgbbuf_line1++ = CLIP8BIT(g);
            *rgbbuf_line1++ = CLIP8BIT(b);

            /*Extract the Y value*/
            start_y_ptr = *yuvbuf_line1++;
            y = (start_y_ptr -16)*298;

            /* rgb conversion */
            r =(y + r_temp)>>8;
            g =(y - g_temp)>>8;
            b =(y + b_temp)>>8;

            /* Clip and store */
            *rgbbuf_line1++ = CLIP8BIT(r);
            *rgbbuf_line1++ = CLIP8BIT(g);
            *rgbbuf_line1++ = CLIP8BIT(b);

            /* fetch y value line 2 */
            start_y_ptr = *yuvbuf_line2++;
            y = (start_y_ptr -16)*298;

            /* rgb conversion */
            r =(y + r_temp)>>8;
            g =(y - g_temp)>>8;
            b =(y + b_temp)>>8;

            /* Clip and store */
            *rgbbuf_line2++ = CLIP8BIT(r);
            *rgbbuf_line2++ = CLIP8BIT(g);
            *rgbbuf_line2++ = CLIP8BIT(b);

            /*Extract the Y value*/
            start_y_ptr = *yuvbuf_line2++;
            y = (start_y_ptr -16)*298;

            /* rgb conversion */
            r =(y + r_temp)>>8;
            g =(y - g_temp)>>8;
            b =(y + b_temp)>>8;


            /* Clip and store */
            *rgbbuf_line2++ = CLIP8BIT(r);
            *rgbbuf_line2++ = CLIP8BIT(g);
            *rgbbuf_line2++ = CLIP8BIT(b);
		}

	}
}


/***************************************************************************

	Function:		remove_artifacts

	Description:	Removes image artifacts caused by misaligned
					decoder macroblock size  

****************************************************************************/

void remove_artifacts(
						u8 *rgb_buf, 
						int decoded_width,   	/* decoded image width 	*/
						int decoded_height,	/* decoded image height	*/
						int encoded_width, 	/* ip image width 		*/
						int encoded_height)	/* ip image height 		*/

{
	
	int i,j, artifact_offset;
	
	/* only necessary to removed artifacts if image size is less than LCD size */
	if(LCD_WIDTH > decoded_width || LCD_HEIGHT > decoded_height)
	{
		/* remove artifact if decoded width is greater than encoded width */	
		if (decoded_width > encoded_width)
		{
			/* calculated horizontal offset i.e start of first artifact */		
			artifact_offset = ((LCD_WIDTH-decoded_width)/2 + encoded_width)*3;
	
			for (i=0; i<decoded_height; i++)		
				for (j=0; j<(decoded_width-encoded_width)*3; j++)	
					*(rgb_buf+j+  artifact_offset+LCD_WIDTH*3*(i+(LCD_HEIGHT-decoded_height)/2)) = 0x00; /* r component */
					*(rgb_buf+j+1+artifact_offset+LCD_WIDTH*3*(i+(LCD_HEIGHT-decoded_height)/2)) = 0x00; /* g component */
					*(rgb_buf+j+2+artifact_offset+LCD_WIDTH*3*(i+(LCD_HEIGHT-decoded_height)/2)) = 0x00; /* b component */
		}				
	
		/* remove artifact if decoded height is greater than encoded height */
		if  (decoded_height > encoded_height)
		{					
			for (i=1; i<=decoded_height-encoded_height; i++)		
				for (j=0; j<LCD_WIDTH*3; j+=3)	
				{
					*(rgb_buf+j+  LCD_WIDTH*3*(i+encoded_height+(LCD_HEIGHT-decoded_height)/2)) = 0x00; /* r component */				
					*(rgb_buf+j+1+LCD_WIDTH*3*(i+encoded_height+(LCD_HEIGHT-decoded_height)/2)) = 0x00;	/* g component */				
					*(rgb_buf+j+2+LCD_WIDTH*3*(i+encoded_height+(LCD_HEIGHT-decoded_height)/2)) = 0x00;	/* r component */
				}												
		}	
	}
}


/*********************************************************************

	Function:		fill_lcd_black

	Description:	Fills RGB buffer with black   

*********************************************************************/

void fill_lcd_black_rgb(						
						u8 *rgb_buf, 					/* rgb buffer       	*/
						int decoded_width,   	/* decoded image width 	*/
						int decoded_height)	/* decoded image height	*/
{
	u32 *p_rgb;
	p_rgb = (u32*)rgb_buf;
	int i,j;
	/* if decoded image is smaller than LCD than must fill background with black */
	if(LCD_WIDTH > decoded_width || LCD_HEIGHT > decoded_height)
	{
		/* 3 bytes for each pixel RGB but are writing 4 bytes each cycle */
		for (i=0; i< (LCD_WIDTH * LCD_HEIGHT * 3 / 4); i++)
		{
			*(p_rgb+i) = 0x00000000;
		}	
	}		
}


/*********************************************************************

	Function:		fill_black_422

	Description:	Fills YUV 422 Image with black if output image
					is smaller than LCD display.  

*********************************************************************/

void fill_black_422(u8 *pYUV422)
{
	u32 *pBuf422;
	pBuf422 = (u32*)pYUV422;
	int i,j;
	
	for (i=0; i< (LCD_WIDTH * LCD_DATA_PER_PIXEL * LCD_HEIGHT/4); i++)
		*(pBuf422+i) = 0x10801080;
}



/*********************************************************************

	Function:		convert_YUVPlanar_422

	Description:	Converts YUV Planar to YUV 422.  

*********************************************************************/

void convert_YUVPlanar_422(
							u8 *ptr_src_yuv, 	
							u8 *ptr_src_yuv422, 				
							int decoded_image_width, 
							int decoded_image_height,
							int actual_image_width,
							int actual_image_height)
{
	
	int width_op, height_op, centre_offset, padding_offset;
	
	int i,j;
	u32 *pBuf;

	pBuf = (u32*)ptr_src_yuv422;
	width_op = LCD_WIDTH;
	height_op = LCD_HEIGHT;
	centre_offset = 0;
	
	// If input image is smaller than LCD screen,
	// Fill background with black & centre image 
	if (decoded_image_width < LCD_WIDTH) 
		width_op = decoded_image_width; 
	if (decoded_image_height < LCD_HEIGHT)
		height_op = decoded_image_height;
	if ((decoded_image_width < LCD_WIDTH) || (decoded_image_height < LCD_HEIGHT))
	{
//		fill_black_background();	
		centre_offset = LCD_WIDTH * (LCD_HEIGHT - height_op) + (LCD_WIDTH - width_op);
	}
	
	/* Convert YUV planar to YUV 422 */
		
	for (i=0; i<height_op; i++)
		for (j=0; j<width_op; j++)
		*(ptr_src_yuv422+1+j*2+i*LCD_WIDTH*2 + centre_offset) = *(ptr_src_yuv+j+i*decoded_image_width); // Y component
				

	for (i=0; i<height_op/4; i++)		// U component
		for (j=0; j<width_op/2; j++)
		{
			*(ptr_src_yuv422+i*LCD_WIDTH*8+LCD_WIDTH*0+j*4+centre_offset) = *(ptr_src_yuv+(decoded_image_width*decoded_image_height)+i*decoded_image_width+j);					
			*(ptr_src_yuv422+i*LCD_WIDTH*8+LCD_WIDTH*2+j*4+centre_offset) = *(ptr_src_yuv+(decoded_image_width*decoded_image_height)+i*decoded_image_width+j);					
			*(ptr_src_yuv422+i*LCD_WIDTH*8+LCD_WIDTH*4+j*4+centre_offset) = *(ptr_src_yuv+(decoded_image_width*decoded_image_height)+i*decoded_image_width+j+decoded_image_width/2);					
			*(ptr_src_yuv422+i*LCD_WIDTH*8+LCD_WIDTH*6+j*4+centre_offset) = *(ptr_src_yuv+(decoded_image_width*decoded_image_height)+i*decoded_image_width+j+decoded_image_width/2);					
		}	

	for (i=0; i<height_op/4; i++)		// V component
		for (j=0; j<width_op/2; j++)
		{
			*(ptr_src_yuv422+i*LCD_WIDTH*8+LCD_WIDTH*0+j*4+2+centre_offset) = *(ptr_src_yuv+(decoded_image_width*decoded_image_height*5/4)+i*decoded_image_width+j);					
			*(ptr_src_yuv422+i*LCD_WIDTH*8+LCD_WIDTH*2+j*4+2+centre_offset) = *(ptr_src_yuv+(decoded_image_width*decoded_image_height*5/4)+i*decoded_image_width+j);					
			*(ptr_src_yuv422+i*LCD_WIDTH*8+LCD_WIDTH*4+j*4+2+centre_offset) = *(ptr_src_yuv+(decoded_image_width*decoded_image_height*5/4)+i*decoded_image_width+j+decoded_image_width/2);					
			*(ptr_src_yuv422+i*LCD_WIDTH*8+LCD_WIDTH*6+j*4+2+centre_offset) = *(ptr_src_yuv+(decoded_image_width*decoded_image_height*5/4)+i*decoded_image_width+j+decoded_image_width/2);					
		}

	/* Remove JPEG decoder height padding caused when the input image not is not a multiple of the pixel macro block */
	/* Fill background with black */
	if (height_op > actual_image_height)
	{					
		for (i=0; i<height_op-actual_image_height; i++)		
			for (j=0; j<LCD_WIDTH/2; j++)	
				*(pBuf+j+LCD_WIDTH/2*(actual_image_height+(LCD_HEIGHT-actual_image_height-4)/2)+LCD_WIDTH/2*i) = 	0x10801080;				
	}
	
	/* Remove JPEG decoder decoded_image_width padding caused when the input image not is not a multiple of the pixel macro block */	
	/* Fill background with black */
	if (width_op > actual_image_width)
	{		
		padding_offset = centre_offset + actual_image_width*2;
		
		for (i=0; i<height_op; i++)		
			for (j=0; j<(width_op-actual_image_width)/2; j++)	
				*(pBuf+j+padding_offset/4 + LCD_WIDTH/2*i) = 	0x10801080;
	}				
			
}

