48247 (566500), страница 5
Текст из файла (страница 5)
void sansserif_font(void);
void gothic_font(void);
При успешной регистрации шрифта возвращаемое значение неотрицательно. Далее для инициализации знакогенератора используется функция settextstyle с необходимыми установками для шрифта. Тогда в присутствии на диске chr-файла отпадает всякая необходимость.
Графическая система позволяет позиционировать на странице видеопамяти выводимый текст с точностью до пикселя, для этого функциям вывода текста нужно некоторую опорную точку. Расположением выводимой строки относительно опорной точки управляет функция
void far settextjustify(int horiz,int vert);
Ее аргументы могут принимать значения констант перечислимого типа text_just:
enum text_just
{
LEFT_TEXT=0, CENTER_TEXT, RIGHT_TEXT,
BOTTOM_TEXT, TOP_TEXT
};
В случае ошибки функция settextjustify устанавливает код ошибки, равный -11, и сохраняет предыдущий режим позиционирования.
Позиционирование горизонтальных строк происходит следующим образом. Если представить прямоугольник, в который вписана выводимая строка, то пара аргументов horiz и vert описывают положение опорной точки в прямоугольнике.
При инициализации графического режима опорная точка всех строк располагается в левом верхнем углу прямоугольника, в который вписывается горизонтальная строка.
В позиционировании вертикальных строк имеются некоторые особенности. Нужно помнить, что функция settextjustify управляет размещением относительно опорной точки именно прямоугольника, содержащего текст. Из этого следует, что по отношению к самому тексту (поскольку он повернут на 90 градусов) позиционирование справа и слева от опорной точки определяется аргументом vert, а сверху или снизу – аргументом horiz. Аргумент vert обрабатывается так же, как и для горизонтальной строки. Аргумент horiz ведет себя по-другому. При значениях LEFT_TEXT и RIGHT_TEXT он дает одинаковый результат – текстовый прямоугольник располагается слева от опорной точки.
В графической библиотеке существует функция, позволяющая получить информацию о текущем режиме вывода графических текстовых сообщений:
void far gettextsettings(struct textsettingstype far *texttypeinfo);
Информация заносится в область памяти, выделенную в программе и содержащую следующую структуру:
struct textsettingstype
{
int font,direction,charsize;
int horiz,vert;
};
Для правильного размещения текста на странице необходимо знать размеры занимаемого им пространства, которое зависит от типа шрифта и его масштабирования. Эту информацию выдают функции:
int far textheight(char far *text_string);
int far textwidth(char far *text_string);
Они возвращают высоту и ширину (в пикселях) прямоугольника, в который был бы вписан текст при выводе строки text_string знакогенератором текущего шрифта с учетом установленного в данный момент масштаба шрифта. Сам вывод текста при этом не производится.
При своей работе функции вывода текста создают в видеопамяти рисунок символов строки, используя таблицу выбранного шрифта. Код, который при этом заносится в атрибуты пикселей, есть текущее значение рисующего цвета (номер входа во внутреннюю палитру), устанавливаемое функцией setcolor. Никаких битовых операций с атрибутами пикселей видеопамяти не предусмотрено. Линии, которыми рисуются символы векторных шрифтов, всегда сплошные тонкие, они не могут модифицироваться функцией setlinestyle.
В отличие от текстового режима, фон выводимых символов (цвет прямоугольника, в который вписана строка) не изменяется. Если нужно вывести строку на каком-либо фоне, то фон нужно нарисовать отдельно, например с помощью функции bar. Другим важным отличием графического режима от текстового является отсутствие режима мигания символа. Можно имитировать процесс мигания, управляя внутренней палитрой с помощью функции setpalette.
Функций вывода текста две:
void far outtext(char far *text_string);
void far outtextxy(int x,int y,char far *text_string);
Обе в качестве аргумента получают указатель на выводимую строку символов. Отличие между этими функциями состоит в выборе опорной точки при позиционировании сообщения на странице. Функция outtext использует для этого текущую графическую позицию. Функция outtextxy получает координаты опорной точки через аргументы x и y. Координаты задаются в системе координат текущего графического окна. При установке режима отсечения изображения за пределами окна обе функции также обеспечивают отсечение фрагментов текста, выходящего за пределы графического окна.
-
-
ПРИЕМЫ ПРОГРАММИРОВАНИЯ ГРАФИЧЕСКОГО РЕЖИМА
-
-
Подключение графической библиотеки
-
Для подключения графической библиотеки можно использовать один из двух приемов. Во-первых, включить в меню оболочки BC++3.1 опцию Options-Linker-Libraries-Graphics library. Во-вторых, создать проект и включить в него исходные cpp-файлы и библиотеку LIB\graphics.lib.
Графический режим может не работать в оболочке, если не хватает места в динамической памяти. Следует увеличить ее размер в пункте Options-Debugger-Heap size.
-
Инициализация графического режима
Простейшую инициализацию осуществляет следующий код
#include
void main()
{
int gd = DETECT, gm;
initgraph( &gd, &gm, “c:\\bc31\\bgi” );
// работаем в графическом режиме
putpixel(100, 200, WHITE);
// ……….
If(!getch())
getch();
closegraph();
}
-
-
Включение драйвера и шрифтов в исполняемый файл
Для включения графического драйвер egavga.bgi в исполняемый файл необходимо:
а) в командной строке выполнить
C:\BC31\BGI\bgiobj.exe egavga
и получить файл egavga.obj;
б) создать проект в оболочке с помощью команды меню Project-Open и включить в него файл egavga.obj и исходные cpp-файлы;
в) затем в тексте программы записать код
registerbgidriver(EGAVGA_driver);
initgraph(&gd, &gm, “”);
Для включения графических шрифтов (файлов с расширением chr) в исполняемый файл необходимо:
а) в командной строке выполнить
C:\BC31\BGI\bgiobj.exe goth
и получить файл goth.obj;
б) создать проект в оболочке с помощью команды меню Project-Open и включить в него файл goth.obj и исходные cpp-файлы;
в) затем в тексте программы записать код
registerbgifont(GOTHIC_font);
initgraph(&gd, &gm, “c:\\borlandc\\bgi”);
//…………………
settextstyle(GOTHIC_FONT, HORIZ_DIR, 10);
outtext(“Hello”);
//…………………
-
-
Рисование геометрических фигур
а) Нарисуем красное солнце
setcolor(RED);
circle(100, 100, 50); //красная окружность
setfillstyle(SOLID_FILL, RED);
floodfill(100, 100, RED); /* рисуем красный круг, указывая внутреннюю точку области и цвет контура */
б) Нарисуем в центре экрана квадрат со стороной a, повернутый относительно центра C на угол против часовой стрелки. Пусть С(x0, y0) – математические координаты точки. Тогда вершины неповернутого квадрата имеют координаты
(x0 + (sqrt(2) / 2) · a · cos( /4 + /2 · k),
y0 + (sqrt(2) / 2) · a · sin( /4 + /2 · k)), где k = 0, 1, 2, 3.
При повороте на угол получаем математические вершины
(fk, gk) = (x0 + (sqrt(2) / 2) · a · cos( /4 + /2 · k + ),
y0 + (sqrt(2) / 2) · a · sin( /4 + /2 · k + )),
где k = 0, 1, 2, 3.
В соответствии с данными, приведенными п.2.11, переводим математические координаты (fk, gk) в координаты пикселей (uk, vk). Затем определяем целочисленный массив A координат вершин замкнутого многоуольника
{u[0], v[0], u[1], v[1] , u[2], v[2], u[3], v[3], u[0], v[0] }.
Теперь рисуем квадрат с помощью одной функции
drawpoly(5, A);
-
Выделение памяти под большие одномерные массивы
Элементами массивов могут быть структуры большого размера. Если и размер массива значителен, то выделить динамическую память под такой массив одним вызовом функции malloc нельзя. Прототип этой функции
void *malloc( unsigntd int T);
Поэтому максимальный размер блока памяти, который она выделяет, не превосходит 64К.
Предлагается эмулировать одномерный динамический массив двумерным динамическим массивом с использованием массива
указателей.
Например, выделим память под long T=50000l структур
struct comp {
float re, im;
};
Общий размер такого массива равен 50 000 * 8 = 400 000 байт. Выберем размеры двумерного массива
int m = 1000; // количество столбцов выбираем произвольно
int n = (int)((T – 1)/ m) +1; // количество строк, нумерация строк
с нуля
В последней (m-1)-й строке прямоугольной динамической матрицы m n могут содержаться элементы, не принадлежащие исходному одномерному массиву. Поэтому при переборе элементов массива необходима соответствующая проверка.
comp ** A = NULL;
A = (comp **) malloc( m * sizeof(comp *));
// проверка выделения памяти
for(int i=0; i < m; i++)
A[i] = (comp *) malloc(k * sizeof(comp));
// проверка выделения памяти
for( i =0; i < m; i++)
for( int j =0; j < n; j++)
if( (long) i*k+j < T) // работаем только с элементами
// исходного одномерного массива {
A[i][j].re = 0;
A[i][j].im = 0;
}
// работа программы
// освобождение памяти
for( i =0; i < m; i++)
free(A[i]);
free(A);
-
Вывод числовой информации
Для вывода форматированных данных используется двухэтапный алгоритм. Вначале данные записываются в строку или, как иногда говорят, в память. Затем строка выводится на графический экран.
Например выведем вещественное число с двумя знаками после запятой.
char buf[5];
float x = 3.1415;
sprintf(buf, “%4.2”, x);
outtextxy(100, 100, buf);
Цифра 4 в форматной строке задает количество байтов в памяти, которые будет занимать выводимая форматированная информация без учета признака конца строки. Прием используется для предотвращения выхода за диапазон массива. Подобная ошибка возникла бы в случае
x = 13.1415.
-
Задержка экрана
Организация задержки экрана не зависит от выбора графического или текстового режима, и реализуется следующим образом
if (!getch())
getch();
Данный код корректно обрабатывает два возможных случая. Во-первых, при нажатии обычной клавиши. При этом с клавиатуры в буфер ввода помещается ненулевой ascii-код символа, соответствующего нажатой клавише. Тогда условие !getch() ложно и второй getch() не вызывается.
Во-вторых, при нажатии клавиши с расширенным кодом (например, функциональные клавиши, стрелки) с клавиатуры в буфер ввода поступают два числа: ноль и scan-код этой клавиши. Тогда условие !getch() истинно, вызывается второй getch(), который считывает scan-код и тем самым очищает буфер.
Другим способом задержки экрана является код
while(!getch())
;
Отметим, что стандартным способом задержки (в учебниках и примерах из справок по функциям) является вызов функции
getch();
Однако этот способ некорректно обрабатывает нажатие клавиши с расширенным кодом. В данном случае в буфере останется один непрочитанный символ, который будет считываться при следующем вводе информации.
-
-
Реакция программы на нажатие конкретной клавиши
В следующем фрагменте выходим из цикла по нажатию клавиши Escape
#define ESC 27
while(1){
if(kbhit()){
char c=getch();
if(c == 0)
{
getch();
continue;
}
else if( c == ESC)
break;
}
//работа цикла
}
-
Организация ввода числовой информации
В графических приложениях ввод текстовой и числовой информации реализуется также в графическом режиме. Для этого нельзя использовать функцию scanf, так как она предполагает текстовый режим.
Предлагается следующий алгоритм для ввода целого положительного числа. Данные считываются с клавиатуры посимвольно, сохраняются в памяти и затем распознаются с помощью функции sscanf форматированного ввода из строки. Строка выводится в графическом режиме посимвольно.
#define ENTER 13
main(){
//инициализация графического режима
char c, buf[2], *str=(char *)malloc(1);
int number;
str[0] = ’\0’;
buf[1] = ‘\0’;
while( (c = getch()) != ENTER){
if( c ’9’)
continue;
buf[0] = c; // введенный символ оформляем в виде строки
outtext(buf); // и выводим на экран в графическом режиме
str = realloc(str, strlen(str) + 2);
str[strlen(str) + 1] = ‘\0’;
str[strlen(str)] = c; // запоминаем символ в памяти
}
sscanf(str, “%d”, &number);
}
-
Проверка выхода аргумента функции из ОДЗ
Проверку выхода переменной x из области допустимых значений функции f(x) можно реализовать в самом теле функции. В случае выхода из ОДЗ функция взводит глобальный флаг, который проверяется в вызывающей функции.
Например,
int flag=0; //глобальный флаг
// работаем с функцией y = x1/2
float f(float x){
if(x<0){
flag=1;
return 0;
}
else{
flag=0;
return sqrt(x);
}
}
void main(){
float x, y;
scanf(“%f”, &x);
y=f(x);
if(flag==1)
printf(“Выход из ОДЗ. ”);
else
printf(“Нет выхода из ОДЗ. Продолжаем вычисления”);
}
-
Графическая и математическая системы координат
Для рисования графика функции программисту удобнее использовать математическую систему координат (МСК), расположенную по центру экрана. Графические функции работают в графической системе координат (ГСК).
а б
Рис. 1. Системы координат: а – математическая; б – графическая
Инициализируем переменные.
int maxx = getmaxx(),
maxy = getmaxy(),
px = 30, // количество пикселей в одной математической единице по оси Ох
py = px * ((float)maxx/maxy); // количество пикселей в одной математической единице по оси Оy.
Соответствие между двумя системами координат представлено
ниже
Соответствие между МСК и ГСК
МСК | ГСК |
(0, 0) ………………………………. | (maxx / 2, maxy / 2) |
(1, 0) ………………………………. | (maxx / 2 + px, maxy / 2) |
(0, 1) ………………………………. | (maxx / 2, maxy / 2 – py) |
(x, y) ….……………………………. | (maxx / 2 + x px, maxy / 2 – y py) |
(x, f(x)) ……………………………. | (maxx / 2 + x px, maxy / 2 – f(x) py) |
Реализуем масштабирование по оси координат Ох так, чтобы график функции y = f(x) на заданном отрезке [a, b] размещался по всей ширине экрана. Для этого найдем коэффициенты u и v линейного отображения g(x) = u x + v, при котором отрезок [a, b] переходит в отрезок [0, maxx]. Из системы линейных уравнений
u a + v = 0
u b + v = maxx
находим u = maxx / (b-a), v = -a maxx / (b-a). Тогда точке (x, f(x)) будет соответствовать пиксель
( (maxx / (b-a)) * x - a * maxx / (b-a) , maxy / 2 - f(x) * py)),
где px = maxx/(b-a). Затем график можно нарисовать с помощью цикла, в котором счетчик является вещественной математической переменной. Например:
for(float x = a; x <= b; x += (b-a) / maxx)
putpixel((maxx / (b-a)) * x - a * maxx / (b-a), maxy / 2- f(x) * py));
Данный цикл при больших a и b может оказаться бесконечным. Это возможно в случае, если шаг цикла (b-a) / maxx будет меньше расстояния между числом a и его ближайшим соседом справа для типа float.
-
Использование двух видеостраниц
Для рисования вращающейся звезды (см. задание 3.4) лучше использовать две видеостраницы.
int page=0;
for(double f = 0; f < 2 M_PI-M_PI / 100; f += M_PI / 50) {
otrisovka(X0, Y0, R1, r2, fi0 + f, COLOR); /* рисуем новую звезду на активной странице, которая по умолчанию имеет нулевой номер*/
setvisualpage(page); /* показываем изображение новой звезды*/
page=abs(page-1); /* меняем номер страницы с 0 на 1 или наоборот*/
setactivepage(page); /* меняем активную страницу*/
otrisovka(X0, Y0, R1, r2, fi0 + f-M_PI / 50, getbkcolor()); /* стираем старую звезду на активной странице*/
}
-
Рисование изображений в bmp-формате
Для создания фона в задаче о снегопаде можно использовать 16-ти цветный bmp-файл, так как это устраняет проблему самостоятельного рисования фонового изображения средствами языка C++. Для загрузки изображения из файла надо выполнить действия:
-
с позиции 22 в файле прочитать высоту рисунка;
-
вычислить ширину записанного изображения
ширина = (размер_файла - 118) / высота;
3) загрузить сам рисунок, начиная с позиции 118, учитывая, что в одном байте содержится 2 пикселя и то, что изображение в файле записано построчно, причем первая строка записана в конец файла, а последняя – с позиции 118.
// карта замещения цветов для создания визуального эффекта
char map[] = {0,12,2,6,9,5,3,8,7,4,10,14,1,13,11,15};
int y0 = getmaxy();
// Открываем картинку
FILE *f = fopen(fon, "rb");
if(f==NULL)
return 2;
// читаем ширину картинки
fseek(f, 0, 2);
long l = ftell(f)-118;
fseek(f, 22, 0);
int w,h;
fread(&h, 2, 1, f);
w = int(l / h);
// читаем и рисуем картинку
fseek(f, 118, 0);
int x=0;
int y=0;
while(1) {
c = fgetc(f);
if(feof(f))
break;
ch = map[c/16];
cl = map[c%16];
putpixel(2*x+0, y0-y, ch);
putpixel(2*x+1, y0-y, cl);
if(++x==w) {
x=0;
y++;
}
}
fclose(f);
-
Работа с мышью
Вызовы BIOS используют программные прерывания. BIOS имеет несколько различных прерываний для разных целей. Для работы с мышью используют прерывание 0x33. Для доступа к этим прерываниям используется функция Си с прототипом в файле
int int86(int num, REGS *in, REGS *out);
где num – номер прерывания. Объединение REGS имеет вид
union REGS{
struct WORDREGS x;
struct BYTEREGS y;
};
struct WORDREGS{
unsigned int ax, bx, cx, dx, si, di, cflags, flags;
};
struct BYTEREGS{
unsigned char al, ah, bl, bh, cl, ch, dl, dh;
};
//Определим глобальную переменную
REGS regs;
//показать курсор
void showcursor(void){
regs.x.ax = 0x01;
int86(0x33,®s,®s);
}
//спрятать курсор
void hidecursor(void){
regs.x.ax = 0x02;
int86(0x33,®s,®s);
}
//получение статуса мыши
void get_mouse_status(int& button,int& x,int& y){
regs.x.ax = 0x03;
int86(0x33,®s,®s);
button = regs.h.bl;
x = regs.x.cx;
y = regs.x.dx;
}
//пример использования мыши
main(){
//инициализация графического режима
int button, x, y;
char str[20];
showcursor();
while(1){
get_mouse_status(button, x, y);
if(x == 0 || y == 0)
break;
sprintf(str, “%d %d”,x, y);
outtext(30, 30, str);
}
hidecursor();
}
-
-
ЗАДАНИЯ ДЛЯ ЛАБОРАТОРНОЙ РАБОТЫ
-
-
Звездное небо
-
На экране в непрерывном режиме рисуются звезды (пиксели) в случайном месте и случайным цветом. Распределение случайных величин равномерное. При наложении новой звезды на другую видимую звезду обе стираются. Рисование прекращается нажатием клавиши Escape. Затем происходит подсчет числа видимых звезд, и процент заполнения неба выводится по центру графического экрана с точностью до сотых долей процента. Использовать готический шрифт размером 1 см.
Рисование звезд и их подсчет реализовать в виде отдельных функций. Получить ответ для двух режимов VGAHI и VGALO.
-
Снегопад
С верхней части и с боковых сторон экрана в непрерывном режиме падают белые снежинки в форме одного пикселя. Выход из программы по нажатию клавиши Escape. Снежинки пролетают экран и снова появляются. Создать на экране графический фон в виде голубого полумесяца, красивого текста и пр. Фон не содержит белого цвета и не должен перерисовываться.
Плотность снегопада вводится в начале программы в текстовом режиме. Максимальное количество снежинок равно 50 000. Параметры снежинки находятся в пользовательской структуре данных. Для хранения информации о снежинках следует использовать двумерный динамический массив структур.
Смещения снежинок содержат хаотическую составляющую по горизонтали и вертикали. Организовать управление ветром с помощью клавиш-стрелок.
-
Рисование графика функции
Нарисовать график функции y = f(x) на отрезке [a, b]. Вещественные границы интервала вводятся с клавиатуры в графическом режиме с возможностью редактирования. График необходимо масштабировать по ширине экрана так, чтобы отрезок [a, b] полностью вписался в экран.
Функция f(x) задается исходным кодом на языке Си. Осуществить проверку выхода переменной x из ОДЗ.
-
Вращение звезды
Написать функцию, которая рисует правильную пятиконечную цветную звезду, со следующими параметрами:
-
x, y – математические координаты центра;
-
R, r – математические радиусы внутренней и внешней окружностей;
-
fi – угол между осью x и одним из больших лучей звезды, в радианах;
-
col – цвет контура звезды;
-
colfill – цвет заливки.
Написать также программу, в которой вращается красная звезда в центре экрана.
-
БИБЛИОГРАФИЧЕСКИЙ СПИСОК
-
Керниган Б. Язык программирования Си / Б. Керниган, Д. Ритчи. М.: Финансы и статистика, 1992. 272 с.
-
Керниган Б. Язык программирования Си. Задачи по курсу Си / Б. Керниган, Д. Ритчи. М.: Финансы и статистика, 1985. 192 с.
-
Юркин А.Г. Задачник по программированию / А.Г. Юркин. СПб.: Питер, 2002. 192 с.
-
Подбельский В.В. Программирование на языке Си: учеб. пособие / В.В. Подбельский, С.С. Фомин. М.: Финансы и статистика, 2005. 600 с.
-
Трофимов С.П. Программирование в Си. Организация ввода-вывода: метод. указания / С.П. Трофимов. Екатеринбург: УГТУ,1998. 20 с.
-
Трофимов С.П. Программирование в Си. Динамически распределяемая память: метод. указания / С.П. Трофимов. Екатеринбург: МИДО, 1998. 14 с.