Лекция 5. Отображение информации с помощью модуля
GDI
1. Контекст устройства
В однозадачных ОС (MS-DOS), любая программа может рисовать непосредст-
венно на экране. В многозадачных ОС программы так действовать не должны, т.к.
при одновременной работе нескольких программ пользователь должен видеть на эк-
ране согласованную картину, сформированную в результате их совместной работы.
Экранная область, принадлежащая программе A, должна быть защищена от информа-
ции, выводимой программой B. Доступом к видеоадаптеру, как и к другим устройст-
вам, управляет ОС. Она позволяет программам выводить информацию только в пре-
делах их окон. В Windows графическое отображение выполняет модуль GDI.
Windows-приложение не может рисовать что-либо непосредственно на экране,
принтере или каком-нибудь другом устройстве вывода. Все операции рисования про-
изводятся на воображаемом "устройстве", представляемом с помощью контекста уст-
ройства. Контекст устройства – эта служебная внутренняя структура Windows, в
которой хранятся все характеристики устройства и его текущего состояния, необхо-
димые модулю GDI для рисования. До начала рисования приложение должно полу-
чить от модуля GDI дескриптор контекста устройства. Этот дескриптор надо переда-
вать в качестве первого параметра всем функциям рисования GDI. Без корректного
дескриптора контекста устройства, GDI не будет знать, на каком именно устройстве и
в какой его области рисовать пикселы. В контексте устройства хранятся параметры,
позволяющие GDI выполнять отсечение и рисовать графические примитивы только
внутри заданных областей. Одни и те же функции GDI могут рисовать примитивы на
различных устройствах, т.к. специфика устройства скрыта в контексте устройства.
MFC избавляет программиста от необходимости непосредственной работы с
дескрипторами контекстов устройств. Дескриптор контекста устройства и функции
рисования GDI инкапсулированы в класс "Контекст устройства" – CDC. От него унас-
ледованы классы для представления различных контекстов устройств (см. табл. 5.1).
Таблица 5.1. Классы различных контекстов устройств
Имя класса
CPaintDC
CClientDC
CWindowDC
С чем связан этот контекст устройства
Клиентская область окна (только в обработчиках OnPaint)
Клиентская область окна
Окно, включая неклиентскую область
CMetaFileDC Метафайл GDI (аналог макроса, в котором запоминаются вызовы функ-
ций GDI и потом их можно многократно воспроизводить)
Объекты этих классов можно создавать как автоматически, так и динамически.
В конструкторе и деструкторе каждого класса вызываются функции GDI для получе-
ния и освобождения дескрипторов контекста устройства. Например, создать контекст
устройства в обработчике OnPaint можно так:
CPaintDC dc(this);
// Вызовы каких-либо функций-членов для рисования примитивов
Конструктору CPaintDC передается указатель на окно, с которым будет связан
контекст устройства.
Класс CPaintDC предназначен для рисования в клиентской области окна при
обработке сообщений WM_PAINT. Но приложения Windows могут выполнять графиче-
56
ское отображение не только в OnPaint. Например, требуется рисовать в окне окруж-
ность при каждом щелчке мышью. Это можно делать в обработчике сообщения мы-
ши, не дожидаясь очередного сообщения WM_PAINT. Для подобных операций отобра-
жения предназначен класс CClientDC. Он создает контекст устройства, связанный с
клиентской областью окна, которым можно пользоваться за пределами OnPaint.
Ниже приведен пример рисования диагоналей в клиентской области окна с по-
мощью CClientDC и двух функций-членов, унаследованных от CDC.
void CMainWindow::OnLButtonDown( UINT nFlags, CPoint point )
{
CRect rect;
GetClientRect(&rect);
CClientDC dc(this);
dc.MoveTo( rect.left, rect.top );
dc.LineTo( rect.right, rect.bottom );
dc.MoveTo( rect.right, rect.top );
dc.LineTo( rect.left, rect.bottom );
}
В редких случаях программе требуется получить доступ ко всему экрану (на-
пример, в программе захвата экрана). Тогда можно создать контекст устройства как
объект класса CClientDC или CWindowDC, но в конструктор передать нулевой указа-
тель. Например, нарисовать окружность в левой верхней части экрана можно так:
CClientDC dc( NULL );
dc.Ellipse( 0, 0, 100, 100 );
1.1 Атрибуты контекста устройства
В контексте устройства хранится ряд атрибутов, влияющих на работу функций
рисования. В классе CDC есть функции-члены для чтения текущих значений и для из-
менения этих атрибутов (табл. 5.2).
Таблица 5.2. Основные атрибуты контекста устройства
Атрибут
Цвет текста
Цвет фона
Режим фона
Режим преобразова-
ния координат
Режим рисования
Текущая позиция
Текущее перо
Текущая кисть
Текущий шрифт
Значение по
умолчанию
Черный
Белый
OPAQUE
MM_TEXT
R2_COPYPEN
(0,0)
BLACK_PEN
WHITE_BRUSH
SYSTEM_FONT
Функция-член CDC для
задания значения
SetTextColor
SetBkColor
SetBkMode
SetMapMode
SetROP2
MoveTo
SelectObject
SelectObject
SelectObject
Функция-член СDC для
получения значения
GetTextColor
GetBkColor
GetBkMode
GetMapMode
GetROP2
GetCurrentPosition
SelectObject
SelectObject
SelectObject
Различные функции рисования CDC пользуются атрибутами по-разному. На-
пример, цвет, ширина и стиль (сплошная, штриховая и т.п.) линии для рисования от-
резка функцией LineTo определяются текущим пером. При рисовании прямоуголь-
ника функцией Rectangle модуль GDI рисует контур текущим пером, а внутреннюю
область заполняет текущей кистью. Цвет текста, фона и шрифт используются всеми
функциями отображения текста. Фоновый цвет применяется также при заполнении
57
промежутков в несплошных линиях. Если фоновый цвет не нужен, его можно отклю-
чить (сделать "прозрачным"):
dc.SetBkMode( TRANSPARENT );
Атрибуты CDC чаще всего изменяются с помощью функции SelectObject.
Она предназначена для "выбора" в контексте устройства объектов GDI 6-ти типов:
• перья (pens)
• кисти (brushes)
• шрифты (fonts)
• битовые карты (bitmaps)
• палитры (palettes)
• области (regions).
В MFC перья, кисти и шрифты представлены классами CPen, CBrush и CFont.
Свойства пера "по умолчанию": сплошная черная линия толщиной 1 пиксел; кисть
"по умолчанию": сплошная белая; шрифт "по умолчанию": пропорциональный
высотой примерно 12 пт. Вы можете создавать объекты-перья, кисти и шрифты с
нужными вам свойствами и выбирать их в любом контексте устройства.
Допустим, динамически были созданы объекты pPen и pBrush – черное перо
толщиной 10 пикселов и сплошная красная кисть. Для рисования эллипса с черным
контуром и красным заполнением можно вызвать следующие функции-члены:
dc.SelectObject( pPen );
dc.SelectObject( pBrush );
dc.Ellipse( 0, 0, 100, 100 );
Функция-член SelectObject перегружена для работы с указателями на раз-
личные объекты GDI. Она возвращает указатель на предыдущий выбранный в кон-
тексте устройства объект того же типа, что и объект, переданный функции в качестве
параметра.
1.2 Режимы преобразования координат
Один из самых сложных для освоения аспектов GDI – применение режимов
преобразования координат. Режим преобразования координат – это атрибут контек-
ста устройства, задающий способ пересчета логических координат в физические ко-
ординаты устройства. Логические координаты передаются функциям рисования CDC.
Физические координаты – это координаты пикселов в экранном окне или на листе
принтера (т.е. на поверхности изображения).
Допустим, вызывается функция Rectangle:
dc.Rectangle( 0, 0, 200, 100 );
Нельзя сказать, что эта функция нарисует прямоугольник шириной 200 пиксе-
лов и высотой 100 пикселов. Она нарисует прямоугольник шириной 200 логических
единиц и высотой 100 единиц. В режиме преобразования координат по умолчанию,
MM_TEXT, 1 логическая единица равна 1-му пикселу. В других режимах масштаб мо-
жет быть иным (см. табл. 5.3). Например, в режиме MM_LOMETRIC 1 логическая еди-
ница равна 1/10 мм. Следовательно, в показанном вызове Rectangle будет нарисован
прямоугольника шириной 20 мм и высотой 10 мм. Режимы, отличные от MM_TEXT,
удобно применять для рисования в одинаковом масштабе на различных устройствах
вывода.
58
Таблица 5.3. Режимы преобразования координат, поддерживаемые модулем GDI
Константа для обозначе-
ния режима
MM_TEXT
MM_LOMETRIC
MM_HIMETRIC
MM_LOENGLISH
MM_HIENGLISH
Расстояние, соответствующее
логической единице
1 пиксел
0.1 мм
0.01 мм
0.01 дюйма
0.001 дюйма
Ориентация координат-
ных осей
x вправо, у вниз
x вправо, у вверх
x вправо, у вверх
x вправо, у вверх
x вправо, у вверх
MM_TWIPS
1/1440 дюйма (0.0007 дюйма) x вправо, у вверх
MM_ISOTROPIC
MM_ANISOTROPIC
Определяется пользователем
(масштаб по осям x и y одина-
ков)
Определяется пользователем
(масштаб по осям x и y задается
независимо)
Определяется пользовате-
лем
Определяется пользовате-
лем
Система координат в режиме MM_TEXT показана на рис. 5.1. Начало координат
располагается в левом верхнем углу поверхности изображения (в зависимости от кон-
текста устройства, это может быть левый верхний угол экрана, окна, клиентской об-
ласти окна). Ось х направлена вправо, ось y вниз, 1 логическая единица равна 1-му
пикселу. В остальных, "метрических", системах ось y направлена вверх, так что сис-
тема координат оказывается правой, но начало координат по умолчанию всегда по-
мещается в левый верхний угол поверхности изображения.
(0, 0)
x
Поверхность изображения
1 единица = 1 пиксел
y
Рис. 5.1. Система координат в режиме MM_TEXT.
1.3 Функции преобразования координат
Для преобразования логических координат в координаты устройства (физиче-
ские координаты) предназначена функция CDC::LPtoDP. Для обратного преобразо-
вания есть функция CDC::DPtoLP.
Допустим, надо вычислить координаты центра клиентской области окна в фи-
зических координатах. Для этого не требуется никаких преобразований, т.к. размеры
клиентской области в пикселах возвращает функция CWnd::GetClientRect:
CRect rect;
GetClientRect( &rect );
CPoint point( rect.Width()/2, rect.Height()/2 );
Для вычисления координат этой точки в режиме MM_LOMETRIC потребуется
функция DPtoLP:
CRect rect;
GetClientRect( &rect );
CPoint point( rect.Width()/2, rect.Height()/2 );
CClientDC dc( this );
dc.SetMapMode( MM_LOMETRIC );
59
dc.DPtoLP( &point );
Функции LPtoDP и DPtoLP часто применяются при обработке сообщений мы-
ши. Windows помещает в структуру сообщения координаты указателя в физической
системе координат. Поэтому, если вы ходите "нарисовать мышью" прямоугольник в
режиме MM_LOMETRIC, то перед рисованием необходимо преобразовать координаты
указателя из физических координат устройства в логические координаты контекста.
Иногда Windows-программисты употребляют термины "клиентские координа-
ты" и "экранные координаты". Клиентские координаты – это физические координаты,
заданные относительно левого верхнего угла клиентской области окна. Экранные ко-
ординаты – это физические координаты, заданные относительно левого верхнего угла
экрана. Преобразование между двумя этими системами выполняется с помощью
функций CWnd::ClientToScreen и CWnd::ScreenToClient.
1.4 Изменение положения начала координат
По умолчанию во всех режимах преобразования координат начало логической
системы координат располагается в левом верхнем углу поверхности изображения.
При работе с правыми системами координат м.б. удобнее поместить начало системы
в другую точку, например, в центр или левый нижний угол окна. Для этого можно
использовать одну из двух функций: CDC::SetWindowOrg (смещение левого верхне-
го угла поверхности изображения) или CDC::SetViewportOrg (смещение начала ло-
гической системы координат).
Предположим, требуется поместить начало логической системы координат в
центр окна. Это можно сделать так (считая, что dc – объект подкласса CDC):
CRect rect;
GetClientRect( &rect );
dc.SetViewportOrg( rect.Width()/2, rect.Height()/2 );
1.5 Получение характеристик устройства
Иногда бывает полезно узнать характеристики устройства, с которым связан
контекст. Для этого предназначена функция CDC::GetDeviceCaps. Например, полу-
чить ширину и высоту экрана (например, 1024х768) можно так:
CClientDC dc( this );
int cx = dc.GetDeviceCaps( HORZRES );
int cy = dc.GetDeviceCaps( VERTRES );
Некоторые возможные параметры функции GetDeviceCaps приведены в табл. 5.4.
Таблица 5.4. Часто используемые параметры функции GetDeviceCaps
Параметр
Значение, возвращаемое функцией GetDeviceCaps
HORZRES Ширина поверхности изображения, в пикселах
VERTRES
HORZSIZE
VERTSIZE
Высота поверхности изображения, в пикселах
Ширина поверхности изображения, в миллиметрах
Высота поверхности изображения, в миллиметрах
LOGPIXELSX Количество пикселов на логический дюйм по горизонтали
LOGPIXELSY Количество пикселов на логический дюйм по вертикали
NUMCOLORS Для дисплея – количество статических цветов, для принтера или плоттера –
количество поддерживаемых цветов
TECHNOLOGY Получение битовых флагов, идентифицирующих тип устройства – дисплей,
принтер, плоттер и др.
60