4. Графический интерфейс
Добавил: | DMT | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Дата создания: | 5 апреля 2008, 15:29 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Дата обновления: | 5 апреля 2008, 15:34 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Просмотров: | 7865 последний --- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Комментариев: | 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).
Регистры графического контроллера. Графический контроллер имеет 9 восьмиразрядных регистров. Чтобы записать значения в один из регистров, нужно выполнить 2 команды:
outportb (0 x 3 CE , номер_регистра); outportb (0 x 3 CF , значение);
Регистры имеют номера и названия, показанные на рис. 4.2:
Рис.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
Рис. 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.
В данном случае копирование производится в режиме 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. Содержимое четырех регистров защелки копируется в битовые плоскости по указанному адресу. Копируется сразу 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 байта – байт-повторитель
• если байт один и если его значение < 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. |
