Читайте также:
|
|
- используются для задания 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 изначально (см. пример выше).
2.3 Как рисовать в окно приложения?
Для того чтобы выводить графику в определенное окно вашего приложения нужно сделать буквально следующее:
Получить дескриптор DC, связанный с окном, в которое вы собираетесь рисовать с помощью функции GetDC(). Нарисовать все, что вы хотите, с помощью функций DC и в конце "освободить" контекст с помощью функции ReleaseDC().
Пример:
//hwnd - дескриптор окна, в которое будем рисовать
HDC hdc;
hdc = GetDC(hwnd);
if ¯(hdc)
{
// рисуем что требуется
...
// освобождаем контекст
ReleaseDC(hwnd, hdc);
}
else
{
// обработка ошибки получения контекста
}
Иным образом производится получение/освобождение дескриптора DC при обработке сообщения WM_PAINT - об этом в следующем разделе.
2.4 Когда рисовать в окно приложения? WM_PAINT - что это?
При выводе графики в Windows есть некоторая тонкость, не всегда очевидная новичкам в программировании под среды с графическим интерфейсом. Казалось бы, если нужно что-то отрисовать в окне - получай его контекст и рисуй. Но не все так просто. Стоит свернуть окно или закрыть его часть другим окном - все, что было нарисовано, пропадет.
Дело в том, что Windows не хранит содержимое клиентской части окна. К клиентской части окна относится ВСЕ, кроме заголовка окна и управляющих элементов (controls): меню, панелей инструментов (toolbar), кнопок и т.д. Приложение само должно позаботиться о том, чтобы отрисовывать свои данные в клиентской области, Windows лишь посылает ему уведомление когда это нужно сделать. Делается это посредством посылки окну сообщения WM_PAINT.
Все необходимые действия по полной перерисовке информации клиентской части окна должны вызываться при обработке события WM_PAINT. Важным понятием при обработке этого сообщения является invalid rectangle. Windows определяет invalid rectangle как наименьшую прямоугольную часть окна, которая была "испорчена" и должна быть перерисована заново. Когда система обнаруживает invalid rectangle в клиентской области окна, она генерирует сообщение WM_PAINT. В ответ на сообщение окно может получить структуру PAINTSTRUCT, которая среди прочего содержит координаты invalid rectangle. Это может пригодиться, если есть желание перерисовывать не все окно, а только ту область, что требуется.
При обработке WM_PAINT должна быть вызвана функция BeginPaint, которая снова делает invalid rectangle `нормальным'. Также BeginPaint возвращает дескриптор DC, который должен быть использован для перерисовки клиентской части окна. Нужно иметь в виду, что при обработке WM_PAINT дескриптор DC окна должен быть получен именно с использованием BeginPaint, а освобожден EndPaint, в то время как во всех других случаях отрисовки нужно использовать другие функции (например, GetDC/ReleaseDC). Если invalid rectangle не делается "нормальным" во время обработки этого события (с помощью BeginPaint или ValidateRect), Windows будет слать WM_PAINT окну постоянно.
Пример обработки WM_PAINT:
//hwnd - дескриптор окна, в которое будем рисовать
HDC hdc;
PAINTSTRUCT PaintStruct;
hdc = BeginPaint(hwnd, &PaintStruct);
if ¯(hdc)
{
// рисуем что требуется
...
// освобождаем контекст
EndPaint(hwnd, &PaintStruct);
}
else
{
// обработка ошибки получения контекста
}
2.5 Как БЫСТРО рисовать в Device Context?
С каждым DC, предназначенным для графического вывода, связан графический объект bitmap (растровое изображение), который хранит массив пикселей, выводимых на устройство. Для того, чтобы быстро переместить графические данные с одного контекста на другой, можно не повторять все действия по отрисовке, а просто скопировать данные связанного с контекстом bitmap. Для этого даны специальные функции быстрого копирования пикселей (BitBlt, StretchBlt).
Зачем это может быть нужно? Дело в том, что если вы часто рисуете достаточно сложную изменяющуюся картинку средствами GDI, сами операции рисования начинают занимать заметное для пользователя время и возникает неприятный эффект мерцания изображения - когда часть картинки уже перерисовалась, а часть еще осталась старой. Для того, чтобы избежать подобного эффекта новая картинка может создаваться в виртуальном DC в памяти, и потом быстро переносится на экран функциями копирования bitmap.
Пример:
// hdc - дескриптор контекста устройства для вывода
// iWidth, iHeight - размеры окна вывода
HDC hMemDC;
hMemDC = CreateCompatibleDC(hdc);
if ¯(hMemDC)
{
// рисуем все что требуется
...
// быстро копируем результат отрисовки
BitBlt(hdc, 0, 0, iWidth, iHeight, hMemDC, 0, 0, SRCCOPY);
// освобождаем контекст
DeleteDC(hMemDC);
}
else
{
// обработка ошибки получения контекста
}
2.6 Как загрузить и вывести на экран изображение?
Пользуясь базовыми функциями WinAPI, это к сожалению не так-то просто. Никаких встроенных функций по загрузке изображения из bmp файла не предусмотрено, поэтому требуется самостоятельно писать функцию загрузки. Эта функциональность уже тысячу раз реализована, одна из реализаций предлагается вам в примере, который прилагается к данному тексту.
В принципе, если вы не собираетесь выводить загружаемое растровое изображение на экран (а, скажем, только обрабатывать и сохранять), то его можно хранить в совершенно произвольных собственных структурах данных. Однако, если вы хотите иметь возможность быстро вывести ваше изображение на экран, или рисовать в нем средствами GDI, придется хранить его определенным образом. Потребуется создать графический объект bitmap, соответствующий параметрам файла bmp, и загрузить в него данные из файла (пиксели). Пример, как это сделать, содержится в классе DSimpleBitmap в примере MFC_GML3.
Для того, чтобы уметь быстро выводить загруженное изображение на экран, требуется сделать следующее - с помощью функции SelectObject привязать к созданному заранее memory DC загруженный bitmap (вместо default bitmap, создающегося вместе с контекстом) и затем функцией копирования битов вывести в дисплейный контекст, связанный с вашим окном. Пример:
// hdc - дескриптор контекста устройства для вывода
// iWidth, iHeight - размеры окна вывода
// hBitmap - дескриптор изображения для отрисовки
HDC hMemDC;
HBITMAP hOldBitmap;
hMemDC = CreateCompatibleDC(hdc);
if (hMemDC)
{
// рисуем все что требуется
hOldBitmap = SelectObject(hMemDC, hBitmap);
// копируем биты
BitBlt(hdc, 0, 0, iWidth, iHeight, hMemDC, 0, 0, SRCCOPY);
// возвращаем старый bitmap
SelectObject(hMemDC, hOldBitmap);
// освобождаем контекст
DeleteDC(hMemDC);
}
else
{
// обработка ошибки получения контекста
}
Не забудьте уничтожить все временные объекты, которые создавались (в данном случае - это memory DC). Не забудьте также перед тем как будете уничтожать memory DC, выбрать в него (через SelectObject) объект bitmap, который был создан вместе с контекстом, в противном случае произойдет утечка ресурсов.
2.7 Как нарисовать что-либо на изображении?
Есть как минимум два способа. Первый - это получить указатель на пиксели растрового изображения (вариант как это сделать см. секцию 4.1) и менять их напрямую. Второй - это рисовать на изображении с помощью функций GDI. Для реализации второго варианта нужно создать DC, связать с ним bitmap, на котором хотите рисовать, и затем использовать стандартные функции вывода графики. Пример:
// hdc - дескриптор некоторого контекста устройства
// hBitmap - дескриптор изображения
HBITMAP hOldBitmap;
// связываем bitmap с контекстом
hOldBitmap = SelectObject(hdc, hBitmap);
// рисуем круг
Ellipse(hdc, 10, 10, 100, 100);
// возвращаем старый bitmap
SelectObject(hdc, hOldBitmap);
Имейте в виду, объект bitmap может быть одновременно связан только с одним DC.
Дата добавления: 2015-11-14; просмотров: 36 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Graphics Device Interface и Device Context | | | Поступление товара. |