// fourier.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "resource.h"
#include <stdio.h>
#include <commdlg.h>
#include <math.h>
#include "pgmfile.h"
#include "four1.h"
#include "fourier.h"

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// The title bar text
HWND g_hWnd;
char filename[255];
Cpgmfile pgmOriginal, pgmFourier, pgmInvFt;
bool bFileOpen;
int iViewState;
int iClickCt;
POINT clicks[CLICKS_WANTED*2];
HBITMAP hBitmap;
HDC hOffScreenDC;
float *pFloatBuff;								//Used to store the complex representation of the FFT image
bool bHardCoded;

// Foward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK	About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	MSG msg;
	HACCEL hAccelTable;
	g_hWnd = NULL;
	bFileOpen = false;
	iViewState = VIEW_NOTHING;
	hBitmap = NULL;
	hOffScreenDC = NULL;
	iClickCt = 0;
	pFloatBuff = NULL;
	bHardCoded = false;

	if(strstr(lpCmdLine, "-hc") != NULL)
		bHardCoded = true;						//Use hard coded pix vals for inv fft

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_FOURIER, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow)) 
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_FOURIER);

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0)) 
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage is only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, (LPCTSTR)IDI_FOURIER);
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= (LPCSTR)IDC_FOURIER;
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);

	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HANDLE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // Store instance handle in our global variable

   g_hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME,
      CW_USEDEFAULT, 0, 200/*CW_USEDEFAULT*/, 200/*0*/, NULL, NULL, hInstance, NULL);

   if (!g_hWnd)
   {
      return FALSE;
   }

   ShowWindow(g_hWnd, nCmdShow);
   UpdateWindow(g_hWnd);

   memset(filename, 0, 255);
   return TRUE;
}

//
//  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;
	TCHAR szHello[MAX_LOADSTRING];
	LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);

	switch (message) 
	{
		case WM_COMMAND:
			wmId    = LOWORD(wParam); 
			wmEvent = HIWORD(wParam); 
			
			switch (wmId)			// Parse the menu selections:
			{
				case IDM_ABOUT:
				   DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
				   break;
				case IDM_CTRLX:
				case IDM_EXIT:
					if(hBitmap != NULL){
						DeleteObject(hBitmap);
						hBitmap = NULL;
					}//if
					if(hOffScreenDC != NULL){
						DeleteDC(hOffScreenDC);
						hOffScreenDC = NULL;
					}//if
					if(pFloatBuff != NULL){
						free(pFloatBuff);
						pFloatBuff = NULL;
					}//if

				   DestroyWindow(hWnd);

				   break;
				case IDM_CTRLO:
				case ID_FILE_OPEN:
					doFileOpen();
					break;
				case IDM_CTRLC:
				case ID_FILE_CLOSE:
					doFileClose();
					break;
				case IDM_CTRLA:
				case ID_FILE_SAVEAS:
					doSaveAs();
					break;
				case IDM_F2:
				case ID_VIEW_FT:{
			
					doViewFT();
					if(bHardCoded){
						doMouseClick(528, 245);
						doMouseClick(500, 239);
					}//if

					break;
								}
				case IDM_F1:
				case ID_VIEW_ORIGINAL:
					doViewOriginal();
					break;
				case IDM_F3:
				case ID_VIEW_OVERLAY:
					doViewOverlay();
					break;
				default:
				   return DefWindowProc(hWnd, message, wParam, lParam);
			}
			break;
		case WM_LBUTTONDOWN:
			doMouseClick(LOWORD(lParam), HIWORD(lParam));
			break;				
		case WM_MOUSEMOVE:
			if( (wParam & MK_SHIFT) == MK_SHIFT)
				doMouseMove(LOWORD(lParam), HIWORD(lParam));
		case WM_PAINT:
			break;
		case WM_DESTROY:
			if(hBitmap != NULL){
				DeleteObject(hBitmap);
				hBitmap = NULL;
			}
			if(hOffScreenDC != NULL){
				DeleteDC(hOffScreenDC);
				hOffScreenDC = NULL;
			}
			if(pFloatBuff != NULL){
				free(pFloatBuff);
				pFloatBuff = NULL;
			}
			PostQuitMessage(0);
			break;
		case WM_ERASEBKGND:{
			hdc = BeginPaint(hWnd, &ps);
			LRESULT lr = doPaintBkgnd(ps);		
			EndPaint(hWnd, &ps);
			if(!lr)
				return DefWindowProc(hWnd, message, wParam, lParam);
			break;
						   }
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

// Mesage handler for about box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		case WM_INITDIALOG:
				return TRUE;

		case WM_COMMAND:
			if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) 
			{
				EndDialog(hDlg, LOWORD(wParam));
				return TRUE;
			}
			break;
	}
    return FALSE;
}

void doFileOpen(){

	if(bFileOpen)
		return;							//Bail out if file already open

	OPENFILENAME of;
	memset(&of, 0, sizeof(of));

	of.lStructSize = sizeof(of);
	of.hwndOwner = g_hWnd;
	char filter[] = "*.pgm";
	of.lpstrFilter = filter;
	char filebuff[200];
	strcpy(filebuff, "*.pgm");
	of.lpstrFile = filebuff;
	of.nMaxFile = 199;
	char initialdir[] = ".\\";			//Current dir
	of.lpstrInitialDir = initialdir;
	if(!GetOpenFileName(&of))			//If they didn't choose a file
		return;
	
	strncpy(filename, filebuff, 254);
	if(pgmOriginal.readFile(filename) < 0){			//Error opening file
		::MessageBox(g_hWnd, "Error opening file.\nThe file could not be opened.", "Error", MB_ICONERROR | MB_OK);
		return;
	}//if

	SetCursor(LoadCursor(NULL, IDC_WAIT));

	SetWindowText(g_hWnd, filename);

	checkMenu(1);

	HMENU hM, hFile;
	hM = GetMenu(g_hWnd);
	hFile = GetSubMenu(hM, 0);
	EnableMenuItem(hFile, 0, MF_BYPOSITION | MF_GRAYED);		//Disable file->open
	EnableMenuItem(hFile, 1, MF_BYPOSITION | MF_ENABLED);		//Enable file->close
	bFileOpen = true;
	iViewState = VIEW_ORIGINAL;
	createBackground(pgmOriginal);
	SetWindowPos(g_hWnd, HWND_TOP, 0, 0, pgmOriginal.getWidth()+6, pgmOriginal.getHeight()+44, SWP_NOMOVE);
	//Now invalidate the background so it's redrawn
	RECT r;
	GetClientRect(g_hWnd, &r);
	InvalidateRect(g_hWnd, &r, TRUE);

	SetCursor(LoadCursor(NULL, IDC_ARROW));
}//doFileOpen

void doFileClose(){
	SetWindowPos(g_hWnd, HWND_TOP, 0, 0, 200, 200, SWP_NOMOVE);	//Change window size to default
	HMENU hM, hFile;
	hM = GetMenu(g_hWnd);
	hFile = GetSubMenu(hM, 0);
	EnableMenuItem(hFile, 0, MF_BYPOSITION | MF_ENABLED);		//Disable file->close
	EnableMenuItem(hFile, 1, MF_BYPOSITION | MF_GRAYED);		//Enable file->open
	bFileOpen = false;
	iViewState = VIEW_NOTHING;
	pgmOriginal.unloadFile();									//Free up memory
	pgmFourier.unloadFile();									//Free up memory
	pgmInvFt.unloadFile();

	if(hBitmap != NULL){
		DeleteObject(hBitmap);
		hBitmap = NULL;
	}
	if(hOffScreenDC != NULL){
		DeleteDC(hOffScreenDC);
		hOffScreenDC = NULL;
	}//if

	if(pFloatBuff != NULL){
		free(pFloatBuff);
		pFloatBuff = NULL;
	}//if

	SetWindowText(g_hWnd, "fourier");

	//Now invalidate the background so it's redrawn
	RECT r;
	GetClientRect(g_hWnd, &r);
	InvalidateRect(g_hWnd, &r, TRUE);

}//doFileClose

//This function will check one of the items in the view menu.
//If the param is 0, all will be unchecked
void checkMenu(int iItem){
	HMENU hM, hView;
		
	hM = GetMenu(g_hWnd);
	hView = GetSubMenu(hM, 1);		//Get the view popup

	switch(iItem){
	case 1:
		CheckMenuItem(hView, 0, MF_BYPOSITION | MF_CHECKED);
		CheckMenuItem(hView, 1, MF_BYPOSITION | MF_UNCHECKED);
		CheckMenuItem(hView, 2, MF_BYPOSITION | MF_UNCHECKED);
		break;
	case 2:
		CheckMenuItem(hView, 1, MF_BYPOSITION | MF_CHECKED);
		CheckMenuItem(hView, 2, MF_BYPOSITION | MF_UNCHECKED);
		CheckMenuItem(hView, 0, MF_BYPOSITION | MF_UNCHECKED);
		break;
	case 3:
		CheckMenuItem(hView, 2, MF_BYPOSITION | MF_CHECKED);
		CheckMenuItem(hView, 1, MF_BYPOSITION | MF_UNCHECKED);
		CheckMenuItem(hView, 0, MF_BYPOSITION | MF_UNCHECKED);
		break;
	default:
		CheckMenuItem(hView, 0, MF_BYPOSITION | MF_UNCHECKED);
		CheckMenuItem(hView, 1, MF_BYPOSITION | MF_UNCHECKED);
		CheckMenuItem(hView, 2, MF_BYPOSITION | MF_UNCHECKED);
		break;
	}//switch
	
}//checkMenu

//Handler for paint messages
LRESULT doPaintBkgnd(PAINTSTRUCT &ps){
	if(bFileOpen){					//If we have a file open and need to draw
		if(hOffScreenDC != NULL){	//If we have a background to draw
			int iWidth, iHeight;

			iWidth = ps.rcPaint.right - ps.rcPaint.left;
			iHeight = ps.rcPaint.bottom - ps.rcPaint.top;
			BitBlt(ps.hdc, ps.rcPaint.left, ps.rcPaint.top, iWidth, iHeight, 
					hOffScreenDC, ps.rcPaint.left, ps.rcPaint.top, SRCCOPY);
			
				
			return 1;
		}//if
	}//if
	return 0;
}//doPaint

//This will set up the background bitmap and off screen DC for blting to the window
void createBackground(Cpgmfile &pgmfile){

	if(hBitmap != NULL)
		::DeleteObject(hBitmap);
	if(hOffScreenDC != NULL)
		::DeleteDC(hOffScreenDC);

	int iWidth = pgmfile.getWidth();
	int iHeight = pgmfile.getHeight();

	HDC hCurrDC = GetDC(g_hWnd);
	hOffScreenDC = CreateCompatibleDC(hCurrDC);
	hBitmap = CreateCompatibleBitmap(hCurrDC, iWidth, iHeight);

	SelectObject(hOffScreenDC, hBitmap);			//Select the bitmap for drawing

	unsigned char *pBuff = (unsigned char *)malloc(sizeof(unsigned char)*iWidth*iHeight);
	pgmfile.getData(pBuff, iWidth*iHeight);

	for(int i=0;i<iHeight;i++){
		for(int j=0;j<iWidth;j++){
			int iVal = pBuff[iWidth*i + j];
			SetPixel(hOffScreenDC, j, i, RGB(iVal, iVal, iVal));
		}//for
	}//for

	free(pBuff);
	ReleaseDC(g_hWnd, hCurrDC);
}//createBackground

//This is the handler for when the user chooses view->fourier from the menu.
//It is responsible for doing all the FFT calls etc. etc.
void doViewFT(){
	
	if(!bFileOpen)							//Bail out if we don't have a file open
		return;

	if(iViewState == VIEW_FOURIER){			//Already viewing fourier
		return;
	}//if
	else
		iViewState = VIEW_FOURIER;

	pgmInvFt.unloadFile();

	SetCursor(LoadCursor(NULL, IDC_WAIT));

	int i, j;
	int iWidth = pgmOriginal.getWidth();
	int iHeight = pgmOriginal.getHeight();
	int iP2Width, iP2Height;

	iP2Width = iWidth;
	while(!isPow2(iP2Width))
		iP2Width++;

	iP2Height = iHeight;
	while(!isPow2(iP2Height))
		iP2Height++;

	unsigned char *pCharBuff = (unsigned char*)malloc(sizeof(unsigned char)*iP2Width*iP2Height);
	pgmOriginal.getData(pCharBuff, iWidth*iHeight);			//Get file data

	float *pRow, *pCol;
	pRow = (float*)malloc(sizeof(float)*iP2Width*2);
	if(pFloatBuff != NULL)
		free(pFloatBuff);

	pFloatBuff = (float*)malloc(sizeof(float)*iP2Width*iP2Height*2);

	for(i=0;i<iHeight;i++){					//Loop for all rows
		
		memset(pRow, 0, sizeof(float)*iP2Width*2);

		for(j=0;j<iWidth;j++){				//Copy row into complex float array
			pRow[j*2] = (float)pow(-1, j+i)*pCharBuff[iWidth*i + j];
			pRow[j*2+1] = 0;				//No imaginary part
		}//for
		
		four1(pRow-1, iP2Width, 1);			//Do 1-D FFT on this row...

		for(j=0;j<iP2Width;j++){				//Copy row into float array
			int iS = 2*j;
			int iD = 2*(i*iP2Width + j);
			pFloatBuff[iD]   = pRow[iS] * iWidth;
			pFloatBuff[iD + 1] = pRow[iS + 1] * iWidth;
		}//for

	}//for

	free(pRow);
	pCol = (float*)malloc(sizeof(float)*iP2Height*2);

	float maxval = 0;
	for(i=0;i<iP2Width;i++){					//Loop for all columns
		
		memset(pCol, 0, sizeof(float)*iP2Height*2);

		for(j=0;j<iHeight;j++){				//Copy column into complex float array
			int iS = 2*iP2Width*j + 2*i;
			int iD = j*2;
			pCol[iD]   = pFloatBuff[iS];
			pCol[iD + 1] = pFloatBuff[iS + 1];
		}//for

		four1(pCol-1, iP2Height, 1);		//Do 1-D FFT on this column

		for(j=0;j<iP2Height;j++){				//Copy into final float array

			int iS = 2*j;
			int iD = 2*j*iP2Width + 2*i;
			pFloatBuff[iD]   = pCol[iS];
			pFloatBuff[iD+1] = pCol[iS + 1];
		}//for

	}//for

	free(pCol);
	free(pCharBuff);

	::DeleteObject(hBitmap);
	hBitmap = ::CreateCompatibleBitmap(hOffScreenDC, iP2Width, iP2Height);
	::SelectObject(hOffScreenDC, hBitmap);

	unsigned char *pNewFile = (unsigned char *)malloc(sizeof(unsigned char)*iP2Width*iP2Height);

	// Now to do some magnitude computing using log10 function...
	for(i=0;i<iP2Height;i++){					//Loop for each row
		for(j=0;j<iP2Width;j++){
			float re, im, mag;
			unsigned char val;
			int fac = (int)pow(-1, i+j);

			re = pFloatBuff[i*iP2Width*2 + 2*j];			//Get real part
			im = pFloatBuff[i*iP2Width*2 + 2*j + 1];		//Get imaginary part
			mag = (float)sqrt(re*re + im*im);
			
			val = (unsigned char)45*log10(1 + mag);				//Log scaling function
			//Note: I have no damn idea why I have to invert this....but it works this way
			val = 255 - val;								
			if(val > 255)
				val = 255;
			if((i==iP2Height/2) || (j==iP2Width/2))
				SetPixel(hOffScreenDC, j, i, RGB(255, 0, 0));		//Pixel is axis color
			else
				SetPixel(hOffScreenDC, j, i, RGB(val, val, val));

			pNewFile[ i*iP2Width + j] = val;

		}//for	

	}//for

	pgmFourier.setData(pNewFile, iP2Width, iP2Height);		//Store this as a pgm file in memory

	free(pNewFile);

	SetWindowPos(g_hWnd, HWND_TOP, 0, 0, iP2Width+6, iP2Height+44, SWP_NOMOVE | SWP_NOOWNERZORDER);

	RECT r;
	GetClientRect(g_hWnd, &r);
	InvalidateRect(g_hWnd, &r, TRUE);
	SetCursor(LoadCursor(NULL, IDC_ARROW));
	checkMenu(2);
	iClickCt = 0;
	memset(&clicks, 0, sizeof(POINT)*3);

}//doViewFT

//Helper function to determine if a number is a power of 2
bool isPow2(unsigned int iVal){

	unsigned int mask = 0x00000001;
	for(int i=0;i<32;i++){
		if( (mask | iVal) == mask)
			return true;
		else
			mask = mask << 1;
	}//for

	return false;
}//isPow2

void doViewOriginal(){

	if(!bFileOpen)							//Bail out if we don't have a file open
		return;

	if(iViewState == VIEW_ORIGINAL){			//Already viewing original
		return;
	}//if
	else
		iViewState = VIEW_ORIGINAL;

	SetCursor(LoadCursor(NULL, IDC_WAIT));

	createBackground(pgmOriginal);
	pgmFourier.unloadFile();					//Free up memory
	pgmInvFt.unloadFile();						//Free up memory
	if(pFloatBuff != NULL){
		free(pFloatBuff);
		pFloatBuff = NULL;
	}

	checkMenu(1);

	SetWindowPos(g_hWnd, HWND_TOP, 0, 0, pgmOriginal.getWidth()+6, 
		pgmOriginal.getHeight()+44, SWP_NOMOVE | SWP_NOOWNERZORDER);

	RECT r;
	GetClientRect(g_hWnd, &r);
	InvalidateRect(g_hWnd, &r, TRUE);

	SetCursor(LoadCursor(NULL, IDC_ARROW));
}//doViewOriginal

void doMouseClick(int x, int y){
 
	if(iViewState == VIEW_FOURIER){
		if(iClickCt >= CLICKS_WANTED)			//We've already got the clicks we need
			return;

		COLORREF cr = RGB(255, 255, 0);
		cr = RGB(0, 0, 255);
		SetPixel(hOffScreenDC, x, y-1, cr);
		SetPixel(hOffScreenDC, x, y+1, cr);
		SetPixel(hOffScreenDC, x-1, y, cr);
		SetPixel(hOffScreenDC, x+1, y, cr);

		int iWidth = pgmFourier.getWidth();
		int iHeight = pgmFourier.getHeight();

		//Now draw the same point at 180 degree rotation
		SetPixel(hOffScreenDC, iWidth-x, iHeight-y-1, cr);
		SetPixel(hOffScreenDC, iWidth-x, iHeight-y+1, cr);
		SetPixel(hOffScreenDC, iWidth-x+1, iHeight-y, cr);
		SetPixel(hOffScreenDC, iWidth-x-1, iHeight-y, cr);

		clicks[iClickCt].x = x;
		clicks[iClickCt].y = y;								//Store the clicked pt
		clicks[iClickCt+CLICKS_WANTED].x = iWidth-x;		//Stored the 180 mirror
		clicks[iClickCt+CLICKS_WANTED].y = iHeight-y;
		
		iClickCt++;

		RECT r;
		GetClientRect(g_hWnd, &r);
		InvalidateRect(g_hWnd, &r, TRUE);
	}//if
}//doMouseClick

void doViewOverlay(){
	if(iViewState == VIEW_OVERLAY)
		return;							//Already showing overlay

	if(iViewState != VIEW_FOURIER){
		::MessageBox(g_hWnd, "Choose this function when viewing the\nFourier transform of the image...", "Error", MB_ICONINFORMATION | MB_OK);
		return;
	}

	if(iClickCt != CLICKS_WANTED){
		::MessageBox(g_hWnd, "You must first click the two points of interest.", "Error", MB_ICONINFORMATION | MB_OK);
		return;							//Need all 3 clicks
	}

	iClickCt = 0;

	SetCursor(LoadCursor(NULL, IDC_WAIT));

	int iWidth = pgmFourier.getWidth();
	int iHeight = pgmFourier.getHeight();
	int i, j;

	float *pRow = (float*)malloc(sizeof(float)*iWidth*2);
	float *pNewBuff = (float*)malloc(sizeof(float)*iWidth*iHeight*2);

	memset(pNewBuff, 0, sizeof(float)*iWidth*iHeight*2);

	//Lets go through and set up the points we wanted to be selected...
	for(i=0;i<CLICKS_WANTED*2;i++){
		int iS = 2*iWidth*clicks[i].y + clicks[i].x;

		pNewBuff[iS] = pFloatBuff[iS];								//Center point (re)
		pNewBuff[iS+1] = pFloatBuff[iS+1];							//Center point (im)

	}//for

	for(i=0;i<iHeight;i++){							//Loop through all rows

		memset(pRow, 0, sizeof(float)*iWidth*2);

		for(j=0;j<iWidth;j++){			//Copy into row array...
			int iS = 2*i*iWidth + 2*j;
			int iD = 2*j;
			pRow[iD] = pNewBuff[iS];
			pRow[iD+1] = pNewBuff[iS+1];
		}//for

		four1(pRow-1, iWidth, -1);		//Do inverse FFT
		
		for(j=0;j<iWidth;j++){			//Copy row into new buffer array
			int iS = 2*j;
			int iD = 2*i*iWidth + 2*j;

			pNewBuff[iD] = pRow[iS];				//Copy real
			pNewBuff[iD+1] = pRow[iS+1];			//Copy imaginary
		}//for
//		}//if
	}//for

	free(pRow);							//Free up mem for the row 
	float *pCol = (float*)malloc(sizeof(float)*iHeight*2);
	for(i=0;i<iWidth;i++){				//Loop through all cols
		memset(pCol, 0, sizeof(float)*iHeight*2);			//Clear out col

		for(j=0;j<iHeight;j++){							//Copy into column array
			int iS = 2*iWidth*j + 2*i;
			int iD = 2*j;
			pCol[iD] = pNewBuff[iS];					//Copy real
			pCol[iD+1] = pNewBuff[iS+1];				//Copy imaginary
		}//for

		four1(pCol-1, iHeight, -1);					//Do inverse FFT

		for(j=0;j<iHeight;j++){						//Copy back into frame buffer
			int iS = 2*j;
			int iD = 2*iWidth*j + 2*i;
			pNewBuff[iD] = pCol[iS] / iHeight;
			pNewBuff[iD+1] = pCol[iS+1] / iHeight;
		}//for

	}//for	

	free(pCol);

	createBackground(pgmOriginal);

	int ih = pgmOriginal.getHeight();
	int iw = pgmOriginal.getWidth();

	unsigned char *pN = (unsigned char *)malloc(sizeof(unsigned char)*ih*iw);

	for(i=0;i<ih;i++){						//Get magnitudes now
		for(j=0;j<iw;j++){
			float re, im;
			re = pNewBuff[2*iWidth*i + 2*j];
			im = pNewBuff[2*iWidth*i + 2*j + 1];

			float mag = sqrt(re*re + im*im);
			unsigned char val = (unsigned char)(mag/100);

//			mag = 45*log10(1 + mag);						//Log scaling function
//			unsigned char val = (unsigned char)mag;				
			val = 255-val;
			if(val < 0)
				val = 0;
			if(val > 255)
				val = 255;
			
			pN[iw*i+j] = val;
				
//			if(val > 150)
				SetPixel(hOffScreenDC, j, i, RGB(val, val, val));//0, 0));

		}//for
	}//for

	free(pNewBuff);

	pgmInvFt.setData(pN, iw, ih);
	free(pN);

	pgmFourier.unloadFile();

	SetWindowPos(g_hWnd, HWND_TOP, 0, 0, iw+6, ih+44, SWP_NOMOVE | SWP_NOOWNERZORDER);

	RECT r;
	GetClientRect(g_hWnd, &r);
	InvalidateRect(g_hWnd, &r, TRUE);
	SetCursor(LoadCursor(NULL, IDC_ARROW));
	checkMenu(3);

	iViewState = VIEW_OVERLAY;

}//doViewOverlay

//Determines if any of the clicked points are in this row.  If so, it will
//return an offset into the clicks array
int ptInRow(int iRow){
	for(int i=0;i<CLICKS_WANTED*2;i++){
		if(iRow == clicks[i].y)
			return i;
	}//for
	return -1;
}//ptInRow

void doSaveAs(){
	if(!bFileOpen)
		return;				//No file open

	OPENFILENAME of;
	memset(&of, 0, sizeof(of));

	of.lStructSize = sizeof(of);
	of.hwndOwner = g_hWnd;
	char filter[] = "*.pgm";
	char defext[] = "pgm";
	of.lpstrFilter = filter;
	of.lpstrDefExt = defext;
	char filebuff[200];
	strcpy(filebuff, "*.pgm");
	of.lpstrFile = filebuff;
	of.nMaxFile = 199;
	char initialdir[] = ".\\";			//Current dir
	of.lpstrInitialDir = initialdir;
	of.Flags = OFN_OVERWRITEPROMPT;
	if(!GetSaveFileName(&of))			//If they didn't choose a file
		return;
	
	char fname[300];
	memset(fname, 0, 255);
	strncpy(fname, filebuff, 254);

	if(stricmp(&fname[strlen(fname)-4], ".pgm") != 0)
		strcat(fname, ".pgm");

	Cpgmfile *pF;
	if(iViewState == VIEW_ORIGINAL)
		pF = &pgmOriginal;
	else if(iViewState == VIEW_FOURIER)
		pF = &pgmFourier;
	else if(iViewState == VIEW_OVERLAY)
		pF = &pgmInvFt;
	else{
		::MessageBox(g_hWnd, "Error saving file.\nUnknown program state!?!?\n\nSorry...", "Error", MB_ICONERROR | MB_OK);
	}

	if(pF->writeFile(fname) < 0){
		::MessageBox(g_hWnd, "Error saving file.\n\nSorry...", "Error", MB_ICONERROR | MB_OK);
	}
	else
		::MessageBox(g_hWnd, "File saved successfully", "Success", MB_ICONINFORMATION | MB_OK);

}//doSaveAs

void doMouseMove(int x, int y){
	if(iViewState != VIEW_FOURIER)
		return;

	int iWidth = pgmFourier.getWidth();
	int iHeight = pgmFourier.getHeight();

	char buffer[100];
	sprintf(buffer, "   x = %d, y = %d", x, y);
	RECT r;
	r.right = iWidth-5;
	r.left = iWidth - 150;
	r.bottom = iHeight;
	r.top = iHeight - 100;

	DrawText(hOffScreenDC, buffer, strlen(buffer), &r, DT_RIGHT | DT_BOTTOM | DT_SINGLELINE);
	::InvalidateRect(g_hWnd, &r, TRUE);

	
}//doMouseMove