Назад | Вперед

Тема 10. Использование графической библиотеки

Краткая характеристика графических режимов работы
Инициализация графического режима
Координаты, окна, страницы
Основные графические примитивы
Краски, палитры, заполнения
Сохранение и выдача изображений
Вывод текста
Включение драйвера и шрифтов в тело программы

Для создания графических изображений в языке Турбо Паскаль (начиная с версии 4.0) в состав Турбо Паскаля включена мощная библиотека графических подпрограмм Graph, остающаяся практически неизменной во всех последующих версиях. Библиотека содержит в общей сложности более 50 процедур и функций, предоставляющих программисту самые разнообразные возможности управления графическим экраном. Для облегчения знакомства с библиотекой все входящие в нее процедуры и функции сгруппированы по функциональному принципу.
Подключение данного модуля к программе осуществляется стандартным способом, т.е. с помощью зарезервированного слова USES:
  Uses Graph;
Так как, стандартное состояние ПК после его включения, а также к моменту запуска программы из среды Турбо Паскаля соответствует работе экрана в текстовом режиме, то любая программа, использующая графические средства компьютера, должна определенным образом инициировать графический режим работы дисплейного адаптера. В этом режиме экран дисплея рассматривается как совокупность очень близко расположенных точек - пикселей, светимостью которых можно управлять с помощью программы.
После завершения работы программы ПК должен быть возвращен в текстовый режим.

Краткая характеристика графических режимов работы

Настройка графических процедур на работу с конкретным адаптером достигается за счет подключения нужного графического драйвера. Напомним, что драйвер - это специальная программа, осуществляющая управление теми или иными техническими средствами ПК. Графический драйвер, как это не трудно догадаться, управляет дисплейным адаптером в графическом режиме. Графические драйверы разработаны фирмой Borland практически для всех типов адаптеров. Обычно они располагаются на диске в отдельном подкаталоге BGI в виде файлов с расширением BGI (от англ.: Borland Graphics Interface - графический интерфейс фирмы Borland). Например, CGA.BGI - драйвер для CG4-адаптера, EGA VGA.BGI - драйвер для адаптеров EGA и VGA и т.п.
Графические возможности конкретного адаптера определяются разрешением экрана, т.е. общим количеством пикселей, а также количеством цветов (оттенков), которыми может светиться любой из них. Кроме того, многие адаптеры могут работать с несколькими графическими страницами. Графической страницей называют область оперативной памяти, используемая для создания «карты» экрана, т.е. содержащая информацию о светимости (цвете) каждого пикселя.
Адаптер CGA (Color Graphics Adapter - цветной графический адаптер) имеет 5 графических режимов. Четыре режима соответствуют низкой разрешающей способности экрана (320 пикселей по горизонтали и 200 по вертикали, т.е. 320x200) и отличаются только набором допустимых цветов - палитрой. Каждая палитра состоит из трех цветов, а с учетом черного цвета несветящегося пикселя - из четырех: палитра 0 (светло-зеленый, розовый, желтый), палитра 1 (светло-бирюзовый, малиновый, белый), палитра 2 (зеленый, красный, коричневый) и палитра 3 (бирюзовый, фиолетовый, светло-серый). Пятый режим соответствует высокому разрешению 640x200, но каждый пиксель в этом случае может светиться либо каким-то одним заранее выбранным и одинаковым для всех пикселей цветом, либо не светиться вовсе, т.е. палитра этого режима содержит два цвета. В графическом режиме адаптер CGA использует только одну страницу.
Адаптер EGA (Enhanced Graphics Adapter - усиленный графический адаптер) может полностью эмулировать графические режимы адаптера CGA. Кроме того, в нем возможны режимы: низкого разрешения (640x200, 16 цветов, 4 страницы) и высокого разрешения (640x350, 16 цветов, 1 страница). В некоторых модификациях используется также монохромный режим (640x350, 1 страница, 2 цвета).
Адаптер MCGA (Multi-Color Graphics Adapter - многоцветный графический адаптер) совместим с CGA и имеет еще один режим - 640x480, 2 цвета, 1 страница. Такими адаптерами оснащались младшие модели серии ПК PS/2 фирмы IBM. Старшие модели этой серии оснащаются более совершенными адаптерами VGA (Video Graphics Array -графический видеомассив. Адаптер VGA эмулирует режимы адаптеров CGA и EGA и дополняет их режимом высокого разрешения (640x480, 16 цветов, 1 страница).
Не так давно появились так называемые cynep-VGA адаптеры (SVGA) с разрешением 800x600 и более, использующие 256 и более цветовых оттенков. В настоящее время эти адаптеры получили повсеместное распространение, однако в библиотеке Graph для них нет драйверов. Поскольку SVGA совместимы с VGA, для управления современными графическими адаптерами приходится использовать драйвер EGAVGA.BGI и довольствоваться его относительно скромными возможностями.
Несколько особняком стоят достаточно популярные адаптеры фирмы Hercules. Адаптер HGC имеет разрешение 720x348, его пиксели могут светиться одним цветом (обычно светло-коричневым) или не светиться вовсе, т.е. это монохромный адаптер. Адаптер HGC+ отличается несущественными усовершенствованиями, а адаптер HIСС (Hercules In Color Card) представляет собой 16-цветный вариант HGC+.
Следующая таблица содержит перечни возможных графических режимов для наиболее распространенных адаптеров EGA и VGA.


Драйвер

Имя константы режима и ее значение

Разрешение экрана (в точках)

Палитра

Число видеостраниц

EGA

EGALo=0
EGAHi=1

640x200
640x350

16 цветов
16 цветов

4
2

EGA64

EGA64Lo=0
EGA64Hi=1

640x200
640x350

16 цветов
4 цвета

1
1

EGAMono

EGAMonoHi=3

640x350

2 цвета

1(2)

VGA

VGALo=0
VGAMed=1
VGAHi=2

640x200
640x350
640x480

16 цветов
16 цветов
16 цветов

4
2
1

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


Символьная константа

Число

Графическтй адаптер

Detect

= 0

Автоопределение

CGA

= 1

Адаптер CGA

MCGA

= 2

Адаптер MCGA

EGA

= 3

Адаптер EGA 256K

EGA64

= 4

Адаптер EGA 64K

EGA Mono

= 5

EGA с монохромным дисплеем

IBM8514

= 6

Адаптер 8514

HercMono

= 7

Адаптер Gercules

ATT400

= 8

Для ПК AT&T

VGA

= 9

Адаптер VGA

PC3270

= 10

Адаптер 3270

СurrentDriver

= -128

Для GetModRange


Наверх

Инициализация графического режима

Таким образом, перед началом работы с графикой необходимо установить наиболее подходящий для данного монитора видеорежим, для чего любая программа, предназначенная для этого, обязательно должна содержать блок вызова процедур инициализации графического режима и обращение к процедуре его завершения. В таком блоке инициализируется графический режим, проверяется правильность инициализации и, если все операции прошли успешно, разрешается дальнейшая работа программы. Процедура инициализации задается обычно с помощью процедуры InitGraph, которая имеет следующий заголовок:
Procedure InitGraph(var Driver,Mode: Integer; Path: String);
  где: Driver - переменная типа Integer, определяет тип графического драйвера;
  Mode - переменная того же типа, задающая режим работы графического адаптера;
  Path - выражение типа String, содержащее имя файла драйвера и, возможно, маршрут его поиска (если драйвера находятся в текущем каталоге, этот параметр должен иметь значение ‘’ - пустая строка). К моменту вызова этой процедуры на одном из дисковых носителей информации должен находиться файл, содержащий нужный графический драйвер. Процедура загружает этот драйвер в оперативную память и переводит адаптер в графический режим работы. Тип драйвера должен соответствовать типу графического адаптера.
Если параметру Driver присваевается значение константы Detect, то система переходит в режим автоопределения и в случае возможности ее переключение в графический режим, инициализируется драйвер режима с максимальным разрешением для данного типа видеоадаптера. При этом в GraphDriver и Mode процедура InitGraph возвратит автоматически определенные значения этих параметров или код ошибки, механизм обнаружения и генерирования которых реализован в модуле Graph с помощью функций GraphResult и GraphErrorMsg.
Функция GraphResult возвращает значение типа Integer, в котором закодирован результат последнего обращения к графическим процедурам. Если ошибка не обнаружена, значением функции будет ноль, в противном случае - отрицательное число, имеющее следующий смысл:
const
grOk = 0;{Нет ошибок} 
grlnitGraph =-1;{He инициирован графический режим} 
grNotDetected =-2;{Не определен тип драйвера} 
grFileNotFind =-3;{Не найден графический драйвер} 
grlnvalidDriver =-4;{Неправильный тип драйвера}
grNoLoadMem =- 5;{Нет памяти для размещения драйвера}
grNoScanMem = - 6;{Нет памяти для просмотра областей}
grNoFloodMem =- 7;{Нет памяти для закраски областей}
grFontNotFound = -8;{Не найден файл со шрифтом}
grNoFontMem =- 9;{Нет памяти для размещения шрифта}
grlnvalidMode =-10;{Неправильный графический режим}
grError =-11;{Общая ошибка}
grIOError =-12;{Ошибка ввода-вывода}
grlnvalidFont =-13;{Неправильный формат шрифта}
grInvalidFontNum=-14; {Неправильный номер шрифта}
Когда все действия с графикой завершены необходимо выйти из графического режима. Это делается с помощью процедуры CloseGraph. В процессе своего выполнения эта процедура освобождает память, распределенную под драйверры графики, файлы шрифтов и промежуточные данные, и восстанавливает тот режим работы адаптера, в котором он находился до выполнения инициализации. 
С учетом всего сказанного, блок вызова процедур инициализации графического режима и обращение к процедуре его завершения может выглядеть, например, следующим образом.

  Uses Graph;
  Var
    GrDriver, GrMode: Integer;
  BEGIN
   GrDriver:=Detect;{режим  автоопределения}
   InitGraph(GrDriver,GrMode,’С:\TP\BGI’);
   If  GraphResult<>0 Then
   Begin
    WriteLn(‘Ошибка инициализации графики’);
    Halt(1);
   End;
   SetBkColor(LightBlue);{установлен цвет фона}
   ReadLn;
   CloseGraph;{завершение графического режима}
  END.
  
С инициализацией графического режима связан и еще целый ряд процедур и функций. Некоторые из них приведены далее в таблице.


Заголовок

Вид структуры

Назначение

RestoreCRTMode

Процедура

Кратковременный возврат в текстовый режим без сброса параметров установленного режима

GetGraphMode:Integer;

Функция

Возвращает значение типа Integer, в котором содержится код установленного режима работы графического адаптера

SetGraphMode(Mode: Integer);

Процедура

Устанавливает новый графический режим работы адаптера, где: Mode - код устанавливаемого режима

DetectGraph(var Driver,Mode: Integer);

Процедура

возвращает в переменной Mode максимально возможный для данного адаптера номер графического режима. Здесь Driver - тип драйвера; Mode - режим работы

GetDriverName: String;

Функция

Возвращает значение типа String, содержащее имя загруженного графического драйвера.

GetMaxMode: Integer;

Функция

Возвращает значение типа Integer, содержащее количество возможных режимов работы адаптера

GetModName(ModNumber: Integer): String;

Функция

Возвращает значение типа String, содержащее разрешение экрана и имя режима работы адаптера по его номеру - ModNumber.

GetModeRange(Drv: Integer; var Min, Max: Integer);

Процедура

Возвращает диапазон возможных режимов работы заданного графического адаптера.


Наверх

Координаты, окна, страницы

Для построения изображений на экране в графическом режиме применяется система координат, которая отличается от текстового режима. Отсчет позиции любой точки начинается от верхнего левого угла экрана, который имеет координаты (0,0). Таким образом, горизонтальная координата (значение X, столбец) экрана увеличивается слева направо, а вертикальная (значение Y, строка) - увеличивается в направлении сверху вниз. При этом экран представляется в виде прямоугольного массива адресуемых точек, а не символов, как в текстовом режиме.
Определение значений максимальных координат экрана в модуле Graph реализовано с помощью функций
  GetMaxX:Integer;
  GetMaxY:Integer;
Поэтому, чтобы построить изображение, необходимо обязательно указать его начальную позицию.
В графических режимах видимого курсора нет, но есть невидимый текущий указатель CP (current pointer), выполняющий те же функции, что курсор в текстовом режиме. Для его перемещения используются процедуры MoveTo и MoveRel. Причем процедура
  MoveTo(X,Y:integer);
перемещает текущий указатель в точку с координатами X иY, а процедура
  MoveRel(dX,dY:integer);
перемещает CP на DX точек по горизонтали и на DY точек по вертикали.
Для определения текущего положения графического курсора используются функции
  GetX:Integer;
  GetY:Integer;
Какие бы изображения ни выводились на экран, все они построены из точек. В модуле Graph для отображения точки на экране используется процедура
  PutPixel(X,Y:Integer;Color:Word);
где X и Y – экранные координаты точки, Color – ее цвет.
При этом Турбо Паскаль позволяет организовать прямой доступ к каждому пикселу экрана с помощью функции
  GetPixel(X,Y:Integer):Word;
которая определяет номер цвета пиксела с координатами (X,Y).
Для EGA/VGA – адаптеров устанавливаются следующие цвета, обозначения которых приведены в таблице 3.1.
Для установки цвета изображения и фона в языке Турбо Паскаль применяются процедуры SetColor и   SetBkColor. Процедура
  SetColor(color:Word)
служит для задания цвета, который будет использоваться процедурами графического вывода, а процедура
  SetBkColor(color:Word)
Устанавливает новый цвет фона.
Таблица 3.1


Константа

Значение

Код

Цвет на экране

EGABlack

0

000000

Черный

EGABlue

1

000001

Синий

EGAGreen

2

000010

Зеленый

EGACyan

3

000011

Голубой

EGARed

4

000100

Красный

EGAMagenta

5

000101

Фиолетовый

EGABrown

20

010100

Коричневый

EGALightGray

7

000111

Светло-серый

EGADarkGray

56

111000

Темно-серый

EGALightBlue

57

111001

Светло-синий

EGALightGreen

58

111010

Светло-зеленый

EGALightCyan

59

111011

Светло-голубой

EGALightRed

60

111100

Светло-красный

EGALightMagenta

61

111101

Светло-фиолетовый

EGAYellow

62

111110

Желтый

EGAWhite

63

111111

Белый

Процедура вывода отрезка прямой на экран (в текущем цвете и стиле) определяется следующим образом.
  Line(X1,Y1,X2,Y2:Integer)
В ней задаютя координаты начальной и конечной точек линии. Для построения отрезков примениются еще две процедуры: LineTo(X,Y) и LineRel(dX,dY) – строят отрезок от текущего положения указателя.
В данном примере экран заполнякется множеством отрезков различных цветов, при этом получается интересный визуальный эффект.

  Uses Graph,Crt;
  Var
    GrDriver,GrMode:Integer;
  BEGIN
    GrDriver:=Detect;{режим  автоопределения}
    InitGraph(GrDriver,GrMode,’’);
    If  GraphResult<>0 Then Begin
     WriteLn(‘Ошибка инициализации  графики’);
     Halt(1);
    End;
    Randomize; {при каждом запуске получается новая картина}
    Repeat
     SETcolor(Random(15));  {случайный выбор цвета}
     Line(Random(GetMaxX),Random(GetMaxY),GetX,GetY);
     LineTo(Random(GetMaxX),Random(GetMaxY));
     LINErEL(50,-50);
    UNTIL KeyPressed;
    CloseGraph;
  END.
  
Турбо Паскаль позволяет вычерчивать линии самых различных стилей: тонкие, широкие, штриховые и т.д. Установка стиля производится процедурой
  SetLineStyle(LineStyle, Pattern, Thickness:Word);
Для установки необходимой видимой области на экране можно воспользоваться процедурой
  SetViewPort(XI,Y1,X2,Y2: Integer; ClipOn: Boolean);
которая определит прямоугольное окно с координатами левого верхнего (XI,Y1) и правого нижнего (X2,Y2) углов окна,  а выражение ClipOn типа Boolean, укажет на «отсечку» не умещающихся в окне элементов изображения (рис. 3.2).
img1
Рис. 3.2. Отсечка изображения в окне

Координаты окна всегда задаются относительно левого верхнего угла экрана. Если параметр ClipOn имеет значение True, элементы изображения, не умещающиеся в пределах окна, отсекаются, в противном случае границы окна игнорируются. Для управления этим параметром можно использовать такие определенные в модуле константы:
const
  ClipOn = True; {Включить отсечку} 
  ClipOff = False; {He включать отсечку} .
И наоборот, процедура
  GetViewSettings(var Viewlnfo: ViewPortType);
будет возвращать координаты и признак отсечки текущего графического окна.
Здесь Viewlnfo - переменная типа ViewPortType. Этот тип в модуле Graph определен следующим образом:
type
  ViewPortType = record
  x1,y1,x2,y2: Integer; {Координаты окна} 
  Clip : Boolean {Признак отсечки}
end ;
Для того чтобы можно было перерисовывать изображение, также как и в текстовом режиме необходима функция «очистки»  экрана. Так, после обращения к процедуре
  ClearDevice
указатель устанавливается в левый верхний угол экрана, а сам экран заполняется цветом фона, заданным процедурой SetBkColor. А такая процедура, как
  ClearViewPort
очищает графическое окно, а если окно не определено к этому моменту - весь экран. При этом, окно заполняется цветом с номером 0 из текущей палитры и указатель перемещается в левый верхний угол окна.
В следующей программе на экране создается окно, которое затем заполняется случайными окружностями (рис. 3.2). После нажатия на любую клавишу окно очищается. Для выхода из программы надо нажать Enter.
Uses CRT,Graph;  
var 
  x1,y1,x2,y2,Err: Integer;  
begin 
  {Инициируем графический режим}  
  xl := Detect; InitGraph(xl,x2,'');  
  Err := GraphResult; 
  if ErrogrOk then 
   WriteLn(GraphErrorMsg(Err))  
  else begin 
   {Определяем  координаты окна с учетом разрешения экрана}  
   x1 := GetMaxX div 4,-y1 := GetMaxY  div 4;  
   x2 := 3*x1; y2 := 3*y1;  
   {Создаем  окно} 
   Rectangle(x1,y1,x2,y2); 
   SetViewPort(x1+1,y1+1,x2-1,y2-1,ClipOn);  
   {Заполняем окно случайными  окружностями}  
   repeat 
    Сirclе(Random(Ge tMaxX),Random(Ge  tMaxX) 
    Random(GetMaxX div 5))  
   until KeyPressed; 
   {Очищаем  окно и ждем нажатия Enter}  
   ClearViewPort; 
   OutTextXY(0,0,'Press Enter...1);  
   ReadLn;  
   CloseGraph  
end  
end. 
img2
Рис. 3.3. Окно со случайными окружностями

При наличии адаптеров, поддерживающих многостраничную работу (EGA, VGA и т.п.) полезно использовать процедуры для работы с видеопамятью, например
  SetActivePage(PageNum: Word),
которая делает указанную страницу видеопамяти активной. Фактически процедура просто переадресует графический вывод в другую область видеопамяти, однако вывод текстов с помощью Write/WriteLn всегда осуществляется только на страницу, которая является видимой в данный момент (активная страница может быть невидимой). Нумерация страниц начинается с нуля.
В свою очередь процедура
  SetVisualPAge(PageNum: Word);
сделает видимой страницу с указанным номером.
Следующая программа сначала рисует квадрат в видимой странице и окружность - в невидимой. После нажатия на Enter происходит смена видимых страниц.
Uses Graph; 
var 
  d,m,e: Integer;  
  s : String;  
begin 
  d := Detect; InitGraph(d, m,  '');  
  e := GraphResult;
  if e <> grOk  then  
   WriteLn (GraphErrorMsg(e))  
  else  {Нет ошибки. Проверяем,  поддерживает ли драйвер 
         многостраничную работу с видеопамятью:}
   if d in [HercMono,EGA,EGA64,MCGA,VGA]  then  
   begin {Используем  многостраничный режим}  
     if d<>HercMono then  
      SetGraphMode(m-1);  
     {Заполняем  видимую страницу} 
     Rectangle(10,10,GetMaxX div  2,GetMaxY div 2);  
     OutTextXY(0,0,'Page 0. Press  Enter...');  
     {Заполняем невидимую}  
     SetActivePage (1); 
     Circle(GetMaxX div 2, GetMaxY div 2,  100);  
     OutTextXY(0,GetMaxY-10,'Page 1.  Press Enter...');  
     {Демонстрируем страницы}  
     ReadLn; 
     SetVisualPage(1);  
     ReadLn; 
     SetVisualPage (0);  
     ReadLn; 
     CloseGraph  
   end  
   else 
     begin {Драйвер не поддерживает  многостраничный режим}  
      s := GetDriverName;  CloseGraph;  
      WriteLn('Адаптер ',s,' использует  только 1 страницу') 
     end 
end. 
В данной программе обратите внимание на оператор
  if doHercMono then SetGraphMode(m-1);
С его помощью гарантированно устанавливается многостраничный режим работы на адаптерах EGA, MCGA, VGA. Как уже говорилось, после инициации графики с Driver=Detect устанавливается режим работы с максимально возможным номером; перечисленные адаптеры в этом режиме могут работать только с одной графической страницей, чтобы обеспечить работу с двумя страницами, следует уменьшить номер режима.


Наверх

Основные графические примитивы

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


Заголовок

Назначение

Rectangle(X1,Y1,X2,Y2: Integer);

Вычерчивает прямоугольник с указанными координатами углов: левого верхнего (X1, Y1) и правого нижнего (Х2, Y2).

DrawPoly(N: Word; var Points)

Вычерчивает произвольную ломаную линию, заданную координатами N точек излома, включая обе крайние точки. Points - переменная типа PointType, содержащая координаты точек излома.

Circle(X,Y: Integer; R: Word);

Вычерчивает окружность с координатами центра X, Y и с радиусом R -  в пикселях.

Arc(X,Y: Integer; BegA,EndA,R: Word);

Чертит дугу окружности X, Y - координаты центра; BegA, EndA - соответственно начальный и конечный углы дуги; R - радиус.

GetArcCoords(var Coords: ArcCoordsType);

Возвращает координаты трех точек: центра, начала и конца дуги

Ellipse(X,Y: Integer; BegA,EndA,RX,RY: Word);

Вычерчивает эллипсную дугу. X, Y - координаты центра; BegA, EndA - соответственно начальный и конечный углы дуги; RX, RY- горизонтальный и вертикальный радиусы эллипса в пикселях.


Наверх

Краски, палитры, заполнения

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


Заголовок

Назначение

Функция GetColor: Word;

Возвращает значение типа Word, содержащее код текущего цвета.

Функция
GetMaxColor: Word;

Возвращает значение типа Word, содержащее максимальный доступный код цвета, который можно использовать для обращения к SetColor

Процедура SetBkColor(Color: Word);

Устанавливает цвет фона

Function GetBkColor: Word;

Возвращает значение типа Word, содержащее текущий цвет фона

Procedure SetPalette(N: Word; Color: Shortlnt);

Заменяет один из цветов палитры на новый цвет. Здесь N - номер цвета в палитре; Color - номер вновь устанавливаемого цвета.

Procedure SetAllPalette(var Palette);

Изменяет одновременно несколько цветов палитры. Параметр Palette в заголовке процедуры описан как нетипизированный параметр. Первый байт этого параметра должен содержать длину N палитры, остальные N байты - номера вновь устанавливаемых цветов в диапазоне от -1 до MaxColors. Код -1 означает, что соответствующий цвет исходной палитры не меняется.

Function
GetPaletteSize: Integer;

Возвращает значение типа Integer, содержащее размер палитры (максимальное количество доступных цветов).

Procedure
GetDefaultPalette(var Palette: PaletteType);

Возвращает структуру палитры, устанавливаемую по умолчанию (в режиме автонастройки). Здесь Palette - переменная типа PaletteType (см. процедуру GetPalette), в которой возвращаются размер и цвета палитры

Procedure
SetFillStyle(Fill,Color: Word);

Устанавливает стиль (тип и цвет) заполнения. Здесь Fill - тип заполнения; Color - цвет заполнения.

Procedure
SetFillPattern(Pattern: FillPatternType;Color: Word);

Устанавливает образец рисунка и цвет штриховки. Образец рисунка задается в виде матрицы из 8x8 пикселей и может быть представлен массивом из 8 байт следующего типа:
type FillPatternType = array [1..8] of Byte;

Procedure
GetFillPattern(var Pattern: FillPatternType);

Возвращает образец заполнения, установленный ранее процедурой SetFillPattern. Здесь Pattern - переменная типа FillPatternType, в которой возвращается образец заполнения.

Procedure GetFillSettings(var Pattlnfo: FillSettingsType);

Возвращает текущий стиль заполнения Здесь Pattlnfo - переменная типа FillSettingsType, в которой возвращается текущий стиль заполнения, В модуле Graph определен тип:
type
FillSettingsType = record
Pattern: Word; {Образец}
Color: Word {Цвет} 
end;

Procedure
FloodFill(X,Y: Integer; Border: Word);

Заполняет произвольную замкнутую фигуру, используя текущий стиль заполнения (узор и цвет). Здесь X, Y- координаты любой точки внутри замкнутой фигуры; Border - цвет граничной линии. Если фигура незамкнута, заполнение «разольется» по всему экрану.

Procedure Bar(X1,Y1,X2,Y2: Integer);

Заполняет прямоугольную область экрана Здесь XJ...Y2 - координаты левого верхнего (X1, Y1) и правого нижнего (Х2, Y2) углов закрашиваемой области.

Procedure
Ваr3D (X1,Y1,X2,Y2,Depth: Integer; Top: Boolean);

Вычерчивает трехмерное изображение параллелепипеда и закрашивает его переднюю грань Здесь X1... Y2 - координаты левого верхнего (X1, Y1) и правого нижнего (Х2, Y2) углов передней грани; Depth - третье измерение трехмерного изображения («глубина») в пикселях; Тор - способ изображения верхней грани.

Procedure
FillPoly(N: Word; var Coords);

Обводит линией и закрашивает замкнутый многоугольник Здесь N - количество вершин замкнутого многоугольника; Coords - переменная типа PointType, содержащая координаты вершин.

Procedure FillEllipse
(X,Y,RX,RY: Integer);

Обводит линией и заполняет эллипс Здесь X, Y - координаты центра; RX, RY- горизонтальный и вертикальный радиусы эллипса в пикселях.

Procedure
Sector(X,Y: Integer; BegA,EndA,RX,RY: Word);

Вычерчивает и заполняет эллипсный сектор. Здесь BegA, EndA - соответственно начальный и конечный углы эллипсного сектора. Остальные параметры обращения аналогичны параметрам процедуры FillEllipse

Procedure PieSlice(X,Y: Integer; BegA,EndA,R: Word);

Вычерчивает и заполняет сектор окружности

В отличие от текстового режима, в котором цвет фона может быть только темного оттенка, в графическом режиме он может быть любым. Установка нового цвета фона немедленно изменяет цвет графического экрана. Это означает, что нельзя создать изображение, два участка которого имели бы разный цвет фона. Для CGA -адаптера в режиме высокого разрешения установка цвета фона изменяет цвет активных пикселей. Замечу, что после замены цвета фона на любой, отличный от 0 (Black) цвет, Вы не сможете более использовать цвет 0 как черный. Он будет заменяться цветом фона, т.к. процедуры модуля Graph интерпретируют цвет с номером 0 как цвет фона. Это означает, в частности, что Вы уже не сможете вернуть фону черный цвет!
С помощью заполнения можно покрывать какие-либо фрагменты изображения периодически повторяющимся узором. Для указания типа заполнения используются следующие предварительно определенные константы:
const
 EmptyFill = 0;{Заполнение фоном (узор отсутствует)} 
 SolidFill = 1;{Сплошное заполнение}
 LineFill = 2;{Заполнение -------}
 LtSlashFill = 3;{Заполнение ///////}
 SlashFill = 4;{Заполнение утолщенными ///}
 BkSlashFill = 5;{Заполнение утолщенными \\\}
 LtBkSlashFill = 6;{Заполнение \\\\\\\}
 HatchFill = 7;{Заполнение +++++++}
 XHatchFill = 8;{Заполнение ххххххх}
 InterleaveFill= 9;{Заполнение прямоугольную клеточку}
 WideDotFill = 10;{Заполнение редкими точками}
 CloseDotFill = 11;{Заполнение частыми точками}
 UserFill = 12;{Узор определяется пользователем}
Если Ваш ПК оснащен цветным экраном, следующая программа продемонстрирует работу процедуры SetBkColor. Программа выводит десять вложенных друг в друга прямоугольников, после чего циклически меняет цвет фона. Для выхода из программы достаточно нажать на любую клавишу.

  Uses Graph, CRT;  
  const 
   NC: array [0..15] of String [12] = 
      ('Black','Blue','Green','Cyan','Red','Magenta', 
       'Brown','LightGray','DarkGray','LightBlue', 
       'LightGreen1,'LightCyan1,'LightRed', 
       'LightMagenta','Yellow','White'); 
  var 
    d, r, e, k, color, dx, dy: Integer; 
  begin 
    {Инициируем графику}   
    d := Detect; InitGraph(d, r, ' ')  ;  
    e := GraphResult; 
	if e <> grOK  then 
     WriteLn(GraphErrorMsg(e))  
    else  
     begin 
     {Выводим текст в центре экрана} 
     OutTextXY(200,GetMaxY div  2,'BACKGROUND COLOR'); 
     dx := GetMaxX div 30; {Приращение длины} 
     dy := GetMaxY div 25; {Приращение высоты} 
     for k := 0 to 9 do{Выводим 10 прямоугольников} 
       Rectangle(k*dx,k*dy,GetMaxX-k*dx,GetMaxY-k*dy); 
     color := black; {Начальный цвет фона}  
     repeat {Цикл смены фона} 
       SetBkColor(color) ; 
       SetFillStyle(0,Color); 
       Bar(345,GetMaxY div 2,440,GetMaxY  div 2+8); 
       OutTextXY(345,GetMaxY div  2,NC[color]); 
       delay(1000); 
       inc(color); 
       if color > White then 
         color := Black
	 until KeyPressed; 
     if ReadKey=#0 then  
      k := ord(ReadKey);  
     CloseGraph  
    end  
  end. 
  end. 
  

Наверх

Сохранение и выдача изображений

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


Заголовок

Назначение

Function
ImageSize(X1,Y1,X2,Y2: Integer): Word;

Возвращает размер памяти в байтах, необходимый для размещения прямоугольного фрагмента изображения.

Procedure
Getlmage(X1,Y1,X2,Y2: Integer; var Buf)

Помещает в память копию прямоугольного фрагмента изображения. Здесь X1...Y2 - координаты углов фрагмента изображения; Buf - переменная или участок кучи, куда будет помещена копия видеопамяти с фрагментом изображения. Размер Buf должен быть не меньше значения, возвращаемого функцией ImageSize с теми же координатами X1....Y2.

Procedure
Putlmage (X,Y: Integer; var Buf; Mode: Word);

Выводит в заданное место экрана копию фрагмента изображения, ранее помещенную в память процедурой Getlmage. Здесь X,Y- координаты левого верхнего угла того места на экране, куда будет скопирован фрагмент изображения; Buf - переменная или участок кучи, откуда берется изображение; Mode - способ копирования.

Как видим, для процедуры Putlmage координаты правого нижнего угла не указываются, так как они полностью определяются размерами вновь выводимой на экран копии изображения. Координаты левого верхнего угла могут быть какими угодно, лишь бы только выводимая копия уместилась в пределах экрана (если копия не может разместиться на экране, она не выводится и экран остается без изменений).
Параметр Mode определяет способ взаимодействия вновь размещаемой копии с уже имеющимся на экране изображением. Взаимодействие осуществляется путем применения кодируемых этим параметром логических операций к каждому биту копии и изображения. Для указания применяемой логической операции можно использовать одну из следующих предварительно определенных констант:
const
  NormalPut= 0;{Замена существующего изображения на копию}
  XorPut = 1;{Исключительное ИЛИ}
  OrPut = 2;{Объединительное ИЛИ}
  AndPut = 3;{Логическое И} 
  NotPut = 4;{Инверсия изображения}
Наиболее часто используются операции NormalPut, XORPut и NotPut. Первая из них просто стирает часть экрана и на это место помещает копию из памяти в том виде, как она там сохраняется. Операция NotPut делает то же самое, но копия выводится в инверсном виде. Для монохромного режима это означает замену светящихся пикселей на темные и наоборот. В цветном режиме операция NotPut применяется к коду цвета каждого пикселя. Например, для White (код 15 или в двоичном виде 1111) эта операция даст код 0000 = 0 = Black, для Red = 4 = 0100 получим 1011 = 11 = LightCyan и т.д. Операция XORPut, примененная к тому же месту экрана, откуда была получена копия, сотрет эту часть экрана. Если операцию применить дважды к одному и тому же участку, вид изображения на экране не изменится. Таким способом можно довольно просто перемещать изображения по экрану, создавая иллюзию движения.
Следующая программа рисует «Неопознанный Летающий Объект» - летающую тарелку на звездном фоне (рис. 14.10).

img3
Рис. 3.4. Иллюстрация процедур Getlmage/Putlmage

  Uses Graph, CRT;  
   const 
   r = 20; {Характерный размер  НЛО}  
   pause = 50; {Длительность  паузы}  
  var 
   d,m,e,xm/ym,x,y/lx,ly,rx,ry, 
   Size,i,dx,dy,Width,Height:  Integer;  
   Saucer : Pointer;  
   label 
   loop;  
  begin 
   {Инициируем графику}  
   d := Detect; lnitGraph(d, m, ' ')  ;  
   e := GraphResult;
   if e <> grOk  then 
    WriteLn(GraphErrorMsg(e))  
   else  
    begin 
     x := r*5; 
     у := r*2; 
     xm := GetMaxX div 4; 
     ym := GetMaxY div 4; 
     {Создаем  "тарелку" из двух эллипсов с усами антенн} 
     Ellipse (х,у,0,360,r,r div 3+2); , 
     Ellipse (х,у-4,190,357,r,r div 3); 
     Line (х+7,у-б,х+10,у-12); 
     Line (x-7,y-6, х-10, у-12); 
     Circle (x+10,y-12,2); 
     Circle (х-10,у-12,2); 
     FloodFill(x+l,y+4,White); 
     {Определяем  габариты НЛО и помещаем его в кучу} 
     1х  := х-r-1; 
     1у  := у-14; 
     гх  := х+r+1; 
     гу  := у+r  div 3+3; 
     Width := rx - lx + 1; 
     Height:= ry - ly + 1; 
     Size := ImageSize(lx, ly, rx, ry); 
     GetMem (Saucer, Size); 
     Getlmage (lx, ly, rx, ry, Saucer^); 
     {Стираем построенное} 
     Putlmage (lx, ly, Saucer^, xorPut); 
     {Создаем звездное небо} 
     Rectangle(xm,ym,3 *xm,3 *ym); 
     SetViewPort(xm+1,ym+1,3*xm-1,3*ym-1,ClipOn); 
     xm := 2*xm; 
     ym := 2*ym; 
     for i:=1 to 200 do 
      PutPixe1 (Random(xm), Random(ym),  White) ; 
     {Задаем  начальное положение НЛО и направление движения} 
     х  := xm div 2; 
     у  := ym div 2; 
     dx := 10; 
     dy := 10; 
     {Основной  цикл} 
     repeat 
      Putlmage(x,y,Saucer^,xorPut); {Изображаем НЛО на}  
      Delay(pause); {новом месте и после}  
      Putlmage (x, у, Saucer^, XorPut);{паузы  стираем его}  
      {Получаем  новые координаты}  
      loop: x := x+dx; 
      у  := y+dy;  
      {НЛО  достиг границы экрана?}  
      if (x<0) or (x+Width+1>xm)  or  
       (у<0) or (y+Height+1>ym)  then  
       begin {Да - НЛО достиг  границы: меняем направление его перемещения}  
        x := x-dx;  
        y:= y-dy; 
        dx : = GetMaxX div 10 -  Random(GetMaxX div 5);  
        dy := GetMaxY div 30 -  Random(GetMaxY div 15);  
        goto loop  
       end 
     until KeyPressed; 
     if ReadKey=#0 then x :=  ord(ReadKey);  
     CloseGraph  
    end  
  end.

Наверх

Вывод текста

Описываемые ниже стандартные процедуры и функции поддерживают вывод текстовых сообщений в графическом режиме. Это не одно и то же, что использование процедур Write или WriteLn. Дело в том, что специально для графического режима разработаны процедуры, обеспечивающие вывод сообщений различными шрифтами в горизонтальном или вертикальном направлении, с изменением размеров и т.д. Однако в стандартных шрифтах, разработанных для этих целей фирмой Borland, отсутствует кириллица, что исключает вывод русскоязычных сообщений.
С другой стороны, процедуры Write и WriteLn после загрузки в память второй половины таблицы знакогенератора (а эта операция легко реализуется в адаптерах EGA и VGA) способны выводить сообщения с использованием национального алфавита, но не обладают мощными возможностями специальных процедур.
Ниже описываются стандартные средства модуля Graph для вывода текста (табл. 3.5).
Таблица 3.5
Процедуры и функции, связанные с выводом текста в графическом режиме


Заголовок

Назначение

Procedure
OutText(Txt: String);

Выводит текстовую строку, начиная с текущего положения указателя.

Procedure OutTextXY (X,Y: Integer; Txt: String);

Выводит строку, начиная с заданного места. Отличается от процедуры OutText только координатами вывода. Указатель не меняет своего положения.

Procedure
SetTextStyle(Font,Direct, Size: Word);

Устанавливает стиль текстового вывода на графический экран. Здесь Font - код (номер) шрифта; Direct - код направления; Size - код размера шрифта.

Procedure SetTextJustify(Horiz,Vert: Word);

Задает выравнивание выводимого текста по отношению к текущему положению указателя или к заданным координатам. Здесь Horiz - горизонтальное выравнивание; Vert - вертикальное выравнивание. Выравнивание определяет как будет размещаться текст - левее или правее указанного места, выше, ниже или по центру. Здесь можно использовать такие константы: LeftText = 0;{Указатель слева от текста}, CenterText= 1;{Симметрично слева и справа,верху и снизу}, RightText = 2;{Указатель справа от текста}, BottomText= 0;{Указатель снизу от текста}, TopText = 2;{Указатель сверху от текста}.

Procedure SetUserCharSize(XI,X2,Yl,Y2: Word);

Изменяет размер выводимых символов в соответствии с заданными пропорциями. Здесь X1...Y2 - выражения типа Word, определяющие пропорции по горизонтали и вертикали. Процедура применяется только по отношению к векторным шрифтам.

Function TextWidth (Txjt: String): Word;

Возвращает длину в пикселях выводимой текстовой строки. Учитываются текущий стиль вывода и коэффициенты изменения размеров символов, заданные соответственно процедурами SetTextStyle и SetUserCharSize.

Function TextHeight(Txt: String): Word;

Возвращает высоту шрифта в пикселях.

Procedure GetTextSettins(var Textlnfo: TextSettingsType);

Возвращает текущий стиль и выравнивание текста. Здесь Textlnfo - переменная типа TextSettingsType, который в модуле Graph определен следующим образом:
type
TextSettingsType = record
Font : Word; {Номер шрифта}
Direction: Word; {Направление}
CharSize : Word; {Код размера}
Horiz : Word; {Горизонтальное выравнивание}
Vert : Word; {Вертикальное выравнивание}
end;

Function InstallUserFont(FileName: String): Integer; 

Позволяет программе использовать нестандартный векторный шрифт. Здесь FileName - имя файла, содержащего векторный шрифт.

Function InstallUserDriver(FileName: String; AutoDetectPtr: Pointer): Integer;

Включает нестандартный графический драйвер в систему BGI-драйверов. Здесь FileName - имя файла, содержащего программу драйвера; AutoDetectPtr - адрес точки входа в специальную процедуру автоопределения типа дисплея, которая в числе прочих процедур должна входить в состав драйвера.

При горизонтальном направлении вывода указатель смещается в конец выведенного текста, при вертикальном - не меняет своего положения. Строка выводится в соответствии с установленным стилем и выравниванием. Если текст выходит за границы экрана, то при использовании штриховых шрифтов он отсекается, а в случае стандартного шрифта не выводится.
Для указания кода шрифта можно использовать следующие предварительно определенные константы:
const
  DefaultFont = 0;{Точечный шрифт 8x8}
  TriplexFont = 1;{Утроенный шрифт TRIP.CHR} 
  SmallFont = 2;{Уменьшенный шрифт LITT.CHR}
  SansSerifFont = 3;{Прямой шрифт SANS.CHR}
  GothicFont = 4;{Готический шрифт GOTH.CHR}
Замечу, что эти константы определяют все шрифты для версий 4.0, 5.0, 5.5 и 6.0. В версии 7,0 набор шрифтов значительно расширен, однако для новых шрифтов не предусмотрены соответствующие мнемонические константы. В этой версии помимо перечисленных Вы можете при обращении к SetTextStyle использовать такие номера шрифтов:


Номер

Файл

Краткое описание

5

scri.chr

«Рукописный» шрифт

6

simp.chr

Одноштриховый шрифт типа Courier

7

tscr.chr

Красивый наклонный шрифт типа Times Italic

8

Icom.chr

Шрифт типа Times Roman

9

euro . chr

Шрифт типа Courier увеличенного размера

10

bold.chr

Крупный двухштриховый шрифт

Шрифт DefaultFont входит в модуль Graph и доступен в любой момент. Это -единственный матричный шрифт, т.е. его символы создаются из матриц 8x8 пикселей. Все остальные шрифты - векторные: их элементы формируются как совокупность векторов (штрихов), характеризующихся направлением и размером. Векторные шрифты отличаются более богатыми изобразительными возможностями, но главная их особенность заключается в легкости изменения размеров без существенного ухудшения качества изображения. Каждый из этих шрифтов размещается в отдельном дисковом файле. Если Вы собираетесь использовать какой-либо векторный шрифт, соответствующий файл должен находиться в Вашем каталоге, в противном случае вызов этого шрифта игнорируется и подключается стандартный.
Замечу, что шрифт DefaultFont создается графическим драйвером в момент инициации графики на основании анализа текстового шрифта. Поэтому, если Ваш ПК способен выводить кириллицу в текстовом режиме, Вы сможете с помощью этого шрифта выводить русскоязычные сообщения и в графическом режиме. В остальных шрифтах эта возможность появляется только после их модификации.
Для задания направления выдачи текста можно использовать константы:
const
HorizDir = 0;{Слева направо}
VertDir = 1;{Снизу вверх}
Как видим, стандартные процедуры OutText и OutTextXY способны выводить сообщения лишь в двух возможных направлениях - слева направо или снизу вверх. Зная структуру векторных шрифтов, нетрудно построить собственные процедуры вывода, способные выводить сообщения в любом направлении.
Каждый шрифт способен десятикратно изменять свои размеры. Размер выводимых символов кодируется параметром Size, который может иметь значение в диапазоне от 1 до 10 (точечный шрифт - в диапазоне от 1 до 32). Если значение параметра равно 0. устанавливается размер 1, если больше 10 - размер 10. Минимальный размер шрифта. при котором еще отчетливо различаются все его детали, равен 4 (для точечного шрифта - 1).


Наверх

Включение драйвера и шрифтов в тело программы

В Турбо Паскале имеется возможность включения графического драйвера и штриховых шрифтов непосредственно в тело программы. Такое включение делает программу независимой от местоположения и наличия на диске драйверов и шрифтов, а также ускоряет подготовку графических программ к работе (шрифты и драйвер загружаются вместе с программой).
Включение драйвера и шрифтов осуществляется по следующей общей схеме. Сначала с помощью вспомогательной программы BINOBJ.EXE, входящей в комплект поставки Турбо Паскаля, драйвер и шрифты преобразуются в OBJ-файл (файл с расширением .OBJ). Для этого вне среды Турбо Паскаля необходимо вызвать утилиту BINOBJ с тремя параметрами: именем преобразуемого файла, именем получаемого OBJ-файла и глобальным именем процедуры. Эти имена, в принципе, могут быть произвольными, правильными для MS-DOS именами. Например:
  c:\tp\binobj cga.bgi cga cgadrv
В результате такого обращения из каталога ТР на диске С будет вызвана программа BINOBJ и ей будут переданы следующие параметры:
  CGA.BGI - имя файла с преобразуемым драйвером;
  CGA - имя файла с расширением .OBJ, т.е. CGA.OBJ, который будет получен в результате исполнения программы BINOBJ;
  CGADRV- глобальное имя, под которым этот драйвер будет известен программе.
После этого можно написать следующий фрагмент программы:

  Uses Graph; 
  Procedure CGADRV; external;  
    {$L CGA.OBJ}  
  var 
    d, r, e : Integer;  
  begin 
    if RegisterBGIDriver (@CGADRV) < 0 then 
    begin 
      WriteLn ('Ошибка при регистрации  драйвера'); 
      halt  
    end; 
    d := CGA; r := CGAHi;  
    InitGraph (d, r, ''); 
    .......
  
Как видно из этого примера, в программе объявляется внешняя процедура с именем CGADRV (глобальное имя, указанное при обращении к BINOBJ), причем дается директива компилятору отыскать в текущем каталоге и загрузить файл CGA.OBJ, в котором находится эта процедура. Затем осуществляется регистрация драйвера путем обращения к функции RegisterBGIDriver. Единственным параметром этой функции является адрес начала драйвера в памяти (@CGADRV). Функция возвращает значение типа Integer, которое служит для контроля правильности завершения процедуры регистрации драйвера: если это значение меньше нуля, обнаружена ошибка, в противном случае функция возвращает номер зарегистрированного драйвера. В примере контролируется правильность регистрации драйвера и, если ошибка не обнаружена, инициируется графический режим работы экрана.
Аналогичным образом можно присоединить к программе стандартные штриховые шрифты (матричный шрифт 8x8 входит в состав модуля Graph и поэтому присоединять его не надо). Присоединение шрифта строится по описанной схеме за тем исключением, что для его регистрации вызывается функция RegisterBGIFont. Например, после преобразования 
  c:\Pascal\binobj litt.chr litt litt
можно использовать операторы
  Procedure Litt;External;
  {$L Litt.obj}
  .......
   if RegisterBGIFont (@litt) < 0 then ...
Обратите внимание: регистрация и драйвера, и шрифтов должна предшествовать инициации графического режима.
Регистрировать можно также драйверы (шрифты), которые не компилируются вместе с программой, а загружаются в динамическую память. Например:
  Uses Graph;  
  var 
   р: Pointer;  
   f: file;  
  begin 
   Assign(f,'Litt.chr'); {Открываем файл}  
   Reset(f,1); {LITT.CHR для чтения}  
   GetMem(p,FileSize(f))  ; {Резервируем для него область кучи нужного размера} 
   BlockRead(f,pA,FileSize(f)){Читаем файл} 
   WriteLn(RegisterBGIFont (p)){Регистрируем  шрифт} 
  end.
  


Наверх

Назад | Вперед