////////////////////////////////////////////////////////////////////
//			     View YUV Picture
//		The YUV Format is YV12,the data is 4Y,1V,1U,
//			the RLE YUV is the same format.
//
//				Southern.Liang
//				   1997.8.11
////////////////////////////////////////////////////////////////////
#include <windows.h>

int	VCDHigh=240;			//240 or 288
BYTE	Frame[352*288+176*144+176*144];	//Y+U+V
BYTE 	ViewBuffer[288][352][3];	//RGB 24
typedef	struct	RIFFheader
	{
	DWORD	dwRiff;		//[0x0]="RIFF"
	DWORD	dwSize;		//[0x4]=dwSizeData+9*4
	DWORD	dwWave;		//[0x8]="YUV "
	DWORD	dwFmt;		//[0xC]="fmt "

	DWORD	dwSizeFmt;	//[0x10]=0x10
	DWORD	dwFormat;	//[0x14]="YV12"	-> YVU12
	DWORD	dwWidth;	//[0x18]=352
	DWORD	dwHeight;	//[0x1C]=240 or 288
	DWORD	dwYUVFmt;	//[0x20]="411 " or "RLE "
	DWORD	dwData;		//[0x24]="data"

	DWORD	dwSizeData;	//[0x28]
	}	RIFFYUVFILE;
RIFFYUVFILE	RiffHeader;

void	DecodeRLE(HFILE hFile,int Width,int High)
{
	int	Size;
	int	i;
	LPBYTE	Ptr;
	BYTE	Val;
	LPBYTE	SrcPtr;

	//Get Data Size
	i=_llseek(hFile,0,FILE_CURRENT);
	Size=_llseek(hFile,0,FILE_END);
	_llseek(hFile,i,FILE_BEGIN);
	//Read All Data in buffer
	Size-=i;
	_lread(hFile,&ViewBuffer[0][0][0],Size);
	SrcPtr=&ViewBuffer[0][0][0];

	//Decode RLE,the RLE coded data is a block not a line.
	Size=Width*High;	//Y Plane Size
	Ptr=Frame;
	do	{//Decode Y Plane
		Val=*SrcPtr++;
		if(Val<0xC0)
			{
			*Ptr++=Val;
			Size--;
			}
		else	{
			i=Val&0x3F;
			Size-=i;
			Val=*SrcPtr++;			
			for(;i>0;i--)
				*Ptr++=Val;
			}
		}while(Size>0);
	//V Plane
	Size=(Width/2)*(High/2);	//V Plane Size
	Ptr=Frame+352*288+176*144;
	do	{//Decode V Plane
		Val=*SrcPtr++;
		if(Val<0xC0)
			{
			*Ptr++=Val;
			Size--;
			}
		else	{
			i=Val&0x3F;
			Size-=i;
			Val=*SrcPtr++;			
			for(;i>0;i--)
				*Ptr++=Val;
			}
		}while(Size>0);
	//U Plane
	Size=(Width/2)*(High/2);	//U Plane Size
	Ptr=Frame+352*288;
	do	{//Decode U Plane
		Val=*SrcPtr++;
		if(Val<0xC0)
			{
			*Ptr++=Val;
			Size--;
			}
		else	{
			i=Val&0x3F;
			Size-=i;
			Val=*SrcPtr++;			
			for(;i>0;i--)
				*Ptr++=Val;
			}
		}while(Size>0);
}
int	InitBuffer(char * FileName)
{
	HFILE	hFile;

	hFile=_lopen(FileName,OF_READ);
	if(hFile==HFILE_ERROR) return 0;
	_lread(hFile,&RiffHeader,sizeof(RIFFYUVFILE));	//header
	if(RiffHeader.dwRiff!=0x46464952)	//RIFF file
		{
		_lclose(hFile);
		return 0;
		}
	VCDHigh=RiffHeader.dwHeight;
	switch(RiffHeader.dwFormat)
		{
		case 0x32315559:	//YU12
			//Y Plane
			_lread(hFile,Frame,352*RiffHeader.dwHeight);
			//U Plane
			_lread(hFile,Frame+352*288,176*RiffHeader.dwHeight/2);
			//V Plane
			_lread(hFile,Frame+352*288+176*144,176*RiffHeader.dwHeight/2);
			break;
		case 0x32315659:	//YV12
			if(RiffHeader.dwYUVFmt==0x20313134)       //Is 411 format
				{
				//Y Plane
				_lread(hFile,Frame,352*RiffHeader.dwHeight);
				//V Plane
				_lread(hFile,Frame+352*288+176*144,176*RiffHeader.dwHeight/2);
				//U Plane
				_lread(hFile,Frame+352*288,176*RiffHeader.dwHeight/2);
				}
			else	{//RLE Format
				DecodeRLE(hFile,RiffHeader.dwWidth,RiffHeader.dwHeight);
				}
			break;
		}
	_lclose(hFile);
	return 1;
}
///////////////////////////////////////////////////////////////////////
//
//			YUV to RGB24 and Display DIB
//
///////////////////////////////////////////////////////////////////////
BITMAPINFO	BitmapInfo=
{
	{
	sizeof(BITMAPINFOHEADER),
	352,
	240,
	1,
	24,
	BI_RGB,
	0,
	0,//	biXPelsPerMeter;
	0,//	biYPelsPerMeter;
	0,
	0
	}
};
void	YUVToRGB(int Y,int U,int V,int * RPtr,int * GPtr,int * BPtr)
{
	int	R,G,B;

	U-=128;
	V-=128;
	R=(int)(V*1.402 -U*0.001);
	G=(int)(U*0.3441+V*0.7141);
	B=(int)(U*1.772 +V*0.001);
	R=Y+R;
	B=Y+B;
	G=Y-G;
	*RPtr=(R<0) ? 0:(R>255) ? 255:R;
	*GPtr=(G<0) ? 0:(G>255) ? 255:G;
	*BPtr=(B<0) ? 0:(B>255) ? 255:B;
}
void	YUVPictureToDIB(void)	//YUV picture to DIB
{
	//Display The Picture
	int	Y,U,V,R,G,B;
	int	x,y;

	for(y=0;y<VCDHigh;y++)
		{
		for(x=0;x<352;x++)
			{
			int 	DataOffset;
			DataOffset=(y/2)*176+x/2;
			Y=Frame[y*352+x];		//Y
			U=Frame[DataOffset+352*288];	//U
			V=Frame[DataOffset+352*288+176*144];//V
			//YUV To RGB
			YUVToRGB(Y,U,V,&R,&G,&B);
			//Write to DIB
			ViewBuffer[VCDHigh-y-1][x][0]=(BYTE)B;
			ViewBuffer[VCDHigh-y-1][x][1]=(BYTE)G;
			ViewBuffer[VCDHigh-y-1][x][2]=(BYTE)R;
			}
		}
}
void	DisplayYUV(HDC hDC)	//Display YUV picture
{
	//Picture High
	BitmapInfo.bmiHeader.biHeight=VCDHigh;
	/////////// Display ///////////
	SetDIBitsToDevice(hDC,0,0,352,VCDHigh,0,0,0,VCDHigh,&ViewBuffer[0][0][0],&BitmapInfo,DIB_RGB_COLORS);
	///////////////////////////////
}
int	Success=0;
long 	APIENTRY WndProc(HWND,UINT,UINT,LONG);
int     APIENTRY WinMain(HANDLE hInstrance,
			HANDLE 	hPrevInstance,
			LPSTR 	lpszCmdLine,
			int 	nCmdShow)
{
	static char szAppName[10]="View YUV";
	static char szCapName[80]="View YUV";
	HWND	hwnd;
	MSG	msg;
	WNDCLASS wndclass;

	//Load YUV Picture
	Success=InitBuffer(lpszCmdLine);
	if(Success)
		{
		YUVPictureToDIB();
		lstrcat(szCapName," -- ");
		lstrcat(szCapName,lpszCmdLine);
		}

	if(!hPrevInstance)
		{
		wndclass.style		=CS_HREDRAW | CS_VREDRAW;
		wndclass.lpfnWndProc	=(WNDPROC)WndProc;
		wndclass.cbClsExtra	=0;
		wndclass.cbWndExtra	=0;
		wndclass.hInstance	=hInstrance;
		wndclass.hIcon		=LoadIcon(NULL,IDI_WINLOGO);
		wndclass.hCursor	=LoadCursor(NULL,IDC_ARROW);
		wndclass.hbrBackground	=(HBRUSH)(COLOR_WINDOW);
		wndclass.lpszMenuName	=NULL;
		wndclass.lpszClassName	=szAppName;

		RegisterClass(&wndclass);
		}
	hwnd=CreateWindow(szAppName,szCapName,
			  WS_OVERLAPPEDWINDOW,
			  CW_USEDEFAULT,	CW_USEDEFAULT,
			  CW_USEDEFAULT,	CW_USEDEFAULT,
			  NULL,NULL,hInstrance,NULL);
	ShowWindow(hwnd,nCmdShow);
	UpdateWindow(hwnd);

	while(GetMessage(&msg,NULL,0,0))
		{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
		}
	return msg.wParam;
}
char	Usage[]="Usage:VIEWYUV PictureName.YUV";
long 	APIENTRY WndProc(HWND hwnd,UINT message,UINT wParam,LONG lParam)
{
	HDC 		hdc;
	PAINTSTRUCT 	ps;

	switch(message)
		{
		case WM_PAINT:
			hdc=BeginPaint(hwnd,&ps);
			if(Success) DisplayYUV(hdc);
			else	TextOut(hdc,0,0,Usage,lstrlen(Usage));
			EndPaint(hwnd,&ps);
			return 0;
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
		}

	return	DefWindowProc(hwnd,message,wParam,lParam);
}
