// Done by TheTutor -- 3/04/02
// Перевод © 2004 Евгений Казеко
// www.gamecoder.kazeko.com
// evgeniy@kazeko.com
/* Эта обучалка о том, что такое контекст устройства. В программе Win32
он обычно объявляется как HDC (handle to a device context, ссылка на
контекст устройства). Итак, для начинающих, что же такое этот контекст
устройства? MSDN определяет его как "структура, которая определяет
набор графических объектов и их атрибутов, а также графические режимы,
влияющие на вывод. Вдобавок, контекст устройства ссылается на физическое
устройство вывода, его имя, драйвер и другие атрибуты."
Это довольно хорошее определение. HDC (если видите HDC, думайте о контексте
устройства) по существу является "указателем" на структуру, которая
содержит всю необходимую информацию о контексте устройства.
Есть четыре типа контекстов устройства. Вот они:
Display - поддерживает операции рисования на видеотерминал
Printer - поддерживает запись/вывод на принтер
Memory - поддерживает операции по работе с bitmap
Information - поддерживает получение данных об устройстве.
В целом, мы можем считать главными двумя контексты display и memory.
В этой обучалке мы будем говорить только о display HDC.
Теперь, когда у вас есть начальное понятие о том, что такое HDC, давайте
поговорим о том, что делает эта простая программа. Все что мы хотим
сделать - это создать окно, получить его HDC и, используя HDC, закрасить
окно черным цветом. Не правда ли просто?
*/
#include <windows.h>
#define WIN_WIDTH 320
#define WIN_HEIGHT 240
#define class_name "GameTutorials_HDC"
// Стандартная callback функция
LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprev, PSTR cmdline, int ishow)
{
HWND hwnd; // Ссылка на наше окно
MSG msg; // В этой переменной будут сохраняться сообщения (такие, как
// щелчок мышью), которые может получить наше окно.
WNDCLASSEX wndclassex = {0}; // Это наш "класс окна". "EX" означает
// "extended style" (расширенный стиль),
// который дает нам больше опций, когда
// мы создаем наше окно (хотя мы и проигнорируем их).
// Заполняем нужные нам поля
wndclassex.cbSize = sizeof(WNDCLASSEX); // Всегда должно быть установлено
wndclassex.style = CS_HREDRAW | CS_VREDRAW; // Стиль класса окна
wndclassex.lpfnWndProc = WinProc; // Указатель туда, где определена WinProc()
wndclassex.hInstance = hinstance; // Ссылка на экземпляр нашего окна
wndclassex.lpszClassName = class_name; // Имя нашего класса окна
wndclassex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // Устанавливаем фоновый
// цвет окна - белый
RegisterClassEx(&wndclassex); // Регистрируем класс окна, чтобы вызовы CreateWindow()
// и CreateWindowEx() знали, какой класс окна использовать
// при создании окна
hwnd = CreateWindowEx(NULL, // Дополнительные атрибуты окна, NULL - их нет
class_name, // Имя, которое мы дали WNDCLASSEX
"Device context", // Текст заголовка окна
WS_OVERLAPPEDWINDOW, // Стиль окна
CW_USEDEFAULT, // x левого верхнего угла окна (по умолчанию)
CW_USEDEFAULT, // y левого верхнего угла окна (по умолчанию)
WIN_WIDTH, // Ширина окна в пикселях
WIN_HEIGHT, // Высота окна в пикселях
NULL, // Ссылка на "родительское окно" (у нас его нет)
NULL, // Ссылка на меню (у нас его нет)
hinstance, // Ссылка на экземпляр окна (передается WinMain())
NULL); // "Дополнительная информация" для передачи
// в WinProc (у нас ее нет)
// Проверка ошибок
if(!hwnd)
return EXIT_FAILURE; // Случилось что-то плохое!
/* А теперь мы получаем HDC, ссылку на контекст устройства DISPLAY.
После этого мы сможем рисовать в нашем окне. Для любого типа
графического устройства, компьютер должен знать, где рисовать.
HDC окна как раз и говорит это компьютеру. */
HDC hdc = GetDC(hwnd);
// Мы должны проверить и убедиться, что вызов сработал.
// Если "hdc" равно NULL, то вызов не удался и мы должны
// прервать программу.
if(!hdc)
return EXIT_FAILURE; // Не удалось получить hdc окна
ShowWindow(hwnd, ishow); // Показать окно (сделать его видимым)
UpdateWindow(hwnd); // Обновить окно (послать сообщение WM_PAINT. Мы поговорим
// об этом в следующих обучалках. Просто знайте, что
// так окно перерисовывается).
/* А теперь мы сделаем окно черным. Рассмотрим это шаг за шагом.
Сперва нам нужно получить прямоугольник RECT который описывает
CLIENT область окна (внутренняя область, в которую не входят края и заголовок). */
RECT rect;
GetClientRect(hwnd, &rect); // К счастью, эта функция как раз и сделает это.
// Передаем ссылку на нужное окно и указатель
// на RECT, и туда запишутся размеры CLIENT области.
/* Теперь, когда у нас есть RECT, мы вызываем заполнение этой области черным.
Разберем параметры:
hdc - контекст устройства, куда мы хотим рисовать (наше окно).
&rect - адрес области в hdc, куда мы хотим рисовать (client область окна).
(HBRUSH)GetStockObject(BLACK_BRUSH) - функция возвращает тип HBRUSH
(благодаря преобразованию типов). Пока что запомните, что так мы задаем
цвет. Попробуйте заменить BLACK_BRUSH на DKGRAY_BRUSH. */
FillRect(hdc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
while(1)
{
// Получаем сообщения, если они есть
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// Здесь проводим все сложные вычисления :)
}
}
/* ОЧЕНЬ важно всегда освобождать память, связанную с HDC. Win2K и выше
сделают это за вас, но даже в этом случае это произойдет только
в конце программы. Если вы будете вызывать GetDC() в цикле, то вы
очень быстро израсходуете память. Чтобы освободить память, просто
сделайте это: */
ReleaseDC(hwnd,hdc); // Освобождает память от HDC
/* А также очень важно освобождать память, связанную с классом окна,
когда вы завершили работу с ним. Когда мы вызываем RegisterClassEx(),
некоторое количество памяти выделяется для описания класса (WNDCLASSEX)
окна, которое мы собираемся создать. Если мы не освободим эту память, то
это будет плохо. Поэтому мы вызываем специальную функцию, для того, чтобы
освободить память, занятую после вызова RegisterClassEx(). В большинстве
случаев вы захотите сделать это в конце вашей Win32 программы.
Примечание. Вам нужно вызывать UnregisterClass() только тогда, когда вы
регистрируете класс окна функцией RegisterClassEx(). Более ранняя функция
RegisterClass() (без Ex), не требует этого вызова. */
UnregisterClass(class_name,hinstance);
return msg.wParam; // Выход из программы
}
// WinProc - оконная процедура
LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
// В зависимости от сообщения мы выполняем различные вещи.
switch(message)
{
/* Как видите, мы сделали WinProc() очень простой.
Просто чтобы вы знали, в зависимости от того, как закрывается окно,
посылается одно из сообщений, либо WM_DESTROY либо WM_CLOSE, поэтому
я всегда проверяю оба и пишу соответствующий код закрытия.
Кроме того, если вы измените размер окна или свернете его
(или, возможно, сделаете что-то еще), то черный цвет, которым мы
заполнили окно, исчезнет. Это потому что эти действия (изменение
размера, сворачивание и другие) автоматически приведут к перерисовке
окна, а мы заполняем окно цветом только один раз. */
case WM_DESTROY:
case WM_CLOSE:
PostQuitMessage(0);
return 0;
} // конец switch(message)
return DefWindowProc(hwnd, message, wparam, lparam);
}
/* Очень важно, чтобы вы поняли, что такое HDC. В программировании Win32 вы увидите
это снова и снова и снова и снова. Если вы все еще не совсем понимаете, то представьте
себе HDC вот так:
HDC это "дверь" к пикселям, из которых состоит окно. Используя DISPLAY DEVICE CONTEXT
(это то, что возвращает вам GetDC()) вы можете рисовать в окне. Win32 позволяет
рисовать в окне многими способами, FillRect() это один из них. В последующих обучалках
вы увидите, что HDC постоянно используется для рисования в окне. Поэтому, все, чем является
HDC - это средством для рисования в окне. */
/*
| TheTutor
| thetutor@gametutorials.com
| © 2000-2002 GameTutorials
*/
// Перевод © 2004 Евгений Казеко
// www.gamecoder.kazeko.com
// evgeniy@kazeko.com
|