
- •1 Устройство графики в Windows
- •1.1 Graphics Device Interface и Device Context
- •1.2 Как рисовать в Device Context?
- •2.3 Как рисовать в окно приложения?
- •2.4 Когда рисовать в окно приложения? wm_paint - что это?
- •2.5 Как быстро рисовать в Device Context?
- •2.6 Как загрузить и вывести на экран изображение?
- •2.7 Как нарисовать что-либо на изображении?
1 Устройство графики в Windows
1.1 Graphics Device Interface и Device Context
Не стану углубляться в теорию строения Windows и ее графической подсистемы (литературы на эту тему написано вполне достаточно), постараюсь коротко изложить некий минимум знаний, который понадобится при программировании простейшей графики в Windows. При этом я постараюсь также дать понимание что и как устроено (пускай на простом уровне).
Во-первых, в Microsoft Windows существует несколько средств для вывода графической информации, включая DirectDraw, OpenGL, GDI и т.д. Мы рассмотрим GDI (Graphics Device Interface) - подсистему Windows, ответственную за вывод графики и текста на дисплей и принтер. Именно она занимается выводом большинства "окошек", которые и составляют то, что видит пользователь Windows на экране. Она является базовым и, пожалуй, простейшим способом вывода графики в Windows.
С графикой Windows с помощью GDI неразрывно связано понятия контекста устройства (device context). Контекст устройства (DC) - это структура данных, содержащая информацию о параметрах и атрибутах вывода графики на устройство (например, дисплей или принтер). Такая информация, в частности, включает в себя: палитру устройства, определяющую набор доступных цветов; параметры пера для черчения линий; параметры кисти для закраски и заливки; параметры шрифта, использующегося для вывода текста.
В GDI существуют пять типов контекста устройства - связанный с дисплеем (Display DC), принтером (Printer DC), контекст виртуального устройства в памяти (Memory DC), контекст метафайла (Metafile DC) и специальный вид контекста - информационный (Information DC).
Первые четыре типа контекста устройства - display, printer, memory и metafile предоставляют унифицированный интерфейс для вывода графической информации на разнотипные устройства, освобождая приложение (и его разработчика) от необходимости заботится о том, куда именно производится вывод графики. Информационный контекст для вывода графики не используется, он служит исключительно для получения информации о параметрах и поддерживаемых режимах устройства, с которым связан.
В чем отличие первых четырех типов контекста? Это можно понять из их названий - Display DC служит для вывода на экран, Printer DC для печати на принтер или графопостроитель, Memory DC служит для создания растровых изображений в памяти с возможностью быстрого их копирования в другие типы контекстов (и обратно), Metafile DC нужен для вывода графики в метафайл. Метафайл - это хранилище последовательности команд GDI, каждая из которых описывает одну графическую функцию. В отличие от растровых файлов, хранящих графическую информацию непосредственно в виде массива пикселов, метафайл ее хранит в виде последовательности команд, которая создает результирующий рисунок.
1.2 Как рисовать в Device Context?
Для вывода графической информации существует набор функций, которые можно разделить на несколько категорий:
Методы рисования линий: LineTo, MoveTo, Polyline, Arc, ArcTo, PolyBezier, и др.
Методы рисования замкнутых фигур: Ellipse, Rectangle, Polygon, Pie, Chord и др.
Методы вывода текста: TextOut, DrawText и т.д.
Функции работы с растровым изображением: GetPixel, SetPixel, FloodFill, BitBlt и т.д.
Существует отдельная категория функций работы с DC по переключению режимов и установке параметров вывода графической информации. Часть из них устанавливается напрямую через определенные функции (например, SetBkColor), часть - с помощью специальных графических объектов:
перо (pen)
- задает режим вывода линий (цвет, толщина, стиль);
кисть (brush)
- регулирует режим закраски фигур (цвет, стиль);
шрифт (font)
- задает свойства шрифта, которым выводится текст;
палитра (palette)
- задает набор используемых в DC цветов;
область (region)
- используются для задания clipping regions - областей отсечения, вне которых вывод графики блокируется.
Работа с графическими объектами производится с помощью их дескрипторов (handles) - HDC, HPEN, HBRUSH, HFONT и т.д. Создание и удаление объектов производится с помощью соответствующих функций - например, объект pen создается с помощью CreatePen, удаляется с помощью DeleteObject. Режимы, задающиеся через графические объекты, переключаются с помощью создания новых объектов и указания контексту (DC) использовать их для вывода графики. Это делается помощью функции SelectObject:
//hdc - дескриптор контекста устройства
HPEN hWhitePen, hBlackPen, hOldPen;
HBRUSH hBlackBrush, hOldBrush;
hWhitePen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
hBlackPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
hBlackBrush = CreateSolidBrush(RGB(0, 0, 0));
// нарисовать белый квадрат
hOldPen = SelectObject(hdc, hWhitePen);
MoveTo(hdc, 10, 10);
LineTo(hdc, 100, 10);
LineTo(hdc, 100, 100);
LineTo(hdc, 10, 100);
LineTo(hdc, 10, 10);
// нарисовать черную окружность
SelectObject(hdc, hBlackPen);
hOldBrush = SelectObject(hdc, hBlackBrush);
Ellipse(hdc, 10, 10, 100, 100);
// вернуть старый объекты pen и brush в DC
SelectObject(hdc, hOldPen);
SelectObject(hdc, hOldBrush);
// освободить ресурсы
DeleteObject(hWhitePen);
DeleteObject(hBlackPen);
DeleteObject(hBlackBrush);
При выборе нового объекта через SelectObject в качестве возвращаемого значения передается дескриптор объекта, бывшего в использовании в DC раньше. Нужно иметь ввиду, что все создаваемые объекты нужно не забывать удалять их после использования. Более того, сам DC всегда создается с некоторыми объектами по умолчанию и при использовании определенных пользователем объектов через SelectObject нужно в конце работы произвести select объектов, которые были в DC изначально (см. пример выше).