When working with graphics, it is important to know the exact size of the area in which you draw. In the last lesson, we built a window and set its size to 500 x 400. However, the area Direct3D will draw in is not 500 x 400 for that window.
In this lesson we are goint to discover the actual size of the drawing area and learn a function to more accurately set it.
When we called CreateWindowEx(), we used 500 and 400 to set the size of the window. However, this differs from the size of the client. The client area is the portion of the window that does not include its border.
Client Size and Window SizeAs you can see here, the window size extends from the edges of the border, while the client size extends the interior of the border. When rendering, we will only be drawing on the client area of the window. Therefore, it is important to know its exact size.
Why is this important exactly? When drawing using Direct3D, you are asked to specify the size of the image to be produced. If the client area of your window is a different size than this image, it gets stretched or shrunk to fit the client area.
Let's compare two screenshots of an example program in a later lesson. The image on the left was taken normally, while the one on the right was taken without AdjustWIndowRect().
Rendering With and Without AdjustWindowRect()As you can see, the screenshot on the right has some obvious distortions. These were created when the image was shrunk to fit the client area.
Rather than setting the window size and then determining the client size, it is ideal to determine the client size in advance, and then calculate the appropriate window size. To do this we will use the function AdjustWindowRect() before creating the window.
This is really a simple function. What it does is take the desired size and position of our client area, and calculate the necessary window position and size to create that client size.
Here is the prototype of the function:
BOOL AdjustWindowRect(LPRECT lpRect,
DWORD dwStyle,
BOOL bMenu);
The first parameter is a pointer to a RECT struct. The RECT that is pointed to contains the coordinates of the desired client area. When the function is called, the RECT is modified to instead contain the coordinates of the window area.
The second parameter is the window style. The function uses this information to determine the size of the window border.
The third parameter is a BOOL value, telling the function whether or not we are using menus. Menus are not technically part of the client area, so it must be taken into consideration.
What does this function look like in real code? Let's take a look. The following is a modification of our call to CreateWindowEx():
RECT wr = {0, 0, 500, 400}; // set the size, but not the position
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); // adjust the size
// create the window and use the result as the handle
hWnd = CreateWindowEx(NULL,
L"WindowClass1",
L"Our First Windowed Program",
WS_OVERLAPPEDWINDOW,
300, // x-position of the window
300, // y-position of the window
wr.right - wr.left, // width of the window
wr.bottom - wr.top, // height of the window
NULL,
NULL,
hInstance,
NULL);
There are a few new lines of code here, shown in bold. Let's take a look at each one and find out what they do exactly.
This is a simple statement. We create a rect (which I've named 'wr' for window rect), and initialize it with the size of the desired client area. We don't put position in the 'left' and 'top' values because for what we are doing we don't need them.
After the RECT is initialized, we call the AdjustWindowRect() function. We fill it with the address of the RECT, the window style, and FALSE to indicate there is no menu.
When AdjustWindowRect() is called, the window width will be the difference between the right and the left, and the height will be the difference between the bottom and the top.
Using these two expressions for the width and the height of the window will give us the correct size of the window.
Here is our new code, including the AdjustWindowRect() function. The parts that have changed are now in bold.
[
Main.cpp]
// include the basic windows header file
#include <windows.h>
#include <windowsx.h>
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// the handle for the window, filled by a function
HWND hWnd;
// this struct holds information for the window class
WNDCLASSEX wc;
// clear out the window class for use
ZeroMemory(&wc, sizeof(WNDCLASSEX));
// fill in the struct with the needed information
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = L"WindowClass1";
// register the window class
RegisterClassEx(&wc);
// calculate the size of the client area
RECT wr = {0, 0, 500, 400}; // set the size, but not the position
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); // adjust the size
// create the window and use the result as the handle
hWnd = CreateWindowEx(NULL,
L"WindowClass1", // name of the window class
L"Our First Windowed Program", // title of the window
WS_OVERLAPPEDWINDOW, // window style
300, // x-position of the window
300, // y-position of the window
wr.right - wr.left, // width of the window
wr.bottom - wr.top, // height of the window
NULL, // we have no parent window, NULL
NULL, // we aren't using menus, NULL
hInstance, // application handle
NULL); // used with multiple windows, NULL
// display the window on the screen
ShowWindow(hWnd, nCmdShow);
// enter the main loop:
// this struct holds Windows event messages
MSG msg;
// wait for the next message in the queue, store the result in 'msg'
while(GetMessage(&msg, NULL, 0, 0))
{
// translate keystroke messages into the right format
TranslateMessage(&msg);
// send the message to the WindowProc function
DispatchMessage(&msg);
}
// return this part of the WM_QUIT message to Windows
return msg.wParam;
}
// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// sort through and find what code to run for the message given
switch(message)
{
// this message is read when the window is closed
case WM_DESTROY:
{
// close the application entirely
PostQuitMessage(0);
return 0;
} break;
}
// Handle any messages the switch statement didn't
return DefWindowProc (hWnd, message, wParam, lParam);
}
When you run this program, you will find that there is actually no difference in the outcome. The truth is, you will not see the difference until you use AdjustWindowRect() in game programming.
While this was a very simple lesson, it is nevertheless an important thing to know when programming any kind of graphics software.
Alright, one more lesson to go and we're on to Direct3D!
Next Lesson: The Real-Time Message Loop
GO! GO! GO!
© 2006-2024 DirectXTutorial.com. All Rights Reserved.
Expand