/*******************************************************************
                  YUV frame treating module
 *******************************************************************/

#include <stdio.h>
#include <stdlib.h>

#define FRAME_C
#include "frame.h"

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 yuv_420_to_422(FRAME *p, int progressive);
void yuv_422_to_444(FRAME *p);

void frame_to_bgr(FRAME *in, unsigned char *out, BGR_CONVERSION_PARAMETER *prm);
void save_frame_bmp(FRAME *in, int frame_count, BGR_CONVERSION_PARAMETER *prm);

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 yuv_420_to_422(FRAME *p, int progressive)
{
	int x,y;
	int w,h,hw;

	w = p->width;
	hw = w >> 1;
	h = p->height;
	
	if(progressive){

		/* copy pass */
		for(y=(h>>1)-1;y>=0;y--){
			memcpy(p->u+(2*y*w), p->u+(y*w), hw);
			memcpy(p->v+(2*y*w), p->v+(y*w), hw);
		}

		/* supplement pass */
		for(y=0;y<(h>>1)-1;y++){
			for(x=0;x<hw;x++){
				p->u[(2*y+1)*w+x] = ( ((unsigned int)p->u[2*y*w+x] + p->u[(2*y+2)*w+x]) >> 1 );
				p->v[(2*y+1)*w+x] = ( ((unsigned int)p->v[2*y*w+x] + p->v[(2*y+2)*w+x]) >> 1 );
			}
		}

		/* last line */
		memcpy(p->u+((h-1)*w), p->u+((h-2)*w), hw);
		memcpy(p->v+((h-1)*w), p->v+((h-2)*w), hw);
		
	}else{
		
		/* copy last 2 lines */
		memcpy(p->u+(h-2)*w, p->u+((h>>1)-2)*w, hw);
		memcpy(p->u+(h-1)*w, p->u+((h>>1)-1)*w, hw);
		memcpy(p->v+(h-2)*w, p->v+((h>>1)-2)*w, hw);
		memcpy(p->v+(h-1)*w, p->v+((h>>1)-1)*w, hw);

		/* upsampling */
		for(y=(h>>2)-2;y>=0;y--){
			for(x=0;x<hw;x++){
				p->u[(y*4+2)*w+x] = ( ( (unsigned int) p->u[(y*2  )*w+x] * 5 + p->u[(y*2+2)*w+x] * 3) >> 3);
				p->u[(y*4+3)*w+x] = ( ( (unsigned int) p->u[(y*2+1)*w+x] * 7 + p->u[(y*2+3)*w+x]    ) >> 3);
				p->u[(y*4+4)*w+x] = ( ( (unsigned int) p->u[(y*2  )*w+x]     + p->u[(y*2+2)*w+x] * 7) >> 3);
				p->u[(y*4+5)*w+x] = ( ( (unsigned int) p->u[(y*2+1)*w+x] * 3 + p->u[(y*2+3)*w+x] * 5) >> 3);

				p->v[(y*4+2)*w+x] = ( ( (unsigned int) p->v[(y*2  )*w+x] * 5 + p->v[(y*2+2)*w+x] * 3) >> 3);
				p->v[(y*4+3)*w+x] = ( ( (unsigned int) p->v[(y*2+1)*w+x] * 7 + p->v[(y*2+3)*w+x]    ) >> 3);
				p->v[(y*4+4)*w+x] = ( ( (unsigned int) p->v[(y*2  )*w+x]     + p->v[(y*2+2)*w+x] * 7) >> 3);
				p->v[(y*4+5)*w+x] = ( ( (unsigned int) p->v[(y*2+1)*w+x] * 3 + p->v[(y*2+3)*w+x] * 5) >> 3);
			}			
		}
	}
}

void yuv_422_to_444(FRAME *p)
{
	int x,y;
	int w,h;

	w = p->width;
	h = p->height;

	for(y=0;y<h;y++){
		/* copy pass */
		for(x=(w>>1)-1;x>=0;x--){
			p->u[y*w+x*2] = p->u[y*w+x];
			p->v[y*w+x*2] = p->v[y*w+x];
		}

		/* supplement pass */
		for(x=0;x<(w>>1)-1;x++){
			p->u[y*w+x*2+1] = ( ( (unsigned int) p->u[y*w+x*2] + p->u[y*w+x*2+2] ) >> 1);
			p->v[y*w+x*2+1] = ( ( (unsigned int) p->v[y*w+x*2] + p->v[y*w+x*2+2] ) >> 1);
		}

		/* last colum */
		p->u[y*w+w-1] = p->u[y*w+w-2];
		p->v[y*w+w-1] = p->v[y*w+w-2];
	}
}

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){
		yuv_420_to_422(in, prm->progressive);
		yuv_422_to_444(in);
	}
	if(prm->chroma_format == 2){
		yuv_422_to_444(in);
	}
	
	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;
	}
}
