16+
ComputerPrice
НА ГЛАВНУЮ СТАТЬИ НОВОСТИ О НАС




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


Версия для печати

Модуль поиска не установлен.

Траектории - свой путь в программировании графики для Windows

22.04.2003

Просто Строители <alexte@pisem.net>

Всё уже описано. К счастью, не обо всём ещё подумано.
Станислав Ежи Лец.

Как известно, вывод любого изображения на экран монитора в операционной системе Windows подразумевает, по крайней мере, два момента: во-первых, определение множества точек (в частном случае образующих кривые), а во-вторых, задание атрибутов контекста поверхности, на которую они будут отображаться. Для получения максимальной гибкости, любое приложение, ориентированное на графический вывод, должно обеспечивать их раздельную настройку.

Начиная с 32-битной версии Windows, Microsoft предлагает программистам для геометрического описания коллекций линий и кривых как единой сущности использовать новый объект - траектория (Path).

В общем случае, траекторией в GDI (Graphics Device Interface - интерфейс графических устройств) называется объект, состоящий из линий, дуг и кривых Безье. Поскольку векторные шрифты описываются прямыми линиями, а шрифты True Type - кривыми Безье, то и текст, выполненный при помощи этих двух типов шрифтов, также может входить в объект "траектория". Главное отличие траекторий от других объектов GDI (перо, кисть, регион, палитра...) состоит в том, что Win32 API (Application Programming Interface - интерфейс прикладного программирования) не содержит функций для их непосредственного создания. Отметим также, что стандартная поставка VCL для Delphi и Builder C++ не предлагает программистам для работы с этими объектами ни компонент, ни класса.

Итак, начнем с того, как же все-таки GDI позволяет создавать траектории?

Для этого необходимо заключить участок кода, рисующий линии, дуги, кривые Безье, между вызовами двух функций BeginPath и EndPath.

Функция BeginPath инициирует начало построения траектории в контексте устройства (далее под контекстом устройства будем понимать канву формы нашего приложения). Когда канва переводится в режим построения траектории, все функции, которые обычно могут быть использованы для ее построения, при своем вызове не рисуют непосредственно на канве, вместо этого они добавляются в объект "траектория", связанный с канвой где-то внутри GDI. Функция EndPath указывает канве, что процесс построения траектории завершен.

Таким образом, как и в случае с другими своими объектами, система позволяет сначала максимально подготовить их для вывода. Сам вывод на канву формы обеспечивают уже другие функции, часть из которых и будут рассмотрены в данной статье.

Для демонстрации примеров кодов, мы выбрали среду Borland C++ Builder, модификация их для сред Visual C или Delphi сводится лишь к синтаксическим правкам и не носит концептуального характера.

Поскольку мы договорились выводить изображение на канву формы, нам понадобится дескриптор (хэндл, манипулятор, указатель - это как кому нравится) канвы, который в качестве параметра входит в большинство функций из GDI API. Получить его можно через свойство Handle класса TCanvas. Таким образом, в тех функциях интерфейса прикладного программирования, где в качестве параметра требуется дескриптор контекста устройства, необходимо использовать следующую запись: Canvas->Handle.

Первый пример показывает, как осуществить при помощи траектории обводку текста контуром, напомним, что в качестве шрифта для текста следует выбирать шрифты из True Type.

Canvas->Brush->Style=bsClear;
Canvas->Pen->Color=clRed;
Canvas->Pen->Width=8;
Canvas->Font->Name="Vivian";
Canvas->Font->Size=48;
BeginPath(Canvas->Handle);
TextOut(Canvas->Handle,20,20,"Vivian", lstrlen("Vivian"));
EndPath(Canvas->Handle);
//Траектория создана.
//Теперь производим ее обводку с одновременным выводом на форму //приложения!
StrokePath(Canvas->Handle);

В данном примере мы использовали шрифт Vivian из стандартной поставки Windows Me.

Отметим важную особенность, присущую траекториям. После обводки ее функцией StrokePath и вывода на форму, она перестает существовать в контексте устройства канвы. Однако ничто не мешает нам создать новую траекторию и, задав другие параметры объекту "перо", опять вывести на канву формы. Например, изменим размер и цвет пера - результат на рис. 2.

Если требуется закрасить область, занимаемую траекторией, однородным цветом, то следует воспользоваться либо функцией FillPath, либо StrokeAndFillPath, которая к тому же произведет и ее обводку.

Попробуем далее разобраться, как осуществить раскраску букв, входящих в траекторию. Для этого будем использовать не просто однородный цвет кисти, а рисунок, загружаемый с жесткого диска компьютера, например, такой (рис. 3.).

Graphics::TBitmap *bitmap;
try
<{
bitmap=new Graphics::TBitmap;
bitmap->LoadFromFile("C:\\file_with_ image.bmp");
Canvas->Brush->Style=bsClear;
Canvas->Font->Name="Vivian";
Canvas->Font->Size=48;
int rwidth=bitm->Width;
int rheight=bitm->Height;
BeginPath(Canvas->Handle);
TextOut(Canvas->Handle,20,20,"Vivian",lstrlen( "Vivian"));
EndPath(Canvas->Handle);
SelectClipPath(Canvas->Handle, RGN_COPY);
BitBlt(Canvas->Handle,20,20,rwidth,rheight, bitm->Canvas->Handle,0,0,SRCCOPY);
}
__finally
{
delete bitmap;
}

Поясним представленный выше код. Сначала в память загружается картинка, при помощи которой будут раскрашиваться буквы, затем устанавливаются параметры канвы формы. Далее создается сама траектория, после чего вызывается функция SelectClipPath, производящая из траектории регион отсечения, в который и копируется наш рисунок функцией BitBlt.

Попробуйте реализовать подобный эффект стандартными средствами VCL или даже WinAPI (только без использования траектории)! Кстати, ничто не мешает нам снова создать траекторию и обвести ею текст.

Для более изощренных манипуляций над траекториями хотелось бы каким-то образом, вслед за ее созданием, получать полное описание последней. GDI предоставляет нам такую возможность через вызовы функции GetPath, которая преобразует внутреннее представление траектории в GDI в два массива: массив точек и массив флагов, при этом между этими двумя массивами существует однозначное соответствие. В таблице 1 перечислены символьные значения флагов и их описание.

Тип/Описание

PT_MOVETO/ Точка с таким флагом является началом отдельной фигуры.

PT_LINETO/ Предыдущая и текущая точки являются концами линии.

PT_BEZIERTO/ Соответствующая точка принадлежит к кривой Безье.

PT_CLOSEFIGURE/ Указывает на последнюю точку в фигуре. Пр исутствует в массиве флагов в виде комбинации с PT_LINETO или PT_BEZIERTO.

Поскольку заранее мы можем не знать размеры массивов, при использовании функции GetPath приходится прибегать к стандартному приему в Win32 API - двойному ее вызову. Первый вызов GetPath вернет количество точек, что позволит динамически создать два массива и передать их указатели в качестве параметров для повторного вызова этой же функции. Второе обращение к GetPath инициализирует массив точек и массив флагов.

Покажем, как, используя эти два массива, можно построить объект "траектория" вдоль произвольной линии, в нашем случае синусоиды.

BeginPath(Canvas->Handle);
TextOut(Canvas->Handle,20,40,"Vivian is a cool font!",lstrlen("Vivian is a cool font!"));
EndPath(Canvas->Handle);
int iSize=GetPath(Canvas->Handle,NULL,NULL,0);
POINT *points=new POINT[iSize];
BYTE *bFlags=new BYTE[iSize];
if (points!=NULL && bFlags!=NULL)
GetPath(Canvas->Handle,points,bFlags,iSize);
BeginPath(Canvas->Handle);
POINT temp[3];
for(int i=0;i<iSize;i++)
{
temp[0].x=points[i].x;
temp[0].y=points[i].y+(int)(cos(temp[0].x/50.0)*20);
temp[1].x=points[i+1].x;
temp[1].y=points[i+1].y+(int)(cos(temp[1].x/50.0)*20);
temp[2].x=points[i+2].x;
temp[2].y=points[i+2].y+(int)(cos(temp[2].x/50.0)*20);
switch(bFlags[i] & ~PT_CLOSEFIGURE)
{
case PT_MOVETO:
MoveToEx(Canvas->Handle,temp[0].x, temp[0].y,NULL);
break;
case PT_LINETO:
LineTo(Canvas->Handle,temp[0].x,temp[0].y);
break;
case PT_BEZIERTO:
PolyBezierTo(Canvas->Handle,temp,3);
i+=2;
break;
}
if(bFlags[i] & PT_CLOSEFIGURE)
CloseFigure(Canvas->Handle);
}
delete[] points;
delete[] bFlags;
EndPath(Canvas->Handle);
SelectClipPath(Canvas->Handle,RGN_COPY);

Данный код строит траекторию и создает регион отсечения. Для получения изображения, представленного на рис.5, осталось закрасить его какой-нибудь картинкой.

Единственной особенностью приведенного выше кода является анализ флагов точек траектории, в зависимости от результатов которого вызывается одна из трех функций: MoveToEx, LineTo, PolyBezieTo. Кстати, в VCL существуют их аналоги.

И в заключение. Если вам потребуется дополнительная информация по работе с траекториями или другими объектами в Windows, то ее можно почерпнуть, в том числе, и из следующих источников:

1. "Programming Windows" by Charles Petzold. Microsoft Press, 1998 - книга, давно ставшая классикой. При желании можно скачать из Интернета в формате CHM (HTML help).

2. "Программирование графики для Windows" Фень Юаня. "Питер", 2002 ("Windows graphics programming. Win32 GDI and DirectDraw" by Feng Yuan. Hewlett-Packard Company, 2001) - фундаментальный труд в этой области.

3. Недра MSDN.

Проработка этих материалов, безусловно, приведет вас к мысли, что написание своего аналога Photoshop v.0.01 или CorelDraw v.0.05 вопрос уже не ваших знаний, а времени, которого, впрочем, всегда не хватает.



статьи
статьи
 / 
новости
новости
 / 
контакты
контакты