4. Графический интерфейс


Добавил:DMT
Дата создания:5 апреля 2008, 15:29
Дата обновления:5 апреля 2008, 15:34
Просмотров:7731 последний ---
Комментариев: 0

4. ГРАФИЧЕСКИЙ ИНТЕРФЕЙС

В данной главе будут рассмотрены методы организации меню и окон в графическом режиме, а также принципы организации графического диалога. Будет изучена структура видеопамяти в графических режимах и способы обращения к видеоадаптеру через регистры графического контроллера. Затем будут рассмотрены форматы хранения графических файлов.

4.1. Организация меню в графическом режиме

Для вывода текста в графическом режиме используется подпрограмма (функция)

void outtextxy ( int x, int y, char *S);

аргументами которой являются координаты левого верхнего угла области вывода строки S . Строка S должна заканчиваться нулем.

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

1) Подпрограммы сохранения и восстановления области вывода меню используют функции getimage() и putimage() . Указатель на область сохранения frame [ i ]. p должен инициализироваться не при создании меню, а в тех случаях, когда i меню становится активным. Это экономит память.

2) Подпрограмма вывода курсора goto_xy() должна быть написана для графического режима. Она выводит прямоугольник с помощью функции putimage() в режиме XOR_PUT . При приеме символа с клавиатуры с помощью bioskey() нужно выводить курсор в позицию (getx(), gety()) , или (8*getx(), 8*gety()) .

3) Изменить подпрограммы вывода нижнего уровня write_char() , write_string() .

4) Если для вывода рисунков используется одно графическое окно, то вместо установки указателя vid_mem на видеопамять, указатель устанавливается на свободную память размером 160*25 байт. После этого подпрограмма сохранения области вывода не требует изменения. Можно считать, что текстовый видеобуфер находится в памяти и имитирует обычную видеопамять, работающую в текстовом режиме. В подпрограмме сохранения restore_video() нужно добавить вывод символов на экран при записи в этот видеобуфер. Это приводит к дальнейшей экономии памяти, но применимо лишь для случая, когда имеется одно графическое окно. Текстовая информация на экране при таком подходе восстанавливается, графическая – нет.

Аналогичным образом организуются графические окна. Как и в текстовом режиме, для каждого окна определяется фрейм, и функции ввода-вывода в качестве аргумента получают номер окна. Перед выводом окна на экран, содержимое области вывода меню сохраняется, после окончания работы с окном – восстанавливается.

4.2. Организация графического диалога

Рассмотрим на примере системы Core основные элементы, на базе которых реализуется диалог, осуществляющий ввод и вывод графических данных.

Окна и отсечение. Вывод производится в окна. При программировании на языке С++ в системе MS DOS это можно осуществлять с помощью любых графических функций, предварительно вызвав функцию

setviewport (startx, starty, endx, endy, 1)

последний аргумент clipflag = 1 . В противном случае при clipflag = 0 , не производится отсечение справа и внизу.

Сегментация. Изображение в окне строится из частей, которые называются сегментами. Каждый сегмент имеет имя и состоит из набора вызовов подпрограмм построения графических примитивов. Под графическими примитивами понимаются точки, отрезки, дуги, многоугольники, текстовые строки и т.д. Сформированный сегмент нельзя вновь открывать и модифицировать. Его можно сделать видимым или невидимым с помощью функции set_visibility (имя_сегмента, v) , где v = 0 делает его невидимым, а v = 1 – видимым. Таким образом, изображение состоит из сегментов, а сегменты – из примитивов и каждый сегмент создается один раз.

Логические интерактивные устройства. Для ввода информации при графическом диалоге применяются следующие устройства:

•  кнопка - устройство для задания выбора;

•  селектор - устройство для указания информации, выведенной на экран;

•  клавиатура - устройство для ввода цепочки литер;

•  валюатор - устройство для генерации величин с плавающей точкой;

•  локатор - устройство для задания экранных координат.

Обычно клавиатура и локатор (''мышь'') выполнены физически. Под кнопкой подразумевается прямоугольник, выведенный на экран, активация которого осуществляется нажатием клавиши < ENTER > или двойным щелчком левой кнопки мыши в области прямоугольника. Селектор моделируется с помощью набора кнопок, которые называются селекторными. Разрешается выбирать несколько кнопок. Селектор можно моделировать как символьную строку, указывающую, например, сегмент по его имени. Валюатор моделируется с помощью прямоугольника или сектора круга. Часть прямоугольника закрашивается при нажатии специальных клавиш. Введенное число определяется как отношение площади закрашенной части прямоугольника к площади всего прямоугольника и принимает значение от 0 до 1. Аналогично площадь сектора круга к площади всего круга определяет число, которое вводится с помощью валюатора.

Обработка сообщений. Каждое окно имеет подпрограмму, которая называется оконной функцией и обрабатывает сообщения, поступающие для данного окна. В системе Core оконная функция работает следующим образом:

while (1)

{

wait (сообщение)

switch (сообщение)

{

case (кнопка): button (имя_кнопки); break ;

case (сегмент): segment (имя_сегмента); break ;

case (клавиша): keyboard (имя_клавиши); break ;

...

}

}

Здесь button(), segment(), keyboard() – подпрограммы обработки сообщений, полученных выбором кнопки, указания сегмента, нажатием клавиши.

4.3. Особенности графического режима

В режиме 13h видеопамять можно рассматривать как символьный массив, находящийся по адресу A0000h . Цвет точки ( х , у ) является байтом этого массива, имеющим индекс 320* у + х . Для вывода точки на экран достаточно записать цвет точки в этот байт, например, операторы:

 

char far *vid_mem = (char far *) 0xA0000000;

vid_mem + 320*y+x = 2;

приведут к появлению зеленой точки в позиции ( х , у ). Наша цель – рассмотреть методы записи и чтения точек в режимах 0х10 и 0х12 .

Установка графического режима. Напишем подпрограмму, устанавливающую графический режим:

void setvmode (int mode)

{

union REGS r;

r.h.al = mode; r.h.ah = 0;

int86 (0x10, &r, &r);

}

Например, если вызвать setvmode (0x12), то установится режим VGA , 16 цветов, с разрешением 640х480 пикселов.

Отображение пикселов на экране. Вся видеопамять в режимах 10 h и 12 h , начиная с адреса 0А0000 h , делится на 4 части, которые называются цветовыми или битовыми плоскостями. Адрес пиксела в видеопамяти в действительности относится не к одному, а к четырем битовым плоскостям. Команды считывания и записи взаимодействуют с видеопамятью через четыре 8-битовых регистра, каждый из которых относится к одной из битовых плоскостей и называется регистром защелки. Каждая битовая плоскость проектируется на видеопамять. Точка экрана ( х , у ) цвета i хранится в видеопамяти со смещением

PixelAddr = y*80 + x/8;

ее маска определяет позицию в байте и равна

PixelMask = 0x80>>(x%8).

Например, при х = 4 и у = 0 маска равна 00001000 b . Обмен между видеопамятью и экраном можно представить схемой (рис. 4.1):

Регистры графического контроллера. Графический контроллер имеет 9 восьмиразрядных регистров. Чтобы записать значения в один из регистров, нужно выполнить 2 команды:

outportb (0 x 3 CE , номер_регистра);

outportb (0 x 3 CF , значение);

Регистры имеют номера и названия, показанные на рис. 4.2:

Номер

Название

Значение по умолчанию

0

Set / Reset

00

1

Enable Set / Reset

00

2

Color Compare

00

3

Data Rotate

00

4

Read Map Select

00

5

Mode

10h

6

Miscelaneous

05h

7

Color Don't Care

0Fh

8

Bit Mask

FFh

Рис.4.2. Регистры графического контроллера

В частности, регистр 5 определяет режимы чтения и записи. Имеется два режима чтения, им соответствуют значения 0 и 1 бита 3 регистра 5, и 4 режима записи, которым соответствуют значения двух младших битов регистра 5. Установка разрядов приводит к изменению режимов чтения и записи. Ниже приведена подпрограмма смены режимов чтения/записи:

void SetRWMode (int ReadMode, int WriteMode)

{

outportb (0 x 3 CE , 5); // в регистр 5

outportb (0 x 3 CF , WriteMode &3+( ReadMode &1)<<3); // записать значение

}

Режим чтения 0. Содержимое четырех битовых плоскостей, соответствующее байту видеопамяти, считывается в четыре регистра защелки. Один из регистров защелки считывается в байт видеопамяти, откуда его можно прочитать, как из обычной памяти, указав номер битовой плоскости в регистре 4.

Пример 1. Для того чтобы прочитать цвет точки, имеющей координаты ( х, у ), установим сначала режимы чтения и записи с помощью оператора

SetRWMode (0, 0);

а затем вызовем подпрограмму, возвращающую цвет точки:

 

int readpixel (int x, int y)

{ unsigned char pixelmask, // маска

latch ; // для регистра защелки

unsigned int pixeladdr = y*80 + x/8;

int plane, color = 0; pixelmask = 0x80 >> (x%8);

for (plane=3; plane>=0; plane--)

{

outportb (0 x 3 CE , 4); // в регистр 4

outportb (0 x 3 CF , plane ); // записать номер битовой плоскости

latch = peekb (0xA000, pixeladdr); latch = (latch&pixelmask)>>(7-x%8);

color = (color<<1)|latch;

}

return color ; }

 

Режим чтения 1. Байт, в который читается байт из видеопамяти, называется байтом центрального процессора. В режиме чтения 1 содержимое четырех битовых плоскостей считывается в четыре регистра защелки. В байт центрального процессора записывается результат сравнения четырех регистров защелки с регистром 2. Пусть, например, регистр 2 равен 1011b. Тогда, если регистры защелки имеют содержимое, показанное на рис 4.3

3

1

0

0

1

1

0

1

0

2

0

1

1

0

1

1

0

1

1

1

1

0

1

1

1

1

0

0

1

1

1

1

1

1

1

0

Рис. 4.3. Содержимое регистров защёлки

то значение 1011 b будет встречаться в столбцах, соответствующих битам 7, 4 и 1. Поэтому результат чтения в режиме 1 в данном случае равен 10010010 b .

Чтобы исключить битовую плоскость из проверки на равенство, надо записать 0 в соответствующий бит регистра 7. В данном случае, чтобы исключить плоскость 2, нужно записать в регистр 7 число 1011 b . Результат чтения будет равен 10011010 b .

Действия в режиме чтения 1, после вызова функции SetRWMode (1, 0) :

•  в регистр 7 записать маску;

•  в регистр 2 записать цвет;

•  прочитать байт, находящийся по адресу 0хА0000000 + у *80 + х /8, он будет результатом чтения.

Режим записи 0. Используются регистры:

•  регистр 0 - установки / сброса;

•  регистр 1 - разрешения установки / сброса;

•  регистр 3 - сдвига данных;

•  регистр 8 - двоичной маски.

Байт процессора может быть использован для обновления любой плоскости битов или всех плоскостей битов, а также для обновления любого из восьми пикселов или всех восьми пикселов.

Регистр двоичной маски указывает, какие биты из восьми могут быть модифицированы. Если некоторый бит в этом регистре равен нулю, то соответствующие ему 4 бита из регистров защелки просто копируются в битовые плоскости. Для каждого бита, равного 1, соответствующие биты регистров защелки объединяются либо с байтом процессора (если регистр 1 равен 0), либо со значением пиксела из регистра 0 (если регистр 1 равен 0 Fh ). Таким образом, возможны два случая:

•  Регистр 1 равен 0Fh . Регистр 0 действует с помощью операции, заданной в битах 4-3 регистра 3 на все биты регистров защелки. В случае, когда регистр 3 равен нулю, производится копирование цвета, заданного в регистре 0 во все точки, адресуемые байтом процессора, для которых биты двоичной маски равны 1.

Например, в случае, когда регистр 0 равен 0101 b , а регистр 8 равен 00011111 b , в битовые плоскости запишутся преобразованные данные, представленные на рис 4.4.

В данном случае копирование производится в режиме COPY_PUT . Если биты 4-3 регистра 3 содержат число 01 , то запись производится с преобразованием AND_PUT , число 10 – OR_PUT , число 11 – XOR_PUT . Для того чтобы записать точки заданного цвета на экран, в этом режиме необходимо произвести действия:

•  в регистр 1 записать 0 Fh ;

•  в регистр 0 записать цвет;

•  в регистр 8 записать маску;

•  записать любой байт по адресу 0хА0000000+80у+х/8;

•  восстановить исходные значения регистров 1 и 8.

 

Пример 2. Напишем подпрограмму записи точки цвета color в позицию ( х , у ):

void putpixel (int color, int x, int y)

{

unsigned addr = x/8 + y*80;

char b, mask = 0x80>>(x&7);

outportb (0x3CE, 1);

outportb (0x3CF, 0xF); // регистр 1 = 0Fh

outportb (0x3CE, 0);

outportb (0x3CF, color); // регистр 0 = color

outportb (0x3CE, 8);

outportb (0x3CF, mask); // регистр 8 = маска

b = peekb (0 xA 000, addr ); // чтение в регистры защелки

pokeb (0 xA 000, addr , b ); // запись любого байта

outportb (0 x 3 CE , 8);

outportb (0 x 3 CF , 0 xFF ); // восстановление регистра 8

outportb (0 x 3 CE , 1);

outportb (0 x 3 CF , 0); // восстановление регистра 1

}

•  Регистр 1 равен 0. Значения пикселов являются результатом операции, указанной в битах 4-3 регистра 3, над байтом процессора и байтами регистров защелки. Операция производится над байтом процессора и каждым из регистров защелки, полученные результаты записываются в соответствующую битовую плоскость. Биты, имеющие нулевые значения в регистре маски, записываются без изменения. Например, байт процессора с = 00100111, записывается в некоторый байт видеопамяти, имеющий адрес Mem , с помощью команды *Mem = c . Преобразование и запись в битовые плоскости показаны на рис. 4.5:

 

Режим записи 1. Содержимое четырех регистров защелки копируется в битовые плоскости по указанному адресу. Копируется сразу 8 пикселов. Байт процессора роли не играет. Регистры масок и режима не действуют.

Пример 3 . Напишем подпрограмму копирования прямоугольной области экрана для случая, когда прямоугольник не пересекается со своим образом и имеет координаты вершин, кратные 8:

 

void copy (int x1, int y1, int x2, int y2, int xnew, int ynew)

// координаты вершин и координаты левого верхнего угла образа

{

unsigned char *v = (char *)0xA0000000;

unsigned SrcAddr, DstAddr;

int Cols, NextRow, x, y;

SrcAddr = y1*80 + (x1>>3); // начальный адрес

DstAddr = ynew*80 + (xnew>>3);

Cols = (x2>>3)-(x1>>3); //число столбцов

NextRow = 80 - Cols; // разница между адресами последнего байта

// строки и первого байта следующей строки

SetRWMode(0,1); // режим чтения - 0, записи - 1

for(y=y1; y<=y2; y++)

{

for(x=1; x<=Cols; x++)

{

*(v+DstAddr) = *(v+SrcAddr);

DstAddr++; SrcAddr++;

}

SrcAddr += NextRow; // переход на

DstAddr += NextRow; // следующую строку

}

SetRWMode(0,1); // восстановление режимов

}

 

Режим записи 2. Младшие четыре бита байта процессора играют роль, аналогичную той, которую играл регистр 0 установки/сброса в режиме записи 0 в случае, когда регистр 1 был равен 0 Fh . Битовые плоскости модифицируются путем объединения значений пикселов в регистрах защелки со значением пиксела, заданного байтом процессора. Содержимое регистров двоичной маски показывает, какие биты из восьми будут модифицированы.

Пример 4 . Напишем подпрограмму вывода точки в режиме записи 2:

 

void writepixel (int x, int y, int color)

{

unsigned char c, mask = 0x80 >> (x % 8);

unsigned addr= y*80 + x/8;

outportb (0x3CE, 8);

outportb (0x3CF, mask);

SetRWMode(0,2); // установить режим записи - 2

outportb (0x3CE, 3); 

outportb (0x3CF, 0); // copy_put

c = peekb (0xA000, addr); // чтение в регистры защелки

pokeb(0xA000, addr,color);

}

 

(Здесь желательно добавить восстановление регистров 5 и 8).

 

Режим записи 3. Новое значение пиксела получается путём комбинирования содержимого регистров защелки и содержимого регистра 0 установки/сброса. Сдвинутый вправо на количество разрядов, записанное в битах 0-3 регистра 3, байт процессора объединяется с помощью операции AND со значением регистра 8 двоичной маски. Полученная в итоге битовая маска играет роль, аналогичную роли значения регистра двоичной маски в режимах 0 и 2.

 

4.4. Форматы хранения графических файлов

Сущ ествуют два принципиально отличающихся метода представления графических изображений: растровый и векторный . Растровое представление означает, что изображение было разбито сеткой. Световое значение (яркость, затемнение или цвет) каждой части (пиксела) сетки записывается индивидуально. Векторное представление определяет изображение в виде серии линий или фигур, возможно с некоторыми закрашенными областями, заполняемыми по специальным шаблонам. Текстовые процессоры, такие как Microsoft Word , часто способны включать как векторные изображения, например, файлы графопостроителя HPGL из программ CAD , так и двоичные изображения, например файлы TIFF из графических редакторов. Тем не менее, текстовые процессоры не обрабатывают изображения, а лишь привязывают их к документу.

Мы рассмотрим два формата, принадлежащие к различным методам представления изображений: PCX и PIC . Они могут быть обработаны текстовыми процессорами Microsoft Word , Word Perfect и др.

Формат PCX. Этот формат является наиболее широко используемым растровым форматом. Современные версии этого формата поддерживают полноцветные картинки (24-битовые цвета) и реализуются либо в качестве палитры, имеющей до 256 цветов, либо как полный 24-битовый RGB с размерами изображений до 64000 x 64000 пикселов. Изображение сжимается методом группового кодирования. Разработчиком новых версий файлов PCX является фирма Zsoft . Версии 0, 2, 3, 4, 5 соответствуют версиям приложения PC Paintbrush . Версия 4 соответствует PC Paintbrush для Windows , а версия 5 – PC Paintbrush IV Plus , PC Paintbrush Plus для Windows , Publisher's Paintbrush .

Файл формата PCX содержит заголовок (128 байт) и данные. В некоторых версиях этого формата добавляется отдельная часть, посвященная палитре с количеством цветов до 256. Мы не будем рассматривать эти версии.

Заголовок определяется с помощью структуры:

struct PCXHEADER

{

char manuf ; // информация о программе, создающей изображение;

// для Paintbruah равно 10

char hard ; // версия, рассматривается 5

char encod ; // 1 – групповое кодирование, 0 – без сжатия

char bitpx ; // число битов на пиксел в каждой плоскости

int x 1, y 1, x 2, y 2; // координаты левого верхнего и правого нижнего

// углов, включительно

int hres ; // горизонтальное разрешение = количество точек на

// дюйм по оси Х при распечатывании

int vres ; // вертикальное разрешение = количество точек на

// дюйм по оси У при распечатывании (72)

char clrma [48]; // палитра для 16 цветов

char vmode ; // резерв, всегда = 0

char nplanes ; // количество битовых плоскостей (4)

int bplin ; // число байт на одну строку в одной битовой плос-

// кости для распакованной строки, в Paintbrush

// выравнивается на границу слова

int palinfo ; // 1 – цветная палитра, 2 – серая

int shres ; // число пикселов экрана в

// горизонтальном направлении (640)

int svres ; // высота экрана в пикселах (480)

char xtra[54]; // резерв

}

Алгоритм сжатия . Считываются строки, имеющие координаты у 1 ? у ? у 2. Каждой строке будет соответствовать строка бит, содержащая сначала биты плоскости 0, затем – плоскостей 1, 2, 3. На рис. 4.6 показано, каким образом формируется эта строка бит.

Число сохраняемых строк равно у 2 - у 1 + 1. Сохраняемая строка состоит из ( int ) (4* ( х 2 - х 1 + 1) / 8) байт, если это число нечетно, то к этой строке добавляется один байт.

Байты считываются из сохраняемой строки бит и упаковываются следующим образом:

•  если байт повторяется, то записываются 2 байта – байт-повторитель и байт-значение (в котором содержится значение повторяемого байта), поскольку число повторений состоит из 6 бит, то оно не больше 63;

•  если байт один и если его значение < 11000000 b , то записывается один байт, равный ;

•  если байт один и если его значение ? 11000000 b , то записываются два байта, первый из которых равен 11000001 b , а второй = 11000000 b + значение.

Кодирование цвета в палитре . В массиве заголовка clrma[48] находится информация о палитре. Каждому регистру палитры отводится три байта. Например, регистру 0 отводятся байты clrma[0] = яркость красного, clrma[1] = яркость зеленого, clrma[48] = яркость синего. Регистру палитры i отводятся байты clrma[3*i], clrma[3*i+1], clrma[3*i+2] .

Формат PIC. Приложение Lotus 1-2-3 генерирует файлы формата PIC для передачи его во вспомогательную программу Lotus печати графики. Файл формата PIC состоит из заголовка (17 байт) и графических команд, оперирующих с областью, состоящей из 3201 ? 2312 точек. Левый нижний угол рисунка имеет координаты (0, 0), а правый верхний – (3200, 2311).

Заголовок состоит из следующих 17 байт:

1, 0, 0, 0, 1, 0, 8, 0, 0х44, 0, 0, 0, 0, 0х0С, 0х7 F , 9, 6.

Каждая команда состоит из однобайтового кода операции, за которым при необходимости следуют аргументы. Значения координат Х и Y представляются как 16-битные целые числа. У этих значений старший байт расположен первым (соглашение Motorola ), даже несмотря на то, что почти все программы, поддерживающие PIC , распространяются на машинах Intel . Обозначим через Х ст и X мл соответственно старший и младший байты Х .

Перечень команд (названия и коды):

•  Color p – установка цвета p ,

код 0хВ p (1 байт);

•  Move x , y - установка текущей позиции в ( x , y ) ,

код 0хА0, х ст , х мл , у ст , у мл (5 байт);

•  Draw x , y – аналогично графической функции языка C++ lineto(x,y) ,

код 0хА2, х ст , х мл , у ст , у мл (5 байт);

•  Fill N , x 0, y 0, …, xN , yN – заполненный многоугольник,

код 0х30, N , х0 ст , х0 мл , …, у N ст , у N мл (4 N +6 байт);

•  Fillo N , x 0, y 0, …, xN , yN – заполненный многоугольник с границей,

код 0х D 0 , N , х0 ст , х0 мл , …, у N ст , у N мл (4 N +6 байт);

•  Size x , y – установка размера последовательно выводящегося текста, координаты х и у задают размеры символа,

код 0хАС , х ст , х мл , у ст , у мл (5 байт);

•  Font N – номер шрифта ( N =0 или N =1 ),

код 0хА7 , N (2 байта);

•  Text N , строка – вывод текста от текущей позиции, старшие 4 бита байта N определяют направление:

0 – горизонтально, слева направо,

1 – текст повернут на 90 ° ,

2 – текст повернут на 180 ° ,

3 – текст повернут на 270 ° ,

а младшие 4 бита байта N управляют выравниванием текстовой строки относительно текущей позиции; строка ограничена прямоугольником, и некоторая точка этого прямоугольника отождествляется с текущей позицией, согласно следующим значениям выравнивания:

0 – центр прямоугольника,

1 – центр левой стороны,

2 – центр правой стороны,

3 – центр нижней стороны,

4 – левый верхний угол,

5 – правый верхний угол,

6 – левый нижний угол,

7 – правый нижний угол,

код 0хА8 , N , символ, символ,…,символ, 0 (последний символ строки равен 0 );

•  End – конец рисунка,

код 0х06 (1 байт).

Примеры. Команда перемещения текущей позиции Move 1000, 2000 кодируется в виде:

0хА0, 3, 232, 7, 208,

поскольку старший байт числа 1000 равен 3, а младший – 232, а для 2000 они равны соответственно 7 и 208. Заполненный треугольник с вершинами (100, 100), (200, 200) и (100, 300) определяется с помощью команды

Fill 2, 100, 100, 200, 200, 100, 300

и кодируется с помощью 14 байт:

0х30, 2, 0, 100, 0, 100, 0, 200, 0, 200, 0, 100, 1, 44.

up