Introduction
This simple app and article demonstrate Windows Message Handling and painting on a Win32 device context. A device context is a structure that defines a set of graphic objects and their associated attributes, as well as the graphic modes that affect output.
The graphics objects include a pen for line drawing, a brush for painting and filling, a bitmap for copying or scrolling parts of the screen, a palette for defining the set of available colors, a region for clipping and other operations.
We will start off by declaring our variables.
#define pi 3.1415926
#define WIDTH 400
#define HEIGHT 400
#define WEXT 380
#define HEXT 210
#define MAGNITUDE_CUTOFF 100
#define NUMCOLOURS 256
#define ID_TIMER 1
#define DIVFACTOR 1
#define XMin -1.4
#define XMax 1.4
#define YMin -1.4
#define YMax 1.4
#define Iter 32
#define Scal 32767
#define ResX 150
#define ResY 150
bool xorDone=false;
char title[]="MandelZoom / Julia Walkabout by Topcoder",buffer[80],String[40];
int max_iterations,mousex,mousey,xorEx=0,xorEy=0,MouseX,MouseY;
double xmin = -2.10, xmax = 0.75, ymin = -1.5 , ymax = 1.5;
double width_fact, height_fact;
double midx,midy,dx,dy;
double dxx,dyy,px,py,zsx,zsy,zex,zey;
int xp,yp,random=0;
float jdx=0,jdy=0,r=0,theta=0,jx=0,jy=0,tempx,tempy;
rektangel rekt;
POINT start,end;
HDC hdc;
HWND Button[2],Edit,StatusBar;
HDC hdcMem;
HBITMAP hbmMem;
HANDLE hOld;
RECT rect;
HBRUSH hbrush = CreateSolidBrush(RGB(0,0,128));
Then we register our window by using RegisterClassEx()
:
int WINAPI WinMain (HINSTANCE hinst, HINSTANCE hprevinst, LPSTR lpCmdLine,int cmdShow)
{
if (hprevinst == NULL)
{
WNDCLASSEX wclass;
wclass.cbSize = sizeof(WNDCLASSEX);
wclass.hInstance = hinst;
wclass.lpfnWndProc = (WNDPROC)WndProc;
wclass.style = CS_HREDRAW | CS_VREDRAW;
wclass.hIcon = LoadIcon(hinst, IDC_ARROW);
wclass.hIconSm = NULL;
wclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wclass.lpszMenuName = NULL;
wclass.cbClsExtra = 0;
wclass.cbWndExtra = 0;
wclass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wclass.lpszClassName = "BASIC2";
if(!RegisterClassEx(&wclass)) return 0;
Then we create the actual window and show it using CreateWindowEx()
and ShowWindow()
.
HWND hwnd = CreateWindowEx(
WS_EX_WINDOWEDGE, "BASIC2", title, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WIDTH+WEXT, HEIGHT+32, NULL, NULL, hinst, NULL);
ShowWindow(hwnd, cmdShow);
UpdateWindow(hwnd);
Now, we start the Win32 message pump with the code below. A Windows application processes messages from the operating system, and most Win32 applications have to be able to translate and dispatch windows messages.
Now we can send and receive windows messages using TranslateMessage()
and DispatchMessage()
.
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage(&msg); }
return msg.wParam;
}
The WM_CREATE Message
The WM_CREATE message
is called upon when the application is being created and instantized. In this code section, controls such as buttons, listboxes, comboboxes are created using the CreateWindow()
function.
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch(msg)
{
case WM_CREATE:
Button[0] = CreateWindow (
"button",
"reset",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
WIDTH+5, HEIGHT-48, WIDTH/2, 30, hwnd,
(HMENU) 5 ,
((LPCREATESTRUCT) lparam)->hInstance,
NULL);
Button[1] = CreateWindow (
"button",
"Exit",
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
WIDTH+(WIDTH/2)+5, HEIGHT-48, (WIDTH/2)-48, 30, hwnd,
(HMENU) 6,
((LPCREATESTRUCT) lparam)->hInstance,
NULL);
SetTimer(hwnd,ID_TIMER,1,NULL);
break;
The WM_PAINT Message
The WM_PAINT
message is called upon if an area on the application needs to be updated.
We place our paint and update functions in this code section.
case WM_PAINT:
Paint(hwnd);
initWalk();
putCursor();
walkabout();
break;
The WM_LBUTTONDOWN Message
The WM_LBUTTONDOWN
message is called upon when the left button is pressed down. When the user presses down the left mouse button, our application stores the mouse's current x and y coordinates in the variables start.x
and start.y
. As the user drags the mouse, a rectangle is drawn, it defines the size of the region of the mandelbrot on which to zoom in on.
case WM_LBUTTONDOWN:
{
if (mouse_x>=0 && mouse_x<=WIDTH && mouse_y>=0 && mouse_y<=HEIGHT)
{
rekt.visa = true;
rekt.sx = (float)mouse_x;
rekt.sy = (float)mouse_y;
rekt.ex = (float)mouse_x;
rekt.ey = (float)mouse_y;
xorEx=(int)rekt.sx+100;
xorEy=(int)rekt.sy+100;
end.x=0;
end.y=0;
start.x=mouse_x;
start.y=mouse_y;
MoveToEx (hdc, start.x, start.y, NULL) ;
zsx=(xmin+(xmax-xmin)*((mousex)-px)/(WIDTH-1));
zsy=(ymin+(ymax-ymin)*((mousey)-py)/(HEIGHT-1));
zex=(xmin+(xmax-xmin)*((mousex)-px)/(WIDTH-1));
zey=(ymin+(ymax-ymin)*((mousey)-py)/(HEIGHT-1));
tempx=xmin;
tempy=ymin;
xmin=zsx;
ymin=zsy;
textout();
}
} break;
The WM_MOUSEMOVE Message
The WM_MOUSEMOVE
message is called upon when the user drags the mouse. As the mouse is being dragged, a rectangular box is drawn to show where the next zoom region will be.
case WM_MOUSEMOVE:
{
if (mouse_x>=0 && mouse_x<=WIDTH && mouse_y>=0 && mouse_y<=HEIGHT)
{
if (mouse_x>=rekt.sx && mouse_y >=rekt.sy )
{
rekt.ex = (float)mouse_x;
rekt.ey = (float)mouse_y;
dx=zex-zsx;
dy=zey-zsy;
}
if (rekt.visa )
{
BitBlt(hdc, 0, 0, WIDTH, HEIGHT, hdcMem, 0, 0, SRCCOPY);
SetROP2(hdc,R2_NOTXORPEN);
int DrawMode = GetROP2(hdc);
MoveToEx (hdc, (int)rekt.sx, (int)rekt.sy, NULL) ;
LineTo (hdc, (int)rekt.ex ,(int)rekt.sy) ;
LineTo (hdc, (int)rekt.ex ,(int)rekt.ey) ;
LineTo (hdc, (int)rekt.sx ,(int)rekt.ey) ;
LineTo (hdc, (int)rekt.sx ,(int)rekt.sy) ;
SetROP2(hdc,R2_COPYPEN);
}
mousex=mouse_x;
mousey=mouse_y;
textout();
}
} break;
The WM_LBUTTONUP Message
The WM_LBUTTONUP
message is called upon when the left button is released. Once the user releases the left mouse button, our application stores the mouse's current x and y coordinates in the variables end.x
and end.y
. Our application now calculates the new interpolants on which to calculate the mandelbrot from.
case WM_LBUTTONUP:
{
if (mouse_x>=0 && mouse_x<=WIDTH && mouse_y>=0 && mouse_y<=HEIGHT)
{
rekt.visa = false;
end.x=mouse_x;
end.y=mouse_y;
rekt.ex =(float)mousex;
rekt.ey =(float)mousey;
if (end.x >start.x+5 && end.y >start.y +5)
{
initWalk();
MouseX=WIDTH/2;
MouseY=HEIGHT/2;
SelectObject(hdcMem, hOld);
DeleteObject(hbmMem);
DeleteDC(hdcMem);
dx=zex-zsx;
dy=zey-zsy;
if (mouse_x>=rekt.sx && mouse_y >=rekt.sy )
{
zex=(xmin+(xmax-xmin)*((mousex)-px)/(WIDTH-1));
zey=(ymin+(ymax-ymin)*((mousey)-py)/(HEIGHT-1));
xmax=zex;
ymax=zey;
dx=zex-zsx;
dy=zey-zsy;
textout();
fract();
putCursor();
walkabout();
}
}
else
{
BitBlt(hdc, 0, 0, WIDTH,
HEIGHT, hdcMem, 0, 0, SRCCOPY);
xmin=tempx;
ymin=tempy;
}
}
} break;
The WM_KEYDOWN Message
The WM_KEYDOWN
message is called upon when a key is pressed down.
case WM_KEYDOWN:
{
int virtual_code = (int) wparam;
int key_bits = (int) lparam;
switch (virtual_code)
{
case 27: {
PostQuitMessage(0); } break;
case 13: {
resetFrac();
fract();
} break;
case 32:{
}break;
case VK_RIGHT: { } break;
case VK_LEFT: { } break;
default: break;
}
} break;
The WM_QUIT and WM_DESTROY Message
The WM_QUIT
and WM_DESTROY
message is called upon when the application is signalling it wants to quit.
WM_QUIT:
case WM_DESTROY:
KillTimer(hwnd,ID_TIMER);
PostQuitMessage(0);
break;
return 0;
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
ReleaseDC(hwnd, hdc);
return 0;
}
And that is all that is required to create a Win32 application that draws and zooms a mandelbrot on a Win32 device context.
Thanks for reading.
History
- 2010-05-08 Updates made
- 2002-06-22 Code complete