Блог → Секреты Turbo Pascal: текст в графическом режиме. Часть 1

В этой статье я вкратце ознакомлю вас с особенностями программирования текстовых интерфейсов в графических режимах EGA/VGA, применяемых в программах на языке Turbo Pascal 6.0. Тем, кто знаком с Турбо-Паскалем, будет значительно проще понять код, приведённый в рамках этой заметки, но и для владеющих другими языками программирования, прочтение статьи может быть интересным и познавательным. Итак, не будем затягивать вступление, а сразу приступим к существу вопроса.

Программный знакогенератор

Я не буду касаться стандартных текстовых процедур из модуля GRAPH.TPU, их подробное описание все желающие могут найти в специальной литературе. Отмечу лишь, что появление приведенных в этом разделе процедур вызвано прозаической проблемой, возникающей при многократном выводе различных текстовых сообщений стандартными процедурами OutTextXY и OutText, в одну к ту же позицию на экране. Кому не приходилось во избежание наложения одной строки на другую перед каждым очередным выводом текста очищать экран от предыдущих сообщений. Все бы ничего, но когда это делается часто, например при высвечивании текущих координат курсора мыши, то неизменно возникает эффект мерцания. Причина его появления заключается в том, что процедуры OutTextXY и OutText рисуют только пикселы самого символа, оставляя неизменными пикселы фона в матрице символа. Наилучший выход из таких ситуаций - изменение при выводе текста на экран наряду с пикселами, составляющими сам символ, и пикселов фона в матрице символа. В этом разделе я расскажу, как это можно сделать.

Символ на экране состоит из расположенных в определенном порядке пикселов в пределах прямоугольной матрицы символа. Он рисуется построчно начиная с верхней строки; его высота определяется количеством строк в матрице, а ширина равна, как правило, 8 пикселам или 1 байту (приведенные процедуры рассчитаны именно на эту ширину). Графические образы символов содержатся в так называемой таблице описания символов (ТОС). Каждый байт таблицы является битовым шаблоном строки символа, поэтому каждый символ в ТОС представлен количеством байтов, равным высоте символа. Располагаются символы в таблице последовательно, в соответствии с их кодом ASCII, отсюда легко вычислить смещение символа относительно начала ТОС (высота умножается на код символа).

Каждый установленный бит в битовом шаблоне указывает на наличие пиксела изображения (т.е. пиксела, входящего в сам символ) в соответствующей строке матрицы символа (см. рисунок - заполненную, в соответствии с содержимым ТОС, матрицу символа размерами 8x8 пикселей).



Программа вывода символов на экран монитора называется программным знакогенератором. Он выполняет несколько функций: определяет адрес ТОС, находит в ней битовый шаблон для данного кода символа, переводит этот шаблон в строку пикселов и изменяет пикселы по определенные адресам в видеопамяти.

ТОС могут располагаться й ППЗУ компьютера, в тексте Вашей программы или в файлах на диске. Проще всего использовать таблицы, хранящиеся в ППЗУ компьютера. Можно получить к ним прямой доступ, определив их адреса вызовом прерывания BIOS 10H, функция 11H, подфункция 30H. Од¬нако для совместимости со знакогенератором BIOS лучше использовать подфункции 22Н, 23Н и 24Н, устанавливающие вектор прерывания 43Н так, чтобы он указывал на ТОС с размерами 8x14, 8x8 и 8x16 пикселов соответственно. Одновременно переменной POINTS из области данных BIOS, расположенной по адресу 0040:0085, присваивается значение высоты символа из выбранной таблицы.

Для использования собственных таблиц, содержащихся в тексте программы или подгружаемых из файла, предусмотрено прерывание 10H, функция 11H, подфункция 21H, которому в качестве параметров передаются адрес таблицы и высота символов в пикселах. Кстати, таблица описания символов размерами 8x8, выводимых процедурами Турбо-Паскаля OutText и OutTextXY, содержится непосредственно в модуле GRAPH.TPU.

Программный знакогенератор формирует символы на экране путем изменения содержимого видеопамяти, причем Вы можете "попросить" его либо просто поменять старые значения пикселов на новые, либо выполнить при записи символа, в видеопамять одну из поразрядных логических операций - AND, OR или XOR. Кроме того, при выводе текста на экран можно изменять пикселы только изображения или и изображения и фона. Изменение одних пикселов изображения может оказаться полезным, когда текст накладывается на детализированное или узорчатое графическое изображение. Однако иногда чтение бывает затруднено - графическое изображение сливается с текстом. Аналогичная проблема, как уже упоминалось, возникает и при многократном выводе различных сообщений в одну и ту же позицию на экране, для устранения которой приходится каждый раз очищать экран от предыдущих сообщений.

Наилучшим выходом из таких ситуаций является одновременное изменение пикселов фона и изображения в матрице символа, что и реализовано в модуле GrafText (см. код 1). GrafText включает в себя четыре процедуры. Первая, VSetTextColor, устанавливает цвета символов, фона и тип поразрядной логической операции символа с содержимым экрана. Вторая, VSetFontType, использует прерывания BIOS для загрузки адреса требуемой ТОС по вектору прерывания 43Н и присвоения переменной BIOS POINTS значения высоты символа. Кроме того, если применяется ТОС, содержащаяся в файле, то VSetFontType загружает шрифт з ОЗУ, причем первый байт в файле задает высоту шрифта. Имя файла передаётся процедуре в качестве параметра. При отсутствии на диске указанного файла остается предыдущий шрифт.
Программый шрифт кириллица - RussianFont - представлен в виде массива из 8x256 байт. Разумеется, мало удовольствия в том, чтобы набирать вручную так много текста, однако в результате Вы навсегда избавитесь от проблемы русификации своих графических программ. (Если у Вас есть файловый шрифт, то можно воспользоваться очень простым способом включения таблицы в текст программы - написать маленькую программку перевода файлового шрифта из байтового формата в текстовый и присоединить полученный файл к своей программе.)

Третья процедура, VOutTextXY, выводит на экран, начиная с указанной (в пикселах) позиции, строку символов с аттрибутами, установленными процедурой VSetTextStyle, и шрифтом, определенным процедурой VSetFontType. При переменной TextBackColor (цвет фона), равной 255, в видеопамяти изменяются только пикселы изображения, а пикселы фона остаются теми же. В процедуре проводится проверка на совмещение символов по границе байтов (т.е. на совмещение левых пикселов символа со старшими битами байтов видеопамяти). Если совмещения нет (переменная Shift >0), то появляются дополнительные накладные расходы на формирование масок изображения и фона для левой и правой сторон символа. Можно сократить объем и повысить производительность процедуры, оставив только код на случай совмещения символов по границе байтов. Четвертая процедура, VOutCharXY, по своей реализации и назначению во многом похожа на VOutTextXY, но вместо строки текста она выводит на экран один символ, код которого получает в качестве параметра. Атрибуты символа также передаются в виде параметров, а установки процедуры VSetTextStyle влияют только на тип поразрядной логической операции.

Unit GrafText;
interface

uses Dos, VC_Graph;
Type GrfFnt = array[l..l] of byte;

Var TextForeColor,TextBackColor,TextLogGlb : byte;
SegFontTabдe,OfsFontTable : word;
CharRow : byte;
FontLoaded : boolean;
FointsBIOS : byte absolute $40:$85;
GrafFont : ^GrfFnt;

Const ROMFont8x8 - 1;
ROMFontl4x8 - 2;
ROMFontl6x8 - 3;
Russian8x8 - 4;
FileFont - 5;
RussianFont: array [1..8*256] of byte

VSetFonlType(2,'');
VSetTextColor( 15,9,0);
VOutTextXY(l()4,25,
'Procedure VSetTextColor(ForeColor,BackColor,TextLog);');
For I:=0 to 15 do
begin

VSetTextColor (1,15-I,0);
VOutTextXY (104,80+I*15,
'Procedure VSetTextColor(ForeColor,BackColor,TextLog);');

end;

Readln;
ClearViewPort;
VSetTextColor(15,9,0);
VOutTextXY(104,25,
'Procedure VOutCharXY(CharCode,X,Y,ForeColor,BackColor');
For I:=0 to 15 do
For J:=0 to 15 do
begin

{ Рисование стандартной таблицы символов ASCII }
VOutCharXY(32,120+J*24,80+I*15,I,15-I);
VOutCharXY(J*16+I,128+J*24,80+I*15,1,15-I);
VOutCharXY(32,136+J*24,80+I*15,1,15-I);

end;
Readln;
end.


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

Отметим, что процедура VOutTextXY работает быстрее, чем аналогичная ей OutTextXY из модуля GRAPH.TPU (вы сможете ещё сильнее увеличить её быстродействие, переписав на ассемблере). И несколько слов о регистрах адаптеров EGA и VGA: это обширная тема, которой автор собирается посвятить отдельную статью, а тем, кому очень интересно, рекомендует посмотреть прекрасные книги по видеоадаптерам. На этом всё, на сегодня, а продолжение заметки я напишу в ближайшие день-два, заходите в мой блог!