// simple paint
// simplepaint_2.src
// modified by John Siino to include SaveBitmap method
// 12/20/07

// #include "SaveBitmap.inc"

#ifdef DEBUG
declare import, OutputDebugStringA(string *str);
#endif


DECLARE IMPORT, GetSystemMetrics(int nIndex), int;

declare import,	GlobalAlloc(flags as unsigned int, size as int), pointer;
              // flags = 0x00 Fixed Memory, = 0x40 initializes memory to zero
              // size: (amount of storage required) times (double = 8, float = int = 4)
              // returns pointer/handle to memory location

declare import,	GlobalReAlloc(handle as unsigned int, newsize as int, flags as unsigned int),pointer;
              // flags = 0x40 initializes memory to zero
              // newsize: (amount of storage required) times (double = 8, float = int = 4)
              // returns pointer/handle to memory location

declare import,	GlobalFree(handle as unsigned int), int;
              // returns null if successful
              // pointer/handle to memory location

declare import,	RtlMoveMemory(dest as pointer, source as pointer, size as int);
              // dest = pointer to the starting address of the move destination
              // source = pointer to the starting address of the block of memory to be moved
              // size: (amount of storage required) times (double = 8, float, int = 4)
              // returns pointer/handle to memory location


// SaveBitmap stuff
struct BITMAP
	{
		int  bmType;
		int  bmWidth;
		int  bmHeight;
		int  bmWidthCHARs;
		WORD bmPlanes;
		WORD bmBitsPixel;
		int  bmBits;
	}

struct BITMAPINFOHEADER
	{
		int  biSize;
		int  biWidth;
		int  biHeight;
		WORD biPlanes;
		WORD biBitCount;
		int  biCompression;
		int  biSizeImage;
		int  biXPelsPerMeter;
		int  biYPelsPerMeter;
		int  biClrUsed;
		int  biClrImportant;
	}

struct BITMAPFILEHEADER, 1
	{
		WORD bfType;
		int  bfSize;
		WORD bfReserved1;
		WORD bfReserved2;
		int  bfOffBits;
	}

CONST BI_RGB = 0;
CONST BI_RLE8 = 1;
CONST BI_RLE4 = 2;
CONST BI_bitfields = 3;

// SaveBitmap stuff
// API functions for dealing with the bitmaps
	DECLARE import,  BitBlt(unsigned int HDCDest, int destx, int desty, int destw, int desth, 
												int HDCSrc, int srcx, int srcy, int rop), int;

//	The CreateCompatibleDC function creates a memory device context (DC) compatible with the specified device.
//	
	DECLARE import,  CreateCompatibleBitmap(unsigned int HDC, int width, int height), int;

	DECLARE import,  CreateCompatibleDC( unsigned int HDC ), int;
	DECLARE import,  SelectObject(unsigned int HDC, int Handle), int;
	DECLARE import,  DeleteDC( unsigned int HDC ), int;
	DECLARE import,  DeleteObject( unsigned int handle ), int;
	DECLARE import,  GetObjectA(unsigned int handle, int size, pointer mem), int;

	DECLARE import,  GetDIBits(unsigned int HDC, unsigned int hbitmap, int start, int lines, 
													pointer bits, pointer info, int usage), int;

	DECLARE import, RtlZeroMemory(pointer pvoid, int length), int;


class DrawWnd : CWindow
{
	declare OnCreate(), int;
	declare OnClose(), int;

	declare OnMouseMove(int x, int y, int flags), int;
	declare OnLButtonDown(int x, int y, int flags), int;
	declare OnLButtonUp(int x, int y, int flags), int;
	declare OnRButtonUp(int x, int y, int flags), int;

	declare OnMenuPick(int nID), int;
	declare OnMenuInit(unsigned int hMenu), int;

	declare OnTimer(int nIDEvent), int;

	// SaveBitmap stuff
	DECLARE SaveBitmap(string filename, int left, int top, int width, int height);
	DECLARE CreateInfoStructure(int hbitmap, BITMAPINFOHEADER info), int;
	//end of SaveBitmap stuff

	int drawcolor, l, t, w, h, tempx, tempy, linesize, mode;
}

int run;

global sub main()
{
run = 1;

DrawWnd win;

CMenu m;

win.Create(0,0,640,480,AWS_CAPTION|AWS_SYSMENU|AWS_BORDER|AWS_SIZE|AWS_MINIMIZEBOX|
					AWS_MAXIMIZEBOX|AWS_MAXIMIZE|AWS_AUTODRAW,0,"Simple Paint 2",0);
m.BeginMenu();
	m.MenuTitle( "&File");
	m.MenuItem( "Print",0,101);
	m.MenuItem( "&Save",0,102);
	m.MenuItem( "&Load",0,103);
	m.MenuItem( "Quit",0,100);
	m.MenuTitle( "Options");
	m.MenuItem( "Color",0,99);
	m.MenuItem( "Clear",0,1);
	m.BeginPopup( "Line Size");
		m.MenuItem( "1",AMF_CHECKED,2);
		m.MenuItem( "2",0,3);
		m.MenuItem( "3",0,4);
		m.MenuItem( "4",0,5);
	m.EndPopup();
	m.MenuTitle( "Tools");
	m.MenuItem( "Line",AMF_CHECKED,6);
	m.MenuItem( "Rectangle",0,7);
	m.MenuItem( "Circle",0,8);
	m.MenuItem( "Spray",0,9);
	m.MenuItem( "FILL",0,10);
m.EndMenu();

win.SetMenu(m.Detach());

do {wait(); }until run = 0;

return 0;
}

//our windows message handlers
DrawWnd::OnCreate(), int
{
	drawcolor = 0;	linesize = 1;
	FrontPen(drawcolor);
	mode = 0;
return true;
}

DrawWnd::OnClose(), int
{
run = 0;
return true;
}

DrawWnd::OnMouseMove(int x, int y, int flags), int
{
	tempx = x;	tempy = y;

	if(flags = 1)
	{
		select mode
		{
			case 1:
				SetRasterOp(RMXORPEN);
				DrawRect(l, t, w, h, RGB(255,255,255));
				SetRasterOp(RMCOPYPEN);

			case 2:
				SetRasterOp(RMXORPEN);
				Ellipse(l, t, w, h, RGB(255,255,255));
				SetRasterOp(RMCOPYPEN);
		}

		w = x - l;	h = y - t;

#ifdef DEBUG
OutputDebugStringA("DrawWnd::OnMouseMove() Line 200");
OutputDebugStringA(" w, h = " + NumToStr(w) + ", " + NumToStr(h) );

#endif

		SELECT mode
		{
			case 0:
				LineTo(x, y);
			case 1:
				SetRasterOp(RMXORPEN);
				DrawRect(l, t, w, h, RGB(255,255,255));
				SetRasterOp(RMCOPYPEN);
	
			case 2:
				SetRasterOp(RMXORPEN);
				Ellipse(l, t, w, h, RGB(255,255,255));
				SetRasterOp(RMCOPYPEN);
		}
	}
return true;
}

DrawWnd::OnLButtonDown(int x, int y, int flags), int
{
	l = x;	t = y;	w = 0;	h = 0;

#ifdef DEBUG
OutputDebugStringA("DrawWnd::OnLButtonDown() Line 228");
OutputDebugStringA(" w, h = " + NumToStr(w) + ", " + NumToStr(h) );

#endif

	Move(l, t);

	IF mode = 3
		StartTimer(10);
	IF mode = 4
		FloodFill(l, t, drawcolor);

return true;	
}

DrawWnd::OnLButtonUp(int x, int y, int flags), int
{
	Select mode
	{
		CASE 1:
		 DrawRect(l, t, x-l, y-t, drawcolor, drawcolor);

		CASE 2:
		 Ellipse(l, t, x-l, y-t, drawcolor, drawcolor);

		CASE 3:
		 StopTimer();
	}

return true;
}

DrawWnd::OnRButtonUp(int x, int y, int flags), int
{
	CMenu m;
	m.BeginContextMenu();
	m.MenuItem("Color",0,99);
	m.MenuItem( "Clear",0,1);
	m.BeginPopup( "Line Size");
		m.MenuItem( "1",(linesize = 1) * AMF_CHECKED,2);
		m.MenuItem( "2",(linesize = 2) * AMF_CHECKED,3);
		m.MenuItem( "3",(linesize = 3) * AMF_CHECKED,4);
		m.MenuItem( "4",(linesize = 4) * AMF_CHECKED,5);
	m.EndPopup();
	m.EndMenu();

	ShowContextMenu(m.Detach(), x, y);

return true;
}

DrawWnd::OnMenuPick(int nID), int
{
int w, h;
unsigned int hBitmap;

string filter, filename;

rect rcClient;

	//clear the window
	IF(nID = 1)
	{
		//GETSCREENSIZE w,h
		w = GetSystemMetrics(0);
		h = GetSystemMetrics(1);
		DrawRect(0, 0, w, h, RGB(255,255,255), RGB(255,255,255));
	}

	//set line size
	IF(nID > 1 & nID < 6)
	{
		SetLineStyle(LSSOLID, nID - 1);
		linesize = nID - 1;
	}

	//pick a tool
	IF(nID > 5 & nID < 11)
	{
		mode = nID - 6;
	}

	//'pick a color
	IF(nID = 99)
	{
		temp = drawcolor;
		drawcolor = ColorRequest(this,drawcolor);
		IF(drawcolor = -1)
			drawcolor = temp;
		FrontPen(drawcolor);
	}

	//quit
	IF nID = 100
	{
		run = 0;
	}

	//print
	IF nID = 101
	{
		PrintWindow();
	}

	//save the image
	IF nID = 102
	{
		filter = "Bitmap Files (*.bmp)|*.bmp||";
		filename = FileRequest("Save Bitmap", this, 0, filter, "bmp");

		if len(filename)
		{
			rcClient = GetClientRect();
			SaveBitmap(filename, rcClient.left, rcClient.top, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top);
		}
	}

	//load a btimap
	IF nID = 103
	{
		filter = "Bitmap Files (*.bmp)|*.bmp||";
		filename = FILEREQUEST("Load Bitmap", this, 1, filter, "bmp");
		if len(filename)
		{
			CBitmap bmp;
			if bmp.LoadFromFile(filename,0)
			{
				w = GetSystemMetrics(0);
				h = GetSystemMetrics(1);
				DrawRect( 0, 0, w, h, RGB(255,255,255), RGB(255,255,255) );
				size = bmp.GetSize();
				bmp.Render(this, 0, 0, size.x, size.y);
			}
			else
				MessageBox(this, "Unable to load image", "Error");
		}
	}
return true;
}


DrawWnd::OnMenuInit(unsigned int hMenu),int
{
	CMenu m;
	m.Attach(hMenu);
	m.CheckMenuItem(6,(mode = 0));
	m.CheckMenuItem(7,(mode = 1));
	m.CheckMenuItem(8,(mode = 2));
	m.CheckMenuItem(9,(mode = 3));
	m.CheckMenuItem(10,(mode = 4));
	m.CheckMenuItem(2,(linesize = 1));
	m.CheckMenuItem(3,(linesize = 2));
	m.CheckMenuItem(4,(linesize = 3));
	m.CheckMenuItem(5,(linesize = 4));
	m.Detach();
return false;
}


DrawWnd::OnTimer(int nIDEvent), int
{
float temp, temp2, out1, out2;
int x;

	for( x = 0; x < 10;x++)
	{
		temp = RND(360) * (3.1415/180.0);
		temp2 = RND(10);
		out1 = SIN(temp) * temp2;
		out2 = COS(temp) * temp2;
		SetPixel(tempx+out1,tempy+out2);
	}

return true;
}

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// methods for saving bitmap files
DrawWnd::SaveBitmap(string filename, int left, int top, int width, int height)
{
	int hdcWin;
	int hdcComp, hbitmap, hbitmapold, quadsize;

	BITMAPINFOHEADER info;

	BITMAPFILEHEADER fileheader;

	unsigned int *lpbits;			// memory
	unsigned int *lpbminfo;			// memory

	unsigned int fileb;				//bfile

	RtlZeroMemory( info, len( BITMAPINFOHEADER ));
	RtlZeroMemory( fileheader, len( BITMAPFILEHEADER ));

	hdcWin = GetHDC();  // virtual IntPtr GetHdc() Gets the handle to the device context associated with this Graphics

	hdcComp = CreateCompatibleDC(hdcWin);

	if (hdcComp = 0)
		{
			messagebox(this, "Couldn't create DC", "Error");
			ReleaseHDC(hdcWin);
			return;
		}
	hbitmap = CreateCompatibleBitmap(hdcwin, width, height);
	
	if (hbitmap = 0)
		{
			messagebox( this, "Couldn't create bitmap", "Error");
			ReleaseHDC( hdcWin );
			return;
		}

	hbitmapold = SelectObject(hdcComp, hbitmap);

	BitBlt(hdcComp, 0, 0, width, height, hdcWin, left, top, 0x00CC0020);
	SelectObject(hdcComp, hbitmapold);
	DeleteDC(hdcComp);

//	at this point hbitmap contains a valid bitmap handle.
//	fill in the BITMAPINFOHEADER and compute the color data
	quadsize = CreateInfoStructure( hbitmap, info );
//	allocate memory and get the 'bits' of the bitmap
	lpbits = GlobalAlloc(0x40, info.biSizeImage );       // allocmem( lpbits, 1, info.biSizeImage );
	lpbminfo = GlobalAlloc(0x40,  len(info) + quadsize ); // allocmem( lpbminfo, 1, len(info) + quadsize );

	RtlMoveMemory( lpbminfo, &info, len(info));  // writemem( lpbminfo, 1, info );
//	RtlMoveMemory(dest as int, source as int, size as int);

	GetDIBits( hdcWin, hbitmap, 0, info.biHeight, lpbits, lpbminfo, 0 );

//	open a binary file and write the header, color data and bitmap bits
	fileb = openfile(filename, MODE_CREATE | MODE_WRITE);
	if(fileb <> 0)
		{
			fileheader.bfType = 0x4d42;
			fileheader.bfSize = len(fileheader) + info.biSize + quadsize + info.biSizeImage;
			fileheader.bfOffBits = len(fileheader) + info.biSize + quadsize;
			write( fileb, fileheader, len(fileheader) );
			write( fileb, lpbminfo, len(info) + quadsize );
			write( fileb, lpbits, info.biSizeImage );
			closefile( fileb );
		}
	
	GlobalFree( lpbminfo );
	GlobalFree( lpbits );

	ReleaseHDC( hdcWin );
	DeleteObject( hbitmap );

return;
}


DrawWnd::CreateInfoStructure(int hbitmap, BITMAPINFOHEADER info),int
{
	bitmap bmp;
	
	word cClrBits;
	int quadsize = 0;

	GetObjectA(hbitmap, len(bmp), &bmp);

	cClrBits = bmp.bmPlanes * bmp.bmBitsPixel;

    if (cClrBits = 1)
		{
			cClrBits = 1;
		}
    else if (cClrBits <= 4)
			{
				cClrBits = 4;
			}
    	else if (cClrBits <= 8)
				{
					cClrBits = 8;
				}
    		else if (cClrBits <= 16)
					{
						cClrBits = 16;
					}
    			else if (cClrBits <= 24)
						{
							cClrBits = 24;
						}
    				else 
        				{
							cClrBits = 32;
						}
	
    info.biSize = len( info );
    info.biWidth = bmp.bmWidth;
    info.biHeight = bmp.bmHeight;
    info.biPlanes = bmp.bmPlanes;
    info.biBitCount = bmp.bmBitsPixel;

    if (cClrBits < 24) { info.biClrUsed = 2^cClrBits; }

	info.biCompression = 0;
	info.biSizeImage = (info.biWidth + 7.0) / 8.0 * (info.biHeight) * cClrBits;

	if(cClrBits < 24) { quadsize = 4 * (2^cClrBits); }

return quadsize;
}
