Rambler's Top100


До этого наши программы работали в текстовом режиме. Кроме него существует ещё и графический. В текстовом режиме вам доступны только 256 символов псевдографики (как бы графики), которые вы можете отобразить на экране. Этих символов достаточно для простых графических работ, например: нарисовать таблицу, написать карточную игру, оболочку (типа Norton Commander), многоконный тестовый редактор (взять даже BP) и для много другого. Графический же режим характеризуется возможностью задавать цвет любой точки экрана. И соответственно мы можем рисовать сложные геометрические фигуры. Для работы с графикой в комплект поставки BP входит модуль graph, в котором содержатся основные функции и процедуры. Так же для работы нужны специальные графические драйвера, которые тоже входят в BP и находятся в папке BP\BGI. Кстати модуль graph называется библиотекой BGI (Borland Graphic Interface). Прежде чем начать работу с графикой нам надо перейти в графический режим, т.к. наша программа запускается по умолчанию в текстовом. И соответственно перейти назад в текстовый после завершения работы. Поэтому каркас программы превращается в нечто более сложное, чем было до этого:

uses Graph;

var
  grDriver: Integer;
  grMode: Integer;
  Errcode: Integer;
begin

  grDriver := Detect;
  InitGraph(grDriver, grMode,'');
  Errcode := GraphResult;

  if Errcode <> grOk then
  begin  
      Writeln('Graphics error:');
     Writeln(GraphErrorMsg(Errcode));
     Writeln('Program aborted...');
     Halt(1)
  end

   ..... do something ...
    Readln;
    CloseGraph
end.

ну что неплохое начало :) Эта программа "ничего" не делает. Просто переходт в графический режим, ждёт нажатия клавиши и выходит назад в текстовый. Теперь давайте разберём, что же мы тут использовали:

procedure InitGraph(var GraphDriver:Integer; var GraphMode: Integer; PathToDriver: string);
процедра инициализации графики. Т.е. по просту говоря для перехода в графический режим. GraphDriver - графический драйвер, которым мы хотим пользоваться. Мы задаём этому параметру значение Detect - авто определение(т.е. программа сама определит наилучший режим). Для современной техники это 640x480x16 :)
GraphMode определяет графический режим, т.к. у нас стоит автоопределение он определится сам (это и будет режим 640х480 16 цветов).
PathToDriver - путь к файлу драйвера. Если путь не задаётся, как это сделали мы, то драйвер ищётся в текущем каталоге программы. Если использовать автоопределение, то нам нужен файл egavga.bgi из папки bp\bgi. На всякий случай, вдруг кто не знает скажу кое-что об обозначениях что значит 640х480х16 - то точная характеристика графического режима: (число точек по горизонтали)х(число точек по вертикали)х(количество цветов). Т.е. 640 точек по горизонтали, 480 по вертикали и 16 цветов. В интернете сейчас можно найти версии файлов *.bgi для работы в более лучших режимах. Однако я всё же буду рассказывать про стандартную поставку, т.к. углубляться в графику мы пока особо не будем. Всему своё время. Следующая функция возвращает нам резльтат предыдущей функции или процедуры, если возникла ошибка:
function GraphResult: Integer;
Если возникла ошибка (например при инициализации графики программа не нашла файл с драйвером), то возвращаемое значение не равно grOk. Расшифровку ошибки можно получить вызвав:
function GraphErrorMsg(Errorcode: Integer): string;
возвращает строку, содержащую содержание ошибки, заданной переменной Errorcode. Не помню писал ли я про halt, так что если повторюсь, то ничего:
procedure Halt ( Exitcode: Word );
останавливает выполнеие программы и передаёт управление операционной системе. Если параметр Exitcode = 1 значит это завершение программы с ошибкой. Ну и последняя процедура:
procedure CloseGraph;
переходит назад в текстовый режим. Обратите внимание на вызов ReadLn перед вызовом CloseGraph. Зачем мы это делаем? Если опустить этот вызов, то после выполнения программы сразу произойдёт выход в текстовый режим и соответственно экран очистится. Что бы посмотреть результаты выполнения программы мы вынужденны ожидать нажатия клавиши. Если вы думаете, что это сложно, то вы ошибаетесь, что бы создать простое пустое окошко в программе для Windows надо написать раза в три-четыре больше :) Этот код будем считать каркасом наших будующих графических программ. Теперь самое время поговорить о координатах. Всё дело в том, что в компьютерах координатные оси направленны по другому. Для примера: мы пользуемся декартовой системой координат ( © by Рене Декарт :), центром которой является точка (0,0) опять же извиняюсь за убогость, но картинку не приаттачить:

        ^ y
        |
        |
        |
--------+------->
        |      x
        |
        |
В компьютерах же используется несколько другая система
 +----------------------->
 |                      x
 |
 |
 |
 |
 |
 |
 V y

Как видите в ней отсутствуют отрицательные числа и ось ординат (y) направленна вниз. Вызвано это следующей причиной. Разрешение экрана определяют в пикселях. Пиксель (сокращение от Picture Element - элемент картинки) - это минимальная логическая точка (или если хотите единица) экрана. Т.е. разрешению экрана 800х600 означает, что по оси Х у нас максимально 800 пикселей, а по оси Y - 600. Один пиксель может состоять из множества физических точек (т.е. точек подсветкой которых можно управлять аппаратно). Так вот левому верхнему углу экрана соответствеут пиксель с координатами (0,0), а правому нижнему в зависимости от разрешения. Если оно 800х600 тогда (799, 599). Пиксель - это всегда целое положительное число. У вас не может быть 1/3 пикселя или пиксель с координатой (-5, -5). Так как мы не можем посветить на экране 1/3 пикселя и мы не можем высветить пиксель (-5, -5) т.к. это выпадает за границу экрана. Кстати количесто цветов на экране определяется количеством бит видеопамяти, отводимой под 1 пиксель. Так если под один пиксель отводится 1 байт (8 бит) тогда это режим с 256 цветами (напомню, что столько может быть различных комбинаций бит в байте). Теперь рассмотрим самую важную процедуру в графике - подсветка пикселя:

procedure PutPixel(X, Y: Integer; color: Word);
соответственно х, у - координаты. Color - цвет. Кстати о цветах. Для первых 16 цветов введены специальные константы. Ниже приводится таблица соответствия цветов и номеров (для текстовой версии к сожалению графа пример останется незаполненной, т.к. нет такой возможности):
Номер цветаНазвание константыРусское название (для текстовой версии)Примерчик
0 BLACKчёрный
1 BLUE синий
2 GREEN зелёный
3 CYAN голубой
4 RED красный
5 MAGENTA фиолетовый
6 BROWN коричневый
7 liGHTGRAY светло серый
8 DARKGRAYтёмно серый
9 liGHTBLUEсветло синий
10 liGHTGREENсветло зелёный
11 liGHTCYANсветло голубой
12 liGHtrEDрозовый
13 liGHTMAGENTAсветло фиолетовый
14 YELLOWжёлтый
15 WHITEбелый

поэтому putpixel (10, 10, 15) и putpixel (10, 10, WHITE) произведёт одинаковый эффект. Таким образом мы можем рисовать любые фигуры, используя посветку точек на экране. Однако в BGI есть специальные процедуры для рисования графических примитивов - линий, окружностей, прямоугольников, вывода текста, работы с изображениями и многим другим. К сожалению в формат выпусков не влезет описать все возможности BGI, да многое и не понадобится. Поэтому проведу обзор самых нужных функций и процедур.

function GetPixel(X,Y: Integer): Word;
возвращает цвет точки с координатами х,у
procedure line(x1, y1, x2, y2: Integer);
рисует линию от точки (x1,y1) к точке (x2, y2). Цвет линии устанавливается с помощью следующей процедуры:
procedure SetColor(Color: Word);
устанавливает текущий цвет для рисования графических объектов. По умолчанию цвет белый.
procedure SetBkColor(ColorNum: Word);
устанавливает цвет фона, по умолчанию цвет фона чёрный. Цвет фона - это цвет экрана на котром мы рисуем. Что-то типа цвета рабочего стола в Windows :)
procedure Circle(X,Y: Integer; Radius: Word);
рисует окружность с центром в точке (x,y) и радиусом Radius. Цвет окружности задаётся с помощью SetColor.
procedure Ellipse(X, Y: Integer; StAngle, EndAngle: Word; XRadius, YRadius: Word);
рисует элипс с центром в точке (x,y), полу-осями xradius, yradius, от угла stangle до угла endangle. Угол считается против часовой стрелки (т.е. 00 - 3 часа, 900 - 12 часов). Цвет задаётся с помощью SetColor.
procedure Bar(x1, y1, x2, y2: Integer);
рисует закрашенный прямоугольник. левый верхний угол (х1,у1) - правый нижний (х2, у2). Стиль кисти и цвет закраски определяется следующей процедурой:
procedure SetFillStyle(Pattern: Word; Color: Word);
устанавливает текущий стиль кисти (pattern) и её цвет (color). Цвет, который задаётся setcolor и setfillstyle не совпадают и применяются к разным графическим объектам. Первый к графическим примитивам, второй к закрашенным объектам. Существует несколько предопределённых стилей, которые приведены в нижеследующей таблице:
Констнанта Численное значение Описание
EMPTY_FILL 0 Закрашивает цветом фона
(несмотря на цвет, который вы указали в setfillstyle)
SoliD_FILL 1 Равномерная закраска
liNE_FILL 2 Закрашивает ---
LTSLASH_FILL 3 Закрашивает ///
SLASH_FILL 4 Закрашивает ///, толстые линии
BKSLASH_FILL 5 Закрашивает \\\, толстые линии
LTBKSLASH_FILL 6 Закрашивает \\\
HATCH_FILL 7 В "клеточку"
XHATCH_FILL 8 В клеточку под углом
INTERLEAVE_FILL 9 Чередование
WIDE_DOT_FILL 10 Широко расположенные точки
CLOSE_DOT_FILL 11 Близко расположенные точки
USER_FILL 12 Стиль определять вам!
что бы лучше понять напишите программу, которая бы в цикле выводила прямоугольники и меняла стиль, тогда вы поймёте что к чему.
procedure FillEllipse(X, Y: Integer; XRadius, YRadius: Word)
рисует закращенный элипс с центром (х,у) и полу-осями xradius, yradius. Стиль и цвет определяются Setfillstyle
procedure ClearDevice;
очищает экран (заполняет его цветом фона).
procedure OutTextXY(X,Y: Integer; TextString: string);
выводит строку textstring, начиная с точки (х,у). В графическом режиме для вывода нужно обязательно использовать эту процедуру.
procedure GetImage(x1, y1, x2, y2: Integer; var BitMap);
копирует картинку в память. Картинка определяется координатами (x1, y1) - левый верхний угол; (x2, y2) - правый нижний угол. BitMap - это область куда мы должны копировать картинку. Размер области памяти складывается из количества необходимого под картинку + 6 байт. Два первых слова этих байт хранят ширину и высоту картинки, третье слово зарезервировано и зачем оно нужно это охраняемая тайна компании Borland :)
procedure PutImage(X, Y: Integer; var BitMap; BitBlt: Word);
соответственно обратная процедура. Рисует изображение из BitMap на экран, начиная с координат (х,у). BitBlt - определяет способ вывода картинки. Ниже даны его возможные значения:
Константа Численное значение Смысл
NormalPut 0 Копирует изображение на экран (цвет каждого пикселя сохраняется)
XORPut 1 Результат получается как XOR, применённый к точке на экране и точке изображения
OrPut 2 как OR, применённый к точке на экране и точке изображения
AndPut 3 как AND, применённый к точке на экране и точке изображения
NotPut 4 Цвет каждого пикселя инвертируется (заменяется на противоположный)
function ImageSize(x1, y1, x2, y2: Integer): Word;
эта функция используется, что бы вычислить размер памяти BitMap в процедуре GetImage, т.е. размер необходимый под сохранение области + 3 слова.
function GetMaxX: Integer; / function GetMaxY Integer;
возвращает максимальное значение Х / Y координаты.
procedure FloodFill(X, Y: Integer; Border: Word);
закрашивает область, которая содержит точку (x,y) и ограничена кривой с цветом Border. Стиль и цвет определяются Setfillstyle
это далеко не полный список процедур и фунций из библиотеки BGI. Что бы понять как они работают лучше всего, что бы вы попробовали вызвать их поочереди и посмотреть что будет. Кстати хороший пример работы этих и других функций и процедур находится в программе BP\EXAMPLES\DOS\BGI\bgidemo.pas. Сейчас я как раз разберу пример от туда. Это пример с НЛО :) запустите и посмотрите, так как надо объяснять пользуясь картинкой. Если НЛО летает медленно, тогда в самом конце измените задержку (например на delay (1000) - в 10 раз быстрее) так как на моём P4 за НЛО не уследить :) этот параметр в примере равен кстати 70 :)

program demo;

uses Crt, Graph;

const
  r  = 20;
  StartX = 100;
  StartY = 50;

var
  grDriver, grMode, Errcode : integer;
  MaxX, MaxY  : word;
  Saucer    : pointer;
  X, Y      : integer;
  ulx, uly  : word;
  lrx, lry  : word;
  Size      : word;
  I         : word;

procedure MoveSaucer(var X, Y : integer; Width, Height : integer);
var
  Step : integer;
begin
  Step := Random(2*r);

  if Odd(Step) then
    Step := -Step;

  X := X + Step;
  Step := Random(r);

  if Odd(Step) then
    Step := -Step;

  Y := Y + Step;

  if X > MaxX then
     X :=  MaxX
  else
    if (X < 0) then
       X := 0;
  if Y > MaxY then
      Y := 1
  else
      if Y < 0 then
        Y := 0
end;

begin
     grDriver := Detect;
     InitGraph(grDriver, grMode,'');
     Errcode := GraphResult;
     if Errcode <> grOk then
     begin
       writeLn ('Graphics error:', GraphErrorMsg(Errcode));
       halt (1)
     end;

  ClearDevice;

  MaxX := getmaxx;
  MaxY := getmaxy;

{ рисуем НЛО }
  Ellipse(StartX, StartY, 0, 360, r, (r div 3)+2);
  Ellipse(StartX, StartY-4, 190, 357, r, r div 3);
  line(StartX+7, StartY-6, StartX+10, StartY-12);
  Circle(StartX+10, StartY-12, 2);
  line(StartX-7, StartY-6, StartX-10, StartY-12);
  Circle(StartX-10, StartY-12, 2);
  SetFillStyle(SolidFill, WHITE);
  FloodFill(StartX+1, StartY+4, GetColor);

{ вычисляем границы прямоугольника в который вмещается НЛО }
  ulx := StartX-(r+1);
  uly := StartY-14;
  lrx := StartX+(r+1);
  lry := StartY+(r div 3)+3;

  Size := ImageSize(ulx, uly, lrx, lry);
  GetMem(Saucer, Size);
  GetImage(ulx, uly, lrx, lry, Saucer^);
  PutImage(ulx, uly, Saucer^, XORput);

{ рисуем звёздное небо :) }
  for I := 1 to 1000 do
    PutPixel(Random(MaxX), Random(MaxY), random (WHITE));

  X := MaxX div 2;
  Y := MaxY div 2;

  repeat
    PutImage(X, Y, Saucer^, XORput);
    Delay (10000);
    PutImage(X, Y, Saucer^, XORput);
    MoveSaucer(X, Y, lrx - ulx + 1, lry - uly + 1);
  until Keypressed;

  FreeMem(Saucer, size);

  ReadLn;
  closegraph
end.

Сначала одна неизвестная до этого момента функция

function Odd(X: Longint): Boolean;
проверяет является ли число нечётным. и возвращает true в случае успеха. Так вот думаю вы ужу понаблюдали за НЛО, теперь разберём как это происходит. Процедура MoveSaucer не осуществляет ничего сверх естественного и просто изменяет координаты НЛО, которые хранятся в глобальных переменных Х,Y. На ней подробно останавливаться думаю не стоит. Так же я не буду комментировать процесс рисования объекта, так как это просто последовательный вызов графических функций безо всяких алгоритмов. Я бы хотел обратить ваше внимание на следующие строки:
Size := ImageSize(ulx, uly, lrx, lry); - вычисляем память, которая нам нужна для хранения картинки НЛО
GetMem(Saucer, Size); - выделяем эту память
GetImage(ulx, uly, lrx, lry, Saucer^); - захватываем изображение НЛО в буффер Saucer.
PutImage(ulx, uly, Saucer^, XORput); - стираем НЛО
тут придётся вспомнить логические операции (как неужели вы их забыли !!!). Если забыли, тогда вернитесь к выпуску "#0E А сила - она, брат, в правде". Так вот что будет, когда мы число xor'им само с собой (a xor a) ??? Правильно 0. То же самое происходит и когда мы вызываем PutImage(ulx, uly, Saucer^, XORput). Ведь в Saucer у нас находится изображение НЛО, когда же мы накладываем изображение само на себя используя операцию xor, то получается 0, т.е. изображение цвет всех точек которого равен 0. А так как цвет фона у нас чёрный (0) то получается эффект стирания, а не перерисовки. Продолжим:
repeat
PutImage(X, Y, Saucer^, XORput); - рисуем НЛО
Delay (10000); - пауза
PutImage(X, Y, Saucer^, XORput); - стираем
MoveSaucer(X, Y, lrx - ulx + 1, lry - uly + 1); - двигаем НЛО
until Keypressed;
вроде бы ничего сложного, но задумайтесь на минутку почему остаются звёзды ? Давайте поставим задержку (delay) совсем большой и присмотримся к НЛО .... через него просвечивают звёзды! (что бы лучше это рассмотреть увеличте количество звёзд ). Всё из-за того что способ применённый здесь не является правильным. Хотя согласитесь рассмотреть эти маленькие точки тяжело и искажения не очень заметны. Теперь рассмотрим внимательнее исходник. Когда мы рисуем НЛО мы опять применяем операцию xor - поэтому если под НЛО находится звёздочка, то она просвечивает, причём с искажением цвета. Однако когда мы стираем НЛО мы вновь применяем xor и НЛО стирается, а звездочке возвращается свой "первозданный" цвет. Давайте обратимся к цифрам. Пускай у нас будет звезда жёлтого (14) цвета.
14 = 1110b
1111b xor 1110b = 0001b - синий цвет. это в момент рисования.
0001b xor 1111b = 1110b - жёлтый. в момент стирания. Как говорят математики ЧТД.
Данную анимацию можно применять для взаимно обратных цветов, т.е. по цветам с одинаковым отступом от концов палитры (палитра - набор цветов, каждому цвету соответствует номер в палитре, помните таблицу констант?). Такими цветами и являются: чёрный-белый (0 -15), синий-жёлтый (1-14) и т.д. Про правильную анимацию я раскажу как нибудь в другой раз.




программирование на Паскале, Pascal, BP, TP, BorlandPascal, TurboPascal turbo pascal 7.0, borland pascal 7.0, языки программирования, pascal учебник
Рад приветствовать! =) Начнем мы с истории данного сайта. Изначально он разрабатывался как типично авторский проект, не неся в себе особых целей. Я лишь хотел освоить азы html верстки и опробывать свои силы в разработке web-сайтов. Тематика "программирование на Паскале" была выбрана не случайно, до этого я долго изучал этот ЯП и всё это вылилось в написание собственного самоучителя по Паскалю. Сейчас это всё вспоминается с некоторой долей иронии и улыбкой на лице. В нынешнее время я полностью занимаюсь web-разработками и отошел от прикладного программирования, но данный проект решил всё же не забрасывать и вдохнуть в него новую жизнь, освежив дизайн и контент.

С уважением, Евгений Злобин
турбо-паскаль скачать, файлы паскаль, бесплтано скачать pascal