// GifAnimation.cpp : implementation file
//

#include "stdafx.h"
#include "YUVplayback.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#include "Convert.h"

/////////////////////////////////////////////////////////////////////////////
// CYUVPlayback

CYUVPlayback::CYUVPlayback()
	:CStatic()
{
	bHaveFile = FALSE;
	m_pFile = NULL;
	m_pFile = new CFile();
	m_pMV = NULL;
	m_pMV = new CFile();

	iTotalFrameNumber = 0;
	iCurrFrameNumber = 0;
	iWidth = 0;
	iHeight = 0;

	bColorful = TRUE;
	bShowMV = FALSE;

	Y = NULL;
	Cb = NULL;
	Cr = NULL;
	RGBbuf = NULL;

	BmpInfo=(BITMAPINFO*)new char [sizeof(BITMAPINFO)+sizeof(RGBQUAD)*256];
}

CYUVPlayback::~CYUVPlayback()
{
	free(Y);
	free(Cb);
	free(Cr);
	free(RGBbuf);

	delete BmpInfo;

	delete m_pFile;
	delete m_pMV;
}

void CYUVPlayback::DoDataExchange(CDataExchange* pDX)
{
	CStatic::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CYUVPlayback)
		// NOTE: the ClassWizard will add DDX and DDV calls here
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CYUVPlayback, CStatic)
	//{{AFX_MSG_MAP(CYUVPlayback)
	ON_WM_PAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CYUVPlayback message handlers

void CYUVPlayback::OnPaint() 
{
	if ( bHaveFile == FALSE )
	{
		CStatic::OnPaint();
		return;
	}

	CPaintDC dc(this); // device context for painting
	
	if (bColorful)
	{
		ShowColorImage(&dc);
	}
	else
	{
		ShowGrayImage(&dc);
	}
}

void CYUVPlayback::SetPathName(char *pathname)
{
	sprintf( sPathName, "%s", pathname );

	bHaveFile = TRUE;
}

void CYUVPlayback::SetYUVSize(int width, int height)
{
	iWidth = width;
	iHeight = height;

	if (Y!=NULL) free(Y);
	if (Cb!=NULL) free(Cb);
	if (Cr!=NULL) free(Cr);
	if (RGBbuf!=NULL) free(RGBbuf);

	if ( (Y =(unsigned char *)malloc(iWidth*iHeight)) == NULL ) 
	{
		//AfxMessageBox("Couldn't allocate memory for RGBbuf\n");
		return;
	}
	if ( (Cb=(unsigned char *)malloc(iWidth*iHeight/4)) == NULL ) 
	{
		//AfxMessageBox("Couldn't allocate memory for RGBbuf\n");
		return;
	}
	if ( (Cr=(unsigned char *)malloc(iWidth*iHeight/4) ) == NULL ) 
	{
		//AfxMessageBox("Couldn't allocate memory for RGBbuf\n");
		return;
	}
	if ( (RGBbuf=(unsigned char *)malloc(iWidth*iHeight*3)) == NULL ) 
	{
		//AfxMessageBox("Couldn't allocate memory for RGBbuf\n");
		return;
	}
}

void CYUVPlayback::SetColorful(BOOL colorful)
{
	bColorful = colorful;
}

void CYUVPlayback::SetShowMV(BOOL bMV)
{
	bShowMV = bMV;
}

void CYUVPlayback::ReStart()
{
	iCurrFrameNumber = 0;

	if ( bHaveFile == FALSE )
		return;

	if(!m_pFile->Open(sPathName, CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone )) 
	{
		//AfxMessageBox("Can't open input file");
		return;
	}
	iTotalFrameNumber = m_pFile->GetLength() / (iWidth*iHeight*3/2);
	iCurrFrameNumber = 0;
	GetYUVData();
	m_pFile->Close();

	Invalidate(FALSE);
}

void CYUVPlayback::GoToFrame(int number)
{
	if ( bHaveFile == FALSE )
		return;

	iCurrFrameNumber = number;
	if (iCurrFrameNumber+1 >= iTotalFrameNumber)
	{
		// roll back
		iCurrFrameNumber = 0;
	}
	if (iCurrFrameNumber-1 < 0)
	{
		// roll back
		iCurrFrameNumber = iTotalFrameNumber-1;
	}

	if(!m_pFile->Open(sPathName, CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone )) 
	{
		//AfxMessageBox("Can't open input file");
		return;
	}
	// Seek to the right position
	m_pFile->Seek(iCurrFrameNumber*iWidth*iHeight*3/2, CFile::begin);
	// Read the Y Cb Cr data
	GetYUVData();
	m_pFile->Close();

	if(!m_pMV->Open("test.mv", CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone )) 
	{
		//AfxMessageBox("Can't open input file");
		return;
	}
	// Seek to the right position
	m_pMV->Seek(iCurrFrameNumber*(B16_SIZE*3+B8_SIZE*3+B4_SIZE*3), CFile::begin);
	GetMVData();
	m_pMV->Close();

	Invalidate(FALSE);
}

void CYUVPlayback::StartPlay()
{
	if ( bHaveFile == FALSE )
		return;

	if (iCurrFrameNumber+1 >= iTotalFrameNumber)
	{
		// roll back
		iCurrFrameNumber = 0;
	}
	else
	{
		// increase the current frame number
		iCurrFrameNumber ++;
	}

	if(!m_pFile->Open(sPathName, CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone )) 
	{
		//AfxMessageBox("Can't open input file");
		return;
	}
	// Seek to the right position
	m_pFile->Seek(iCurrFrameNumber*iWidth*iHeight*3/2, CFile::begin);
}

void CYUVPlayback::ContinuePlay()
{
	// Read the Y Cb Cr data
	if ( bHaveFile == FALSE || m_pFile == NULL )
		return;
	if (iCurrFrameNumber+1 >= iTotalFrameNumber)
	{
		// roll back
		iCurrFrameNumber = 0;
		m_pFile->Seek(0, CFile::begin);
	}
	else
	{
		// increase the current frame number
		iCurrFrameNumber ++;
	}
	GetYUVData();
	Invalidate(FALSE);
}

void CYUVPlayback::PausePlay()
{
	if ( bHaveFile == FALSE || m_pFile == NULL )
		return;
	m_pFile->Close();
}

void CYUVPlayback::NextFrame()
{
	if ( bHaveFile == FALSE )
		return;

	if (iCurrFrameNumber+1 >= iTotalFrameNumber)
	{
		// roll back
		iCurrFrameNumber = 0;
	}
	else
	{
		// increase the current frame number
		iCurrFrameNumber ++;
	}

	if(!m_pFile->Open(sPathName, CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone )) 
	{
		//AfxMessageBox("Can't open input file");
		return;
	}
	// Seek to the right position
	m_pFile->Seek(iCurrFrameNumber*iWidth*iHeight*3/2, CFile::begin);
	// Read the Y Cb Cr data
	GetYUVData();
	m_pFile->Close();

	if(!m_pMV->Open("test.mv", CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone )) 
	{
		//AfxMessageBox("Can't open input file");
		return;
	}
	// Seek to the right position
	m_pMV->Seek(iCurrFrameNumber*(B16_SIZE*3+B8_SIZE*3+B4_SIZE*3), CFile::begin);
	GetMVData();
	m_pMV->Close();

	Invalidate(FALSE);
}


void CYUVPlayback::PrevFrame()
{
	if ( bHaveFile == FALSE )
		return;

	if (iCurrFrameNumber-1 < 0)
	{
		// roll back
		iCurrFrameNumber = iTotalFrameNumber-1;
	}
	else
	{
		// decrease the current frame number
		iCurrFrameNumber --;
	}

	if(!m_pFile->Open(sPathName, CFile::modeRead | CFile::typeBinary | CFile::shareDenyNone )) 
	{
		//AfxMessageBox("Can't open input file");
		return;
	}
	// Seek to the right position
	m_pFile->Seek(iCurrFrameNumber*iWidth*iHeight*3/2, CFile::begin);
	// Read the Y Cb Cr data
	GetYUVData();
	m_pFile->Close();

	Invalidate(FALSE);
}

// requrire the file point is valid and correct
void CYUVPlayback::GetYUVData()
{
	if ( m_pFile->Read(Y, iWidth*iHeight) != iWidth*iHeight )
	{
		//AfxMessageBox("Get to end of file");
		return;
	}
	if ( m_pFile->Read(Cb, iWidth*iHeight/4) != iWidth*iHeight/4 )
	{
		//AfxMessageBox("Get to end of file");
		return;
	}
	if ( m_pFile->Read(Cr, iWidth*iHeight/4) != iWidth*iHeight/4 )
	{
		//AfxMessageBox("Get to end of file");
		return;
	}
}

void CYUVPlayback::GetMVData()
{
	if ( m_pMV->Read(bs.b16, B16_SIZE*3) != B16_SIZE*3 )
	{
		//AfxMessageBox("Get to end of file");
		return;
	}
	if ( m_pMV->Read(bs.b8, B8_SIZE*3) != B8_SIZE*3 )
	{
		//AfxMessageBox("Get to end of file");
		return;
	}
	if ( m_pMV->Read(bs.b4, B4_SIZE*3) != B4_SIZE*3 )
	{
		//AfxMessageBox("Get to end of file");
		return;
	}
}

void CYUVPlayback::ShowMotionVector(CDC *pDC)
{
	int xx, yy, x, y;
	CRect rect;
	GetClientRect(&rect);

	HBRUSH hBrush = CreateSolidBrush( RGB(255,255,255) );
	CPen redPen(PS_SOLID, 1, RGB(255,0, 0) );
	CPen greenPen(PS_SOLID, 1, RGB(0, 255,0) );
	CPen bluePen(PS_SOLID, 1, RGB(0,0, 255) );
	for (y=0; y<iHeight/16; y++) {
		for (x=0; x<iWidth/16; x++) {
			if (bs.b16[y*iWidth/16+x].v == 0) continue;
			pDC->SelectObject(&redPen);
			xx = rect.Width() * (x*16+8+bs.b16[y*iWidth/16+x].mvx) / iWidth;
			yy = rect.Height() * (y*16+8+bs.b16[y*iWidth/16+x].mvy) / iHeight;
			pDC->MoveTo(xx, yy);
			xx = rect.Width() * (x*16+8) / iWidth;
			yy = rect.Height() * (y*16+8) / iHeight;
			pDC->LineTo(xx, yy);
			pDC->Rectangle(xx-1, yy-1, xx+1, yy+1);
		}
	}
	for (y=0; y<iHeight/8; y++) {
		for (x=0; x<iWidth/8; x++) {
			if (bs.b8[y*iWidth/8+x].v == 0) continue;
			pDC->SelectObject(&greenPen);
			xx = rect.Width() * (x*8+4+bs.b8[y*iWidth/8+x].mvx) / iWidth;
			yy = rect.Height() * (y*8+4+bs.b8[y*iWidth/8+x].mvy) / iHeight;
			pDC->MoveTo(xx, yy);
			xx = rect.Width() * (x*8+4) / iWidth;
			yy = rect.Height() * (y*8+4) / iHeight;
			pDC->LineTo(xx, yy);
			pDC->Rectangle(xx-1, yy-1, xx+1, yy+1);
		}
	}
	for (y=0; y<iHeight/4; y++) {
		for (x=0; x<iWidth/4; x++) {
			if (bs.b4[y*iWidth/4+x].v == 0) continue;
			pDC->SelectObject(&bluePen);
			xx = rect.Width() * (x*4+2+bs.b4[y*iWidth/4+x].mvx) / iWidth;
			yy = rect.Height() * (y*4+2+bs.b4[y*iWidth/4+x].mvy) / iHeight;
			pDC->MoveTo(xx, yy);
			xx = rect.Width() * (x*4+2) / iWidth;
			yy = rect.Height() * (y*4+2) / iHeight;
			pDC->LineTo(xx, yy);
			pDC->Rectangle(xx-1, yy-1, xx+1, yy+1);
		}
	}
	DeleteObject(hBrush);
}

void CYUVPlayback::ShowColorImage(CDC *pDC)
{
	int BPP = 24;

	BmpInfo->bmiHeader.biSize=sizeof (BITMAPINFOHEADER);
	BmpInfo->bmiHeader.biWidth=iWidth;
	BmpInfo->bmiHeader.biHeight=iHeight;			// negative means up-to-bottom 
	BmpInfo->bmiHeader.biPlanes=1;
	BmpInfo->bmiHeader.biBitCount=(BPP<8?8:BPP);	// Our raster is byte-aligned
	BmpInfo->bmiHeader.biCompression=BI_RGB;
	BmpInfo->bmiHeader.biSizeImage=0;
	BmpInfo->bmiHeader.biXPelsPerMeter=11811;
	BmpInfo->bmiHeader.biYPelsPerMeter=11811;
	BmpInfo->bmiHeader.biClrUsed=0;
	BmpInfo->bmiHeader.biClrImportant=0;

	ColorSpaceConversions conv;
	conv.YV12_to_RGB24(Y, Cb, Cr, RGBbuf, iWidth, iHeight);

	CRect rect;
	GetClientRect(&rect);
	pDC->SetStretchBltMode(STRETCH_DELETESCANS);
	StretchDIBits(pDC->m_hDC,0,0,rect.Width(),rect.Height(),0,0,iWidth,iHeight,
				  RGBbuf, BmpInfo, DIB_RGB_COLORS,SRCCOPY);

	if (bShowMV)
	{
		ShowMotionVector(pDC);
	}
}

void CYUVPlayback::ShowGrayImage(CDC *pDC)
{
	int i;         
	int nNum1,nNum2;
	HANDLE hMem;
	BYTE *lpBuf;

	int BPP = 8;

	BmpInfo->bmiHeader.biSize=sizeof (BITMAPINFOHEADER);
	BmpInfo->bmiHeader.biWidth=iWidth;
	BmpInfo->bmiHeader.biHeight=iHeight;			// negative means up-to-bottom 
	BmpInfo->bmiHeader.biPlanes=1;
	BmpInfo->bmiHeader.biBitCount=(BPP<8?8:BPP);	// Our raster is byte-aligned
	BmpInfo->bmiHeader.biCompression=BI_RGB;
	BmpInfo->bmiHeader.biSizeImage=0;
	BmpInfo->bmiHeader.biXPelsPerMeter=11811;
	BmpInfo->bmiHeader.biYPelsPerMeter=11811;
	BmpInfo->bmiHeader.biClrUsed=0;
	BmpInfo->bmiHeader.biClrImportant=0;

	HANDLE hloc1;
	RGBQUAD *argbq;
	hloc1 = LocalAlloc(LMEM_ZEROINIT | LMEM_MOVEABLE,(sizeof(RGBQUAD) * 256));
	argbq = (RGBQUAD *) LocalLock(hloc1);
	for(i=0;i<256;i++) {
		argbq[i].rgbBlue=i;
		argbq[i].rgbGreen=i;
		argbq[i].rgbRed=i;
		argbq[i].rgbReserved=0;
	}
	memcpy(BmpInfo->bmiColors, argbq, sizeof(RGBQUAD) * 256);
	LocalUnlock(hloc1);
	LocalFree(hloc1);

    hMem=GlobalAlloc(GHND, iWidth*iHeight);
	lpBuf=(BYTE *)GlobalLock(hMem);
	
    //将倒置的图象放正 
	//Make the inverse image normal
	for(i=0;i<iHeight;i++)
	{
		nNum1=(iHeight-i-1)*iWidth;
		nNum2=i*iWidth;
		memcpy(lpBuf+nNum1, Y+nNum2, iWidth);
	}

	CRect rect;
	GetClientRect(&rect);
	pDC->SetStretchBltMode(STRETCH_DELETESCANS);
	StretchDIBits(pDC->m_hDC,0,0,rect.Width(),rect.Height(),0,0,iWidth,iHeight,
				  lpBuf, BmpInfo, DIB_RGB_COLORS, SRCCOPY);  

	GlobalUnlock(hMem);
	GlobalFree(hMem);

	if (bShowMV)
	{
		ShowMotionVector(pDC);
	}
}
