2 Работа с графикой с использованием классов, свойств и функций компонент Borland C++ Builder

В начало

Основные понятия

В среде C++Builder существует три рода объектов, которые имеют отношение к графике:

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

  • Графика - представляет растровое изображение некоторого файла или ресурса (битового образа, пиктограммы или метафайла).

    C++Builder определяет производные от базового класса TGraphic объектные классы:

    • TBitmap,

    • Ticon,

    • TMetafile.

  • Рисунок (TPicture) представляет собой контейнер для графики, который может содержать любые классы графических объектов. Таким образом, контейнерный класс TPicture может содержать битовый образ, пиктограмму, метафайл или некоторый другой графический тип, определенный пользователем, а приложение может стандартно обращаться ко всем объектам контейнера посредством объекта TPicture.

Отметим, что графические объекты Windows взаимосвязаны. Так - объект TPicture всегда содержит некоторую графику, которой в свою очередь, может потребоваться для отображения канва, а единственный стандартный графический класс канвы - это TBitmap.

Как отмечалось выше, Borland С++ Builder инкапсулирует функции Windows GDI на разных уровнях. Наиболее завершенным является интерфейс, предоставляемый свойством Canvas (канва), объектного класса канвы, его графических компонент. Использование канвы снимает с программиста заботу при выводе изображений об инициализации контекста устройства и его освобождении. Наличия вложенных свойств (характеристик пера, кисти, шрифтов, растровых изображений) также не требует слежения за состояниями ресурсов - основная задача - это определение характеристик для этих графических объектов и грамотное их использование. Речь об этом в следующем параграфе.

В начало

Объектный класс канвы

Инкапсулированные и перегруженные функции GDI и WinApi объектного класса канвы многие авторы относят к трем различным уровням. В этой условной классификации функции высокого уровня обеспечивают возможность рисования линий, фигур и текста. Определение свойств и методов манипулирования графическими примитивами канвы отнесены к среднему уровню. Нижний уровень обеспечивается доступ к самим функциям Windows GDI. Классификация не бесспорна, но она позволяет ориентироваться в достаточно большом колличестве свойств и методов канвы и, поэтому, приведем эту классификацию.
Уровень Метод (Функция) Свойства Действие
   Высокий  MoveTo  PenPos  Определяет текущую позицию пера
 LineTo  PenPos  Рисует прямую до заданной точки
 Rectangle    Рисует прямоугольник
 Ellipse    Рисует эллипс
 Arc    Рисует дугу
 Polyline    Рисует ломаную линию
 PolyBezier    Рисует кривую Блейзера
 Chord    Рисует сектор
 DrawFocusRect    Рисует прямоугольник
 FrameRect    Выводит рамку вокруг прямоугольника
 Pie    Выводит сектор круга
 TextOut    Выводит текстовую строку
 TextHeight    Задает высоту текстовой строки
 TextWidth    Задает ширину для вывода текстовой строки
 TextRect    Вывод текста внутри прямоугольника
 FillRect    Заливка указанного прямоугольника цветом и текстурой текущей кисти
 FloodFill    Заливка области канвы (произвольной формы) заданным цветом
   Средний    Pen  Используется для установки цвета, стиля, ширины и режима пера
   Brush  Используется для установки цвета и текстуры при заливке графических фигур и фона канвы.
   Font  Используется для установки шрифта заданного цвета, размера и стиля
   Pixels  Используется для чтение и записи цвета заданного пикселя канвы
 CopyRect  CopyMode  Копирует прямоугольную область канвы в режиме CopyMode
 BrushCopy    Копирует прямоугольную область канвы с заменой цвета
 Draw    Рисует битовый образ, пиктограмму, метафайл в заданном месте канвы
 StretchDraw    Рисует битовый образ, пиктограмму или метафайл так, чтобы целиком заполнить заданный прямоугольник
   Низкий    Handle  Используется как параметр при вызове функций Windows GDI

В начало

Примеры использования функций канвы для рисования приметивов

Основы рисования примитивов рассмотрены выше при рассмотрении функций GDI, здесь, на примерах, не повторяя уже описанные ранее приемы вывода примитивов, показаны лишь некоторые особенности их отображения с использованием функций канвы.

Прстейший пример относится к рисованию линий, кроме того показано как можно задавать параметры пера:

void __fastcall
TForm1::Button1Click(TObject *Sender)
{
 //Задаем цвет пера
 Canvas->Pen->Color=(TColor)RGB(255,0,0);
 //Задаем ширину  пера
 Canvas->Pen->Width=5;
 //Можно переместить перо в исходную точку так
 Canvas->MoveTo(100,200);
 //Или тпереместить перо так
 TPoint tPoint;
 tPoint.x=100;
 tPoint.y=200;
 Canvas->PenPos=tPoint;
 //И рисуем линию от исходной точки 100,200 до конкечной 0,50
 Canvas->LineTo(0,50);
 //Освобождать и восстанавливать ничего не надо
 //однако канва запоминает установленные параметры
}

Пример рисования дуги и секторов:

void __fastcall
TForm1::Button1Click(TObject *Sender)
{
 //Стиль кисти
 Canvas->Brush->Style=bsHorizontal;
 //Цвет кисти
 Canvas->Pen->Color = clBlue;
 //Рисуем дугу
 Canvas->Arc(0,0,500,500,250,0,50,0);
 //Рисуем сектор изменяя стиль взаимодействия цвета пера и холста
 Canvas->Pen->Mode=pmWhite;
 Canvas->Chord(0,0,250,250,250,125,0,0);
 //Освобождать и восстанавливать ничего не надо 
 //Но помним, что канва запомнила установленные параметры
}

Пример рисования ломаных линий (перо, при желании, можно задать как и ранее):

void __fastcall 
TForm1::Button1Click(TObject *Sender)
{
 TPoint tPoints[6];
 Canvas->Pen->Color = clRed;
  Canvas->Pen->Width=3;
 tPoints[0].x = 40;
 tPoints[0].y = 10;
 tPoints[1].x = 20;
 tPoints[1].y = 60;
 tPoints[2].x = 70;
 tPoints[2].y = 30;
 tPoints[3].x = 10;
 tPoints[3].y = 30;
 tPoints[4].x = 60;
 tPoints[4].y = 60;
 tPoints[5].x = 40;
 tPoints[5].y = 10;
 Canvas->Polyline(tPoints,5);
}

Пример рисования кривых Блейзера:

void __fastcall 
TForm1::Button1Click(TObject *Sender)
{ 
 TPoint tPoints[7];
 tPoints[0]=TPoint(0,0);
 tPoints[1]=TPoint(800,30);
 tPoints[2]=TPoint(0,40);
 tPoints[3]=TPoint(550,400);
 tPoints[4]=TPoint(350,200);
 tPoints[5]=TPoint(550,400);
 tPoints[6]=TPoint(0,500);
 Canvas->PolyBezier(tPoints,6);
}

Пример отображения прямоугольников и эллипсов различными способами и использования кистей:

void __fastcall
TForm1::Button1Click(TObject *Sender)
{
 //Задаем цвет пера
 Canvas->Pen->Color=(TColor)RGB(0,255,0);
 //Задаем щирину  пера
 Canvas->Pen->Width=1;
 //Стиль пера - пунктир
 Canvas->Pen->Style=psDot;
 //Стиль вывода замкнутой фигуры, зависяший от цвета пера
 //и канвы ( заменяет своими возможностями) функцию SetBkMode()
 //Эдесь прозрачный фон
 Canvas->Pen->Mode=pmCopy;
 //Рисуем прямоугольник по точкам
 Canvas->Rectangle(0,0,100,100);
 //Здесь, аналог GDI непрозрачного фона
 Canvas->Pen->Mode=pmWhite;
 //Рисуем элипс вписанный в прямоугольник
 Canvas->Ellipse(250,250,350,550);
 //Создаем перо функцией CreatePen() 1 - толщина пера
 HPEN hPen=CreatePen(PS_DASHDOTDOT,1,RGB(255,0,0));
 //Устанавливаем это перо как текущее
 Canvas->Pen->Handle=hPen;
 //Изменяем стиль вывода
 Canvas->Pen->Mode=pmCopy;
 //Стиль кисти - вертикальная штриховка
 Canvas->Brush->Style=bsVertical;
 //Можно переопределить прозрачность и так
 SetBkMode(Canvas->Handle,OPAQUE);
 //Рисуем прямоугольник с закругленными краями
 Canvas->RoundRect(100,100,200,200,50,50);
 //Координаты можно задать и так
 TRect tRect;        //Координаты точек
 tRect.Left=100;     //Левыя
 tRect.Right=500;    //Правая
 tRect.Top=250;      //Верхняя
 tRect.Bottom=450;   //Нижняя
 //Используем кисть для закрашивания объекта
 Canvas->Brush->Color=(TColor)RGB(0,0,255);
 Canvas->Rectangle(tRect);
 Canvas->Brush->Color=(TColor)RGB(0,255,255);
 //Стиль кисти
 Canvas->Brush->Style=bsCross;
 //Или по  координатам
 Canvas->RoundRect(100,300,250,450,50,50);
}

Использование кисти для заливки фигур:

void __fastcall 
TForm1::Button1Click(TObject *Sender)
{
 Canvas->Brush->Color=(TColor)RGB(0,255,255);
 TRect tRect(0,0,100,100);
 Canvas->FillRect(tRect);
 //Рисуем прямоугольник по точкам
 Canvas->Rectangle(0,0,100,100);
 //Освобождать и восстанавливать ничего не надо
}

И интересный эффект заполнения канвы цветом кисти:

void __fastcall
TForm1::Button1Click(TObject *Sender)
{
 //Параметр fsBorder -  заполнить всю область 
 //цветом кисти, до края канвы
 Canvas->Brush->Color=(TColor)RGB(255,0,255);
 //Исходная точка в центре канвы, в данном случае 
 //окна приложения
 Canvas->FloodFill(Width/2,Height/2, NULL, fsBorder);
 //Меняем цвет и повторяем
 Canvas->Brush->Color=(TColor)RGB(255,0,0);
 Canvas->FloodFill(Width/2,Height/2,NULL, fsBorder);
}

Рисование рамки вокруг прямоугольника:

void __fastcall 
TForm1::Button1Click(TObject *Sender)
{
 Canvas->Brush->Color=(TColor)RGB(125,0,255);
 TRect tRect;          //Координаты точек
 tRect.Left=100;       //Левыя
 tRect.Right=500;      //Правая
 tRect.Top=250;        //Верхняя
 tRect.Bottom=450;     //Нижняя
 Canvas->FrameRect(tRect);
}

Из примеров видно, что (с учетом некоторой модификации параметров функций) рисование графических примитивов аналогично интерфейсу GDI. Однако, так как не требуется следить за состоянием контекста и его графических объектов, написание кода значительно упрощается.

В начало

Вывод текста на канву

Вывод текста на канву предельно прост. Требуется только задать характеристики шрифта (свойство Font канвы), текст в формате AnsiString и использовать метод TextOutA() канвы. Следующий пример показывает как это делается и, также, демонстрирует то, что канва сохраняет заданные свойства - нажатие кнопки Button2, в обработчике нажатия которой изменен лишь цвет, не изменяет остальных свойств шрифта, заданных в обработчике нажатия кнопки Button1.

void __fastcall
TForm1::Button1Click(TObject *Sender)
{
 AnsiString vasS="Пример текста";
 //Цвет текста
 Canvas->Font->Color=clRed;
 //Размер шрифта в точках
 Canvas->Font->Size=20;
 //Стиль шрифта
 TFontStyles tFontStyle;
 //Зачеркнутый, наклонный, жирный, подчепкнутый
 tFontStyle << fsStrikeOut << fsItalic << fsBold << fsUnderline;
 Canvas->Font->Style =tFontStyle;
 //Имя шрифта
 Canvas->Font->Name="Times";
 //Вывод текста
 Canvas->TextOutA(10,10,vasS);
}
void __fastcall TForm1::Button2Click(TObject *Sender)
{
 AnsiString vasS="Пример текста";
 Canvas->Font->Color=clBlue;
 Canvas->TextOutA(50,50,vasS);
}

graphics_2.jpg

Рис 1. Пример работы со шрифтами.

Полезные функции для работы с текстом:

  • TextWidth(AnsiString vasS); - возвращает ширину текста в пикселях, необходимую для отображения строки vasS заданным шрифтом канвы;

  • TextHeight(AnsiString vasS); - возвращает высоту текста в пикселях, необходимую для отображения строки vasS заданным шрифтом канвы.

    Эти функции полезны когда надо быть уверенным, что при езменении размера шрифта текст не выдет за грани компонента.

    В следующем примере показано изменение ширины компонента TListBox, при выводе на его канву текста у которого заранее изменен шпифт. В обработчик нажатия кнопки Button1 поместить функцию TextWidth() и за ней функцию TextOutA() нельзя, так как перирисовка компонента происходит после завершения обработчика и часть текста будет утеряна. В примере, кроме того, показано задание цвета в виде 16ти ричного числа и высоты шрифта в пикселях.

    void __fastcall
    TForm1::Button1Click(TObject *Sender)
    {
     //Цвет текста
     ListBox1->Canvas->Font->Color=(TColor)0x00FF7D7D;
     //Высота текста в пикселях
     ListBox1->Canvas->Font->Height=25;
     AnsiString vasS="Пример текста";
     ListBox1->Width=ListBox1->Canvas->TextWidth(vasS)+20;
    }
    void __fastcall 
    TForm1::Button3Click(TObject *Sender)
    {
     AnsiString vasS="Пример текста";
     ListBox1->Canvas->TextOutA(2,10,vasS);
    }
    

    Каждый шрифт поддерживает один или больше наборов символов, которые и определяют их написание. Следующий пример хотя и выводит абракадабру первые две строки, но показывает как, используя свойство Charset, сменить набор символов шрифта установленный по умолчанию. Кроме того можно отметить разницу набора символов RUSSIAN_CHARSET и DEFAULT_CHARSET - как правило они разные. Пятая функция показывает использование еще одного свойства - Pitch. Символы в шрифтах с переменным шагом отличаются по ширине (значения fpDefault, fpVariable), но если установить значение свойства равным fpFixed, ширина символов будет одинаковой.

    AnsiString vasS="Пример текста";
    ListBox1->Canvas->Font->Name="ArialBlack";
    ListBox1->Canvas->Font->Charset = TURKISH_CHARSET;
    ListBox1->Canvas->TextOutA(2,10,vasS);
    ListBox1->Canvas->Font->Charset = SYMBOL_CHARSET;
    ListBox1->Canvas->TextOutA(2,30,vasS);
    ListBox1->Canvas->Font->Charset = RUSSIAN_CHARSET;
    ListBox1->Canvas->TextOutA(2,50,vasS);
    ListBox1->Canvas->Font->Charset =  DEFAULT_CHARSET;
    ListBox1->Canvas->TextOutA(2,70,vasS);
    ListBox1->Canvas->Font->Pitch=fpFixed;
    ListBox1->Canvas->TextOutA(2,90,vasS);
    

    Следущее свойство шрифта PixelsPerInch относится к отображению текста и графики при печати, о чем речь будет вестись отдельно, но кратко рассмотрено здесь, чтобы позже при возврате к свойствам шрифтов вновь не повторяться. Принтер, о чем уже говорилось, также имеет канву. Вывод на печать в Borland C++ Builder - это вывод на канву принтера. При смене принтера, размер шрифтов может отмасштабироваться не правильно. Чтобы сделать правильное масштабирование, необходимо установить свойство PixelsPerInch шрифта. Свойство PixelsPerInch определяет количество пикселей в 1 дюйме. Для его установки можно воспользоваться следующим кодом:

    TPrinter *ptPrint = Printer();
    ptPrint->Canvas->Font->PixelsPerInch=
       GetDeviceCaps(ptPrint->Canvas->Handle,LOGPIXELSY);
    

    Отметим также, что свойство PixelsPerInch имеют не только шрифты, но и, например Экран (Screen) и может быть исрользовано при установке фиксированных размеров компонент (1 сантиметр равен 1 * (Screen->PixelsPerInch/2.54, 1 дюйм = 25.4 мм).

    И песледнее - шрифт также имеет свойство Handle, которое может быть использовано для быстрого восстановления характеристик шрифта канвы по умолчанию (за исключением цвета).

    Далее рассмотрим работу с изображения в Borland C++ Builder.

    В начало

    Продолжение. Работа с графикой и рисунками средствами Borland C++ Builder

    В начало главы

    В начало раздела

    Домой