. Сообщения получает невидимое окно, создающееся специально для этого. Поэтому событие
OnTimer
за час при секундном интервале также возникнет меньше, чем 3600 раз.
Некоторую специфику имеют и сообщения от клавиатуры. При обработке таких сообщений можно использовать функцию
GetKeуState
, которая возвращает состояние любой клавиши (нажата-отпущена) в момент возникновения данного события. Именно в момент возникновения, а не в момент вызова функции. Если функцию
GetKeyState
использовать при обработке не клавиатурного сообщения, оно вернет состояние клавиши на момент последнего извлеченного из очереди клавиатурного сообщения.
1.1.11. Графика в Windows API
Та
часть Windows API, которая служит для работы с графикой, обычно называется GDI (Graphic Device Interface). Ключевое понятие в GDI — контекст устройства (Device Context, DC). Контекст устройства — это специфический объект, хранящий информацию о возможностях устройства, о способе работы с ним и о разрешенной для изменения области. В Delphi контекст устройства представлен классом
TCanvas
, свойство
Handle
которого содержит дескриптор контекста устройства.
TCanvas
универсален в том смысле, что с его помощью рисование в окне, на принтере или в метафайле выглядит одинаково. То же самое справедливо и для контекста устройства. Разница заключается только в том, как получить в разных случаях дескриптор контекста.
Большинство методов класса
TCanvas
являются "калькой" с соответствующих (в большинстве случаев одноименных) функций GDI. Но в некоторых случаях (прежде всего в методах вывода текста и рисования многоугольников) параметры методов
TCanvas
имеют более удобный тип, чем функции GDI. Например, метод
TCanvas.Polygon
требует в качестве параметра открытый массив элементов типа
TPoint
, а соответствующая функция GDI — указатель на область памяти, содержащую координаты точек, и число точек. Это означает, что до вызова функции следует выделить память, а потом — освободить ее. Еще нужен код, который заполнит эту область памяти требуемыми значениями. И ни в коем случае нельзя ошибаться в количестве элементов массива. Если зарезервировать память для одного числа точек, а при вызове функции указать другое, программа будет работать неправильно. Но для простых функций работа через GDI ничуть не сложнее, чем через
TCanvas
. Для получения дескриптора контекста устройства существует много функций. Только для того, чтобы получить дескриптор контекста обычного окна, существуют четыре функции:
BeginPaint
,
GetDC
,
GetWindowDC
и
GetDCEx
. Первая из них возвращает контекст клиентской области окна при обработке сообщения
WM_PAINT
. Вторая дает контекст клиентской области окна, который можно использовать в любой момент времени, а не только при обработке
WM_PAINT
. Третья позволяет получить контекст всего окна, вместе с неклиентской частью. Последняя же дает возможность получить контекст определенной области клиентской части окна.
После того как дескриптор контекста получен, можно воспользоваться преимуществами класса
TCanvas
. Для этого необходимо создать экземпляр такого класса и присвоить его свойству
Handle
полученный дескриптор. Освобождение ресурсов нужно проводить в следующем порядке сначала свойству Handle присваивается нулевое значение, затем уничтожается экземпляр класса
TCanvas
, после этого с помощью подходящей функции GDI освобождается контекст устройства. Пример такого использования класса
TCanvas
демонстрируется листингом 1.17.
Листинг 1.17. Использование класса
TCanvas
для работы с произвольным контекстом устройства
var
DC: HDC;
Canvas: TCanvas;
begin
DC := GetDC(...); // Здесь возможны другие способы получения DC
Canvas := TCanvas.Create;
try
Canvas.Handle := DC; // Здесь рисуем, используя Canvas
finally
Canvas.Free;
end;
// Освобождение объекта Canvas не означает освобождения контекста DC
// DC необходимо удалить вручную
ReleaseDC(DC);
end;
Использование класса
TCanvas
для рисования на контексте устройства, для которого имеется дескриптор, показано в примере PanelMsg на прилагающемся компакт-диске.
Разумеется, можно вызывать функции GDI при работе через
TCanvas
.
Для этого им просто нужно передать в качестве дескриптора контекста значение свойства
Canvas.Handle
. Коротко перечислим те возможности GDI, которые разработчики VCL почему-то не сочли нужным включать в
TCanvas
: работа с регионами и траекториями; выравнивание текста по любому углу или по центру; установка собственной координатной системы; получение детальной информации об устройстве; использование геометрических перьев; вывод текста под углом к горизонтали; расширенные возможности вывода текста; ряд возможностей по рисованию нескольких кривых и многоугольников одной функцией; поддержка режимов заливки. Доступ ко всем этим возможностям может быть осуществлен только через API. Отметим также, что Windows NT/2000/XP поддерживает больше графических функций, чем 9x/МЕ. Функции, которые не поддерживаются в 9x/ME, также не имеют аналогов среди методов TCanvas, иначе программы, написанные с использованием данного класса, нельзя было бы запустить в этих версиях Windows.
GDI предоставляет очень интересные возможности по преобразованию координат, но только в Windows NT/2000/XP; в Windows 9x/ME они не поддерживаются. С помощью функции
SetWorldTransform
можно задать произвольную матрицу преобразования координат, и все дальнейшие графические операции будут работать в новой координатной системе. Матрица позволяет описать такие преобразования координат, как поворот, сдвиг начала координат и масштабирование, т.е. возможности открываются очень широкие. Также существует менее гибкая, но тоже полезная функция преобразования координат —
SetMapMode
, которая поддерживается во всех версиях Windows. С ее помощью можно установить такую систему координат, чтобы во всех функциях задавать координаты, например, в миллиметрах, а не пикселах. Это позволяет использовать один и тот же код для вывода на устройства с разными разрешениями.
Некоторые возможности GDI, которым нет аналогов в
TCanvas
, демонстрируются примером GDI Draw.
Для задания цвета в GDI предусмотрен тип
COLORREF
(в модуле Windows определен также его синоним для Delphi —
TColorRef
). Это 4-байтное беззнаковое целое, старший байт которого определяет формат представления цвета. Если этот байт равен нулю (будем называть этот формат нулевым), первый, второй и третий байты представляют собой интенсивности красного, зеленого и синего цветов соответственно. Если старший байт равен 1, два младших байта хранят индекс цвета в текущей палитре устройства, третий байт не используется и должен быть равен нулю. Если старший байт равен 2, остальные байты, как и в нулевом формате, показывают интенсивность цветовых составляющих.
Тип
TColorRef
позволяет менять глубину каждого цветового канала от 0 до 255, обеспечивая кодирование 16 777 216 различных оттенков (это соответствует режиму
TrueColor
). Если цветовое разрешение устройства невелико, GDI подбирает ближайший возможный цвет из палитры. Если старший байт
TColorRef
равен нулю, цвет выбирается из текущей системной палитры (по умолчанию эта палитра содержит всего 20 цветов, поэтому результаты получаются далекими от совершенства). Если же старший байт равен 2, то GDI выбирает ближайший цвет из палитры устройства. В этом случае результаты получаются более приемлемыми. Если устройство имеет большую цветовую глубину и не задействует палитру, разницы между нулевым и вторым форматом
COLORREF
нет.
Примечание
Хотя режимы HighColor (32 768 или 65 536 цветов) не обладают достаточной цветовой глубиной, чтобы передать все возможные значения
TColorRef
, палитра в этих режимах не используется и ближайший цвет выбирается не из палитры, а из всех цветов, которые способно отобразить устройство. Поэтому выбор нулевого формата в этих режимах дает хорошие результаты.
В Windows API определены макросы (а в модуле
Windows
, соответственно, одноименные функции)
RGB
,
PaletteIndex
и
PaletteRGB
.
RGB
принимает три параметра — интенсивности красного, зеленого и синего компонентов и строит из них значение типа
TColorRef
нулевого формата.
PaletteIndex
принимает в качестве параметра номер цвета в палитре и на его основе конструирует значение первого формата. Макрос
PaletteRGB
эквивалентен
RGB
, за исключением того, что устанавливает старший байт возвращаемого значения равным двум. Для извлечения интенсивностей отдельных цветовых компонентов из значения типа