Если присмотреться к надписи, видно, что внутренняя часть контуров букв содержит тот самый рисунок, который был загружен в обработчик
OnCreate
(как будто мы нарисовали этот рисунок через трафарет, имеющий форму надписи). По сути, так оно и есть, только называется это не трафарет, а регион отсечения. Регион — это специальный объект, который хранит область произвольной формы. Способы применения регионов различны (см. разд. 1.3.3), и один из них — это использование региона для отсечения графического вывода. Если установить регион отсечения для контекста устройства, то, что бы мы ни выводили потом в данный контекст, все, что лежит за пределами региона отсечения, игнорируется.
Соответственно, чтобы сделать такую надпись, нужно создать регион, совпадающий по форме с этой надписью. В GDI есть целый ряд функций для создания регионов различной формы, но вот для создания региона в форме букв функции нет. Зато GDI поддерживает другие объекты — траектории. Строго говоря, это не совсем объекты, траектория не имеет дескриптора (по крайней мере, API не предоставляет этот дескриптор программам), и в каждом контексте устройства может быть только одна траектория. Создание траектории начинается с вызова функции
BeginPath
, заканчивается вызовом функции
EndPath
. Графические функции, вызванные между
BeginPath
и
EndPath
, не выводят ничего в контекст устройства, а то, что должно быть выведено, вместо этого запоминается в траектории (которая
представляет собой совокупность замкнутых кривых). С траекторией можно выполнить много полезных операций (см., например, разд. 1.3.4). В нашем случае между вызовами
BeginPath
и
EndPath
мы вызываем
DrawText
. формируя таким образом траекторию, состоящую из контуров букв. Затем с помощью функции
PathToRegion
мы создаем регион, границы которого совпадают с контурами траектории, т.е., в данном случае, регион, совпадающий по форме с надписью.
Примечание
На самом деле не все графические функции, вызванные между
BeginPath
и
EndPath
, добавляют контуры к траектории. Это зависит от версии операционной системы. Подробнее этот вопрос обсуждается в разд. 1.3.4.
В ходе работы программы регион не меняется, так что нет нужды создавать его каждый раз при обработке события
OnPaint
. Он создается только один раз, и его дескриптор сохраняется в поле
FRgn
формы для дальнейшего использования.
Все, что осталось сделать, — это установить регион отсечения с помощью функции
SelectClipRgn
, отобразить рисунок и убрать регион отсечения, чтобы не мешал в дальнейшем.
Теперь рассмотрим, как рисуются звезды в правом верхнем углу окна (листинг 1.35).
Листинг 1.35. Рисование звезд
var
I: Integer;
Star: array[0..4] of TPoint;
...
// Следующая группа команд рисует две звезды справа от
// надписи. Эти звезды демонстрируют использование двух
// режимов заливки: WINDING и ALTERNATE. Для простых
// фигур эти режимы дают одинаковые результаты, разница
// возникает только при закрашивании сложных фигур,
// имеющих самопересечения.
Canvas.Pen.Style := psSolid;
Canvas.Pen.Width := 1;
Canvas.Pen.Color := clRed;
Canvas.Brush.Style := bsSolid;
Canvas.Brush.Color := clRed;
// Вычисляем координаты вершин звезды. Они помещаются
// в массив Star в следующем порядке (если первой
// считать верхнюю вершину и нумеровать остальные по
// часовой стрелке от нее): 1-3-5-2-4
for I := 0 to 4 do
begin
Star[I].X := Round(380 + 90 * Sin(0.8 * I * Pi));
Star[I].Y := Round(100 - 90 * Cos(0.8 * I * Pi));
end;
// Устанавливаем режим заливки WINDING. При
// использовании этого режима закрашивается все
// содержимое многоугольника независимо от того,
// как именно он нарисован.
SetPolyFillMode(Canvas.Handle, WINDING);
Canvas.Polygon(Star);
// Сдвигаем координаты звезды, чтобы нарисовать ее