Графические устройства


Графические устройства

         

Исходная цветовая палитра

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

После включения пли перезагрузки компьютера в DAC-регистры записывается палитра цветов, хранящаяся в BIOS. Ее структура не зависит от установленного видеорежима, но в зависимости от установленного режима прикладным задачам доступны только первые 16 или все 256 цветов. В последнем случае принято говорить о стандартной палитре VGA.

Программа для визуализации палитры

Словесное описание каждого цвета едва ли позволит наглядно представить, как выглядит эта палитра, целесообразнее составить программу, позволяющую увидеть все 256 цветов на экране монитора. Для упрощения программы надо использовать стандартный графический режим VGA IBM (его код I3h). В таком случае потребуется минимум вспомогательных действий, а сравнительно низкое разрешение позволит получить более наглядное изображение. Текст программы приведен в примере 4.1.
Для получения завершенной задачи надо выполнить следующие действия:

1. Создать исходный файл, содержащий текст примера 4.1. Имя файла вы можете выбрать по своему усмотрению, а тип должен быть asm. 2. Обработать исходный файл Макроассемблером (произвести компиляцию) для получения объектного модуля, имеющего тип obj. 3. Обработать объектный модуль с помощью компоновщика (LINK), в результате чего будет получен файл задачи, имеющий тип ехе.

Конкретный способ выполнения перечисленных действий зависит от того, используете вы пакет PWB иди нет. Начиная с версии 6.0, Макроассемблер MASM поставляется в комплекте с пакетом PWB (programmer's workBench— инструментальные средства для программирования). В случае использования пакета все перечисленные в списке действия выполняются непосредственно в рабочей среде, которую поддерживает PWB. Если же он не используется, то сначала создается исходный файл с помощью любого текстового редактора. Затем для его обработки вызывается MASM, который создает объектный модуль. Наконец, для построения задачи из объектного модуля вызывается компоновщик. В любом случае при построении задачи примера 4.1 будет выдано сообщение об отсутствии сегмента, содержащего стек. Это простое предупреждение, а не признак ошибки.

Пример 4.1. Программа для визуализации стандартной палитры

Code SEGMENT начало сегмента "code"
ASSUME CS:code связь регистра CS с сегментом "code"
start: mov ax, 13h код установки видеорежима 13h int lOh установка видеорежима
outscr: mov ax, OAOOOh AOOO — сегмент видеопамяти
mov es, ax пишем его в регистр ES
xor di, di 0 — начальный адрес видеопамяти
mov ex, 200 количество строк на экране
1р_1: push ex сохраняем счетчик повторов
mov ex, 320 указываем размер строки
xor al, al код первой точки (0)
1р_2: stosb рисуем точку
inc al увеличиваем код точки на 1
loop lp_2 управление выводом строки
pop ex восстанавливаем счетчик строк
loop iP..1 управление выводом строк
mov ax, OCOlh код функции ожидания ввода
int 21h DOS ждет нажатия на клавишу
mov ax, 03 код установки видеорежима 3
int lOh установка видеорежима
mov ax, 4COOh код функции завершения задачи
int 21h DOS завершает выполнение задачи
ends конец сегмента "code"
end start конец текста программы
code

Суть выполняемых в программе действий заключается в следующем. На экран последовательно выводится 200 строк. При выводе каждой строки в видеопамять последовательно записывается содержимое регистра ai, которое в исходе равно 0 и после вывода каждой точки увеличивается на 1. Может показаться, что ai изменяется от 0 до 319, но это не так. Регистр al содержит восемь разрядов, поэтому его содержимое будет монотонно нарастать от 0 до 255, на 256-м шаге оно окажется равным нулю, затем будет снова нарастать и в конце строки достигнет значения 63. Все строки заполнены одинаково, поэтому при выполнении программы на экране возникнут разноцветные полосы ("занавес"), каждая из них показывает, какой цвет закодирован в конкретном оде-регистре. Прежде чем обсуждать получаемую картину, завершим описание программы.

Две первые и две последние строчки программы содержат информацию, относящуюся к ее оформлению. Точка входа в программу имеет метку start. Выполнение программы начинается с установки видеорежима VGA IBM, его код 13h, разрешение составляет 320x200 точек, размер палитры 256 цветов.

Далее в регистр es записывается код сегмента видеобуфера АОООЬ. Прямая запись значений в сегментные регистры невозможна, поэтому используется регистр-посредник ах. В регистре di устанавливается нулевой адрес, соответствующий началу строки. Пара регистров es:di выбрана для того, чтобы записывать коды точек командой stosb.
На экране переход с одной строки на другую выполняет видеоконтроллер при достижении конца очередной строки. Программа же просто выводит в цикле 1р_2 количество точек, совпадающее с размером строки для данного режима. Измените 320 на 319 или 321 и картинка "рассыпается", поскольку начало нового цикла вывода не будет совпадать с началом строки на экране.

После заполнения экрана надо выдержать паузу, чтобы вы могли увидеть и оценить полученный результат. Для этого программа обращается к DOS с запросом на ввод символа с клавиатуры. Никаких предупреждающих сообщений на экран не выводится. Возвращение в программу произойдет после того, как вы нажмете одну из информационных клавиш клавиатуры — букву, цифру, <пробел>, <Enter> и пр. После этого произойдет немедленное завершение задачи (возврат в DOS).

Построенная задача выведет на экран интересующие нас цвета при условии, что палитра установлена. Дело в том, что загрузку палитры при смене режимов работы видеокарты можно запретить, записав 1 в третий разряд слова с адресом оооо:С48Э из области данных BIOS. Обычно этот разряд очищен, и палитра загружается при любых изменениях режимов (как текстовых, так и графических). Одна из функций прерывания int юъ, относящихся к группе i2h, предназначена для разрешения или запрещения загрузки палитры. При ее вызове в регистре ы указывается код 3ih, а в регистре ai — 0 или 1.

Устанавливаемая DOS палитра в книге описана примерно так (это не цитата, а скорее вольный перевод оригинала). Первые 16 DAC-регистров содержат палитру для режима CGA, в следующих 16-ти регистрах записаны коды разных оттенков серого цвета. Затем располагаются три основные группы, занимающие по 72 регистра и содержащие коды цветов высокой, средней и низкой интенсивности. Каждая группа делится на 3 одинаковых подгруппы, содержащие коды цветов высокого, среднего и низкого насыщения. Последние восемь регистров просто очищены, им соответствует черная полоса. Тут автор книги допустил неточность, — фактически при установке палитры последние 8 регистров не заполняются. После включения компьютера они очищаются, но их содержимое могут изменить программы, работающие в графических режимах. Поэтому вместо черной полосы, соответствующей последним восьми линиям, вы можете увидеть другой цвет.

Описанная программа позволяет получить качественное представление о цветах палитры, установленной по умолчанию. Если вас интересуют точные значения, т. е. коды этих цветов, то придется составить собственную программу для распечатки содержимого базовых регистров. В следующем разделе рассмотрены функции BIOS, позволяющие определить содержимое любого DAC-регпстра. Здесь мы опишем младшую часть устанавливаемой BIOS палитры, которая является палитрой CGA.

Стандартная палитра CGA

Установка и поддержка BIOS стандартной палитры CGA вызвана требованием совместимости с устаревшим программным обеспечением.

Программы, создававшиеся для IBM PC/XT и IBM PC/AT, должны выполняться на современных моделях ПК без каких-либо ограничений. Кроме того, палитра CGA нужна при работе в текстовых режимах, которые устанавливаются при первоначальной загрузке компьютера и DOS. Наконец, при описании функций BIOS очень часто приводятся коды цветов палитры CGA. В этом отношении рассматриваемые в данной книге примеры не являются исключением.

Содержимое первых 16-ти DAC-регистров (палитра CGA) показано в табл. 4.2. В ней перечислены коды, названия цветов и содержимое байтов соответствуюших DAC-регмстров. По интенсивности цвета делятся на две группы -, средняя и высокая интенсивность, соответственно таблица разделена па две половины (серый цвет является исключением). Коды — это шестнадшгге-ричные числа. Соответствие между ними и уровнями интенсивности в процентах такое: 3F - 100%, 2А - 67%, 15 -33%.
Поскольку цвет точки зависит от содержимого соответствующего ее коду DAC-регистра, то в дальнейшем, говоря о конкретном цвете, мы будем приводить коды трех базовых цветов (г, д, ь), хранящихся в указанной последовательности в байтах ода-регистра. Например, черному цвету соответствуют коды 0, 0, 0, а белому — коды 3F, 3F, 3F.

Таблица 4.2. Названия и коды цветов палитры CGA

Код точки
Название цвета
Коды базовых цветов
Красный
Зеленый
Синий
0
Черный
00
00
00
1
Синий
00
00
2
Зеленый
00
2А '
00
3
Циан
00
4
Красный
00
00
5
Фиолетовым
00
6
Коричневый
15
00
7
Белый
8
Серый
15
15
15
9
Синий
15
15
3F
А
Зеленый
15
3F
15
В
Циан
15
3F
3F
С
Красный
3F
15
15
D
Фиолетовый
3F
15
3F
Е
Желтый
3F
3F
15
F
Белый
3F
3F
3F

Сравните вторую половину табл. 4.2 с табл. 4.1. Вы увидите, что одноименным цветам в них соответствуют разные значения базовых цветов. Например- красному цвету высокой интенсивности в табл. 4.2 соответствует тройка кодов в 3F, 15, 15, а в табл. 4.1 — зг, оо, оо. Какой из них считать красным. 5 какойи не совсем красным? Еще один пример. В таблицах редактора CorelDrаw фиолетовому цвету средней интенсивности соответствуют коды 26, 00, 2б, а высокой интенсивности — 3F, 26, 3F. В табл. 4.2 для этих же названий цветов указаны другие значения.

Эти примеры иллюстрируют тот факт, что наше восприятие цвета весьма субъективно. То, что одному кажется фиолетовым цветом, другой склонен считатьо пурпурным. Кроме того, наша способность различать близкие цвета весьма индивидуальна. Разница в интенсивности двух близких цветов с кодами 26,00, 26 и 2А, 00, 2А составляет всего 4/64 = 6.25% и вполне возможно, что» она будет неразличима для глаза.

Простая установка палитры

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

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

Формат и место палитры в файле

Папитра или таблица цветов содержит коды цветов, использованных при создании рисунка. В большинстве стандартов код цвета занимает 3 байта, в которых указана интенсивность базовых цветов — красного, зеленого и синего. Если базовые цвета перечислены именно в такой последовательности, то мы будем говорить, что палитра имеет формат г, а, ь. Если в такой палитре описано N цветов, то она занимает в файле зы байтов. Исключением является стандарт BMP (см. приложение А). В этом случае строка может содержать три или четыре байта, а палитра занимает в файле соответственно SN или 4N байтов. В трех первых байтах хранятся коды синего, зеленого и красного цветов. Если в строке есть четвертый байт, то он очищен. В таких случаях мы будем говорить, что палитра имеет формат b, g, r или b, g, r, 0.

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

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

Мы опишем установку палитры при работе с файлами, соответствующими стандарту PCX, аналогичные действия при работе с файлами стандарта BMP описаны в приложении А данной книги.

Расположение и варианты палитры PCX

Стандарт PCX создавался в то время, когда ПК поддерживали видеорежимы CGA и EGA. Поэтому в заголовке файла, начиная с адреса 10h, было зарезервировано 48 байтов, что позволяет разместить 16-цветную палитру. С появлением видеорежима VGA возникла необходимость изменения или доработки стандарта PCX, поскольку размер заголовка не позволял разместить 256-цветную палитру. Для сохранения совместимости с уже существующими файлами было решено оставить без изменения все, что касаюсь режимов CGA и EGA, и расположить 256-цветную палитру в конце файла, после образа рисунка. В этом случае место, занимаемое палитрой EGA, не используется.

Первой это сделала фирма Genius Microprogramming в 1988 году. В соответствии с ее версией стандарта PCX сразу после образа рисунка располагается байт, содержащий код ОА, а после него следует палитра формата r, g, b. Коды базовых цветов сокращены до 6-ти разрядов, т. е. формат палитры полностью соответствует стандарту VGA IBM и она может быть установлена с помощью уже описанного запроса 1012h прерывания int 10h.

В том же году разработчик стандарта PCX фирма ZSoft приняла аналогичные дополнения. Однако, в отличие от версии Genius, байт, расположенный перед палитрой, содержит код ос, а коды базовых цветов содержат 8 разрядов (независящие от устройства коды). Такая палитра не полностью соответствует стандарту VGA IBM — перед ее установкой все байты надо сдвинуть на 2 разряда вправо.

Таким образом, на сегодняшний день существуют, по крайней мере, два способа хранения 256-цветной палитры в стандарте PCX. Различить их можно по коду, находящемуся в байте, расположенному после образа рисунка.

Как получить доступ к палитре

Для доступа к палитре файл надо открыть, прочитать его заголовок (первые 80h байтов) и извлечь оттуда данные о размерах рисунка и кода точки. Если последний равен восьми, то палитра находится в конце файла после образа рисунка.

Расположение нужных величин в заголовке файла формата PCX описано в разделе.
Перед чтением палитры в оперативную память файл надо принудительно позиционировать так, чтобы сохраняемый DOS указатель находился на расстоянии 769 байтов от конца файла. Затем надо прочитать 769 байтов в буфер обмена. После чтения нулевой байт буфера обмена должен содержать код ОА или OC, в противном случае произошла ошибка, которая означает, что файл не соответствует стандарту PCX, или был неверно позиционирован.

Если нулевой байт буфера обмена содержит код ос, то перед установкой палитры надо сдвинуть содержимое ее байтов на два разряда вправо. В противном случае (если буфер обмена содержит код ОА) сдвиг не требуется. Теперь формат палитры, находящейся в буфере обмена, соответствует стандарту VGA IBM и ее можно устанавливать (копировать в регистры цвета).

Позиционирование файла

В процессе построения рисунка формата PCX приходится дважды принудительно изменять текущую позицию файла. Рассмотрим, как это делается.

При открытии файла по указанному имени DOS в своем разделе данных создает специальную структуру (или таблицу), в которой хранятся величины, необходимые для доступа к файлу. Порядковый номер этой структуры называется file handle (или просто ссылка), при успешном открытии файла DOS возвращает значение ссылки в регистре ах. Задача должна сохранить ссылку, например, в переменной Handler, и указывать ее при всех последующих обращениях к файлу.

В созданной DOS структуре, помимо прочих величин, хранится указатель позиции, в которой находится файл, сразу после открытия это число 0. При каждом чтении или записи значение указателя увеличивается на количество прочитанных или записанных байтов. Поэтому если вы последовательно читаете или записываете данные, то дополнительные действия по изменению позиции файла не требуются. Однако в некоторых случаях может понадобиться принудительное изменение позиции (значения указателя) без чтения или записи данных. Для выполнения таких действий предназначена специальная функция DOS, которая называется Lseek.

Все функции DOS вызываются через прерывания int 2ih, а код функции указывается в регистре ah. Код функции Lseek равен 42h. В регистре al указывается точка, относительно которой выбирается новая позиция, это числа 0, 1 или 2. Соответственно эти числам точкой отсчета является начало, текущая позиция, или конец файла. В паре регистров cx:dx задается 32-разрядное число, для получения новой позиции, его значение прибавляется к точке отсчета. При работе с файлами в регистр bx всегда записывается ссылка (file handle). После выполнения функции DOS возвращает в паре регистров dx:ax новое значение указателя, если эта величина используется в задаче, то ее надо сохранить.

Подпрограмма установки палитры PCX

В примере 4.6 приведен текст подпрограммы, выполняющей простую установку палитры из файла формата PCX. Предполагается, что файл был открыт, и его заголовок обработан. После возврата из подпрограммы задача должна проверить состояние С-раз-ряда. Если он очищен, то палитра успешно установлена. В противном случае подпрограмма обнаружила ошибку при позиционировании или в структуре файла.

Пример 4.6. Установка 256-цветной палитры из файла PCX

SetPpal : pusha сохранение содержимого регистров
; Позиционирование на коне! i файла PCX
mov ax, 4202h код запроса позиционирования файла
mov bx, Handler указываем ссылку на файл
mov ex, -I старшая часть числа -769
mov dx, -T69 младшая часть числа -769
int 21h обращение к функциям DOS
j с spexit -> ошибка позиционирования
/ Чтение палитры в буфер и проверка признака
mov ex, T 69 размер считываемой порции данных
call readf чтение данных в буфер обмена
xor si, si нулевой адрес в буфере обмена
lods byte ptr f s : [si] чтение в al нулевого байта
cmp al, OAh в байте находится код ОА ?
je spal -> да, исключаем масштабирование
cmp al, OCh в байте находится код ОС ?
je @F -> да, выполняем масштабирование
stc установка признака ошибкиjmp short spexit ;
-> на завершение подпрограммы
Масштабирование (сдвиг ее держимого байтов) палитры
@@: mov ex, 768 ; указываем размер палитры
rapal: shr byte ptr fs:[si], 0 2 сдвиг очередного байта
inc si адрес следующего байта
loop ir.pal управление повторами цикла
; Установка палитры (копире вание в регистры цвета видеокарты)
spal : push es сохраняем содержимое es
push fs сохраняем содержимое fs
pop es выталкиваем его в es
mov dx, 01 адрес начала палитры
xor bx, bx номер первого регистра цвета
mov ex, 256 кол-во устанавливаемых регистров
mov ax, 1012h код запроса на установку палитры
int lOh обращение к функции BIOS
pop es восстанавливаем содержимое es
; Позиционирование файла на начало рисунка
mov ax, 4200h код запроса позиционирования файла
mov bx, Handler указываем ссылку на файл
xor ex, ex старшая часть величины смещения
mov dx, 8 Oh младшая часть величины смещения
int 21h обращение к функциям DOS
spexit : popa восстановление регистров
ret возврат из подпрограммы

Основные фрагменты подпрограммы отделены друг от друга комментарием. В примере 4.6 используются все регистры общего назначения, поэтому первая команда pusha сохраняет их содержимое в стеке. Следующие четыре команды формируют данные для запроса на позиционирование файла. В регистр ai записывается код 2, являющийся признаком перемещения указателя в заданную позицию, а в пару регистров cx:dx — смещение этой позиции от конца файла (отрицательное число 769). Затем происходит обращение к DOS (int 2ih) для исполнения запроса. Если при позиционировании зафиксирована ошибка, то команда jc spexit выполнит переход на метку spexit для завершения подпрограммы с установленным С-разрядом.

В случае успешного позиционирования в буфер обмена считываются 769 байтов, содержащих признак ось или ОАП и собственно палитру. Для чтения используется подпрограмма readf, текст которой был приведен в примере 3.23. После чтения нулевой байт буфера обмена содержит код типа палитры. Он помещается в регистр ai и аначизируется. Если код равен OAh, то масштабирование исключается. Если код равен ось, то нужно выполнить масштабирование палитры. Если код отличается от ОАП и och, то устанавливается С-разряд и происходит возврат из подпрограммы.

Для масштабирования палитры в регистр сх записывается ее размер и выполняется цикл сдвигов содержимого каждого байта на 2 разряда вправо.

Начиная с команды, имеющей метку spal, формируются исходные данные для функции BIOS, устанавливающей палитру. В регистр es надо записать значение сегмента буфера обмена (содержимое регистра fs), а в регистр dx — адрес начала палитры в этом сегменте (1). Номер первого устанавливаемого DAC-регистра равен нулю, а количество устанавливаемых одс-ре-гистров равно 256. В регистр ах помещается код запроса (ioi2h) и происходит обращение к BIOS (int 10h). После возвращения из BIOS надо восстановить исходное содержимое регистра es, сохраненное в стеке.

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

Образ рисунка отстоит на 80h байтов от начала файла. Поэтому в примере в регистры ai и сх записывается 0, а в регистр dx код 80h. После позиционирования восстанавливается содержимое регистров (команда рора) и происходит возврат из подпрограммы. Если при позиционировании возникнет ошибка, то произойдет возврат с установленным С-разрядом.

При выходе из описанной подпрограммы С-разряд будет установлен не только при ошибках позиционирования файла, но и при отсутствии кодов OAh или ось перед палитрой. В основной программе уже невозможно определить причину ошибки. Если она важна, то в подпрограмме можно сформировать дополнительные признаки, уточняющие характер ошибки.

После успешной установки палитры можно приступать к построению рисунка на экране. Выбор способа построения зависит от содержимого байта 2 заголовка файла. Если он очищен, то образ рисунка не сжат и для вывода на экран используется подпрограмма BigDraw (см. пример 3.22). Если же байт 2 заголовка содержит единицу, то образ рисунка упакован и для распаковки при выводе на экран применяется подпрограмма PackDrw (см. пример 3.26).

Таким образом, к этому моменту вы располагаете всей информацией, необходимой для работы с файлами стандарта PCX, содержащими рисунки, использующие палитру. О работе с файлами этого же стандарта, но не использующими палитру, мы поговорим в главе 7 (см. раздел).

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

Манипуляции с палитрой цветов

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

В данном разделе будет показан способ записи новых цветов в свободные регистры видеокарты с исключением повторения одинаковых цветов. Он может быть полезен, например, при оформлении "рабочего стола", когда задача выводит на экран заставку, различные рамки, линейки, элементы меню, значки ярлыков и прочие оформительские атрибуты. Необходимые для этого рисунки обычно используют общую палитру, содержащую ограниченное количество цветов. Например, для Windows 3.X и ее. приложений создано много рисунков различных значков, хранящихся в виде вмр-файлов и пиктограмм, использующих стандартную (для Windows) 16-цветную палитру.

Системная палитра

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

При установке палитры добавляемого рисунка предпринимается попытка разместить ее в системной палитре и если она окажется успешной, то дополненная системная палитра, или только ее дополнение, копируется в регистры цвета видеокарты.
Для размещения точной копии содержимого регистров цвета требуется 768 байтов (3 256). При работе с системной палитрой использовать трехбайтовый код цвета неудобно, поэтому к трем байтам каждого цвета лучше добавить четвертый пустой байт. Это увеличит размер занимаемой памяти до 1024 байтов, но существенно упростит работу с палитрой.

Большинство вспомогательных рисунков, применяемых для оформления рабочей области экрана, хранится в файлах форматов BMP и ico. Поэтому мы выберем следующее расположение кодов базовых цветов в каждой строке системной палитры: синий, зеленый, красный, пустой байт (формат b, g, r, 0). Такой вариант системной палитры не является точной копией содержимого регистров цвета видеокарты. Однако хранящиеся в ней коды цветов строго соответствуют кодам, находящимся в регистрах видеокарты.

Учитывая достаточно большой размер палитры, ее не целесообразно хранить в разделе данных задачи. Лучше выделить отдельный сегмент, а в разделе данных зарезервировать следующие два слова:

Syspal dw 0 ; смещение палитры от начала сегмента (обычно 0)
dw 0 ; код сегмента памяти, в котором хранится палитра

Если память распределяет программист при подготовке исходного текста задачи, то место размещения палитры описывается в тексте программы и нули заменяются конкретными значениями. Если же память распределяется динамически, т. е. пространство для размещения палитры выделяется в процессе выполнения задачи, то нужные значения записываются в syspai и Syspai+2 после того, как будет определен код сегмента для хранения палитры.

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

numcol dw 0 ; количество цветов, хранящихся в системной палитре

Инициализация системной палитры

После выделения пространства для системной палитры в ее строки и в регистры цвета видеокарты надо записать коды цветов, которые не будут изменяться во время выполнения задачи. В Windows 3X таких цветов 20, они называются "статическими" и применяются для оформления окон (цвета рамок, фона, текста и пр.). Прикладные задачи могут использовать, но не могут изменять эти цвета.

Мы ограничимся двумя статическими цветами — черным и белым. Код черного цвета (0, 0, 0, 0) надо записать в нулевую, а код белого (ЗF, 3F, 3F, 0) в последнюю (255-ю) строку syspai. Инициализация палитры производится в той части программы, где выполняются подготовительные действия. Туда надо вставить следующую группу команд:

Igs di, dword ptr Syspal gs:di = адрес Syspal в памяти
xor eax, eax eax = код черного цвета
mov gs:[di], eax запись кода в начало Syspal
mov eax, 003F3F3Fh eax = код белого цвета
mov gs:[di+1020], eax запись кода в конец Syspal
mov numcol, 01 количество цветов в палитре
; Сюда надо вставить команды из примера 4.2

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

Внимание!
В системную палитру записано 2 цвета, а переменной numcol присвоено значение 1. Это сделано потому, что код белого цвета будет обрабатываться нестандартно. По мере заполнения палитры предельно допустимым значением rjumcoi является 255, а не 256, т. к. последняя строка палитры занята кодом белого цвета.

Пополнение системной палитры

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

Рассмотрим конкретный пример. Предположим, что после инициализации системной палитры на экран выводится рисунок, использующий стандартную для Windows ЗХ 16-цветную палитру. При ее добавлении в системной палитре появятся 14 новых цветов, поскольку черный и белый там уже находились. Теперь при добавлении на экран любого количества рисунков, использующих 16-цветную палитру Windows, системная палитра не изменяется. В этом случае эффективность описанного алгоритма очевидна.
Добавленные в системную палитру цвета надо записать в регистры цвета видеокарты, иначе их нельзя будет использовать. Запись в регистры видеокарты производится либо при добавлении каждого нового цвета, либо после обработки всех цветов добавляемой палитры. Вы можете выбрать любой из этих вариантов по своему усмотрению, очевидных преимуществ друг перед другом у них нет.

Новые номера цветов

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

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

Index db 256 dup (?); выделение 256 байтов для массива Index

Массив index имеет следующую структуру. Номера байтов соответствуют номерам строк палитры добавляемого рисунка, а содержимое байтов является номерами строк системной палитры. При построении рисунка по коду точки выбирается соответствующий байт массива index и его содержимое записывается в видеопамять.
Подпрограмма установки палитры. Текст подпрограммы, выполняющей описанные действия, приведен в примере 4.7. Перед ее вызовом добавляемая палитра должна быть прочитана в буфер обмена, и адрес ее начала указан в регистрах fs:di (сегмент : смещение). Размер палитры (количество строк, или описанных в ней цветов) помещается в регистр сх. Как прочитать палитру и определить ее размер при работе с файлами формата BMP, описано в приложении А данной книги.

Если при добавлении новых цветов произойдет переполнение системной палитры, то перед возвратом из подпрограммы будет установлен С-разряд и восстановлено исходное значение переменной numcoi.

Пример 4.7. Установка палитры добавляемого рисунка формата BMP

AnlsPal pusha сохранение "всех" регистров
PushReg <gs,numcol> сохранение содержимого gs и numcol
xor bx, bx исходный номер байта в Index
anls 1 : PushReg <cx,bx> сохранение содержимого сх и bx
mov ebx, fs: [di] ! ! очередной добавляемый цвет
shr ebx, 02 масштабирование кода этого цвета
and ebx, 03F3F3Fh выделение 6-ти младших разрядов
mov dx, OFFh номер белого цвета в SysPal
cmp ebx, 03F3F3Fh добавляется белый цвет ?
jz anls 4 -> да
Igs si, dword ptr Sy spal; установка сегмента и смещения
mov ex, numcol количество цветов в Syspal
xor dx, dx очистка номера строки в Syspal
; Цикл сравнения с цвет< ами, записанными в Syspal
anls_2: lods dword ptr gs : [si ] чтение текущего цвета из Syspal
cmp eax , ebx сравнение с добавляемым цветом
jz anls 4 -> цвета совпали
inc dx номер следующей строки Syspal
loop anls 2 управление циклом сравнений
; Добавляемый цвет отли' лается от хранящихся в Syspal
cmp numcol, 255 использованы все регистры цвета ?
jnz anls 3 => нет
stc установка С-разряда
PopReg <bx, ex, numcol> восстановление bx, сх и numcol
jmp short anls 5 "короткий" переход на метку anls 5
; Запись нового цвета в Syspal и в регистр цвета
anls_3: mov [si] , ebx добавляем новый цвет в Syspal
inc numcol увеличиваем счетчик цветов в Syspal

Выполнение примера 4.7 начинается с сохранения в стеке содержимого всех используемы.фегистров. Команда pusha не работает с сегментными регистрами, поэтом, содержимое gs сохраняет макрос pushReg. Он же помещает в стек исходноезначение переменной numcol для того, чтобы его можно было восстановить) случае переполнения системной палитры. Третья команда очищает регистр bx, который используется как счетчик байтов массива Index.

Основные действия выполняются в двух вложенных циклах. Внешний имеет
метку anis_i.. внутренний — anis_2.

Внешний цш! повторяется столько раз, сколько цветов (строк) содержит палитра добашемого рисунка. Эта величина указывается в регистре сх при вызове подпрограммы. Оба цикла используют общий регистр сх для хранения счетчикаювторов, поэтому выполнение внешнего цикла начинается с сохранения в:теке его содержимого. Кроме того, в стеке сохраняется содержимое bx.поскольку его изменяет следующая команда. Обе величины сохраняет макрос PushReg.

После сохранения содержимого сх и bх в регистр ebx считывается код очередной строк палитры добавляемого рисунка. Коды базовых цветов в ней сокращаются до шести разрядов путем сдвига содержимого регистра ebx на два разряда впаво и выделения шести младших разрядов каждого байта.

Код белого иета расположен в конце системной палитры, поэтому во внешнем цик: надо проверить, не является ли анализируемый цвет белым. Если это такго выполняется переход на метку anis_4 для записи номера белого цвета: в очередной байт массива index. В противном случае происходит подготовка к выполнению цикла сравнений. Для этого в регистры gsrsi загружается адрес системной палитры, в регистр сх помещается количество хранящихся в ней цветов, а регистр dx очищается.

Первые пять команд внутреннего цикла выполняют сравнение кода цвета, находящегося в регистре ebx, с кодами строк системной палитры. При совпадении происходит переход на метку anls_4 для записи текущего номера строки системной палитры в очередной байт массива index. В противном случае сравнение продолжается, пока не будут проверены все коды.

После завершения цикла сравнений надо проверить, осталось ли в Syspal место для записи нового цвета. Если осталось, то выполняется переход на метку anis_3. В противном случае устанавливается признак переноса, восстанавливаются сохраненные в стеке величины и происходит возврат на вызывающий модуль.

Если в syspai есть свободное место, то код добавляемого цвета записывается в первую свободную строку, значение numcol увеличивается на 1 и происходит обращение к BIOS для записи кода в регистр цвета видеокарты, номер которого совпадает с номером строки системной палитры. Затем выполняется фрагмент, первая команда которого имеет метку anis_4.

Из стека восстанавливается содержимое регистра bx и в указанный в нем байт массива index записывается номер строки системной палитры, содержащей анализируемый цвет. Находящийся в регистре di адрес увеличивается на 4 так, чтобы он указывал начало следующей строки добавляемой палитры. Из стека восстанавливается содержимое регистра сх и команда loop anis_i повторяет выполнение внешнего цикла до тех пор, пока не будут обработаны все строки (цвета) палитры добавляемого рисунка.

Если добавление палитры закончилось успешно, то изменять значение переменной numcol нельзя, поэтому сохраненное в стеке значение выталкивается в регистр ах. Затем восстанавливается содержимое регистра gs и всех остальных регистров и происходит возврат на вызывающий модуль.

Если для установки добавляемой палитры недостаточно места, то при возврате из подпрограммы AnisPal будет установлен С-разряд регистра флагов и сохранено исходное значение numcol. Поэтому при неудачной попытке установки палитры цвета, записанные в системную палитру и регистры видеокарты, не учитываются при последующих обращениях к подпрограмме AnisPal.

Изменения в тексте подпрограммы

Как уже говорилось, пример 4.7 составлен исходя из предположения, что палитра добавляемого рисунка хранится в файле в формате b, g, r, 0. Если по каким-то соображениям вам нужно работать с палитрами формата г, д, ь, то в тексте примера 4.7 надо изменить две команды. Комментарий к заменяемым командам начинается с двух восклицательных знаков.

Для размещения в регистре ebx трех базовых цветов текущей строки палитры добавляемого рисунка команда mov ebx, fs: [di] заменяется приведенной в примере 4.8 группой, состоящей из пяти команд.

Пример 4.8. Преобразование формата r, g, b в формат b, g, r, 0.

xor ebx , ebx ; очистка Реги тра ebx
mov bh, fs: [di] ; чтение в bh кода красного цвета
mov Ы, fs: [dl+1] ; чтение в bl кода зеленого цвета
shl ebx , 08 ; сдвиг ebx на 8 разрядов влево
mov Ы, fs: [di+2] ; чтение в bl кода синего цвета

И второе изменение. Строка палитры формата r, g, b занимает в памяти три байта, поэтому команду переадресации add di, 04 надо заменить командой add di, 03.

Подпрограмма построения строки

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

Текст подпрограммы приведен в примере 4.9. Входные параметры для нее указываются так же, как и для всех подпрограмм, описанных в разделе . Пара регистров fs:si содержит адрес начала образа строки в оперативной памяти, регистр di задает адрес видеопамяти, начиная с которого записываются коды точек. Как обычно, должно быть установлено окно видеопамяти, содержащее адрес, указанный в регистре di, а в регистр es помещен код видеосегмента. Количество точек в строке помещается в регистр сх. Предполагается, что массив index находится в разделе данных.

Пример 4.9. Вывод строки рисунка с изменением кодов точек

drawl ine: push bx сохранение содержимого bx
lea bx, Index bx = адрес массива Index
drwln 1 : lods byte ptr fs: [si] ; чтение исходного кода точки
xlat перекодировка al = [bx + al]
stosb запись кода точки в видеопамять
or di, di начало нового сегмента ?
jne @F -> нет, обход команды call NxtWin
call NxtWin установка следующего окна
@@: loop drwln 1 управление повторами цикла
pop bx восстановление содержимого bx
ret возврат из подпрограммы

"Изюминкой" примера 4.9 является перекодировка точек рисунка с помощью команды xlat. При ее выполнении суммируется содержимое регистров bх и ai и в регистр ai копируется содержимое байта, расположенного по вычисленному адресу. Остальные действия, выполняемые в примере, уже неоднократно обсуждались, поэтому мы не будем повторяться.

Недостаточно места в системной палитре

Ограниченное количество цветов, которые можно одновременно вывести на экран, является "Ахиллесовой пятой" видеорежимов PPG. Вместе с тем, именно по этой причине код точки занимает всего 1 байт, и манипуляции с графическими объектами выполняются достаточно просто и быстро.

В общем случае задача одновременного вывода на экран двух рисунков, использующих разные палитры цветов, неразрешима. Ее можно решить только при определенных ограничениях на размеры палитр и разнообразие описанных в них цветов.

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

В семействе Windows 3X применяется комбинированное решение. Рисунки, предназначенные для оформления рабочего стола, как уже говорилось, используют стандартную 16-цветную палитру, которая мало чем отличается от палитры CGA, приведенной в