// 1394Edit.cpp : implementation file
//
#define WIN32_LEAN_AND_MEAN

#include "stdafx.h"
#include "1394Edit.h"
//#include <windows.h>
//#include <malloc.h>
#include "fwxVideo.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CHexEdit

CHexEdit::CHexEdit()
{ }

CHexEdit::~CHexEdit()
{ }

BEGIN_MESSAGE_MAP(CHexEdit, CEdit)
	//{{AFX_MSG_MAP(CHexEdit)
	ON_WM_CHAR()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CHexEdit message handlers
/////////////////////////////////////////////////////////////////////////////
// CDecimalEdit

CDecimalEdit::CDecimalEdit()
{ }

CDecimalEdit::~CDecimalEdit()
{ }

BEGIN_MESSAGE_MAP(CDecimalEdit, CEdit)
	//{{AFX_MSG_MAP(CDecimalEdit)
	ON_WM_CHAR()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDecimalEdit message handlers

void CDecimalEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
    if (iswdigit(nChar) || iswcntrl(nChar))
    {
        CEdit::OnChar(nChar, nRepCnt, nFlags);
    }
}

void CHexEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	if (iswdigit(nChar) ||iswcntrl(nChar) ||
	   ((towupper(nChar) >= _T('A')) && (towupper(nChar) <= _T('F'))))
    {
        CEdit::OnChar(nChar, nRepCnt, nFlags);
    }
}



////////// 
/////////////
///  from fwxvideo.cpp
////////////
///////////



CVideoProcess:: CVideoProcess( void ) 
{
	fInitialized = 0; 
		fwx_holdBitmap = NULL;
		fwx_hBitmap = NULL;
		fwx_holdPal = NULL;
		fwx_hGrayPal = NULL;
		fwx_pBI = NULL;
		fwx_pLogColorPal = NULL;
		fwx_pLogGrayPal = NULL;
		fwx_hMemDC = NULL;
		fwx_hDisplayDC = NULL;
		fwx_pColorTransTable = NULL; 
		fwx_pGrayTransTable = NULL;

	fwx_pRGBData = NULL;
	fwx_hColorPal= NULL;
	fwx_nBPP    = 0;
	fwx_nWidth  = 0;
	fwx_nHeight = 0;
	fwx_bColor = 0;
	fwx_nLastError = 0;
}


BOOL CVideoProcess::fwxInitDisplay(UINT nBPP, UINT nWidth, UINT nHeight, HWND hWnd)
{
	fwx_nLastError = FWX_VIDEO_ERROR_NONE;
	fInitialized = 1; 
	
	fwxDeleteDisplay(hWnd);

	if ((nWidth == 0) || (nHeight == 0))
	{
		fwx_nLastError = FWX_VIDEO_ERROR_PARAMETER;
		return FALSE;
	}

	// Only supporting 8 and 16bpp
	if ((nBPP != 8) && (nBPP != 16))
	{
		fwx_nLastError = FWX_VIDEO_ERROR_PARAMETER;
		return FALSE;		// Don't support anything else, yet
	}
	else
		fwx_nBPP = nBPP;	// Save for use by other functions

	// Save the width and height for use by other functions
	fwx_nWidth = nWidth;
	fwx_nHeight = nHeight;

	if (nBPP == 16)
	{
		fwx_pBI = (LPBITMAPINFO) new BYTE[sizeof(BITMAPINFOHEADER) +
										  sizeof(RGBQUAD) * 3];
		if (fwx_pBI == NULL)
		{
			fwx_nLastError = FWX_VIDEO_ERROR_ALLOCATION;
			return FALSE;
		}
		// Create the color masks
		fwx_pBI->bmiColors[0].rgbRed = 0;
		fwx_pBI->bmiColors[0].rgbGreen = 0x7C;
		fwx_pBI->bmiColors[0].rgbBlue = 0;
		fwx_pBI->bmiColors[0].rgbReserved = 0;
		fwx_pBI->bmiColors[1].rgbRed = 0;
		fwx_pBI->bmiColors[1].rgbGreen = 0x03;
		fwx_pBI->bmiColors[1].rgbBlue = 0xE0;
		fwx_pBI->bmiColors[1].rgbReserved = 0;
		fwx_pBI->bmiColors[2].rgbRed = 0;
		fwx_pBI->bmiColors[2].rgbGreen = 0;
		fwx_pBI->bmiColors[2].rgbBlue = 0x1F;
		fwx_pBI->bmiColors[2].rgbReserved = 0;

		// CreateDIBSection failed in WinCE unless we used BI_BITFIELDS
		fwx_pBI->bmiHeader.biCompression = BI_BITFIELDS;
	}
	else
	{
		fwx_pBI = (LPBITMAPINFO) new BYTE[sizeof(BITMAPINFO)];
		if (fwx_pBI == NULL)
		{
			fwx_nLastError = FWX_VIDEO_ERROR_ALLOCATION;
			return FALSE;
		}

		fwx_pBI->bmiHeader.biCompression = BI_RGB;
	}

	// Fill in BITMAPINFOHEADER for the dib section
	fwx_pBI->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	fwx_pBI->bmiHeader.biWidth = nWidth;
	fwx_pBI->bmiHeader.biHeight = nHeight * -1;	// Flip the image vertically
	fwx_pBI->bmiHeader.biPlanes = 1;
	fwx_pBI->bmiHeader.biBitCount = nBPP;
	fwx_pBI->bmiHeader.biSizeImage = nWidth*nHeight*(nBPP/8);
	fwx_pBI->bmiHeader.biXPelsPerMeter = 0;
	fwx_pBI->bmiHeader.biYPelsPerMeter = 0;
	fwx_pBI->bmiHeader.biClrUsed = 0;
	fwx_pBI->bmiHeader.biClrImportant = 0;

	if (nBPP == 8) // Only use palettes for 8bpp displays
	{
		// Create the logical color palette for 256 colors
		fwx_pLogColorPal = (LPLOGPALETTE) new BYTE[sizeof(LOGPALETTE) +
												   (256 * sizeof(PALETTEENTRY))];
		if (fwx_pLogColorPal == NULL)
		{
			delete []fwx_pBI;
			fwx_nLastError = FWX_VIDEO_ERROR_ALLOCATION;
			return FALSE;
		}
		fwx_pLogColorPal->palVersion = 0x300;
		fwx_pLogColorPal->palNumEntries = 256;

		// Create an 8-bit RGB color palette (332), spread evenly throughout
		// the spectrum. Any color improvements can be done here. This is
		// not an optimal color palette.
		BYTE palidx;
		for (BYTE R=0; R < 8; R++)
		{
			for (BYTE G=0; G < 8; G++)
			{
				for (BYTE B=0; B < 4; B++)
				{
					palidx = ((R << 5) | (G << 2) | B);
					fwx_pLogColorPal->palPalEntry[palidx].peRed = R * 36;
					fwx_pLogColorPal->palPalEntry[palidx].peGreen = G * 36;
					fwx_pLogColorPal->palPalEntry[palidx].peBlue = B * 85;
					fwx_pLogColorPal->palPalEntry[palidx].peFlags = 0;
				}
			}
		}

		if ((fwx_hColorPal = CreatePalette(fwx_pLogColorPal)) == NULL)
		{
			delete []fwx_pBI;
			delete []fwx_pLogColorPal;
			fwx_nLastError = FWX_VIDEO_ERROR_CREATEPALETTE;
			return FALSE;
		}

		// Create the logical palette for gray scale
		fwx_pLogGrayPal = (LPLOGPALETTE) new BYTE[sizeof(LOGPALETTE) +
												  (256 * sizeof(PALETTEENTRY))];
		if (fwx_pLogGrayPal == NULL)
		{
			delete []fwx_pBI;
			delete []fwx_pLogColorPal;
			fwx_nLastError = FWX_VIDEO_ERROR_ALLOCATION;
			return FALSE;
		}
		fwx_pLogGrayPal->palVersion = 0x300;
		fwx_pLogGrayPal->palNumEntries = 256;
		// Create a grayscale palette
		for (UINT i=0; i < 256; i++)
		{
			fwx_pLogGrayPal->palPalEntry[i].peRed =
			fwx_pLogGrayPal->palPalEntry[i].peGreen =
			fwx_pLogGrayPal->palPalEntry[i].peBlue = (BYTE)i;
			fwx_pLogGrayPal->palPalEntry[i].peFlags = 0;
		}

		if ((fwx_hGrayPal = CreatePalette(fwx_pLogGrayPal)) == NULL)
		{
			delete []fwx_pBI;
			delete []fwx_pLogColorPal;
			delete []fwx_pLogGrayPal;
			fwx_nLastError = FWX_VIDEO_ERROR_CREATEPALETTE;
			return FALSE;
		}
	}

	// Create the YUV color translation tables
	if (!fwxInitTransTables(nBPP))
	{
		delete []fwx_pBI;
		delete []fwx_pLogColorPal;
		delete []fwx_pLogGrayPal;
		return FALSE;
	}

	// Create the display DC based on client DC
	fwx_hDisplayDC = GetDC(hWnd);
	if (fwx_hDisplayDC == NULL)
	{
		delete []fwx_pBI;
		delete []fwx_pLogColorPal;
		delete []fwx_pLogGrayPal;
		fwx_nLastError = FWX_VIDEO_ERROR_GETDC;
		return FALSE;
	}

	// Create the memory DC from the client DC
	fwx_hMemDC = CreateCompatibleDC(fwx_hDisplayDC);
	if (fwx_hMemDC == NULL)
	{
		delete []fwx_pBI;
		delete []fwx_pLogColorPal;
		delete []fwx_pLogGrayPal;
		ReleaseDC(hWnd, fwx_hDisplayDC);
		fwx_nLastError = FWX_VIDEO_ERROR_CREATECOMPATIBLEDC;
		return FALSE;
	}

	UINT nUsage = (nBPP == 8) ? DIB_PAL_COLORS : DIB_RGB_COLORS;
	// create a dib section for direct drawing
	fwx_hBitmap = CreateDIBSection(fwx_hMemDC, fwx_pBI, nUsage,
								   &fwx_pRGBData, NULL, 0);
	if (fwx_hBitmap == NULL)
	{
		delete []fwx_pBI;
		delete []fwx_pLogColorPal;
		delete []fwx_pLogGrayPal;
		DeleteDC(fwx_hMemDC);
		ReleaseDC(hWnd, fwx_hDisplayDC);
		fwx_nLastError = FWX_VIDEO_ERROR_CREATEDIBSECTION;
		return FALSE;
	}

 	// Select this bitmap for drawing context
	fwx_holdBitmap = (HBITMAP)SelectObject(fwx_hMemDC, fwx_hBitmap);
	if (fwx_holdBitmap == NULL)
	{
		delete []fwx_pBI;
		delete []fwx_pLogColorPal;
		delete []fwx_pLogGrayPal;
		DeleteObject(fwx_hBitmap);
		DeleteDC(fwx_hMemDC);
		ReleaseDC(hWnd, fwx_hDisplayDC);
		fwx_nLastError = FWX_VIDEO_ERROR_SELECTBITMAP;
		return FALSE;
	}

	// Select the color mode and palette (8-bit only)
	if (!fwxSelectColorPalette())
	{
		delete []fwx_pBI;
		delete []fwx_pLogColorPal;
		delete []fwx_pLogGrayPal;
		SelectObject(fwx_hMemDC, fwx_holdBitmap);
		DeleteObject(fwx_hBitmap);
		DeleteDC(fwx_hMemDC);
		ReleaseDC(hWnd, fwx_hDisplayDC);
		return FALSE;
	}

	return TRUE;
}

BOOL CVideoProcess::fwxSelectColorPalette(void)
{
	if (!fwx_hMemDC)
	{
		fwx_nLastError = FWX_VIDEO_ERROR_DATA;
		return FALSE;
	}

	if (fwx_nBPP == 16)
	{
		// 16bpp doesn't use palettes
		fwx_bColor = TRUE;
		return TRUE;
	}
	else
	{
		// Only 8bpp uses palettes
		if (!fwx_hColorPal)
		{
			fwx_nLastError = FWX_VIDEO_ERROR_DATA;
			return FALSE;
		}

		HPALETTE hPal = SelectPalette(fwx_hMemDC, fwx_hColorPal, FALSE);
		if (hPal == NULL)
		{
			fwx_nLastError = FWX_VIDEO_ERROR_SELECTPALETTE;
			return FALSE;
		}
		else
		{
			fwx_bColor = TRUE;
			if (fwx_holdPal == NULL)
				fwx_holdPal = hPal;
			return TRUE;
		}
	}
}

BOOL CVideoProcess::fwxSelectGrayPalette(void)
{
	if (!fwx_hMemDC)
	{
		fwx_nLastError = FWX_VIDEO_ERROR_DATA;
		return FALSE;
	}

	if (fwx_nBPP == 16)
	{
		// 16bpp doesn't use palettes
		fwx_bColor = FALSE;
		return TRUE;
	}
	else
	{
		// Only 8bpp uses palettes
		if (!fwx_hGrayPal)
		{
			fwx_nLastError = FWX_VIDEO_ERROR_DATA;
			return FALSE;
		}

		HPALETTE hPal = SelectPalette(fwx_hMemDC, fwx_hGrayPal, FALSE);
		if (hPal == NULL)
		{
			fwx_nLastError = FWX_VIDEO_ERROR_SELECTPALETTE;
			return FALSE;
		}
		else
		{
			fwx_bColor = FALSE;
			if (fwx_holdPal == NULL)
				fwx_holdPal = hPal;
			return TRUE;
		}
	}
}


// Calc the RGB Red component from the YUV data
BYTE CVideoProcess::fwxCalcYUVRed(int Y, int V)
{
	int R = Y + ((int)(1.4020 * V));

	// Fix any out of range values
	if (R < 0)
		R = 0;
	else if (R > 255)
		R = 255;

	return (BYTE)R;
}

// Calc the RGB Green component from the YUV data
BYTE CVideoProcess::fwxCalcYUVGreen(int Y, int U, int V)
{
	int G = Y - ((int)(0.3441 * U)) - ((int)(0.7139 * V));

	// Fix any out of range values
	if (G < 0)
		G = 0;
	else if (G > 255)
		G = 255;

	return (BYTE)G;
}

// Calc the RGB Blue component from the YUV data
BYTE CVideoProcess::fwxCalcYUVBlue(int Y, int U)
{
	int B = Y + ((int)(1.7718 * U));

	// Fix any out of range values
	if (B < 0)
		B = 0;
	else if (B > 255)
		B = 255;

	return (BYTE)B;
}

BOOL CVideoProcess::fwxValidateParam( void )
{
	if (!fwx_hDisplayDC || !fwx_hMemDC)
	{
		fwx_nLastError = FWX_VIDEO_ERROR_DATA;
		return FALSE;
	}
	if ((fwx_nBPP != 8) && (fwx_nBPP != 16))
	{
		fwx_nLastError = FWX_VIDEO_ERROR_DATA;
		return FALSE;
	}
	return TRUE;
}

BOOL CVideoProcess::fwxProcessFrame(UINT nMode, BYTE *pFrame)
{
	BYTE *pData = pFrame;
	nFrameSize = fwx_nWidth * fwx_nHeight;

	switch(nMode)
	{
	case 1:
	case 3:
		// YUV Data is in the form of (U, Y1, V, Y2)
		switch(fwx_nBPP)
		{
		case 16:
			if (fwx_bColor)
			{
				/// mid video here
				WORD *pRGBDest = (WORD *)fwx_pRGBData;
				for (long i=0; i < nFrameSize; i++ )
				{
					YUVidx = CALC_YUV_INDEX(*(pData+1), *pData, *(pData+2));
					*pRGBDest++ = ((WORD *)fwx_pColorTransTable)[YUVidx];
					YUVidx = CALC_YUV_INDEX(*(pData+3), *pData, *(pData+2));
					*pRGBDest++ = ((WORD *)fwx_pColorTransTable)[YUVidx];
					// Increment the pointers for 2 pixels
					//pRGBDest += 2;
					pData += 4;
					i++;
				}
			}
			else
			{
				WORD *pRGBDest = (WORD *)fwx_pRGBData;
				for (long i=0; i < nFrameSize; i++)
				{
					pRGBDest[0] = ((WORD *)fwx_pGrayTransTable)[*(pData+1)];
					pRGBDest[1] = ((WORD *)fwx_pGrayTransTable)[*(pData+3)];
					// Increment the pointers for 2 pixels
					pRGBDest += 2;
					pData += 4;
					i++;
				}
			}
			break;
		case 8:
		default:
			if (fwx_bColor)
			{
				BYTE *pRGBDest = (BYTE *)fwx_pRGBData;
				for (long i=0; i < nFrameSize; i++)
				{
					YUVidx = CALC_YUV_INDEX(*(pData+1), *pData, *(pData+2));
					pRGBDest[0] = ((BYTE *)fwx_pColorTransTable)[YUVidx];
					YUVidx = CALC_YUV_INDEX(*(pData+3), *pData, *(pData+2));
					pRGBDest[1] = ((BYTE *)fwx_pColorTransTable)[YUVidx];
					// Increment the pointers for 2 pixels
					pRGBDest += 2;
					pData += 4;
					i++;
				}
			}
			else
			{
				BYTE *pRGBDest = (BYTE *)fwx_pRGBData;
				for (long i=0; i < nFrameSize; i++)
				{
					pRGBDest[0] = ((BYTE *)fwx_pGrayTransTable)[*(pData+1)];
					pRGBDest[1] = ((BYTE *)fwx_pGrayTransTable)[*(pData+3)];
					// Increment the pointers for 2 pixels
					pRGBDest += 2;
					pData += 4;
					i++;
				}
			}
			break;
		}	// End bpp switch
		break;	// Mode 1 & 3

	case 2:
		// YUV Data is in the form of (U, Y1, Y2, V, Y3, Y4)
		switch(fwx_nBPP)
		{
		case 16:
			if (fwx_bColor)
			{
				WORD *pRGBDest = (WORD *)fwx_pRGBData;
				for (long i=0; i < nFrameSize; i++)
				{
					YUVidx = CALC_YUV_INDEX(*(pData+1), *pData, *(pData+3));
					pRGBDest[0] = ((WORD *)fwx_pColorTransTable)[YUVidx];
					YUVidx = CALC_YUV_INDEX(*(pData+2), *pData, *(pData+3));
					pRGBDest[1] = ((WORD *)fwx_pColorTransTable)[YUVidx];
					YUVidx = CALC_YUV_INDEX(*(pData+4), *pData, *(pData+3));
					pRGBDest[2] = ((WORD *)fwx_pColorTransTable)[YUVidx];
					YUVidx = CALC_YUV_INDEX(*(pData+5), *pData, *(pData+3));
					pRGBDest[3] = ((WORD *)fwx_pColorTransTable)[YUVidx];
					// Increment the pointers for 4 pixels
					pRGBDest += 4;
					pData += 6;
					i += 3;
				}
			}
			else
			{
				WORD *pRGBDest = (WORD *)fwx_pRGBData;
				for (long i=0; i < nFrameSize; i++)
				{
					pRGBDest[0] = ((WORD *)fwx_pGrayTransTable)[*(pData+1)];
					pRGBDest[1] = ((WORD *)fwx_pGrayTransTable)[*(pData+2)];
					pRGBDest[2] = ((WORD *)fwx_pGrayTransTable)[*(pData+4)];
					pRGBDest[3] = ((WORD *)fwx_pGrayTransTable)[*(pData+5)];
					// Increment the pointers for 4 pixels
					pRGBDest += 4;
					pData += 6;
					i += 3;
				}
			}
			break;
		case 8:
		default:
			if (fwx_bColor)
			{
				BYTE *pRGBDest = (BYTE *)fwx_pRGBData;
				for (long i=0; i < nFrameSize; i++)
				{
					YUVidx = CALC_YUV_INDEX(*(pData+1), *pData, *(pData+3));
					pRGBDest[0] = ((BYTE *)fwx_pColorTransTable)[YUVidx];
					YUVidx = CALC_YUV_INDEX(*(pData+2), *pData, *(pData+3));
					pRGBDest[1] = ((BYTE *)fwx_pColorTransTable)[YUVidx];
					YUVidx = CALC_YUV_INDEX(*(pData+4), *pData, *(pData+3));
					pRGBDest[2] = ((BYTE *)fwx_pColorTransTable)[YUVidx];
					YUVidx = CALC_YUV_INDEX(*(pData+5), *pData, *(pData+3));
					pRGBDest[3] = ((BYTE *)fwx_pColorTransTable)[YUVidx];
					// Increment the pointers for 4 pixels
					pRGBDest += 4;
					pData += 6;
					i += 3;
				}
			}
			else
			{
				BYTE *pRGBDest = (BYTE *)fwx_pRGBData;
				for (long i=0; i < nFrameSize; i++)
				{
					pRGBDest[0] = ((BYTE *)fwx_pGrayTransTable)[*(pData+1)];
					pRGBDest[1] = ((BYTE *)fwx_pGrayTransTable)[*(pData+2)];
					pRGBDest[2] = ((BYTE *)fwx_pGrayTransTable)[*(pData+4)];
					pRGBDest[3] = ((BYTE *)fwx_pGrayTransTable)[*(pData+5)];
					// Increment the pointers for 4 pixels
					pRGBDest += 4;
					pData += 6;
					i += 3;
				}
			}
			break;
		}	// End bpp switch
		break;	// Mode 2


	case 5:
		// The data contains only Y data
		switch(fwx_nBPP)
		{
		case 16:
			{
			WORD *pRGBDest = (WORD *)fwx_pRGBData;
			for (long i=0; i < nFrameSize; i++)
			{
				pRGBDest[0] = ((WORD *)fwx_pGrayTransTable)[*pData];
				// Increment the pointers for 1 pixel
				pRGBDest++;
				pData++;
			}
			}
			break;
		case 8:
		default:
			{
			BYTE *pRGBDest = (BYTE *)fwx_pRGBData;
			for (long i=0; i < nFrameSize; i++)
			{
				pRGBDest[0] = ((BYTE *)fwx_pGrayTransTable)[*pData];
				// Increment the pointers for 1 pixel
				pRGBDest++;
				pData++;
			}
			}
			break;
		}	// End bpp switch


	case 0:
	default:
		// The YUV data is in the form (U, Y, V)
		switch(fwx_nBPP)
		{
		case 16:
			if (fwx_bColor)
			{
				WORD *pRGBDest = (WORD *)fwx_pRGBData;
				for (long i=0; i < nFrameSize; i++)
				{
					// small video here
					YUVidx = CALC_YUV_INDEX(*(pData+1), *pData, *(pData+2));
					*pRGBDest++ = ((WORD *)fwx_pColorTransTable)[YUVidx];
					//pRGBDest++;
					pData += 3;
				}
			}
			else
			{
				WORD *pRGBDest = (WORD *)fwx_pRGBData;
				for (long i=0; i < nFrameSize; i++)
				{
					pRGBDest[0] = ((WORD *)fwx_pGrayTransTable)[*(pData+1)];
					pRGBDest++;
					pData += 3;
				}
			}
			break;
		case 8:
		default:
			if (fwx_bColor)
			{
				BYTE *pRGBDest = (BYTE *)fwx_pRGBData;
				for (long i=0; i < nFrameSize; i++)
				{
					YUVidx = CALC_YUV_INDEX(*(pData+1), *pData, *(pData+2));
					pRGBDest[0] = ((BYTE *)fwx_pColorTransTable)[YUVidx];
					pRGBDest++;
					pData += 3;
				}
			}
			else
			{
				BYTE *pRGBDest = (BYTE *)fwx_pRGBData;
				for (long i=0; i < nFrameSize; i++)
				{
					pRGBDest[0] = ((BYTE *)fwx_pGrayTransTable)[*(pData+1)];
					pRGBDest++;
					pData += 3;
				}
			}
			break;
		}	// End bpp switch
	}	// End video mode switch

	if (!BitBlt(fwx_hDisplayDC, 0, 0, fwx_nWidth, fwx_nHeight,
				fwx_hMemDC, 0, 0, SRCCOPY))
	{
		fwx_nLastError = FWX_VIDEO_ERROR_BITBLT;
		return FALSE;
	}
	else
		return TRUE;
}

BOOL CVideoProcess:: fwxShowFrame(UINT nMode)
{
	if (!BitBlt(fwx_hDisplayDC, 0, 0, fwx_nWidth, fwx_nHeight,
				fwx_hMemDC, 0, 0, SRCCOPY))
	{
		fwx_nLastError = FWX_VIDEO_ERROR_BITBLT;
		return FALSE;
	}
	else
		return TRUE;
}


BOOL CVideoProcess:: fwxInitTransTables(UINT nBPP)
{
	// If color tables were already created, just return
	if (fwx_pGrayTransTable || fwx_pColorTransTable)
		return TRUE;

	// Create the YUV color translation table
	BYTE palidx;
	WORD pixval;
	UINT YUVidx, Yidx;
	int rY, rU, rV;
	BYTE Y, U, V;
	BYTE R, G, B;

	switch(nBPP)
	{
	case 16:
		// Only need 256 colors for grayscale
		if ((fwx_pGrayTransTable = (LPVOID) new WORD[0x100]) == NULL)
		{
			fwx_nLastError = FWX_VIDEO_ERROR_ALLOCATION;
			return FALSE;
		}

		// Create a grayscale color table in RGB (555), MSB not used
		for(Yidx=0; Yidx < 0x100; Yidx++)
		{
			WORD b = (WORD)Yidx;
			// Same value for each 5 bits
			pixval = 0;
			pixval = ((b >> 3) << 10) | ((b >> 3) << 5) | (b >> 3);
			((WORD *)fwx_pGrayTransTable)[Yidx] = pixval;
		}

		// Create a 32k color translation table
		if ((fwx_pColorTransTable = (LPVOID) new WORD[0x8000L]) == NULL)
		{
			delete []((WORD *)fwx_pGrayTransTable);
			fwx_nLastError = FWX_VIDEO_ERROR_ALLOCATION;
			return FALSE;
		}
		memset((WORD *)fwx_pColorTransTable, 0, 0x8000L);

		for(Y=0; Y <= 0x1F; Y++)
		{
			for (U=0; U <= 0x1F; U++)
			{
				for (V=0; V <= 0x1F; V++)
				{
					// Restore the true YUV values
					rY = Y << 3;
					rU = (U << 3) - 128;
					rV = (V << 3) - 128;
					// Calculate the RGB values
					R = fwxCalcYUVRed(rY, rV);
					G = fwxCalcYUVGreen(rY, rU, rV);
					B = fwxCalcYUVBlue(rY, rU);
					// Create a 15bit RGB value (555), MSB not used
					pixval = 0;
					pixval = ((WORD)(R >> 3) << 10) | ((WORD)(G >> 3) << 5) | (WORD)(B >> 3);
					// Calculate the YUV index based on (555) bit YUV value
					YUVidx = CALC_YUV_INDEX((Y<<3), (U<<3), (V<<3));
					// Assign the RGB value for this YUV index
					((WORD *)fwx_pColorTransTable)[YUVidx] = pixval;
				}
			}
		}
		break;
	case 8:
	default:
		// Create a 256 grayscale translation table
		if ((fwx_pGrayTransTable = (LPVOID) new BYTE[0x100]) == NULL)
		{
			fwx_nLastError = FWX_VIDEO_ERROR_ALLOCATION;
			return FALSE;
		}

		// Create a grayscale color table
		for(Yidx=0; Yidx < 0x100; Yidx++)
		{
			// Find the closet color in the palette for this gray color
			palidx = (BYTE)GetNearestPaletteIndex(fwx_hGrayPal, RGB(Yidx,Yidx,Yidx));
			if (palidx == CLR_INVALID)
			{
				palidx = 0;
			}
			((BYTE *)fwx_pGrayTransTable)[Yidx] = palidx;
		}

		// Create a 32k color translation table
		if ((fwx_pColorTransTable = (LPVOID) new BYTE[0x8000L]) == NULL)
		{
			delete []((BYTE *)fwx_pGrayTransTable);
			fwx_nLastError = FWX_VIDEO_ERROR_ALLOCATION;
			return FALSE;
		}

		// Loop through all possible YUV points for a (555)bit YUV value
		for(Y=0; Y <= 0x1F; Y++)
		{
			for (U=0; U <= 0x1F; U++)
			{
				for (V=0; V <= 0x1F; V++)
				{
					// Restore the true YUV values, use the 6 or 5 MSBs
					rY = (Y << 3);
					rU = (U << 3) - 128;
					rV = (V << 3) - 128;
					// Calculate the RGB value from the YUV value
					R = fwxCalcYUVRed(rY, rV);
					G = fwxCalcYUVGreen(rY, rU, rV);
					B = fwxCalcYUVBlue(rY, rU);
					// Find the closet color in the palette for this RGB value
					palidx = (BYTE)GetNearestPaletteIndex(fwx_hColorPal, RGB(R,G,B));
					if (palidx == CLR_INVALID)
					{
						palidx = 0;
					}
					// Calculate the YUV index based on (555)bit YUV value
					YUVidx = CALC_YUV_INDEX((Y<<3), (U<<3), (V<<3));
					// Assign the palette index for this YUV index
					((BYTE *)fwx_pColorTransTable)[YUVidx] = palidx;
				}
			}
		}
		break;
	}

	return TRUE;
}


BOOL  CVideoProcess:: fwxSaveTrueBitmap(UINT nMode, BYTE *pFrame, TCHAR *szFileName)
{
	if ((pFrame == NULL) || (szFileName == NULL))
	{
		fwx_nLastError = FWX_VIDEO_ERROR_PARAMETER;
		return FALSE;
	}
	if (wcslen((const unsigned short *)szFileName) <= 0)
	{
		fwx_nLastError = FWX_VIDEO_ERROR_PARAMETER;
		return FALSE;
	}

	// The thread should be suspended for this function
	BITMAPFILEHEADER bf;
	BITMAPINFOHEADER bi;

	HANDLE hFile = CreateFile(szFileName, GENERIC_WRITE, 0, NULL,
							  CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		fwx_nLastError = FWX_VIDEO_ERROR_CREATEFILE;
		return FALSE;
	}

	// Setup a 24-bit bitmap
	bi.biSize = sizeof(BITMAPINFOHEADER);
	bi.biWidth = fwx_nWidth;
	bi.biHeight = fwx_nHeight;
	bi.biPlanes = 1;
	bi.biBitCount = 24;
	bi.biCompression = BI_RGB;
	bi.biSizeImage = fwx_nWidth * fwx_nHeight * 3;
	bi.biXPelsPerMeter = 0;
	bi.biYPelsPerMeter = 0;
	bi.biClrUsed = 0;
	bi.biClrImportant = 0;

	bf.bfType = 0x4D42;
	bf.bfSize = (DWORD) sizeof(BITMAPFILEHEADER) +
				bi.biSize +
				bi.biSizeImage;
	bf.bfReserved1 = 0;
	bf.bfReserved2 = 0;
	bf.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + bi.biSize;

	DWORD bwrote;
	if (!WriteFile(hFile, &bf, sizeof(BITMAPFILEHEADER),
				   &bwrote, NULL))
	{
		fwx_nLastError = FWX_VIDEO_ERROR_WRITEFILE;
		CloseHandle(hFile);
		return FALSE;
	}

	if (!WriteFile(hFile, &bi, sizeof(BITMAPINFOHEADER),
				   &bwrote, NULL))
	{
		fwx_nLastError = FWX_VIDEO_ERROR_WRITEFILE;
		CloseHandle(hFile);
		return FALSE;
	}

	BYTE *pData = pFrame;
	float nDataLen;
	switch(nMode)
	{
	case 1:
	case 3:
		nDataLen = 2.0;
		break;
	case 2:
		nDataLen = 1.5;
		break;
	case 5:
		nDataLen = 1.0;
		break;
	case 4:
	case 0:
	default:
		nDataLen = 3.0;
		break;
	}
	
	int Y1, U, V, Y2, Y3, Y4;
	BYTE RGBData[12];
	for (int h=fwx_nHeight-1; h >= 0; h--)
	{
		pData = pFrame + (ULONG)(h * fwx_nWidth * nDataLen);
		for (UINT w=0; w < fwx_nWidth; w++)
		{
			switch(nMode)
			{
			case 1:		// YUV format 4:2:2, (U, Y1, V, Y2)
			case 3:
				Y1 = (int)(*(pData+1));
				Y2 = (int)(*(pData+3));
				U = ((int)(*pData)) - 128;
				V = ((int)(*(pData+2))) - 128;
				RGBData[2] = fwxCalcYUVRed(Y1, V);
				RGBData[1] = fwxCalcYUVGreen(Y1, U, V);
				RGBData[0] = fwxCalcYUVBlue(Y1, U);
				RGBData[5] = fwxCalcYUVRed(Y2, V);
				RGBData[4] = fwxCalcYUVGreen(Y2, U, V);
				RGBData[3] = fwxCalcYUVBlue(Y2, U);
				if (!WriteFile(hFile, RGBData, 6, &bwrote, NULL))
				{
					fwx_nLastError = FWX_VIDEO_ERROR_WRITEFILE;
					CloseHandle(hFile);
					return FALSE;
				}
				w++;		// Processed 2 pixels
				pData += 4;
				break;
			case 2:		// YUV format 4:1:1, (U, Y1, Y2, V, Y3, Y4)
				Y1 = (int)(*(pData+1));
				Y2 = (int)(*(pData+2));
				Y3 = (int)(*(pData+4));
				Y4 = (int)(*(pData+5));
				U = ((int)(*pData)) - 128;
				V = ((int)(*(pData+3))) - 128;
				RGBData[2] = fwxCalcYUVRed(Y1, V);
				RGBData[1] = fwxCalcYUVGreen(Y1, U, V);
				RGBData[0] = fwxCalcYUVBlue(Y1, U);
				RGBData[5] = fwxCalcYUVRed(Y2, V);
				RGBData[4] = fwxCalcYUVGreen(Y2, U, V);
				RGBData[3] = fwxCalcYUVBlue(Y2, U);
				RGBData[8] = fwxCalcYUVRed(Y3, V);
				RGBData[7] = fwxCalcYUVGreen(Y3, U, V);
				RGBData[6] = fwxCalcYUVBlue(Y3, U);
				RGBData[11] = fwxCalcYUVRed(Y4, V);
				RGBData[10] = fwxCalcYUVGreen(Y4, U, V);
				RGBData[9] = fwxCalcYUVBlue(Y4, U);
				if (!WriteFile(hFile, RGBData, 12, &bwrote, NULL))
				{
					fwx_nLastError = FWX_VIDEO_ERROR_WRITEFILE;
					CloseHandle(hFile);
					return FALSE;
				}
				w += 3;		// Processed 4 pixels
				pData += 6;
				break;
			case 4:		// RGB format
				RGBData[2] = *pData;
				RGBData[1] = *(pData+1);
				RGBData[0] = *(pData+2);
				if (!WriteFile(hFile, RGBData, 3, &bwrote, NULL))
				{
					fwx_nLastError = FWX_VIDEO_ERROR_WRITEFILE;
					CloseHandle(hFile);
					return FALSE;
				}
				pData += 3;
				break;
			case 5:		// Monochrome format, Y data only
				RGBData[2] = *pData;
				RGBData[1] = *pData;
				RGBData[0] = *pData;
				if (!WriteFile(hFile, RGBData, 3, &bwrote, NULL))
				{
					fwx_nLastError = FWX_VIDEO_ERROR_WRITEFILE;
					CloseHandle(hFile);
					return FALSE;
				}
				pData++;
				break;
			case 0:		// YUV format 4:4:4, (U,Y1,V)
			default:
				Y1 = (int)(*(pData+1));
				U = ((int)(*pData)) - 128;
				V = ((int)(*(pData+2))) - 128;
				RGBData[2] = fwxCalcYUVRed(Y1, V);
				RGBData[1] = fwxCalcYUVGreen(Y1, U, V);
				RGBData[0] = fwxCalcYUVBlue(Y1, U);
				if (!WriteFile(hFile, RGBData, 3, &bwrote, NULL))
				{
					fwx_nLastError = FWX_VIDEO_ERROR_WRITEFILE;
					CloseHandle(hFile);
					return FALSE;
				}
				pData += 3;
				break;
			}	// end mode switch
		}		// end width for loop
	}			// end height for loop

	CloseHandle(hFile);
	return TRUE;
}


BOOL CVideoProcess:: fwxSaveScreenShot(void)
{
	BITMAPFILEHEADER bm_fh;
	TCHAR FileName[16];
	strcpy(FileName, _T("BVScreen.bmp"));

	HANDLE hFile = CreateFile(FileName, GENERIC_WRITE, 0, NULL,
							  CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		fwx_nLastError = FWX_VIDEO_ERROR_CREATEFILE;
		return FALSE;
	}

	BITMAP bm;
	if (GetObject(fwx_hBitmap, sizeof(BITMAP), &bm) == 0)
	{
		fwx_nLastError = FWX_VIDEO_ERROR_DATA;
		CloseHandle(hFile);
		return FALSE;
	}

	BITMAPINFOHEADER bi;
	bi.biSize = sizeof(BITMAPINFOHEADER);
	bi.biWidth = bm.bmWidth;
	bi.biHeight = bm.bmHeight;
	bi.biPlanes = bm.bmPlanes;
	bi.biBitCount = bm.bmBitsPixel;
	bi.biCompression = BI_RGB;
	bi.biSizeImage = bm.bmWidth*bm.bmHeight*(bm.bmBitsPixel/8);
	bi.biXPelsPerMeter = 0;
	bi.biYPelsPerMeter = 0;
	bi.biClrUsed = 0;
	bi.biClrImportant = 0;

	bm_fh.bfType = 0x4D42;
	bm_fh.bfSize = (DWORD)sizeof(BITMAPFILEHEADER) +
					bi.biSize + 256*4 +
					bi.biSizeImage;
	bm_fh.bfReserved1 = 0;
	bm_fh.bfReserved2 = 0;
	if (fwx_nBPP == 8)
	{
		bm_fh.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) +
						  bi.biSize + 256*4;
	}
	else
	{
		bm_fh.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + bi.biSize;
	}


	DWORD bwrote;
	if (!WriteFile(hFile, &bm_fh, sizeof(BITMAPFILEHEADER),
				   &bwrote, NULL))
	{
		fwx_nLastError = FWX_VIDEO_ERROR_WRITEFILE;
		CloseHandle(hFile);
		return FALSE;
	}

	if (!WriteFile(hFile, &bi, sizeof(BITMAPINFOHEADER),
				   &bwrote, NULL))
	{
		fwx_nLastError = FWX_VIDEO_ERROR_WRITEFILE;
		CloseHandle(hFile);
		return FALSE;
	}

	if (fwx_nBPP == 8)
	{
		if (fwx_bColor)
		{
			if (!WriteFile(hFile, fwx_pLogColorPal->palPalEntry, 256*4,
						   &bwrote, NULL))
			{
				fwx_nLastError = FWX_VIDEO_ERROR_WRITEFILE;
				CloseHandle(hFile);
				return FALSE;
			}
		}
		else
		{
			if (!WriteFile(hFile, fwx_pLogGrayPal->palPalEntry, 256*4,
						   &bwrote, NULL))
			{
				fwx_nLastError = FWX_VIDEO_ERROR_WRITEFILE;
				CloseHandle(hFile);
				return FALSE;
			}
		}
	}

	// Must flip the picture vertically
	for (int h=bm.bmHeight-1; h >= 0; h--)
	{
		if (!WriteFile(hFile, ((BYTE *)bm.bmBits)+(h*bm.bmWidth),
					   bm.bmWidth, &bwrote, NULL))
		{
			fwx_nLastError = FWX_VIDEO_ERROR_WRITEFILE;
			CloseHandle(hFile);
			return FALSE;
		}
	}

	CloseHandle(hFile);
	return TRUE;
}

BOOL CVideoProcess:: fwxDeleteDisplay(HWND hWnd)
{
	if ( !fInitialized )
		return TRUE;

	if (fwx_hBitmap && fwx_hMemDC)
	{
		if (fwx_holdBitmap)
		{
			SelectObject(fwx_hMemDC, fwx_holdBitmap);
			fwx_holdBitmap = NULL;
		}
		if (DeleteObject(fwx_hBitmap))
			fwx_hBitmap = NULL;
	}
	if (fwx_hColorPal && fwx_hMemDC)
	{
		if (fwx_holdPal)
		{
			SelectObject(fwx_hMemDC, fwx_holdPal);
			fwx_holdPal = NULL;
		}
		if (DeleteObject(fwx_hColorPal))
			fwx_hColorPal = NULL;
	}
	if (fwx_hGrayPal)
		if (DeleteObject(fwx_hGrayPal))
			fwx_hGrayPal = NULL;

	if (fwx_pBI)
	{
		delete fwx_pBI;
		fwx_pBI = NULL;
	}
	if (fwx_pLogColorPal)
	{
		delete []fwx_pLogColorPal;
		fwx_pLogColorPal = NULL;
	}
	if (fwx_pLogGrayPal)
	{
		delete []fwx_pLogGrayPal;
		fwx_pLogGrayPal = NULL;
	}
	if (fwx_hMemDC)
	{
		if (DeleteObject(fwx_hMemDC))
			fwx_hMemDC = NULL;
	}
	if (fwx_hDisplayDC)
	{
		if (ReleaseDC(hWnd, fwx_hDisplayDC))
			fwx_hDisplayDC = NULL;
	}

	// Delete the translation table only if there isn't a window
	// The destructor of the CVideo Wnd object in BusView will call
	// this function and will not have a window at that point
	if (!IsWindow(hWnd))
	{
		switch(fwx_nBPP)
		{
		case 16:
			if (fwx_pColorTransTable) {
				delete [](WORD *)fwx_pColorTransTable;
				fwx_pColorTransTable = NULL; 
			}
			if (fwx_pGrayTransTable) {
				delete [](WORD *)fwx_pGrayTransTable;
				fwx_pGrayTransTable = NULL;
			}
			break;
		case 8:
		default:
			if (fwx_pColorTransTable) {
				delete [](BYTE *)fwx_pColorTransTable;
				fwx_pColorTransTable = NULL; 
			}
			if (fwx_pGrayTransTable) {
				delete [](BYTE *)fwx_pGrayTransTable;
				fwx_pGrayTransTable = NULL;
			}
			break;
		}
	}

	return TRUE;
}

// Get the last error that occured
ULONG  CVideoProcess:: fwxGetLastVideoError(void)
{
	return fwx_nLastError;
}

// Get the text for an error code
LPCTSTR CVideoProcess:: fwxGetVideoErrorText(ULONG nErrorCode)
{
	UINT index = 0;

	static LPCTSTR fwxVideoErrorText[] = 
	{
		TEXT("Unknown or invalid error code"),
		TEXT("No error occurred"),
		TEXT("Invalid parameter"),
		TEXT("Invalid data"),
		TEXT("Memory allocation failed"),
		TEXT("Create palette failed"),
		TEXT("Get display device dontext failed"),
		TEXT("Create memory device context failed"),
		TEXT("Create DIB section failed"),
		TEXT("Select bitmap failed"),
		TEXT("Select palette failed"),
		TEXT("Bitmap transfer failed"),
		TEXT("Error creating file"),
		TEXT("Error writing to file")
	};

    switch(nErrorCode) 
    {
	    case FWX_VIDEO_ERROR_NONE:					index = 1;	break;
		case FWX_VIDEO_ERROR_PARAMETER:				index = 2;	break;
		case FWX_VIDEO_ERROR_DATA:					index = 3;	break;
		case FWX_VIDEO_ERROR_ALLOCATION:			index = 4;	break;
		case FWX_VIDEO_ERROR_CREATEPALETTE:			index = 5;	break;
		case FWX_VIDEO_ERROR_GETDC:					index = 6;	break;
		case FWX_VIDEO_ERROR_CREATECOMPATIBLEDC:	index = 7;	break;
		case FWX_VIDEO_ERROR_CREATEDIBSECTION:		index = 8;	break;
		case FWX_VIDEO_ERROR_SELECTBITMAP:			index = 9;	break;
		case FWX_VIDEO_ERROR_SELECTPALETTE:			index = 10;	break;
		case FWX_VIDEO_ERROR_BITBLT:				index = 11;	break;
		case FWX_VIDEO_ERROR_CREATEFILE:			index = 12;	break;
		case FWX_VIDEO_ERROR_WRITEFILE:				index = 13; break;
		default:									index = 0;  break;
    }

    return fwxVideoErrorText[index];
}

//#include "fwxDCam.cpp"

