Та часть 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. С ее помощью можно установить такую систему координат, чтобы во всех функциях задавать координаты, например, в миллиметрах, а не пикселах. Это позволяет использовать один и тот же код для вывода на устройства с разными разрешениями.