ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы.



 

Часть 19

Глава 17. Все о GDI
Многим типам приложений Windows для организации полного интерфейса пользователя нужны только окна, блоки диалога и управления. Но некоторым приложениям (например, рисования и манипулирования изображениями) требуется наличие графических средств для заполн
ения окон. Эта графика может быть в форме линий, форм, текста и побитовых образов.
Для предоставления приложениям графических функциональных возможностей Windows имеет набор функций, называемый графическим интерфейсом устройства (graphics device interface - GDI). GDI можно представить себе как графическую машину, которую используют при
ложения Windows для отображения и манипулирования графикой. Функции GDI предоставляют вашему приложению возможности рисования, которые не зависят от не зависят от используемого дисплея. Например, вы можете использовать одни и те же функции для организаци
и вывода на дисплей EGA, на дисплей VGA и даже на принтер PostScript. Аппаратная независимость реализуется через использование драйверов устройств, которые переводят функции GDI в команды, которые способно понимать используемое устройство вывода.
Контекст дисплея
В отличие от традиционных программ, основанных на DOS, программы Windows никогда не организуют вывод на пиксели экрана, а делают его на некоторый логический элемент, называемый контекстом дисплея. Контекст дисплея это виртуальная поверхность с присущими 
ей атрибутами: ручка, кисть, шрифт, цвет фона, цвет текста и текущая позиция.
Когда вы вызываете функции GDI для рисования на контексте дисплея, связанный с этим контекстом драйвер устройства переводит действия по рисованию в соответствующие команды. Эти команды воспроизводят насколько возможно точно действия рисования на дисплее,
 независимо от возможностей самого дисплея. Дисплей может быть монохромным экраном низкого разрешения или экраном с четырьмя миллионами цветовых оттенков.
Контекст дисплея можно представить себе как холст для рисования. Окно это картина, включая рамку. Вместо рисования на картине в рамке вы рисуете на холсте, а уже затем устанавливаете его в рамку. Аналогично этому, вы рисуете на контексте дисплея окна. Ко
нтекст дисплея обладает рядом инструментов рисования, например, ручки, кисти и шрифты. Контекст дисплея это управляемый Windows элемент, похожий на элемент окна с тем исключением, что контекст дисплея не имеет соответствующего элемента ObjectWindows.
Управление контекстом дисплея
Для организации рисования на контексте дисплея сначала нужно получить контекст дисплея для нужного окна. Требования к памяти со стороны контекста дисплея очень велики, поэтому можно одновременно организовать доступ только к пяти контекстам дисплея в кажд
ом сеансе Windows. Это значит, что каждое окно не может поддерживать свой собственный контекст дисплея. Оно получает его только в случае необходимости и освобождает при первой возможности. Это может несколько обескуражить вас, но вы не имеете контроля на
д атрибутами контекста дисплея. Другое окно и даже другое приложение может сменить атрибуты контекста дисплея. Кроме того, инструменты рисования не являются частью памяти контекста дисплея. Они могут быть выбраны в контекст дисплея каждый раз при его пол
учении.
Процесс получения, использования и освобождения контекста дисплея подробно описан в этой главе.
Что в контексте дисплея ?
Хотя обычно вам не будет нужно изменять большинство атрибутов контекста дисплея, важно по крайней мере знать, что в нем содержится. Данный раздел кратко описывает некоторые элементы контекста дисплея, включая побитовые отображения, цвета, распределения, 
области и инструменты рисования. Некоторые из этих тем также рассматриваются более подробно в некоторых других разделах этой главы.
Побитовая графика
Действительная поверхность контекста дисплея называется побитовым отображением. Побитовые отображения представляют конфигурацию памяти конкретного устройства. Следовательно, они зависят от вида адресуемого устройства. Это создает проблему, поскольку сохр
аненные для одного побитовые отображения будут несовместимы с другим устройством. GDI имеет ряд средств для разрешения этой проблемы, включая аппаратно-независимые побитовые отображения. Имеются следующие функции GDI, которые создают побитовые отображени
я: CreateCompatibleDC, CreateCompatibleBitmap и CreateDIBitmap. Имеются следующие функции GDI по манипулированию побитовыми отображениями: BitBlt, StretchBlt, StretchDIBits и SetDIBitsToDevice.
Цвет
Цвет, который устройство использует для рисования, хранится в цветовой палитре. Если вы желаете добавить цвет, которого нет в цветовой палитре, то его можно добавить. Более часто вы будете настраивать драйвер устройства на аппроксимацию нужного цвета пут
ем смешивания цветов палитры. Работа с цветовой палитрой более подробно рассматривается в разделе данной главы "Использование цветовой палитры".
Режимы распределения
Очень трудно выбрать устройство рисования, когда заранее неизвестно, какое устройство будет использоваться для отображения. Большинство приложений игнорируют эту проблему и предполагают, что вполне удовлетворительно будет работать единица рисования по ум
олчанию (один пиксель). Однако, некоторые приложения требуют, чтобы отображение точно воспроизводило размеры нужно образа. Для таких приложений GDI допускает различные режимы распределения, некоторые из которых не зависят от аппаратуры. Каждый из методов
 распределения имеет свою размерную единицу и систему координатной ориентации. Режим распределения по умолчанию устанавливает начало координат в левом верхнем углу контекста дисплея и положительным направлением оси X вправо и положительным направлением о
си Y вниз. Каждый контекст дисплея имеет атрибуты распределения для интерпретации задаваемых вами координат.
Иногда нужно транслировать логические координаты, используемые вами для рисования, в физические координаты побитового распределения. Для большинства приложений начало координат для экрана это его левый верхний угол, но для окна началом координат будет ле
вый верхний угол области клиента. Некоторые окна прокручивают свою поверхность клиента так, что начало координат не будет даже находиться в области клиента. Некоторые функции GDI работают только в конкретной системе координат, поэтому преобразование коор
динат просто необходимо. В GDI имеется ряд функций для подобного пересчета координат: ScreenToClient, ClientToScreen, DPToLP и LPToDP.
Области вырезанного изображения
Для предотвращения рисования вне заданной области каждый контекст дисплея имеет атрибут области вырезанного изображения. Область вырезанного изображения может быть сложным многоугольником или эллипсом, внутри которого и может происходить действительное р
исование на виртуальной поверхности контекста дисплея. Для большинства приложений выделяемая по умолчанию область вырезанного изображения будет вполне достаточной. Изменять эту область придется только для приложений, которые воспроизводят некоторые специ
альные визуальные эффекты.
Инструментальные средства рисования
Для выполнения большинства действий по рисованию контекст дисплея использует три инструмента: ручку, кисть и шрифт. Ручка используется для рисования линий, дуг и полилиний, которые представляют собой линии из нескольких сегментов. Атрибутами ручки являют
ся ее цвет, толщина и стиль (например, непрерывная или пунктирная). Кисть используется для заполнения цветом замкнутых областей, например, прямоугольников, прямоугольников со скругленными вершинами, эллипсов и многоугольников. При рисовании замкнутых обл
астей функции используют ручки для рисования их границ, а кисти для закрашивания их внутренней области. Имеется четыре типа кистей: непрерывная, штриховки, с побитовым образцом и аппаратно-независимая с побитовым образцом. Шрифт используется при рисовани
и текста на контексте дисплея. Шрифт имеет высоту, ширину, шаг, имя фамилии и лица.
Все эти три инструмента используют атрибут контекста дисплея, цвет фона, при рисовании. Инструментальные средства рисования подробно рассмотрены в разделе данной главы "Инструментальные средства рисования".
Инструментальные средства рисования
Контекст дисплея управляет отображением графики на экране. Для иного способа отображения графики можно изменить инструменты, с помощью которых создается изображение. Атрибуты инструментов задают проявление изображений с помощью функций GDI, например, Lin
eTo, Rectange и TextOut. Ручки задают внешний вид линий, кисти задают внешний вид закрашенных областей и шрифт задает внешний вид изображаемого текста.
Для задания атрибутов инструмента программа Windows выбирает логический инструмент в контекст дисплея. Логический инструмент создается вашей программой путем заполнения полей определенной записи, TLogPen, TLogBrush или TLogFont. Текущий инструмент это оп
ределенный в Windows, представляющий самый общие варианты атрибута, например, непрерывная черная ручка, серая кисть или системный шрифт.
Основные инструменты
Основные инструменты создаются функцией GDI GetStockObject. Например:
var
 TheBrush: HBrush
begin
 TheBrush:=GetStockObject(LtGray_Brush);
 ...
end;
где LtGray_Brush есть целая константа, определенная в модуле WinTypes в ObjectWindows. Приведем список всех имеющихся констант основного инструмента:
     Таблица 17.1. Основные инструменты рисования
----------------------------------------------------------------
Кисти               Ручки               Шрифты
----------------------------------------------------------------
White_Brush         White_Pen           OEM_Fixed_Font
LtGray_Brush        Black_Pen           ANSI_Fixed_Font
Gray_Brush          Null_Pen            ANSI_Var_Font
DkGray_Brush                            System_Font
Black_Brush                             Device_Default_Font
Null_Brush                              System_Fixed_Font
Hoolow_Brush
----------------------------------------------------------------
В отличие от логических инструментов основные инструменты не удаляются после использования.
Логические инструменты
Записи логических инструментов, TLogPen, TLogBrush и TLogFont, содержат поля для хранения каждого атрибута инструмента. Например, TLogPen.lopnColor содержит значение цвета ручки. Каждый тип записи определяет свой собственный набор атрибутов, соответствую
щий типу инструмента.
Логические ручки
Вы можете создавать логические ручки с помощью функций Windows CreatePen или CreatePenInderect. Например:
ThePen:=CreatePen(ps_Dot, 3, RGB(0, 0, 210));
ThePen:=CreatePenInderect(@ALogPen);
Определение записи TLogPen следующее:
TLogPen=record
 lopnStyle: Word;
 lopnWidth: TPoint;
 lopnColor: Longint;
end;
Поле стиля, lopnStyle, содержит константу, задающую стиль линии.
     Рис.17.1. Стили линий для ручки
----------------------------------------------------------------
Константа           Результат
----------------------------------------------------------------
PS_SOLID
PS_DASH
PS_DOT
PS_DASHDOT
PS_DASHDOTDOT
PS_NULL
----------------------------------------------------------------
Поле толщины, lopnWidth, содержит точку, координата x которой задает толщину линии в координатах устройства. На экране VGA, если задано значение 0, то будет рисоваться линия толщиной в один пиксель. Значение координаты y игнорируется.
Поле цвета, lopnColor, содержит значение Longint, байты которого задают величины интенсивности основных цветов (красного, зеленого и синего), смешение которых и дает нужных цвет. Значение lopnColor должно иметь вид $00bbggrr, где bb есть значение синего 
цвета, gg есть значение зеленого цвета и rr есть значение красного цвета. Доступный диапазон интенсивности для каждого первичного цвета от 0 до 255, или от 0 до FF в шестнадцатеричном исчислении. Следующая таблица показывает некоторые примеры значений цв
ета:
     Таблица 17.2. Примеры значений цвета
----------------------------------------------------------------
Значение       Цвет
----------------------------------------------------------------
$00000000      черный
$00FFFFFF      белый
$000000FF      красный
$0000FF00      зеленый
$00FF0000      синий
$00808080      серый
----------------------------------------------------------------
В качестве альтернативы для воспроизведения цвета можно использовать функцию RGB. RGB(0,0,0) возвратит черный цвет, RGB(255,0,0) возвратит красный и т.д.
Логические кисти
Вы можете создавать логические кисти с помощью функций Windows CreateHatchBrush, CreatePatternBrush, CreateDIBPatternBrush или CreateBrushInderect. Например:
TheBrush:=CreateHatchBrush(hs_Vertical, RGB(0, 255, 0));
TheBrush:=CreateBrushInderect(@ALogBrush);
Определение записи TLogBrush следующее:
TLogBrush=record
 lbStyle: Word;
 lbColor: Longint;
 lbHatch: Integer;
end;
Поле стиля, lbStyle, содержит константы, задающие стиль кисти:
- bs_DIBPattern указывает, что образец кисти задан аппаратно-независимым побитовым распределением.
- bs_Hatched задает один из заранее определенных образцов штриховки (см. lbHatch).
- bs_Hollow это пустая кисть.
- bs_Pattern использует левый верхний угол 8 на 8 пикселей   побитового распределения, которое находится в этот момент в памяти.
- bs_Solid это непрерывная кисть.
Поле lbColor содержит значение цвета, аналогично записи TLogPen. Это поле игнорируется кистями со стилями bs_Hollow и bs_Pattern. 
Поле lbHatch содержит целую константу, задающую образец штриховки для кисти со стилем bs_Hatched. Если стиль bs_DIBPattern, то lbHatch содержит регулятор побитового распределения.
     Рис.17.2. Стили штриховки для кисти
----------------------------------------------------------------
Константа           Результат
----------------------------------------------------------------
HS_BDIAGONAL
HS_CROSS
HS_DIAGCROSS
HS_FDIAGONAL
HS_HORIZONTAL
HS_VERTICAL
----------------------------------------------------------------
Логические шрифты
Вы можете создавать логические шрифты с помощью функций Windows CreateFont или CreateFontInderect.
Определение записи TLogBrush следующее:
TLogFont=record
 lfHight: Integer;
 lfWidht: Integer;
 lfEscapement: Integer;
 lfOrientation: Integer;
 lfWeight: Integer;
 lfItalic: Byte;
 lfUnderline: Byte;
 lfStrikeOut: Byte;
 lfCharSet: Byte;
 lfOutPrecision: Byte;
 lfClipPrecision: Byte;
 lfQuality: Byte;
 lfPitchAndFamily: Byte;
 lfFaceName: array[0..lf_FaceSize - 1] of Byte;
end;
При использовании TLogFont для создания шрифта вы задаете атрибуты нужного вам шрифта. Однако, ваша программа не использует эту информацию для генерации шрифта на экране. Вместо этого она налагает запрос экранного шрифта на текущий экранный шрифт сеанса 
Windows.
Поле lfHight задает необходимую высоту шрифта. Нулевое значение устанавливает размер по умолчанию. Положительное значение есть высота элемента в логических единицах. Отрицательное значение воспринимается как положительное.
Поле lfWidht задает нужную ширину букв в единицах устройства. Если задан ноль, то коэффициент сжатия сохраняется.
Для поворачиваемого текста lfEscapement задает значение в десятых долях градуса, на которое поворачивается текст против часовой стрелки. lfOrientation делает аналогичный поворот каждого символа.
Параметр lfWeight задает нужный вес символов. В качестве значений можно использовать константы fw_Light, fw_Normal, fw_Bold и fw_DontCare.
Для трех атрибутов шрифта, lfItalic, lfUnderline и lfStrikeOut нужно задать ненулевые значения.
В поле lfCharSet требуется задать конкретный набор символов, ANSI_CharSet, OEM_CharSet или Symbol_CharSet. Набор символов ANSI содержится в "Руководстве пользователя по Microsoft Windows", в Приложении B.
Поле lfOutPrecision задает, как точно создаваемый Windows шрифт должен соответствовать запросам на размеры и позиционирование. Значение поля по умолчанию Out_Default_Precis. Поле lfClipPrecision задает способ рассмотрения частично видимых символов. Значе
ние поля по умолчанию Clip_Default_Precis.
Поле lfQuality показывает как точно предоставляемый Windows шрифт соответствует запрошенным атрибутам шрифта. Может быть установлено значение Default_Quality, Draft_Quality или Proof_Quality. Для значения Proof_Quality жирные, подчеркнутые, наклонные и р
астянутые шрифты синтезируются, даже если их нет.
Поле lfPitchAndFamily задает шаг и семейство шрифта. Оно может быть результатом логической операции or между константой шага и константой семейства.
     Таблица 17.3. Константы шага и семейства шрифта
----------------------------------------------------------------
Константы шага           Константы семейства
----------------------------------------------------------------
Default_Pitch            ff_Modern
Fixed_Pitch              ff_Roman
Variable_Pitch           ff_Script
                         ff_Swiss
                         ff_Decorative
                         ff_DontCare
----------------------------------------------------------------
И, наконец, lfFaceName это строка, которая задает запрошенный вид букв. Если задано значение 0, то вид букв будет строиться на основании значений других полей TLogFont.
Приведем несколько примеров шрифтов с исходными кодами, определяющими их записи TLogFont:
procedure MyWindow.MakeFont;
var
 MyLogFont: TLogFont;
begin
 with MyLogFont do
 begin
  lfHight:=30;
  lfWidht:=0;
  lfEscapement:=0;
  lfOrientation:=0;
  lfWeight:=fw_Bold;
  lfItalic:=0;
  lfUnderline:=0;
  lfStrikeOut:=0;
  lfCharSet:=ANSI_CharSet;
  lfOutPrecision:=Out_Default_Precis;
  lfClipPrecision:=Clip_Default_Precis;
  lfQuality:=Default_Quality;
  lfPitchAndFamily:=Variable_Pitch or ff_Swiss;
  StrCopy(@FaceName, 'Helv');
 end;
 TheFont:=CreateFontInderect(@MyLogFont);
end;
procedure MyWindow.MakeFont;
var
 MyLogFont: TLogFont;
begin
 with MyLogFont do
 begin
  lfHight:=10;
  lfWidht:=0;
  lfEscapement:=0;
  lfOrientation:=0;
  lfWeight:=fw_Normal;
  lfItalic:=Ord(True);
  lfUnderline:=Ord(True);
  lfStrikeOut:=0;
  lfCharSet:=ANSI_CharSet;
  lfOutPrecision:=Out_Default_Precis;
  lfClipPrecision:=Clip_Default_Precis;
  lfQuality:=Default_Quality;
  lfPitchAndFamily:=Fixed_Pitch or ff_DontCare;
  StrCopy(@FaceName, 'Courier');
 end;
 TheFont:=CreateFontInderect(@MyLogFont);
end;
procedure MyWindow.MakeFont;
var
 MyLogFont: TLogFont;
begin
 with MyLogFont do
 begin
  lfHight:=30;
  lfWidht:=0;
  lfEscapement:=0;
  lfOrientation:=0;
  lfWeight:=fw_Normal;
  lfItalic:=0;
  lfUnderline:=0;
  lfStrikeOut:=0;
  lfCharSet:=Symbol_CharSet;
  lfOutPrecision:=Out_Default_Precis;
  lfClipPrecision:=Clip_Default_Precis;
  lfQuality:=Proof_Quality;
  lfPitchAndFamily:=Fixed_Pitch or ff_Roman;
  StrCopy(@FaceName, 'Rmn');
 end;
 TheFont:=CreateFontInderect(@MyLogFont);
end;
Отображение графики в окнах
Рисование это процесс отображения контекста окна. Приложение Windows отвечает за рисование ее окон при их первом появлении и их изменении, например, после восстановления из пиктограммы или перекрытия другими окнами. Windows не обеспечивает автоматическог
о рисования контекста окон, она только информирует окно, когда ему нужно нарисовать себя. Данный раздел показывает, как рисовать в окне, объясняет механизм рисования и объясняет использование контекста дисплея.
В данном разделе термин "рисование" относится к отображению графики в окне. Рисование это автоматическое отображение графики при первом появлении или изменении окна. С другой стороны рисование это процесс создания и отображения специфических изображений 
в другие моменты времени под управлением программы. Под графикой понимается как текст так и элементы изображения, например, побитовые распределения и прямоугольники.
Рисование в окнах
Для рисования любого текста или изображения в объекте окна сначала нужно получить контекст дисплея. После рисования контекст дисплея нужно освободить. (В одном сеансе Windows доступны только пять элементов контекста дисплея.) Вы можете использовать регул
ятор контекста дисплея в качестве аргумента любой графической функции Windows.
Управление контекстом дисплея
Обычно вы будете определять поле объекта окна для хранения регулятора текущего контекста дисплея, как HWindow хранит регулятор окна:
type
 TMyWindow=object(TWindow)
 TheDC: HDC;
 ...
end;
Для получения контекста дисплея для окна вызывается функция Windows GetDC:
TheDC:=GetDC(HWindow);
Уже затем вы можете выполнять операции рисования на контексте дисплея. Вы можете использовать регулятор контекста дисплея в графических функциях Windows:
LineTo(TheDC, Msg.LParamLo, Msg.LParamHi);
Как только вы закончили работу с контекстом дисплея, освободите его с помощью функции ReleaseDC:
ReleaseDC(HWindow, TheDC);
Не вызывайте GetDC дважды в строке, если между ними не стоит ReleaseDC. Это скорее всего приведет к сбою системы в процессе работы вашей программы, поскольку вы выйдете за пределы доступного контекста дисплея.
Вызов графических функций окна
Одно из правил GDI состоит в том, что для работы функций необходимо в качестве аргумента задавать контекст дисплея. Обычно вы будете вызывать эти функции из методов типа окна. Например, TextOut это функция рисования текста на контексте дисплея в заданном
 месте:
TheDC:=GetDC(HWindow);
TextOut(TheDC, 50, 50, 'Sample Text', 11);
ReleaseDC(HWindow, TheDC);
Раскрашивание окон
Когда возникает необходимость в рисовании окна, оно становится ошибочным, это значит что изображение дисплея не соответствует действительности и должно быть изменено. Это происходит в момент первоначального отображения окна, восстановления из пиктограммы
 или удаления другого окна, которое перекрывало часть данного окна. Во всех этих случаях Windows посылает сообщение wm_Paint соответствующему приложению. Это сообщение автоматически вызывает метод Paint вашего окна. Один из параметров Paint, PaintDC, ест
ь контекст дисплея, который используется для рисования.
Метод TWindow Paint ничего не рисует, поскольку объекты TWindow не имеют графики для рисования. В типе вашего окна определим метод Paint, который будет вызывать методы и функции, изображающие в окне текст и графику.
Единственное, что вы можете сделать с контекстом дисплея, это выбрать в него новый инструмент рисования, например, ручки других цветов или кисти других образцов. Вам придется указать эти инструменты в контексте дисплея рисования вашего метода Paint.
После завершения работы метода Paint контекст дисплея рисования автоматически освобождается.
Стратегия графики
Метод Paint отвечает за рисование текущего содержимого окна в любой момент времени, включая первое появление этого окна. Следовательно, метод Paint должен уметь рисовать все "постоянные" изображения окна. Кроме того, он должен уметь восстанавливать любые
 изображения, добавленные в окно после его первого появления. Для воспроизведения этой "динамической" графики метод Paint должен иметь доступ к инструкциям или данным, с помощью которых было создано изображение.
Вы можете сделать выбор одного из двух возможных вариантов. Первый подход состоит в выделении нескольких графических методов и при динамическом рисовании вызывать их из метода Paint. Другой подход, показанный в примере Главы 3, состоит в хранении данных,
 относящихся к графическому контексту окна, в полях объекта этого окна. Эти данные могут включать, например, координаты, формулы и побитовые распределения. Затем метод Paint повторно вызывает графические функции, которые нужны для преобразования этих дан
ных в изображения.
Используя эти стратегии и способность объекта хранить свои собственные данные и функции вы можете разрабатывать очень развитые и впечатляющие графические приложения.
Использование инструментов рисования
Контекст дисплея разрешает вам осуществлять рисование в окне и, кроме этого, он содержит инструментальные средства рисования: ручки, кисти, шрифты и палитры, которые вы используете для рисования текста и изображений. При рисовании линии на контексте дисп
лея она появляется со следующими атрибутами текущей ручки: цвет, стиль (непрерывная, пунктирная и т.п.) и толщина. При закрашивании области она появляется со следующими атрибутами текущей кисти: образец и цвет. При рисовании текста в контексте дисплея он
 появится с атрибутами текущего шрифта: шрифт (Modern, Roman, Swiss и т.п.), размер, стиль (наклонный, жирный и т.п.). Палитра содержит набор текущих доступных цветов.
Контекст дисплея содержит по одному типу каждого инструмента рисования. Вновь полученный контекст дисплея содержит набор инструментов по умолчанию: тонкая черная ручка, непрерывная черная кисть, системный шрифт и палитра по умолчанию. Если этот набор вас
 устраивает, то нет необходимости его изменять.
Для изменения набора инструментов по умолчанию, нужно создать новый инструментальный элемент и выбрать его в контекст дисплея. Например, при выборе новой ручки старая автоматически удаляется. Мы рекомендуем вам сохранять старые инструменты и повторно уст
анавливать их после окончания использования новых:
var
 NewPen, OldPen: HPen;
 TheDC: HDC;
begin
 {задать ширину пера 10}
 NewPen:=CreatePen(ps_Solid, 10, RGB(0, 0, 0));
 TheDC:=GetDC(AWindow^.HWindow);
 OldPen:=SelectObject(TheDC, NewPen);
 {выполнить черчение}
 SelectObject(TheDC, OldPen);
 ReleaseDC(AWindow^.HWindow, TheDC);
 DeleteObject(NewPen);
end;
Как показано в данном примере, новый инструмент рисования должен быть создан, а затем удален. Подобно контексту дисплея, элементы хранятся в памяти Windows. Если их не удалить, это приводит к потерям памяти и возможности возникновения сбоя. Как и для кон
текста дисплея, вы должны хранить регуляторы инструментов рисования в переменных типа HPen, HBrush, HFont и HPalette.
Функция Windows DeleteObject удаляет инструменты рисования из памяти Windows. Ни в коем случае не удаляйте инструменты рисования, которые выбраны в данный момент в контекст дисплея !
Контекст дисплея может хранить только по одному инструменту рисования каждого типа в данный момент времени, поэтому нужно отслеживать доступные инструменты рисования. Очень важно удалить их все до завершения работы вашего приложения. Один из методов, (ис
пользован в примере Главы 3) состоит в определении поля объекта окна с именем ThePen для хранения регулятора текущей ручки. Когда пользователь выбирает новый стиль ручки, создается новая ручка, а старая удаляется. Следовательно, окончательная ручка будет
 удалена методом головного окна CanClose. Вам не нужно удалять набор инструментов по умолчанию, поставляемый во вновь полученном контексте дисплея.
Есть два способа создания новых инструментов рисования. Самый простой способ состоит в использовании существующего альтернативного инструмента, называемого опорным. Список опорных инструментов приведен в Таблице 17.1.
Для установки опорного инструмента в объект контекста дисплея используются методы SetStockPen, SetStockBrush, SetStockFont и SetStockPalette. Например:
ThePen:=GetStockObject(Black_Pen);
Не удаляйте опорные инструменты из памяти Windows, поскольку вы будете настраивать их.
Иногда нет опорного инструмента, который имел бы нужный вам атрибут. Например, все опорные ручки воспроизводят тонкие линии, а вам требуется толстая. В этом случае имеется два способа создания настроенных инструментов рисования. Один способ состоит в выз
ове функций Windows CreatePen, CreateFont, CreateSolidBrush или CreateDIBPatternBrush. Эти функции используют параметры, которые описывают нужный инструмент, и возвращают регулятор инструмента, который используется в вызовах SelectObject.
Другой способ создания настроенный инструментов состоит в построении описания атрибутов логического инструмента. Логический инструмент реализуется структурами данных Windows TLogPen, TLogBrush, TLogFont и TLogPalette. Например, TLogPen имеет поля для хра
нения толщины цвета и стиля. После создания структуры данных логического инструмента, она передается в качестве параметра в CreatePenInderect, CreateBrushInderect, CreateFontInderect или CreatePalette. Эти функции возвращают регуляторы инструмента, котор
ые могут быть использованы в вызовах SelectObject. В данном примере устанавливается синяя ручка для рисования в контексте дисплея окна:
procedure SampleWindow.ChangePenToBlue;
var
 ALogPen: TLogPen;
 ThePen: HPen;
begin
 ALogPen.lopnColor:=RGB(0, 0, 255);
 ALogPen.lopnStyle:=ps_Solid;
 ALogPen.lopnWidth.X:=0;
 ALogPen.lopnWidth.Y:=0;
 ThePen:=CreatePenInderect(@ALogPen);
 SelectObject(TheDC, ThePen);
end;
Графические функции GDI
Данный раздел описывает различные вызовы API, которые вы можете использовать для рисования изображений в окне.
Функции рисования текста
Функция рисования текста использует для рисования заданный текущий шрифт контекста дисплея. Функция TextOut рисует текст в заданной точке. TextOut выравнивает текст в зависимости от текущих значений флагов форматирования текста. По умолчанию происходит в
ыравнивание слева. Текущий метод выравнивания можно посмотреть с помощью функции GetTextAlign и установить с помощью функции SetTextAlign.
Функция TextOut это самая часто используемая функция рисования текста. Используя установленные по умолчанию флаги форматирования текста данный метод Paint рисует выравненный слева массив символов, левый верхний угол которого имеет координаты (10,15).
procedure TMyWindow.Paint(PaintDC: HDC; var PaintINfo: TPaintStruct);
var
 MyTextString: array[0..20] of Char;
begin
 StrCopy(MyTextString, 'Hello, World');
 TextOut(PaintDC, 10, 15, MyTextString, StrLen(MyTextString));
end;
Рис.17.3. Результат выполнения функции TextOut
Функции рисования линий
Функции рисования линии используют для рисования заданную текущую ручку контекста дисплея. Большинство линий рисуется с использованием функций MoveTo и LineTo. Эти функции воздействуют на атрибут контекста дисплея, текущую позицию. Если использовать анал
огию с карандашом и листом бумаги, то текущая позиция это точка, где карандаш касается бумаги.
MoveTo и LineTo
Функция MoveTo перемещает текущую позицию в заданные координаты. Функция LineTo рисует линию из текущей позиции к точке с заданными координатами. Заданные координаты затем становятся текущей позицией. Следующий метод Paint рисует линию от (100,150) до (1
0,15).
procedure TMyWindow.Paint(PaintDC: HDC; var PaintINfo:
TPaintStruct);
begin
 MoveTo(PaintDC, 100, 150);
 LineTo(PaintDC, 10, 15);
end;
Рис.17.4. Результат выполнения функции LineTo
PolyLine
Функция Polyline рисует последовательность линий, соединяющих заданные точки. По действию она аналогична выполнению последовательности функций MoveTo и LineTo, однако, Polyline выполняет эту операцию намного быстрее и никак не воздействует на текущую поз
ицию ручки. Следующий метод Paint рисует прямой угол.
procedure TMyWindow.Paint(PaintDC: HDC; var PaintInfo:
 TPaintStruct);
var
 Points: array[0..2] of TPoint;
begin
 Points[0].X:=10;
 Points[0].Y:=15;
 Points[1].X:=10;
 Points[1].Y:=150;
 Points[2].X:=100;
 Points[2].Y:=150;
 Polyline(PaintDC, @Points, 3);
end;
Рис.17.5. Результат выполнения функции Polyline
Arc
Функция Arc рисует дуги по периметру эллипса, ограниченного заданным прямоугольником. Дуга начинается в точке пересечения эллипса и линии из центра эллипса в заданную точку начала. Дуга рисуется против часовой стрелки до тех пор, пока она не достигнет то
чки пересечения эллипса с линией из центра эллипса к заданной точке конца.
Следующий метод Paint рисует верхнюю четверть окружности с началом в (40,25) и окончанием в (10,25), используя ограничивающий прямоугольник (10,10), (40,40), начальную точку (0,0) и конечную точку (50,0). Действие производится даже в том случае, если зад
анная начальная и конечная точка не лежат на дуге.
procedure TMyWindow.Paint(PaintDC: HDC; var PaintInfo: TPaintStruct);
begin
 Arc(PaintDC, 10, 10, 40, 40, 50, 0, 0, 0);
end;
Рис.17.6. Результат выполнения функции Arc
Функции закрашивания области
Функции закрашивания области используют текущую ручку заданного контекста дисплея для рисования границ области и текущую кисть для закрашивания ее внутренней части. Они не производят никакого воздействия на текущую позицию.
Rectangle
Функция Rectangle рисует прямоугольник от его левого верхнего угла к правому нижнему. Например, следующий оператор метода Paint рисует прямоугольник от (10,15) до (100,150).
Rectangle(PaintDC, 10, 15, 100, 150);
Рис.17.7. Результат выполнения функции Rectangle
RoundRect
Функция RoundRect рисует прямоугольник со скругленными углами. Скругления углов определены как четверти эллипса. Например, следующий оператор метода Paint рисует прямоугольник от (10,15) до (100,150), углы которого будут скруглены четвертями эллипса шири
ной 9 и высотой 11.
RoundRect(PaintDC, 10, 15, 100, 150, 9, 11);
Рис.17.8. Результат выполнения функции RoundRect
Ellipse
Функция Ellipse рисует эллипс, задаваемый ограничивающим его прямоугольником. Следующий пример рисует эллипс в прямоугольнике от (10,15) до (110,70).
Ellipse(PaintDC, 10, 50, 100, 150);
Рис.17.9. Результат выполнения функции Ellipse
Pie и Chord
Функции Pie и Chord рисуют секторы эллипса. Они рисуют дугу, подобно функции Arc. Однако, результатом Pie и Chord будут области. Функция Pie соединяет центр эллипса с его граничными точками. Следующая функция Pie рисует верхнюю четверть круга, заключенно
го в прямоугольник от (10,10) до (40,40).
Pie(PaintDC, 10, 10, 40, 40, 50, 0, 0, 0);
Рис.17.10. Результат выполнения функции Pie
Функция Chord соединяет две граничные точки дуги.
Chord(PaintDC, 10, 10, 40, 40, 50, 0, 0, 0);
Рис.17.11. Результат выполнения функции Chord
Polygon
Функция Polygon рисует непрерывную последовательность сегментов линий, аналогично функции Polyline, но в конце работы замыкает область, рисуя линию от последней заданной точки к первой заданной точке. И, наконец, он заполняет полученный многоугольник тек
ущей кистью, используя установленный режим закрашивания многоугольника. Следующий метод Paint рисует и закрашивает прямоугольный треугольник.
procedure TMyWindow.Paint(PaintDC: HDC; var PaintInfo: TPaintStruct);
var
 Points: array[0..2] of TPoint;
begin
 Points[0].X:=10;
 Points[0].Y:=15;
 Points[1].X:=10;
 Points[1].Y:=150;
 Points[2].X:=100;
 Points[2].Y:=150;
 Polygon(PaintDC, @Points, 3);
end;
Рис.17.12. Результат выполнения функции Polygon
Использование палитры
Некоторые типы дисплейных устройств компьютера могут показывать множество цветов, но только ограниченное их число в каждый момент времени. Системная или физическая палитра это группа или набор цветов, которые в данный момент доступны дисплею для одноврем
енного отображения. Windows дает вашему приложению частичное управление над цветами, входящими в системную палитру устройства. Если ваше приложение использует только простые цвета, то вам нет необходимости непосредственно использовать палитру.
Однако, изменение палитры системы воздействует на все изображение, имеющееся на экране, включая другие приложения. Одно приложение может вызвать появление всех других приложений в некорректных цветах. Менеджер палитры Windows разрешает эту проблему, согл
асовывая изменения системной палитры с приложениями. Windows предоставляет каждому приложению свою логическую палитру, которая представляет собой группу цветов, используемых приложением. Менеджер палитры связывает запрошенные логической палитрой цвета с 
имеющимися цветами системной палитры. Если запрошенный цвет отсутствует в системной палитре, менеджер палитры может добавить его. Если в логической палитре задано больше цветов, чем может содержаться в системной палитре, то для дополнительных цветов подб
ирается максимально похожий цвет системной палитры.
Когда приложение становится активным имеется возможность заполнить системную палитру цветами из логической палитры. Это действие может повлиять на распределение цветов, заданных логическими палитрами других приложений. В любом случае Windows резервирует 
20 цветов в системной палитре для общего представления цветовой гаммы всех приложений и самого Windows.
Установка палитры
Логические палитры являются инструментами рисования, такими же как ручки и кисти, описанные в разделе "Инструментальные средства рисования". Для создания логической палитры используется функция CreatePalette, которая берет указатель на запись данных TLog
Palette, создает новую палитру и возвращает ее регулятор, который передается в SelectPalette для выбора палитры в контекст дисплея. Запись TLogPalette содержит поля хранения номера версии Windows (сейчас $0300), число элементов палитры и массив элементов
 палитры. Каждый элемент палитры это запись типа TPaletteEntry. Тип TPaletteEntry имеет три байтовых поля для спецификации цвета (peRed, peGreen и peBlue) и одно поле для флагов (peFlags).
GetStockObject(Default_Palette) создает палитру по умолчанию, состоящую из 20 цветов, которые всегда присутствуют в палитре системы.
После выбора палитры в контекст дисплея с помощью SelectPalette, он должен до использования "реализовать" ее. Это делается с помощью функции Windows RealizePalette:
ThePalette:=CreatePalette(@ALogPalette);
SelectPalette(TheDC, ThePalette, 0);
RealizePalette(TheDC);
RealizePalette помещает цвета из вашей логической палитры в системную палитру устройства. Сначала Windows проверяет соответствие цветов с уже имеющимися в системной палитре, а затем добавляет ваши новые цвета в палитру системы, если для этого есть место.
 Цветам, которым не нашлись идентичные цвета в системной палитре, выбирается наиболее соответствующий цвет из палитры системы.
Ваше приложение должно реализовать свою палитру до рисования, как это делается для других инструментов рисования.
Рисование с палитрой
После реализации палитры вашего приложения, оно может осуществлять рисование с использованием его цветов. Цвета палитры можно задавать прямо или косвенно. Для прямого задания цвета используется индекс палитры, TColorRef. Индекс палитры TColorRef есть зна
чение типа Longint, где старший байт установлен в 1, а индекс элемента логической палитры содержится в двух младших байтах. Например, $01000009 задает девятый элемент логической палитры. Это значение можно использовать везде, где ожидается аргумент TColo
rRef. Например:
ALogPen.lopnColor:=$01000009;
Если ваше дисплейное устройство допускает использование полного 24-битового цвета без системной палитры, то использование индекса палитры неоправданно ограничивает вас цветами вашей логической палитры. Чтобы избежать этого ограничения, вы можете задавать
 цвет палитры косвенно, используя относительное значение палитры TColorRef. Относительное значение TColorRef почти совпадает с абсолютным значением RGB TColorRef, но байт старшего разряда установлен в 2. Три младших байта содержат значение цвета RGB. Нап
ример, $020000FF задают значение чистого красного цвета. Если устройство поддерживает системную палитру, то Windows подберет максимально соответствующий RGB цвет логической палитры. Если устройство не поддерживает системную палитру, то TColorRef использу
ется так, как если бы он задавал явное значение RGB.
Выбор в палитре
Windows определяет функцию, которая позволяет вам получать информацию относительно палитры. GetPaletteEntries берет индекс, диапазон и указатель на TPaletteEntry и заполняет буфер заданными элементами палитры.
Модификация палитры
Есть два способа изменения элементов логической палитры. Функция SetPaletteEntries берет те же самые аргументы, что и GetPaletteEntries и меняет заданные элементы на те, на которые указывает третий аргумент. Обратите внимание на то, что произведенные изм
енения не отражаются в системной палитре до вызова RealisePalette и их не видно до перерисовки области клиента. Функция AnimatePalette берет те же аргументы, что и SetPaletteEntries, но используется для быстрых изменений палитры приложения и они немедлен
но становятся видимыми. При вызове AnimatePalette элементы палитры с полем peFlags установленным на константу pc_Reserved будут заменены на соответствующие новые элементы и это найдет немедленное отражение в системной палитре. На другие элементы это ника
к не повлияет.
Например, вам нужно взять первые десять элементы палитры, сменить их значение, добавив на единицу содержание красного цвета и уменьшив содержимое синего и зеленого. Все эти изменения должны сразу же стать видимыми (Предполагается, что некоторые из элемен
тов имеют установленное значение pc_Reserved):
GetObject(ThePalette, SizeOf(NumEntries), @NumEntries);
if NumEntries >= 10 then
begin
 GetPaletteEntries(ThePalette, 0, 10, @PaletteEntries);
 for i:=0 to 9 do
 begin
  PaletteEntries[i].peRed:=PaletteEntries[i].peRed+40;
  PaletteEntries[i].peGreen:=PaletteEntries[i].peGreen-40;
  PaletteEntries[i].peBlue:=PaletteEntries[i].peBlue-40;
 end;
 AnimatePalette(ThePalette, 0, 10, @PaletteEntries);
end;
Вместо AnimatePalette мы могли бы использовать:
 SetPaletteEntries(ThePalette, 0, 10, @PaletteEntries);
 RealizePalette(ThePalette);
и затем перерисовать окно, чтобы увидеть изменения цветов.
Реакция на изменения палитры
Когда окно принимает сообщение wm_PaletteChanged, это свидетельствует о том, что активное окно сменило системную палитру путем реализации ее логической палитры. Окно, принявшее сообщение,может отреагировать на нее тремя способами: Оно может ничего не дел
ать (очень быстрый способ, но может привести к некорректным цветам), оно может реализовать свою логическую палитру и перерисовать себя (медленнее, но цвета будут максимально корректными), либо оно может реализовать свою логическую палитру и затем использ
овать функцию UpdateColors для быстрого изменения области клиента в соответствии с системной палитрой. UpdateColors в общем случае работает быстрее, чем перерисовка области клиента, но при ее использовании могут быть некоторая потеря точности в цветопере
даче. Поле WParam записи TMessage, переданной в сообщении wm_PaletteChanged, содержит регулятор окна, которое реализовало свою палитру. Если в ответ вы решили реализовать свою собственную палитру, сначала убедитесь в том, что этот регулятор не является р
егулятором вашего окна, чтобы не создать бесконечного цикла.
Следующая программа создает и реализует логическую палитру из восьми цветов. При нажатии левой кнопки на мыши она будет рисовать раскрашенные квадраты с образцами каждого из цветов логической палитры. При нажатии правой кнопки происходит сдвиг цветов лог
ической палитры. Используется индекс палитры TColorRef, поэтому когда логическая палитра меняется раскрашенные квадраты также сменят свой цвет. При использовании индекса палитры TColorRef может оказаться удобным использование функции PaletteIndex.
Полный текст программы содержится в файле PALTEST.PAS на ваших дистрибутивных дискетах.



Яндекс цитирования