КАК ваще можно рисовать на этом "ужасном" языке?

КАК и что мы будем рисовать?
КАК перейти в графический режим?
КАК раскрасить весь мир?
КАК рисовать простейшие фигуры?
КАК рисовать многоугольники?
КАК закрашивать свои фигуры?
КАК вывести текст?
КАК поменять шрифт?
КАК нарисовать своего героя?
КАК вывести картинку из файла?
КАК подключить свой графический драйвер?
КАК обойтись без файла EGAVGA.BGI?

Вопрос: КАК и что мы будем рисовать?

Когда-то, давным-давно, когда дебри ДОСа простирались от края и до края, программисты были самыми несчастными людьми. Им приходилось программировать графику под ДОС... Эта операционная система, ветхая, как и сами компьютеры, не реализует ничего, кроме работы с файлами, да и то на довольно низком уровне. Толку от неё практически никакого не было, и с графикой приходилось работать напрямую, используя 10-ый вектор прерывания. О прерываниях в следующем разделе, а пока скажем лишь, что работу с регистрами скрыли от нас всевозможные графические библиотеки. В компании Borland тоже сделали одну. Теперь она часть языка СИ, того, что для ДОС. Так уж повелось, что Паскаль, скажем, всегда рассматривается комплексно, с графикой. В СИ - язык отдельно, графика отдельно. Есть книги по языку, есть книги по графике. Разделяй и властвуй!
Работа с графикой осложняется тем, что надо подключать специальный EGA/VGA-драйвер, разработнный Borland для инициализации и работы с графическим режимом видеоплаты. Для этого надо этим файлом располагать. И конечно же, в ОС Windows этот драйвер не поддерживается. Куда деваться? использовать старый добрый Borland C++ 3.0 (3.1). Там и драйвер есть в папке BGI и поддерживает она его. Но для работы вашей программы с графикой, придётся комплектовать её файлом EGAVGA.BGI. Его целесообразно размещать в той же папке, что и программа или в подпапке, чтобы случайно не стёрли. Также надо разрешить компилятору работать с графикой. Выставьте флажок в меню: options->linker->libraries->Graphic library. Убеждаю вас, что ни Visual C++, ни Borland C++ 5.0 к низкоуровневой графике вас и близко не подпустят. Освоив эту графику, вы без труда освоите графику под Windows, когда мы с нею столкнёмся. Принципиальных отличий нет, если не считать некоторых изменений в именах функций и того, что эти функции могут быть членами класса (Visual C++). Главное - понять концепцию.

Вопрос: КАК перейти в графический режим?

Самый распространённый способ - использовать функцию initgraph, которая задаёт и режим и резрешение и указывает путь к EGAVGA.BGI. Рассмотрим её синтаксис:

initgraph(*int far grdriver, *int far grmode, char *path);

Первый аргумент указывает тип видеоадаптера. Он принимает числовое значение:
Аргумент grdriver:

Тип видеоадаптера
Значение аргумента:
автораспознавание
DETECT или 0
CGA
1
MCGA
2
EGA
3
EGA64
4
EGA-Mono
5
зарезервировано
6
Mono Hercules
7
ATT400
8
VGA
9
PC3270
10

Обо всех этих видеоадаптерах можно продробно прочитать в книге по мониторам или по железу ПК. Скажу только, что все мониторы на сегодняшний день остаются верны стандарту VGA (256 цветов) и его расширению Super VGA (SVGA), который поддерживает True Color, High Color (24 и 32-битовые цветовые плоскости) и др. Это не мешает быть им совместимыми с EGA (16 цветов), на который вобщем-то и расчитан ДОС. Формат CGA - совершенно ужасные цвета, которые, если вы понмните реализовывал допотопный компьютер ZX Spectrum - не зелёный, а салатовый, не розовый, а пунцовый и др. Hercules в своё время был конкурентом для них всех, но его задавили. PC3270 если память не изменяет разработка фирмы IBM. Теперь каждая фирма придерживается общего стандарта аналогового формата VGA (EGA/CGA были цифровые). Драйвер EGAVGA.BGI позволит вам реализовать только 16 цветов. На большее он не расчитан. Однако, существует несколько самопальный драйвер SVGA256.BGI, который даёт возможность создавать очень красивые игры и приложения. К сожалению, он не совместим с Windows 2000 (NT). Скачать.

Второй аргумент - графический режим, то есть разрешение. Иногда ещё режим называют модой.

Режим
Значение аргумента
VGA 640x200 16 цветов (низкое разрешение)
0
VGA 640x350 16 цветов (среднее разрешение)
1
VGA 650х480 16 цветов (высокое разрешение)
2
EGA 640x200 16 цветов (низкое разрешение)
0
EGA 640x350 16 цветов (среднее разрешение)
1

Третий аргумент - путь к файлу драйвера EGAVGA.BGI. Если этот файл в папке c:\tc\bgi\, то значение его будет таково: c:\\tc\\bgi\\. В виду того, что одинарный слэш (\) уже зарезервирован, в СИ, в отличие от других языков, все пути указываются с двойными слэшами. Тем не менее, если вы прописываете путь к библиотечному файлу, то кавычки будут одинарными:

#include "c:\tc\bin\my\biblioteka.h"

Итак, у вас всё готово, чтобы перевести компьютер в графический режим.

#include <conio.h>
#include<graphics .h>

void main()
{
clrscr();
cprintf("Текстовый режим.");
getch();

//Переходим в графический режим!
int a=9, b=2;
initgraph(&a, &b, "c:\\tc\\bgi\\");

outtext("А это графический режим!");
getch();

closegraph();
cprintf("\nУвы, снова текстовый!");
getch();

}


Вопрос: КАК раскрасить весь мир?

Существует 16 стандартных цветов:

№ цвета
Цвет
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


Функция setcolor(int color); меняет цвет на указанный в скобках, и дальше всё рисуется в этом цвете. Цифры вы можете брать из таблицы. Родственная ей функция setbkcolor(int color); меняет цвет фона. На цвет рисунка это не влияет.

Узнать какой установлен текущий цвет и цвет фона можно с помощью родственных функций: getcolor(), getbkcolor(). В СИ под ДОС, цвет - целое число. В приложениях под Windows цвет - особый тип данных состоящий из трёх компонент: красной, синей и зелёной. Там вы не связаны 16 цветами!


Вопрос: КАК рисовать простейшие фигуры?

Простейшие фигуры в программировании называются графическими примитивами. Вот некоторые из них:

*Линия
Напишем программу, которая рисует линии, причём тип линии каждый раз меняется с помощью setlinestyle.

#include <graphics.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <conio.h>

//массив стилей линий
char *lname[] = {
"SOLID_LINE",
"DOTTED_LINE",
"CENTER_LINE",
"DASHED_LINE",
"USERBIT_LINE"
};

int main(void)
{

int gdriver = DETECT, gmode, errorcode;

int style, midx, midy;
char stylestr[40];

initgraph(&gdriver, &gmode, "c:\\tc\\bgi");

//проверка возможно ли инициализировать графику
errorcode = graphresult(); //возвращаем код ошибки
if (errorcode != grOk) //и если ошибка
{
printf("Graphics error: %s\n", grapherrormsg(errorcode)); //Выводим стандартное сообщение
getch();
exit(1); //аварийный выход из программы
}

midx = getmaxx() / 2; //находим координаты середины экрана
midy = getmaxy() / 2; //деля максимум по х и по у пополам

for (style=SOLID_LINE; style<=USERBIT_LINE; style++)
{

setlinestyle(style, 1, 1); //устанавливаем стиль линии
strcpy(stylestr, lname[style]); //копируем в строку название типа
line(0, 0, midx-10, midy); //рисуем саму линию. Аргументы функции х1, у1, х2, у2.
rectangle(0, 0, getmaxx(), getmaxy()); //рисуем рамку


outtextxy(midx, midy, stylestr); //выводим по центру название стиля

getch();
cleardevice(); //очищаем экран
}

closegraph(); //возвращаемся в текстовый режим
return 0;
}

Примечание. Тип линии относится не только к команде line, но ко всем другим графическим примитивам. Рамки и всё остальное тоже будет пунктирным, если не вернуть стиль на исходный.

Ещё есть хорошая функция linerel(x,y), которая рисует линию из текущей позиции в позицию, указанную в аргументах и lineto(x,y), что сводит с данной точкой начало координат

*Окружность
Задав координаты центра и радиус, мы нарисуем окружность.
circle(x, y, radius);

*Точка
putpixel(x,y,color); выводит точку по координатам цвета color. Применив генератор случайных чисел, вы сможете вывести на экран звёздное небо.

*Рамка

Для рамки надо указать координаты левого верхнего и правого нижнего углов:
bar(x1,y1,x2,y2);

*3D рамка. 3-х мерная рамка применяется для вывода столбиковых диаграмм.
bar3d(x1,y1,x2,y2, width, topflag);
width - Толщина рамки, число topflag задаёт наложение верха на рамку. Ег обычно ставят 1.

bar3d(10,10,100,100,10,1);

*Прямоугольник

rectangle(x1,y1,x2,y2);

*Сектор

Вы рисуете окружность, из которой выкушен сектор. Функцию удобно использовать для круговых диаграмм в экономических приложениях. Представьте, что центр окружности в начале координат. Начальный и конечный угол отсчитывается так, чтобы получился нужный сектор. Например, чтобы выкусить первую четверть окружности, начальный угол (stangle) будет =0, а конечный (endangle) = 90. Также надо указать и радиус.

#include<graphics.h>
#include<conio.h>

main()
{
int a=9, b=2;
initgraph(&a, &b,"");

setbkcolor(15); //цвет фона

setfillstyle(1,4); //задаём стиль заполнения сектора
pieslice(100,100,0,110,50); //сам сектор от 0 до 110 градусов

setfillstyle(1,14);
pieslice(100,100,110,160,50);

setfillstyle(1,1);
pieslice(100,100,160,280,50);

setfillstyle(1,2);
pieslice(100,100,280,360,50);

getch();


}


Вопрос: КАК рисовать многоугольники?

Функция fillpoly принимает массив парных точек и число парных точек, затем соединяет их линиями. Ваше дело задать количество вершин своего многоугольника. Если их очень много, фигура получится гладенькой, и можно нарисовать что-то хорошее, типа Микки-Мауса или Чип и Дейла. Раньше этот приём часто применялся в играх. Причём, если фигурка маленькая, то неровности заметно меньше.

#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

int main(void)
{
int gdriver = DETECT, gmode, errorcode;
int i, maxx, maxy;
int poly[8];
initgraph(&gdriver, &gmode, "");

maxx = getmaxx(); //Получаем максимальные координаты по х и по у.
maxy = getmaxy();

poly[0] = 20; //1-я вершина
poly[1] = maxy / 2;

poly[2] = maxx - 20; //2-я вершина
poly[3] = 20;

poly[4] = maxx - 50; //3-я вершина
poly[5] = maxy - 20;

poly[6] = maxx / 2; //4-я - то же, что и первая
poly[7] = maxy / 2;

//Рисуем, меняя способы заполнения
for (i=EMPTY_FILL; i<USER_FILL; i++)
{
setfillstyle(i, getmaxcolor());
fillpoly(4, poly);
getch();
}
closegraph();
return 0;
}


Вопрос: КАК закрашивать свои рисунки?

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

fillellipse(x,y, xradius, yradius);
Заполняет эллипс текущим стилем заполнения. У эллипса два радиуса по х и по у, поэтому надо указать оба. Если они равны, получится окружность.

Нарисуем окно, как в Windows.

#include <graphics.h>
#include <conio.h>
#include <dos.h>

//Процедура вывода окна
void okno(int x1, int y1, int x2, int y2)
{
setfillstyle(1,7);
bar(x1,y1,x2-1,y2-1);
line(x1,y2-1,x2,y2-1);
setcolor(0);
line(x2-2,y2,x2-2,y1);
}


//Рисуем кнопку
void button(int x1, int y1, int x2, int y2)
{
setfillstyle(1,7);
bar(x1,y1,x2-1,y2-1);
line(x1,y2-1,x2-2,y2-1);
setcolor(0);
line(x2-2,y2-2,x2-2,y1);
setcolor(15);
line(x1,y1,x1,y2-2);
setcolor(15);
line(x1,y1,x2-2,y1);

}

main()
{
//Сразу объявим все координаты
int a=9,d=2;
int x1=10,y1=10,x2=300,y2=100;
int xb1=100,yb1=70,xb2=200,yb2=90;
int xt=x1+35,yt=y1+10;
int xc=xb1+40;
int yc=yb1+6;

initgraph(&a, &d, "c:\\tc\\bgi");

//Рисуем окно и кнопку
okno(x1,y1,x2,y2);
button(xb1, yb1, xb2, yb2);

//Выводим на них текст
setcolor(0);
outtextxy(xc,yc,"OK");
outtextxy(xt,yt,"Semenido LTD presents...");
setbkcolor(0);

_AX=1;
geninterrupt(0x33);

getch();

}

Ну что, похоже? В своё время многие увлекались тем, что создавали свой Windows - писали под ДОС флажки, поля ввода, галочки, кнопки. И куда всё исчезло? Когда есть Visual C++ и Delphi не хочется забивать себе голову такими мелочами...

Теперь посложнее - нарисуем пароходик, чтобы низ был красный, а верх - жёлтый.

#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

int main(void)
{
int gdriver = DETECT, gmode;
int i, maxx, maxy;
//Массив координат палубы
int poly[18]={100,100,
100,90,
110,90,
110,60,
130,60,
130,90,
140,90,
140,100,
100,100
};

//Массив координат корпуса
int poly1[8]={70,100,
170,100,
140,130,
100,130
};

initgraph(&gdriver, &gmode, "");

maxx = getmaxx();
maxy = getmaxy();

//Рисуем палубу
setfillstyle(1, 14);
fillpoly(9, poly);

//Рисуем корпус
setfillstyle(1, 4);
fillpoly(4,poly1);

//Рисуем дым
setfillstyle(1, 1);
fillellipse(185,30,40,15);
fillellipse(140,30,10,10);
fillellipse(130,40,10,10);
fillellipse(120,50,10,10);

//Иллюминатор
setfillstyle(1,0);
fillellipse(119,80,8,8);

outtextxy(170,25,"BIOS");
getch();

closegraph();
return 0;
}


Вопрос: КАК вывести текст?

Собственно, мы это уже делали с помощью функции outtext(char *text), где в скобках строка, которую надо вывести на экран. Но если вы хотите вывести строку в нужное место, используйте outtextxy(int x, int y, char *text); Она позволяет выводить текст так же легко, как в текстовом режиме при использовании gotoxy(x,y)!


Вопрос: КАК поменять шрифт?

Русский шрифт поменять можно только изменив разрешение экрана. Тогда он будет большой и некрасивый. Иначе можно забацать только латиницу. Налицо притеснения кириллоязычных пользователей! Шрифты хранятся в папке BGI и носят расширения **.chr.
Задать размер текста, а также его шрифт поможет функция settextstyle();

#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>

//массив шрифтов
char *fname[] = { "DEFAULT font",
"TRIPLEX font",
"SMALL font",
"SANS SERIF font",
"GOTHIC font"
};

int main(void)
{

int gdriver = 9, gmode=0;
int style, midx, midy;
int size = 1; //размер шрифта

initgraph(&gdriver, &gmode, ""); //Если драйвер в текущей папке, пишем пустые кавычки

midx = getmaxx() / 2; //вычисляем середину экрана
midy = getmaxy() / 2;

settextjustify(CENTER_TEXT, CENTER_TEXT); //задаём выравнивание по центру по х и по у

//прокручиваем все шрифты и выводим на экран
for (style=DEFAULT_FONT; style<=GOTHIC_FONT; style++)
{
cleardevice();
settextstyle(style, HORIZ_DIR, size); //HORIZ_DIR - вывод текста слева-направо
outtextxy(midx, midy, fname[style]); //пишем по центру названия из массива fname
size++; //увеличиваем размер шрифта на 1
getch();
}

closegraph();
return 0;
}

Кроме вывода слева-направо, можно задать и другое расположение шрифта:
VERT_DIR - вертикальное расположение.


Вопрос: КАК нарисовать своего героя?

С помощью функций рисования графических примитивов и функций копирования изображения (putimage/getimage) на экран, можно выводить нехитрые фигурки типа колобков и яблок, которыми пользователь сможет управлять с клавиатуры. Объектно-ориентированный подход, и пара-тройка алгоритмов... Налицо игра! В своё время весь рынок был завален самопальными играми под DOS. К 2000-му году мы наконец-то победили эти игрушки с надписью: "Да здравствует Сибирское хаккерство!". Сделаем простенькую заготовку для игры, где по экрану бегает некое существо и управляется с клавиатуры. Применив знания по массивам и рисованию графических примитивов, вы без труда сворганите ему лабиринт, в котором он будет существовать.

Итак, кого будем рисовать? Пусть это будет колобок из игры PACMAN. Нарисуем его с помощью сектора pieslice - кусок пирога.


#include <graphics.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>


//Прототип функции рисования. Это значит, что сама функция будет объявлена после тела main()
void draw(int x, int y);

int main(void)
{

int gdriver = DETECT, gmode;
void *ptr; //резервируем пустой указатель - стартовая ячейка памяти,
// начиная с которой мы будем хранить наше изображение
int x, y, maxx;
unsigned int size;

initgraph(&gdriver, &gmode, "");

maxx = getmaxx();
//Начальные координаты
x = 30;
y = 30;

//Цвет фона
setbkcolor(2);

//Рисуем героя
draw(x, y);

//Вычисляем, сколько изображение будет занимать в памяти
size = imagesize(x-10, y-10, x+10, y+10);
//Выделяем память начиная с того места, куда указывает указатель
ptr = malloc(size);
//Копируем в память изображение
getimage(x-10, y-10, x+10, y+10, ptr);

int key;
int nx=x;
int ny=y;


do
{
key=getch(); //Обработка нажатия клавиш

switch(key){
case 80: ny+=10; break;
case 72: ny-=10; break;
case 77: nx+=10; break;
case 75: nx-=10; break;
}

//Стираем старое изображение наложением на него нового
putimage(x-10, y-10, ptr, XOR_PUT);

//Меняем координаты соответственно нажатой клавише
x = nx;
y=ny;

//Выводим изображение
putimage(x-10, y-10, ptr, XOR_PUT);

}while(key!=27);

//Освобождаем память
free(ptr);
//Закрываем графику
closegraph();
return 0;
}

//Функция рисования. Сюда можно запрограммировать любой рисунок
void draw(int x, int y)
{

setcolor(14);
setfillstyle(1,14);
moveto(x, y);
pieslice(x,y,20,350,10);
setfillstyle(1,4);
fillellipse(x+1,y-4,2,2);
}

Ну что, понравилось? На досуге подумайте, как из этой заготовки сделать полноценную игру!


Вопрос: КАК вывести картинку из файла?

Однозначного ответа на этот вопрос нет. Прежде всего, есть ли смысл? ДОС и так можно раскрасить. В любом случае, если вы и решились, можно вывести только картинку 256 цветов, не очень большую, не очень весомую. Для этого нужно знать формат файла, основы работы с динамической памятью и при всём при этом тело функции получится на несколько экранов. Форматы gif, pcx, pic и tiff описаны в книге В.Ю. Романова "Популярные форматы файлов для хранения графических изображений на IBM PC" (Москва, "Унитех", 1992 г.) . Если покопаться и в сети, ответ найти можно, да овчинка выделки не стоит. Если моя книга выдержит второе издание, в нём будет несколько процедур для BMP, PCX и TIFF файлов.

Кстати, не факт, что подобная процедура вообще будет работать на вашем компьютере. Windows очень капризно работает с такими программами. Писалось это не вчера и не для наших компьютеров. легче всего выводить картинки под управлением C++ Builder или Delphi. Даже в Visual C++ стандартного механизма по выводу изображений из файла нет, и приходится всячески изощряться. Так вот о нас с вами разработчики не заботятся...

Вопрос: КАК подключить свой драйвер?


Допустим, вы скачали svga256.bgi. Стандартная функция initgraph не походит, так как она только для EGAVGA.BGI, поэтому надо применить функцию installuserdriver. Рассмотрим нижеприведённый пример:

#include <stdio.h>
#include <conio.h>
#include <graphics.h>

main()
{
int a=installuserdriver("c:\\tc\\bgi\\svga256m.bgi",0), d=2;

initgraph(&a, &d,"");
getch();

}

Вопрос: КАК обойтись без EGAVGA.BGI?

Действительно, часто не хочется включать в свою программу лишние файлы. Хочется, чтобы был один EXE-файл безо всяких вложений. Есть несколько способов обойтись без вложения файла EGAVGA.BGI.

Способ 1.

Воспользоваться утилитой BGIOBJ.exe, которая находится в папке BGI. Эта программа преобразует драйвер EGAVGA.BGI в двоичный объектный файл EGAVGA.OBJ, который можно включить в файл проекта **.PRG вашего компилятора.

Способ 2.

Использовать свою графическую библиотеку. Если это действительно оправдано, можно программировать графику, используя библиотеку dos.h и 10-й вектор прерывания. Тогда вместо initgraph у вас будет ваша собственная функция. Назовём её init()

void init(int num)
{
_AX=num;
geninterrupt(0x10);

}

Но тогда для каждй стандартной функции вам придётся писать свою функцию, пользуясь справочником по DOS. Это трудоёмко. Без initgraph ни одна функция graphics.h работать не будет.

* * *

Итак, вы познакомились с основными графическими возможностями Turbo C под DOS. Теперь даже написать маленькую игру для вас не проблема. Умение программировать графику пригодится вам не единожды, а поняв концепцию графики в DOS, вы без труда поймёте всю графику в Windows, когда мы до неё дойдём.

Назад Содержание Вперёд

Сайт управляется системой uCoz