КАСАТКИН К.А.
ПРОЦЕДУРЫ И ФУНКЦИИ МОДУЛЯ GRAPH


1. Графический режим

Видеосистема компьютера состоит из трех основных частей: видеопамяти, видеоадаптера и монитора.

Видеопамять -- это область памяти, хранящая графические данные.

Видеоадаптер -- устройство, управляющее выводом видеоинформации на экран монитора.

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

Графический режим определяет количество точек по горизонтали и вертикали, а также количеством цветов в палитре.

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

Взаимодействие программы и видеосистемы в графических режимах обеспечивают драйверы -- они управляют работой видеоадаптера. В драйверах применяется так называемый графический интерфейс фирмы Borland -- Borland Graphics Interface (BGI). Драйверы в составе Borland Pascal собраны в файлах, имеющих расширение BGI: CGA.BGI, EGAVGA.BGI, HERC.BGI, IBM8514.BGI, ATT.BGI, PC3270.BGI и др. Каждый из этих драйверов обеспечивает работу в нескольких графических режимах. Поддерживаемые графические режимы группируются в файлах *.BGI по принципу схожести организации видеопамяти.


Задание. Найдите в системе помощи Паскаль таблицу констант, задающих различные графические режимы. Перепишите таблицу в свою тетрадь.


Для того, чтобы использовать графический режим работы дисплея в интегрированной среде программирования Borland Pascal, необходимо подключить библиотеку графических функций и процедур Graph:


uses Graph, <другие используемые модули>;

Процедура InitGraph(var GraphDriver, GraphMode : integer; Path : string); переключает компьютер в графический режим, загружает в память компьютера соответствующий BGI-драйвер; параметры GraphDriver и GraphMode определяют графический режим. Константы, задающие конкретный графический режим с присущими ему разрешением и количеством цветов в палитре, представлены в выписанной вами таблице.

Проще всего задать автоматический выбор необходимого графического драйвера и режима. Чтобы графический режим определялся автоматически, номер графического режима GraphDriver, передаваемый в процедуру InitGraph должен быть равен нулю или константе Detect = 0. Например, для SVGA монитора при обращении к процедуре InitGraph с параметром Detect установится режим VGA с высоким разрешением $640 \times 480$ и с 16 цветами в палитре. Для прямого задания данного режима применяются константы VGA = 9 и VGAHi = 2.

Параметр Path по типу string задает каталог на диске, в котором находится используемый графический драйвер. Для EGA, VGA и SVGA мониторов это обычно драйвер EGAVGA.BGI. В нашем случае этот драйвер находится на диске C:, в подкаталоге BGI каталога BP, значит, в качестве параметра Path следует использовать строку 'C:\BP\BGI'.

Если неправильно задать путь к драйверу, на этапе компиляции программы это не обнаруживается, ошибка произойдет при выполнении программы. Драйвер *.BGI, используемый программой, при компиляции не включается в состав исполняемого файла, поэтому для исполнения программы на других компьютерах может понадобится новое указание пути к драйверу графического режима. Если драйвер находится в рабочем каталоге, путь к нему задается пустой строкой Path=''.

Функция GraphResult возвращает код возможной ошибки при инициализации графики. Если ошибки нет, то результат функции равен нулю. Этот результат сравнивается с константой grOk, равной нулю.

Функция GraphErrorMsg возвращает строку сообщения о типе ошибки в виде целого числа -- кода ошибки, определенного функцией GraphResult.

Процедура CloseGraph выгружает графический драйвер из памяти и восстанавливает текстовый режим работы видеоадаптера. Для выхода из графического режима без выгрузки драйвера используется процедура RestoreCrtMode, а для последующего возвращения обратно -- SetGraphMode.

Пример программы, использующей графический режим
uses Graph;                                  {подключен модуль Graph}

procedure OpenGraph;                         {инициализация режима графики}
var
  Gd   : Integer;                            {графический драйвер}
  Gm   : Integer;                            {графический режим}
begin
  Gd := Detect;                              {режим автоопределения}
  InitGraph(Gd, Gm, 'C:\BP\BGI');            {включаем графичеческий режима}
  if GraphResult <> grOk then Halt;          {если не удалось,}
                                             {то происходит остановка программы}
end;

Begin
  OpenGraph;                                 {вызов процедуры инициализации графики}

 
  {----  блок графических процедур и функций ----}

  Line(0, 0, GetMaxX, GetMaxY);
  Readln;                                    {задержка до нажатия на Enter         }
  CloseGraph;                                {закрытие графического режима         }
End.




2. Экран

Особенность системы координат экрана в графическом режиме состоит в том, что верхний левый угол имеет координаты (0, 0), ось X направлена слева направо, а ось Y направлена сверху вниз. Нижний правый угол имеет максимальные значения, определяемые выбранным графическим режимом.

Функции GetMaxX и GetMaxY возвращают максимальные значения X и Y для установленного режима. Например, (GetMaxX div 2, GetMaxY div 2) -- координаты средней точки экрана.

Процедура SetViewPort(x1,y1,x2,y2,:integer;Clip:boolen) устанавливает прямоугольную активную область экрана для вывода изображения. Пары параметров (x1,y1) и (x2, y2) - координаты соответственно верхнего левого и нижнего правого углов визуального порта. Переменная логического типа Clip определяет, выводить ли изображение, если оно выходит за границы заданного прямоугольника. Если Clip=True, изображение за границами области обрезается; если Clip=False, изображение выводится и за указанными границами.

Установка графического окна с параметром Clip=False можно использовать для переноса начала координат в некоторую точку экрана, при этом точки, расположенные левее или выше начала координат, будут иметь отрицательные координаты.

Процедура ClearViewPort стирает все изображения в области текущего визуального порта. Чтобы вернуться к работе с полным экраном, вызывается процедура SetViewPort(0,0,GetMaxX,GetMaxY,True).


3. Графический указатель

В графическом режиме действует понятие указатель, подобное понятию курсора в текстовом режиме. Некоторые процедуры выводят графические образы на экран с привязкой к координатам указателя, другие работают с абсолютными координатами. Указатель, в отличие от курсора, невидим.

Процедура MoveTo(X,Y:integer) перемещает указатель в новую точку (X,Y).

Процедура MoveRel(DeltaX,DeltaY:integer) перемещает указатель в точку, отстоящую от текущей позиции курсора на DeltaX по горизонтали и на DeltaY по вертикали.

При использовании процедур, выводящих линии и текст, начиная с текущей позиции указателя, последний изменяет свои координаты, перемещаясь как перо.

Функции GetX : integer и GetY : integer возвращают текущее положение указателя.

Процедура ClearDevice очищает графический экран и устанавливает указатель позиции в положение (0, 0).



4. Установка цвета

Драйвер EGAVGA.BGI позволяет использовать 16 цветов, каждому из которых присвоен код и константа.


КОНСТАНТЫ ЦВЕТОВ
Низкой яркости Высокой яркости
0 Black черный 8 DarkGray темно-серый
1 Blue синий 9 LightBlue голубой
2 Green зеленый 10 LightGreen светло-зеленый
3 Cyan циан 11 LightCyan светлый циан
4 Red красный 12 LightRed ярко-красный
5 Magenta малиновый 13 LightMagenta светло-малиновый
6 Brown коричневый 14 Yellow желтый
7 LightGray светло-серый 15 White белый

Процедура SetColor(Color:word) задает цвет выводимых в графическом режиме на экран линий, фигур и символов. Параметр Color может принимать значения целого типа от 0 до 15 или имя одной из соответствующих констант. По умолчанию используется белый цвет. Установка цвета действует на те линии и тексты, которые выводятся на экран после ее вызова. Но не меняет цвет линий, выведенных на экран до ее вызова.

Процедура SetBkColor(Color:word) устанавливает цвет фона для всего экрана. По умолчанию используется черный цвет фона.



5. Рисование точки, линии и линейных фигур

Процедура PutPixel(X,Y:integer;Color:word) выводит на экран точку с заданными координатами X, Y и цветом Color. Пример 5.1. В верхнюю левую область экрана размером $100 \times 100$ пикселей случайным образом выводятся точки. Цвет каждой точки выбирается также случайно. Имитация звездного неба.
procedure PlotStars;

var 
  Color : 0..15;                               {объявлена переменная для цвета     }

begin
  Randomize;                                   {запускаем генератор случайных чисел}
  repeat                                       {выводим точку цвета Color          }
                                               {в случайно выбранную позицию       } 
    Color := Random(16);
    PutPixel(Random(100), Random(100), Color);
    Delay(100);                                {задержка 100 милисекунд            }
  until KeyPressed;
  ReadLn;
end;
Пример 5.2. С помощью процедуры вывода точки построить на экране график косинуса.
procedure PlotCosDots;{график косинуса}

const
  Color = LightGreen;  {цвет точек}
  MaxX  = 639;         {граница по оси X}
  MaxY  = 479;         {граница по оси Y}
  A     = 100;         {амплитуда}
  Freq  = 4;           {частота}
  dy    = MaxY div 2;  {смещение графика по оси Y}

var
  x, y : integer;

begin
  for x:=0 to MaxX do
    begin
      y := Round(A * Cos(Freq*x*Pi/180));
      PutPixel(x, dy - y, Color);
    end;
end;

Процедура Line(x1,y1x2,y2:integer) строит отрезок от точки (x1,y1) до точки (x2,y2). Цвет отрезка задается с помощью процедуры SetColor.

Пример 5.3. В область экрана размером Width$\times$Height пикселей случайным образом выводятся линии. Цвет каждой линии выбирается также случайно.

procedure DrawLines(Left, Top, Width, Height : integer);
{   Left, Top - левый верхний угол прямоугольной  области;
    Width  - ширина прямоугольной  области;
    Height - высота прямоугольной  области;
}
var
  Color : byte;

begin
  Randomize;
  repeat
    Color := Random(15) + 1;
    SetColor(Color);
    Line(Random(Width) + Left, Random(Height) + Top,
         Random(Width) + Left, Random(Height) + Top);
  until KeyPressed;
end;

Пример 5.4. Вывести на экран оси координат: ось X -- по середине, а ось Y -- в крайнюю левую позицию.

procedure PutAxes(c : 0..15); {координатные оси}
{Построение осей координат цветом c}
begin
  SetColor(c);
  Line(0, GetMaxY div 2, GetMaxX, GetMaxY div 2); {ось X}
  Line(0, 0, 0, GetMaxY);                         {ось Y}
end;

Пример 5.5. С помощью процедуры рисования линии Line построить на экране график косинуса.

procedure PlotCosLines;{график косинуса}

const
  Color = LightGreen;  {цвет линии}
  MaxX  = 639;         {граница по оси X}
  MaxY  = 479;         {граница по оси Y}
  A     = 100;         {амплитуда}
  Freq  = 4;           {частота}
  dy    = MaxY div 2;  {смещение графика по оси Y}

var
  x, y, xPred, yPred : integer;
                        {xPred и yPred хранят
                         предыдущие точки графика}
begin
  xPred:=0;
  yPred:=Round(A * Cos(Freq * xPred * Pi/180));
  SetColor(Color);
  for x:=0 to MaxX do
    begin
      y := Round(A * Cos(Freq * x * Pi/180));
      Line(xPred, dy - yPred, x, dy - y);
      xPred:=x;
      yPred:=y;
    end;
end;

Процедура LineTo(x,y:integer) -- чертит линию от текущего положения указателя до точки (x,y), перемещая в нее указатель. Рекомендуется применять для вычерчивания ломаных линий по заданным точкам, как правило "не отрывая пера". Рассмотрим предыдущий пример с использованием процедуры LineTo) вместо процедуры Line. В первом варианте необходимо сохранять координаты предыдущей точки графика xPred и yPred, которые по существу заменяют текущий указатель. Преимущества процедуры LineTo) очевидны.

Пример 5.6. С помощью процедуры рисования линии LineTo построить на экране график косинуса.

procedure PlotCos_LineTo;{график косинуса}

const
  Color  = LightGreen;          {цвет линии                  }
  MaxX   = 639;                 {граница по оси X            }
  MaxY   = 479;                 {граница по оси Y            }
  A      = 100;                 {амплитуда                   }
  Freq   = 4;                   {частота                     }
  dy     = MaxY div 2;          {смещение графика по оси Y   }
  StartX = 0;                   {начальное значение аргумента}
var
  x, y : integer;

begin                   {графический указатель
                         устанавливается в начальную точку}
  MoveTo(StartX, dy - Round(A * Cos(Freq * StartX * Pi/180)));
  SetColor(Color);
  for x:=0 to MaxX do
    begin
      y := Round(A * Cos(Freq * x * Pi/180));
      LineTo(x, dy - y);
    end;
end;

Процедура LineRel(DeltaX,DeltaY:integer) чертит линию от текущего положения курсора до точки, координаты которой отличаются от координат начала линии на (DeltaX,DeltaY), указатель перемещается в последнюю точку. Целесообразно применять в том случае, когда определяется относительное перемещение некоторой точки, такого рода задачи часто возникают при моделировании различных физических процессов и отслеживании траекторий в некоторых произвольных пространствах.

Задание. Напишите программу, реализующую упрощенную визуальную модель броуновского движения. Используйте процедуру LineRel.

Процедура Rectangle(x1,y1x2,y2:integer) строит прямоугольник, один из углов которого задан координатами (x1,y1), а противоположный координатами (x2,y2).

Пример 5.7. На экран выводится прямоугольник, левый верхний и правый нижний углы которого определяются с помощью функции Random.

Procedure SetRectangleRandom;
{случайным образом строит прямоугольники}
var  X1, Y1, X2, Y2 : integer;

begin
  Randomize;
  repeat
    X1 := Random(GetMaxX);
    Y1 := Random(GetMaxY);
    X2 := Random(GetMaxX - X1) + X1;
    Y2 := Random(GetMaxY - Y1) + Y1;
    Rectangle(X1, Y1, X2, Y2);
    Delay(13000);                     {задержка на 13000 мс}
    ClearDevice;                      {очистка экрана}
  until KeyPressed;
end;

Процедура Circle(X,Y:integer;R:word) строит на экране окружность с центром в точке (X,Y) и радиусом R. Радиус определяет число пикселов в направлении оси X, при этом число пикселов в направлении оси Y определяется учетом графического режима так, чтобы линейный размер по осям был одинаков.

Пример 5.8. Следующая процедура рисует на экране концентрические окружности.

procedure DrawCircles;

const
  MinR  = 1;   {радиус первой окружности}
  MaxR  = 5;   {радиус последней окружности}
  XO    = 320; {координаты общего}
  YO    = 240; {центра окружностей}
  Scale = 30;  {коэффициент увеличения}
var
  Radius: Integer;

begin
  for Radius := MinR to MaxR do
    Circle(XO, YO, Radius * Scale);
end;

Процедура Arc(X,Y:integer;StartAngle,EndAngle:word; R:word) чертит дугу окружности с центром (X,Y), радиусом R; StartAngle и EndAngle -- начальный и конечный углы окружности, заданные в градусах. Дуга чертится, начиная от StartAngle до EndAngle, в направлении против часовой стрелки.

Процедура
Ellipse(X,Y:integer;StartAngle,EndAngle:word;Rx,Ry:word)
строит эллипс или его дугу, (X,Y) -- центр, StartAngle и EndAngle -- начальный и конечный углы, которые определяются так же, как в процедуре Arc. Rx и Ry -- полуоси эллипса в направлении осей X и Y, выраженные в пикселах.

DrawPoly(NumPoints:word;varPolyPoints); -- вычерчивает ломаную линию, заданную массивом точек. Параметр NumPoints определяет число точек линии; переменная PolyPoints является массивом точек, то есть массивом объектов

PointType = record
              x, y : integer;
            end;

Например, для вычерчивания замкнутого треугольника должна быть объявлена переменная Triangle : array [1..4] of PointType и определены координаты углов треугольника: Triangle[1].x := ... и т.д. Чтобы построить треугольник, надо вызвать процедуру DrawPoly (4,Triangle) -- четвертая точка совпадает с первой.

Пример 5.9. Процедура, выводящая на экран график косинуса с помощью процедуры DrawPoly.

procedure DrawPoly_Cos;   {график косинуса}

const
  Color = LightGreen;  {цвет графика}
  A     = 100;         {амплитуда}
  Freq  = 4;           {частота}
  Pi    = 3.14151828;
  Pi2   = 2 * Pi;
  Scal  = 100;         {масштаб}
  Pi001 = Pi/Scal;
  N     = 200;         {число точек графика}

var
  t        : real;                           {аргумент функции}
  my2, i   : integer;
  Cos_Func : array  [1..N + 1] of PointType; {массив точек}

begin
  my2 := GetMaxY div 2;    {середина экрана по оси Y}
  t := 0.0;                {задание стартовых значений}
  i := 0;                  {счетчик точек в Cos_Func}
  SetColor(Color);
  repeat                   {цикл заполнения Cos_Func}
    inc(i);
    Cos_Func[i].X := Round(Scal * t);
    Cos_Func[i].Y := -Round(A * Cos(Freq * t)) + my2;
    t := t + Pi001;
    DrawPoly(i, Cos_Func); {рисование графика косинуса}
  until t > Pi2;
end;

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


DrawPoly(20, Cos_Func[100])

Такой вызов процедуры выведет ломаную по точкам с номерами 100, 101, ..., 119.



Стиль линии
Линия характеризуется:

Установка типа линии выполняется процедурой
SetLineStyle(LineStyle:word;Pattern:word;Width:word) Для первого параметра LineStyle предусмотрено пять вариантов стилей линии, которым соответствуют числа от 0 до 4 или константы:


Для последнего параметра Width предусмотрено два типа толщины линии:

Для всех LineStyle, кроме UserBitLn, второй параметр не имеет значения и может быть равным 0. При задании LineStyle = UserBitLn, можно задать любой периодический рисунок штриховки линии с периодом 16 точек. Шаблон штриховки определяется двоичным кодом переменной Pattern. Например, Pattern:=$F6F6 или Pattern:=63222, что соответствует
Pattern=1111011011110110 в двоичном коде.

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



Упражнения
5.1. Напишите процедуру, выводящую на экран тетрадный лист в клетку. Предусмотрите возможность изменения размеров клеток.
5.2. Напишите процедуру, выводящую на экран график тангенса.
5.3. Напишите процедуру, выводящую на экран астроиду, задаваемую уравнениями $x={cos^3}(t), y={sin^3}(t)$.
5.4. Напишите процедуру, выводящую на экран график в полярных координатах $\rho=1-cos \varphi$.
5.5. Напишите процедуру, выводящую на экран изображение конуса, невидимые линии обозначить пунктиром.


6. Закрашенные фигуры

Тип закраски определяется процедурой SetFillStyle(FillStyle, Color), где Color -- цвет закраски, а FillStyle определяет тип закраски, или шаблон заполнения (например, штриховка). Для сплошной закраски параметр FillStyle должен быть равным 0. Наиболее распространенные шаблоны определены в модуле Graph как константы. По умолчанию производится сплошная закраска белым цветом.

Существует еще один способ закраски -- заливка областей. Процедура FloodFill(x,y : integer; ColorBorder : word) закрашивает область экрана произвольной формы по заданным шаблону и цвету. Границей закрашиваемой области должна быть замкнутая линия цвета ColorBorder, не имеющая разрывов. (x,y) -- любая точка внутри закрашиваемой области.


Задание. Найдите в системе помощи Паскаль таблицу констант, задающих шаблоны закраски. Перепишите таблицу в свою тетрадь.

Процедура Bar(x1, y1, x2, y2 : integer) рисует закрашенный прямоугольник. Границы прямоугольника не очерчены линией.

Процедура Bar3D(x1, y1, x2, y2 : integer; Depth : word; Top : boolean) -- аксонометрическое изображение прямоугольного параллелепипеда; (x1,y1) и (x2,y2) -- координаты его фронтальной грани, Depth -- толщина. Параметр логического типа Top определяет, рисовать ли верхнюю грань. Если параметр Top = TopOn = True, верхняя грань изображается, если Top = TopOff = False -- нет. Закрашивается только фронтальная грань. Все границы очерчены линиями, определяемыми параметрами процедуры SetLineStyle. Отсутствие верхней грани позволяет поставить несколько разноцветных кубиков друг на друга, что бывает полезно при построении диаграмм.

Процедура FillEllipse(X,Y : integer; Rx, Ry : word) -- закрашенный эллипс с центром (X,Y), и полосями Rx и Ry по направлениям X и Y соответственно, выраженными в пикселах. Граница эллипса -- сплошная линия обычной или тройной толщины, в зависимости от стиля линии, заданного процедурой SetLineStyle. Процедура PieSlice(X,Y : integer; StartAngle, EndAngle : word; R : word) -- закрашенный сектор окружности с центром (X,Y) и радиусом R, ограниченный углами StartAngle и EndAngle. Независимо от порядка следования углов в заголовке, выделяется сектор между большим и меньшим углами. Прямые линии, ограничивающие сектор, изображаются способом, заданным процедурой SetLineStyle, внешняя дуга не очерчивается. Процедуру применяют, в основном, для построения круговых диаграмм.

Процедура Sector(X,Y : integer; StartAngle, EndAngle : word; Rx,Ry : word) -- закрашенный сектор эллипса, к этой процедуре относится все сказанное о процедуре PieSlice.

Процедура FillPoly(NumPoints : word; varPolyPoints); -- закрашенный многоугольник, который определяется аналогично DrawPoly.

Упражнения
6.1. Напишите процедуру, выводящую изображение снеговика.
6.2. Напишите процедуру, выводящую равносторонний n-угольник и соответствующую ему звезду. Число вершин n задается с клавиатуры.


7. Вывод текста

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

В графическом режиме текст выводится процедурой
OutTextXY(x,y:integerMyText:string),
где x, y -- координаты верхнего левого угла выводимого текста, заданного строкой MyText.

Установка текущего шрифта производится процедурой
SetTextStyle(Font:word;Direction:word;Size:word),
где Font -- тип шрифта, Direction -- признак горизонтального или вертикального направления надписи, Size -- размер -- целый коэффициент, на который умножаются размеры букв.

Числовая информация преобразуется с помощью процедуры Str к строковому виду.


Упражнения
7.1. Напишите процедуру, выводящую на экран треугольник, трапецию, окружность, эллипс с соответствующими надписями.
7.2. Напишите процедуру, моделирующую полет тела, брошенного под углом к горизонту.
7.3. Напишите процедуру, выводящую на экран систему координат. В процедуру должен передаваться параметр по типу запись. Предусмотрите в записи поля, содержащие информацию о положении на экране начала координат; наименовнии осей; о границах числовых значений, выводимых для каждой оси; о шаге, с которым выводятся эти значения и т.д.


8. Спрайты

Спрайт -- аппаратное или программное средство формирования динамического изображения. Спрайт представляет собой растровое графическое изображение небольшого размера (например, 35$\times$35 пикселов), которое может перемещаться по экрану независимо от остального изображения. Спрайт накладывается на основное изображение, перекрывая его.

Со спрайтом связана структура данных следующего вида:

type
  SpriteType = record
    Height, Width : integer;
    Data          : pointer;
  end;

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

Спрайт надо уметь создавать и удалять. Напишем процедуры для выполнения этих действий. Будем предполагать, что для хранения одного пиксела отводится один байт памяти. Операции создания и удаления спрайта подразумевают выделение и освобождение памяти для размещения соответствующей данному спрайту структуры данных.

procedure CreateSprite(var Sprite : SpriteType; Width, Height : integer);

begin
  if Sprite.Data <> nil then KillSprite(Sprite);
  Sprite.Width := Width;
  Sprite.Height := Height;
  GetMem(Sprite.Data, Width * Height);
end;
procedure KillSprite(var Sprite : SpriteType);

begin
  with Sprite do
    begin
      FreeMem(Data, Width * Height);
      Width := 0;
      Height := 0;
      Data := nil;
    end;
end;
Для работы со спрайтами в модуле Graph предусмотрены:
процедура GetImage(X1, Y1, X2, Y2 : integer; var BitMap : pointer); -- копирует в ОЗУ прямоугольную область экрана; (X1,Y1), (X2,Y2) -- координаты противоположных углов прямоугольника, ограничивающего копируемую область экрана; BitMap -- указатель на область памяти, отведенную для хранения данного изображения;
функция ImageSize(X1, Y1, X2, Y2 : integer) : word; -- определяет размер в байтах памяти, необходимой для хранения прямоугольной области экрана; значение, возвращаемое функцией -- объем информации в байтах;
процедура PutImage(X, Y : integer; var BitMap : pointer; BitBlt : word); -- выводит изображение из памяти компьютера в указанную область экрана с заданной логической операцией наложения нового изображения на старое; X, Y -- координаты верхнего левого угла для выводимого изображения; BitMap -- указатель на область памяти; BitBlt -- код логической операции.

Рисование на экране является действием с битами. При прорисовке производятся логические операции между битами памяти монитора и битами изображения. Две последовательно произведенные операции XOR приведут биты памяти монитора в исходное состояние. Если есть некоторое изображение на экране, то использовав его в качестве фона и нарисовав на нем картинку, можно восстановить его, прорисовав картинку еще раз. Эта операция производится при помощи процедуры PutImage(X, Y : integer; Data^, XORPut).

Пример 8.1. Движение спрайта-круга по экрану, содержащему графические объекты.

procedure Animation;

const
  r = 9;

var
  x1, y1, x2, y2, a, b : integer;
  size : word;
  p : pointer;

begin
  SetFillStyle(SolidFill, Red); {красная заливка для круга}
  PieSlice(50, 50, 0, 360, r);  {круг с радиусом r}
  Size := ImageSize(40, 40, 60, 60);
  GetMem(p, Size);
  GetImage(40, 40, 60, 60, p^);
  SetFillStyle(1, Blue);        {общий синий фон}
  Bar(1, 1, 640, 480);
  SetFillStyle(1, Green);       {зеленый цвет фигур фона}
  a := 100;
  while (a < 550) do begin      {создание семи фигур фона}
    Bar(a, 150, a + 20, 250);
    a := a + 70;
  end;
  a := 10;
  repeat
    PutImage(a, 200, p^, 1);    {организация движения круга}
    Delay(20);                  {задержка движения}
    PutImage(a, 200, p^, XORPut);
    a := a + 1;
  until KeyPressed;
  FreeMem(P, Size);
end;

Задание. Переработайте процедуру Animation, применив в ней процедуры KillSprite и CreateSprite.
Упражнения
8.1. Напишите процедуру, моделирующую движение некоторого изображения по синусоиде.
8.2. Напишите процедуру, моделирующую полет N шариков и их абсолютно упругое отражение от границ экрана.

7. Программа "Простейший графический редактор"

uses Graph, Crt;               {подключен модуль Graph}
const
{------------Константы для управляющих клавиш---------}
  Esc   = #27;
  right = '6';
  left  = '4';
  up    = '8';
  down  = '2';
  space = ' ';
  Del   = 'X';
{-----------------------------------------------------}
  Title = 'Графический редактор';    {Титульная строка}
  HT    = 20;                        {Высота строки заголовка}
  HS    = 35;                        {Высота строки статуса}

var
  x,    y    : integer;
  Stat       : string[10]; {статус "кисти"}
  DrawColor  : 0..15;      {цвет "краски"}
  Key        : char;       {символ нажатой клавиши}

procedure OpenGraph;      {инициализация режима графики}
var
  Gd : Integer;     {для драйвера}
  Gm : Integer;     {для графического режима}
begin
  Gd := Detect;     {режим автоопределения}
                        {инициализация}
  {в качестве примера указан конкретный путь к драйверу}
  InitGraph(Gd, Gm, 'C:\BP\BGI');
  if GraphResult <> grOk then {если инициализация не удалась}
   Halt;             {программа остановлена}
end;
{-------Выводит содержимое статусной строки-----}
{x, y - координаты указателя;
 stat - статус рисовать/не рисовать             }
procedure StatusBar(x, y : integer; stat : string);
var
  StrX, StrY : string[5]; {Для перевода координат
                           в строковый тип      }
begin
  Str(x : 4, StrX);
  Str(y : 4, StrY);
  SetViewPort(1, GetMaxY - HS, Pred(GetMaxX), Pred(GetMaxY), ClipOff);
  SetColor(White);
  Rectangle(Pred(GetMaxX), HS, -1, -1);
  ClearViewPort;
  SetTextStyle(DefaultFont, HorizDir, 2);
  OutTextXY(1, 1, 'X =' + StrX + '  Y =' + StrY + ' |' + stat + '|  X - delete');
end;

procedure InitGR;
{инициализация графического редактора}
begin {задание начальных установок}
  stat := 'no draw';
  DrawColor := Black;
  x := GetMaxX div 2;
  y := GetMaxY div 2;
  ClearDevice;
  Rectangle(0,0,GetMaxX, GetMaxY);
  Rectangle(0,0,Pred(GetMaxX), HT);
  SetFillStyle(SolidFill, Blue);
  Bar(1, 1, Pred(GetMaxX), HT - 1);
  SetColor(White);
  SetTextStyle(DefaultFont, HorizDir, 2);
  OutTextXY(1,1, Title);
end;

procedure SetBorder;
{задает границы рабочего поля редактора}
begin
  SetViewPort(1, Succ(HT), Pred(GetMaxX), Pred(GetMaxY) - HS, ClipOn);
end;
Begin
  OpenGraph;   {вызов процедуры инициализации графики}
  InitGR;      {вызов процедуры инициализации графического редактора}
  repeat
    StatusBar(x, y, stat);
    SetBorder;
    while KeyPressed do ReadKey;
    Key := ReadKey;

    case Key of
      Right : if (x <= Pred(GetMaxX)     ) then inc(x);
      Left  : if (x >= 1                 ) then dec(x);
      Up    : if (y >= HT                ) then dec(y);
      Down  : if (y <= Pred(GetMaxY) - HS) then inc(y);
      Space : begin
                if Stat = 'no draw' then begin
                  Stat := '   draw';
                  DrawColor := White;
                end
                  else begin
                   Stat := 'no draw';
                   DrawColor := Black;
                  end;
              end;
      Del   : begin
                SetBorder;
                ClearViewPort;
              end;
    end;

    PutPixel(x, y, DrawColor); {"КИСТЬ" РИСУЕТ}
  until (Key = Esc);

  CloseGraph; {закрыть графический режим}
End.