/*******************************************************************
                  YUV frame treating module
 *******************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define FRAME_C
#include "frame.h"

/* in yuv_to_bgr.asm */
extern void __stdcall chroma420i_to_422_mmx(unsigned char *data, int width, int height);
extern void __stdcall chroma422_to_444_mmx(unsigned char *data, int width, int height);
extern void __stdcall yuv444_to_bgr_mmx(FRAME *in, unsigned char *out, BGR_CONVERSION_PARAMETER *prm);

const unsigned char uchar_clip_table[1024] = {
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  0,  0,  0,  0,  0,  0,  0,
	  0,  1,  2,  3,  4,  5,  6,  7,
	  8,  9, 10, 11, 12, 13, 14, 15,
	 16, 17, 18, 19, 20, 21, 22, 23,
	 24, 25, 26, 27, 28, 29, 30, 31,
	 32, 33, 34, 35, 36, 37, 38, 39,
	 40, 41, 42, 43, 44, 45, 46, 47,
	 48, 49, 50, 51, 52, 53, 54, 55,
	 56, 57, 58, 59, 60, 61, 62, 63,
	 64, 65, 66, 67, 68, 69, 70, 71,
	 72, 73, 74, 75, 76, 77, 78, 79,
	 80, 81, 82, 83, 84, 85, 86, 87,
	 88, 89, 90, 91, 92, 93, 94, 95,
	 96, 97, 98, 99,100,101,102,103,
	104,105,106,107,108,109,110,111,
	112,113,114,115,116,117,118,119,
	120,121,122,123,124,125,126,127,
	128,129,130,131,132,133,134,135,
	136,137,138,139,140,141,142,143,
	144,145,146,147,148,149,150,151,
	152,153,154,155,156,157,158,159,
	160,161,162,163,164,165,166,167,
	168,169,170,171,172,173,174,175,
	176,177,178,179,180,181,182,183,
	184,185,186,187,188,189,190,191,
	192,193,194,195,196,197,198,199,
	200,201,202,203,204,205,206,207,
	208,209,210,211,212,213,214,215,
	216,217,218,219,220,221,222,223,
	224,225,226,227,228,229,230,231,
	232,233,234,235,236,237,238,239,
	240,241,242,243,244,245,246,247,
	248,249,250,251,252,253,254,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
	255,255,255,255,255,255,255,255,
};

FRAME *new_frame(int width, int height);
void delete_frame(FRAME *p);

void frame_to_bgr(FRAME *in, unsigned char *out, BGR_CONVERSION_PARAMETER *prm);
void frame_to_bgr_mmx(FRAME *in, unsigned char *out, BGR_CONVERSION_PARAMETER *prm);

static void chroma420i_to_422(unsigned char *data, int width, int height);
static void chroma420p_to_422(unsigned char *data, int width, int height);
static void chroma422_to_444(unsigned char *data, int width, int height);

FRAME *new_frame(int width, int height)
{
	FRAME *r;

	r = (FRAME *)malloc(sizeof(FRAME));

	r->width = width;
	r->height = height;

	r->y = (unsigned char *)malloc(width * height);
	r->u = (unsigned char *)malloc(width * height);
	r->v = (unsigned char *)malloc(width * height);

	return r;
}

void delete_frame(FRAME *p)
{
	free(p->y);
	free(p->u);
	free(p->v);
	
	free(p);
}

void chroma420i_to_422(unsigned char *data, int width, int height)
{
	int x,y;
	int hw;

	hw = width >> 1;
	
	/* copy last 2 lines */
	memcpy(data+(height-2)*width, data+((height>>1)-2)*width, hw);
	memcpy(data+(height-1)*width, data+((height>>1)-1)*width, hw);

	/* upsampling */
	for(y=(height>>2)-2;y>0;y--){
		for(x=0;x<hw;x++){
			data[(y*4+2)*width+x] = ( ( (unsigned int) data[(y*2  )*width+x] * 5 + data[(y*2+2)*width+x] * 3) >> 3);
			data[(y*4+3)*width+x] = ( ( (unsigned int) data[(y*2+1)*width+x] * 7 + data[(y*2+3)*width+x]    ) >> 3);
			data[(y*4+4)*width+x] = ( ( (unsigned int) data[(y*2  )*width+x]     + data[(y*2+2)*width+x] * 7) >> 3);
			data[(y*4+5)*width+x] = ( ( (unsigned int) data[(y*2+1)*width+x] * 3 + data[(y*2+3)*width+x] * 5) >> 3);
		}			
	}

	/* process top 6 lines */
	for(x=0;x<hw;x++){
		data[4*width+x] = ( ( (unsigned int) data[      x]     + data[2*width+x] * 7) >> 3);
		data[5*width+x] = ( ( (unsigned int) data[width+x] * 3 + data[3*width+x] * 5) >> 3);
		data[2*width+x] = ( ( (unsigned int) data[      x] * 5 + data[2*width+x] * 3) >> 3);
		data[3*width+x] = ( ( (unsigned int) data[width+x] * 7 + data[3*width+x]    ) >> 3);
	}
}

void chroma420p_to_422(unsigned char *data, int width, int height)
{
	int x,y;
	int hw;

	hw = width >> 1;
	
	/* copy pass */
	for(y=(height>>1)-1;y>=0;y--){
		memcpy(data+(2*y*width), data+(y*width), hw);
	}

	/* supplement pass */
	for(y=0;y<(height>>1)-1;y++){
		for(x=0;x<hw;x++){
			data[(2*y+1)*width+x] = ( ((unsigned int)data[2*y*width+x] + data[(2*y+2)*width+x]) >> 1 );
		}
	}

	/* last line */
	memcpy(data+((height-1)*width), data+((height-2)*width), hw);

}

void chroma422_to_444(unsigned char *data, int width, int height)
{
	int x,y;

	for(y=0;y<height;y++){
		/* copy pass */
		for(x=(width>>1)-1;x>=0;x--){
			data[y*width+x*2] = data[y*width+x];
		}

		/* supplement pass */
		for(x=0;x<(width>>1)-1;x++){
			data[y*width+x*2+1] = ( ( (unsigned int) data[y*width+x*2] + data[y*width+x*2+2] ) >> 1);
		}

		/* last colum */
		data[y*width+width-1] = data[y*width+width-2];
	}
}

void frame_to_bgr_mmx(FRAME *in, unsigned char *out, BGR_CONVERSION_PARAMETER *prm)
{
	if(prm->chroma_format == 1){
		if(prm->progressive){
			chroma420p_to_422(in->u, in->width, in->height);
			chroma420p_to_422(in->v, in->width, in->height);
		}else{
			chroma420i_to_422_mmx(in->u, in->width, in->height);
			chroma420i_to_422_mmx(in->v, in->width, in->height);
		}
		chroma422_to_444_mmx(in->u, in->width, in->height);
		chroma422_to_444_mmx(in->v, in->width, in->height);
	}else if(prm->chroma_format == 2){
		chroma422_to_444_mmx(in->u, in->width, in->height);
		chroma422_to_444_mmx(in->v, in->width, in->height);
	}

	yuv444_to_bgr_mmx(in, out, prm);
}	

void frame_to_bgr(FRAME *in, unsigned char *out, BGR_CONVERSION_PARAMETER *prm)
{
	int i, j;
	unsigned char *y, *u, *v;
	unsigned char *r, *g, *b;

	if(prm->chroma_format == 1){
		if(prm->progressive){
			chroma420p_to_422(in->u, in->width, in->height);
			chroma420p_to_422(in->v, in->width, in->height);
		}else{
			chroma420i_to_422(in->u, in->width, in->height);
			chroma420i_to_422(in->v, in->width, in->height);
		}
		chroma422_to_444(in->u, in->width, in->height);
		chroma422_to_444(in->v, in->width, in->height);
	}else if(prm->chroma_format == 2){
		chroma422_to_444(in->u, in->width, in->height);
		chroma422_to_444(in->v, in->width, in->height);
	}
	
	y = in->y;
	u = in->u;
	v = in->v;

	b = out;
	g = out+1;
	r = out+2;

	for(i=0;i<in->height;i++){
		for(j=0;j<in->width;j++){
			b[j*3] = uchar_clip_table[UCHAR_CLIP_TABLE_OFFSET+(( prm->yg * ((int)y[j] - prm->yo) + prm->bu * (((int)u[j]) - 128)) >> 16)];
			g[j*3] = uchar_clip_table[UCHAR_CLIP_TABLE_OFFSET+(( prm->yg * ((int)y[j] - prm->yo) + prm->gu * (((int)u[j]) - 128) + prm->gv * (((int)v[j]) - 128)) >> 16)];
			r[j*3] = uchar_clip_table[UCHAR_CLIP_TABLE_OFFSET+(( prm->yg * ((int)y[j] - prm->yo) + prm->rv * (((int)v[j]) - 128)) >> 16)];
		}
		y += in->width;
		u += in->width;
		v += in->width;
		b += in->width * 3;
		g += in->width * 3;
		r += in->width * 3;
	}
}


