Графические построения

Чтобы что-то нарисовать, художнику необходим холст, на который он наносит краски. Точно также для рисования на компьютере необходим объект-холст, на котором специальными командами строится изображение. Он так и называется – Canvas (англ. "холст"). Объект типа TCanvas входит в состав очень многих визуальных компонентов. Например, каждая форма имеет свой холст, на котором можно рисовать. Объект Canvas имеется также у таких компонентов, как Panel, Image, Grid и многих других. При этом в объектном типе TCanvas уже реализована масса методов для рисования самых различных изображений – велосипед изобретать не придется.

Главный компонент для работы с изображениями – Image. В него можно и загрузить статическую картинку (скажем, фон) из внешнего файла, и отрисовывать программным путем любые вещи – графики функций, геометрические фигуры, анимацию и пр. Данный компонент обеспечивает лишь плоскую (2D) графику. Для работы с 3D графикой обычно применяется встроенная в Windows библиотека OpenGL, рассмотрение которой выходит за пределы настоящего курса.

Самое простое применение компонента TImage – размещение статического изображения, загруженного из внешнего файла. Данный компонент поддерживает графические форматы ani, gif, bmp, wmf, emf, jpeg, ico. Кратко рассмотрим, чем они отличаются.

- формат ani – растровый анимированный курсор. Когда в Windows на экране вертятся песочные часы или скачет лошадка – это и есть отображение ani-файла. Правда, в компоненте TImage лошадка скакать не будет, а отобразится только первый кадр анимации;

- формат gif – растровый формат, широко используемый в Интернете. Недостаток – поддерживаются только 256 цветов. Достоинство – возможность задания прозрачных областей и возможность создания так называемых анимированных gif-файлов, состоящих из последовательности кадров (как и для ani-файлов, TImage будет отображать только первый кадр);

- формат bmp – ветеран, существует еще со времен операционной системы DOS. Это растровый формат с различными вариантами цветовой глубины (от черно-белой картинки до 16 млн. цветов) и с возможностью сжатия информации без потерь. Прозрачные области не поддерживаются;

- формат wmf (Windows metafile) – векторный формат, специально разработанный для Windows. Внутри это последовательность команд отрисовки изображения. Компактен, отлично подходит для вывода чертежей, схем, графиков. Формат wmf понимают практически все программы: Word, Visio, Corel, Компас, AutoCAD и многие другие;

- формат emf (enhanced metafile) – улучшенная версия wmf. Достоинства те же.

- формат jpeg (Joint Photographic Experts Group) – особый растровый формат. В нем реализовано сжатие изображения с потерями. За счет устранения мелких деталей изображение удается сжать в 100 и более раз, поэтому данный формат очень популярен в Интернете. Один из главных недостатков – отсутствие возможности создавать прозрачные области;

- формат ico – растровый формат хранения значков ("иконок") Windows фиксированного размера 16х16 или 32х32 пиксела.

Графический файл загружается в компонент Image при помощи его свойства Picture (в Инспекторе объектов открывается окошко просмотра картинки с кнопкой "Load"). При этом содержимое файла физически копируется и добавляется в exe – файл нашей программы. Поэтому нежелательно таким образом загружать многомегабайтные картинки: программа разрастается и начинает заметно тормозить.

Внешним видом загруженной картинки управляют свойства Center (центрирование изображения), Stretch (если True, то картинка автоматически масштабируется в размер компонента Image), AutoSize (противоположна Stretch: при AutoSize=True компонент Image принимает размер загруженной картинки). Если свойство Transparent=True, то при загрузке картинки с прозрачными областями (в форматах gif, wmf, emf) сквозь них будет виден фон. Свойство Proportional указывает, можно ли искажать пропорции загруженной картинки или нет. Наконец, свойство IncrementalDisplay включает режим постепенного вывода больших изображений. При этом картинка выводится на экран по частям, чтобы пользователь видел, что процесс идет, и программа не зависла.

Для самостоятельного рисования на объекте Image существует масса способов. Во-первых, у объекта Canvas, принадлежащего объекту Image, доступен массив пикселов, изменяя который, можно заняться "вышиванием бисером" – построением картинки по точкам. Давайте, изобразим звездное небо. Поместим на форму компоненты Image1 и Button1. Установим следующие значения свойств компонента Image1: Transparent=False; Stretch=False; AutoSize=False; Center=False и создадим следующий обработчик нажатия кнопки:

 

procedure TForm1.Button1Click(Sender: TObject);

 

var i:word;

 

begin

with Image1.Canvas do

begin

Brush.Color:=clBlack;

FillRect(Rect(0,0,Image1.Width,Image1.Height));

for i:=1 to 1000 do

Pixels[random(Image1.Width),random(Image1.Height)]:=

clWhite

end

end;

 

Рассмотрим приведенный фрагмент. Оператор WITH "выносит за скобки" ссылку на объект Image1.Canvas, чтобы не писать его каждый раз. Свойство Brush объекта Canvas отвечает за цвет фона, поэтому устанавливаем его в черный. Метод FillRect заполняет прямоугольник с указанными координатами только что заданным цветом фона. А вот координаты требуется задать через специальный тип данных TRect. Чтобы им воспользоваться, нужно применить стандартную функцию Rect(x1,y1,x2,y2), где x1, y1 и x2, y2 – координаты концов диагонали прямоугольника. В нашем случае мы хотим заполнить все поле, поэтому левый нижний угол прямоугольника лежит в точке (0,0), а правый верхний – в точке, координаты которой равны ширине и высоте всего компонента Image1. Далее в цикле из прямоугольного массива Pixels случайным образом выбирается элемент и ему присваивается новое значение цвета. После запуска при каждом нажатии на кнопку в окне компонента Image1 будет отображаться вполне реалистичное звездное небо (Рис. 2).

 

Рис. 2. Изображение звездного неба.

 

Вернемся к строчке Brush.Color:=clBlack. А еcли мы захотим сделать фон светлее, показав, скажем, предрассветное небо? В стандартном наборе цветов подходящего серо-голубого оттенка не предусмотрено. Как быть? Прежде всего, нужно подобрать требуемый цвет в любой программе, поддерживающей цветовую модель RGB. Можно воспользоваться Adobe Photoshop или CorelDRAW. Подобрав там нужный цвет, следует запомнить его составляющие. Приятный серо-голубой оттенок образуется при R=203, G=201, B=226. Изменим в программе всего одну строчку:

 

Brush.Color:=RGB(203,201,226);

 

Встроенная функция RGB преобразует интенсивности трех базовых цветов в тип данных TColor. Запустите программу – фон звездного неба изменится на серо-голубой, отсутствующий в стандартном наборе цветов Delphi.

Теперь нарисуем что-нибудь более сложное. Принцип рисования заключается в том, что картинка строится пером (pen), которое перемещается по холсту, проводя на нем линии. Кроме пера, у холста еще есть уже знакомая нам кисть (brush), которой можно закрашивать большие области. Наконец, имеются отдельные команды для построения отрезков, прямоугольников, эллипсов (окружность – частный случай эллипса). Давайте нарисуем изображение, показанное на Рис. 3.

 

 

Рис. 3. Пример изображения.

 

Надо понимать, что размер нашего поля рисования в пикселах – величина переменная. Пользователь может растянуть форму, поменять разрешение экрана и т.д. Поэтому ширину поля рисования нужно брать из свойства Image1.Width, а высоту – из свойства Image1.Height.

Как и обычно, процедуру рисования будем писать так, чтобы она вызывалась при нажатии на кнопку. Для этого поместим на форму кнопку "Рисовать". Процедура рисования будет выглядеть следующим образом:

procedure TForm1.Button1Click(Sender: TObject);

begin

WITH Image1.Canvas DO

BEGIN

Brush.Color:=clWhite;

Pen.Color:=clBlack;

FillRect(Image1.ClientRect);

MoveTo(0,Image1.Height DIV 2);

LineTo(Image1.Width,Image1.Height DIV 2);

MoveTo(Image1.Width DIV 2,0);

LineTo(Image1.Width DIV 2,Image1.Height);

Brush.Color:=clGreen;

Rectangle(Image1.Width DIV 2 - 50,

Image1.Height DIV 2 - 50,

Image1.Width DIV 2 + 50,

Image1.Height DIV 2 + 50);

Brush.Color:=clRed;

Ellipse(Image1.Width DIV 2 - 10,

Image1.Height DIV 2 - 10,

Image1.Width DIV 2 + 10,

Image1.Height DIV 2 + 10)

END

end;

Разберемся, что здесь написано. Команды Brush.Color и Pen.Color задают будущие цвета заливки и контура, соответственно. Команда FillRect, как мы уже знаем, рисует закрашенный прямоугольник. Здесь она применена для очистки всего поля рисования, поэтому в качестве прямоугольника задается тот прямоугольник, который занимает весь объект Image1. Он хранится в свойстве ClientRect. Проще говоря, команда FillRect(Image1.ClientRect)заливает весь холст цветом заливки.

Рисование отрезка от точки x1,y1 в точку x2,y2 выполняется пером в два этапа. Сначала перо командой MoveTo(x1,y1) позиционируется без проведения линии в начальную точку отрезка, а затем отрезок проводится командой LineTo(x2,y2). Координаты начал и концов отрезков показаны на Рис. 3, они легко вычисляются. Не забывайте, что координаты на экране могут быть только целыми, поэтому надо использовать операцию целочисленного деления DIV.

Команда Rectangle рисует прямоугольник с заданными цветами контура и заливки. Прямоугольник задается координатами концов любой его диагонали. В нашем случае длина стороны прямоугольника будет 20 пикселов и он строится симметрично относительно середины холста.

Команда Ellipse строит окружности и эллипсы. Параметры ее немного необычны – нужно указать координаты концов диагонали прямоугольника, в который будет вписан эллипс или окружность.

Порядок рисования фигур имеет значение. Если переставить местами команды Rectangle и Ellipse, то прямоугольник закроет собой окружность, и вы ее никогда не увидите.

В таблице представлены основные методы рисования и их параметры:

 

Метод Что делает Пример
Ellipse Рисует эллипс, вписанный в невидимый квадрат с заданными координатами верхнего левого угла и правого нижнего. Если координаты х и y углов будут совпадать, то получится круг. Canvas.Ellipse(0,0,50,50);
FillRect Рисует прямоугольник, закрашенный цветом текущей кисти (brush) Canvas.FillRect (Rect(0,0,100,100));
FloodFill Заполняет данную область цветом текущей кисти, до тех пор пока не будет достигнут край заданного цвета Canvas.FloodFill(10, 10, clBlack, fsBorder);
Rectangle Рисует прямоугольник (или квадрат), заполненный цветом текущей кисти и обрамлённый цветом текущего пера Canvas.Rectangle (Rect (20, 20, 50, 50));
RoundRect Тоже, что и Rectangle, но с загруглёнными углами. Canvas.RoundRect (20, 20, 50, 50, 3, 3);
TextOut Рисует данную текстовую строку, начиная с координат (x,y) - фон текста заполняется текущим цветом кисти. Canvas.TextOut (10, 10, 'Some text');

 

Определенную сложность вызывает построение дуги (Рис. 4). Метод Arc(X1, Y1, X2, Y2, X3, Y3, X4, Y4: Integer) строит дугу эллипса, вписанного в прямоугольник с координатами диагонали X1, Y1, X2, Y2. Дуга отрисовывается против часовой стрелки от точки пересечения эллипса с прямой (центр эллипса)-(x3,y3) до точки (центр эллипса)-(x4,y4).

 

Рис. 4. Построение дуги.

 

Помимо сплошной заливки, можно применять и другие ее виды (штриховка, точки и т.д.), меняя свойство Style объекта Brush (Рис. 5):

 

 

Рис. 5 Способы заливки.

 

Значение bsSolid соответствует сплошной заливке, bsClear – ее отсутствию, что позволяет рисовать незакрашенные фигуры.

Для линий свойство Pen.Style устанавливает тип линии:

 

psSolid сплошная линия
psDash пунктирная линия
psDot линия из точек
psDashDot штрих – пунктирная линия
psDashDotDot линия "тире – точка – точка"
psClear линия не рисуется (например, для построения объекта без контура)