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



 

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

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

В данном разделе будет показан способ записи новых цветов в свободные регистры видеокарты с исключением повторения одинаковых цветов. Он может быть полезен, например, при оформлении "рабочего стола", когда задача выводит на экран заставку, различные рамки, линейки, элементы меню, значки ярлыков и прочие оформительские атрибуты. Необходимые для этого рисунки обычно используют общую палитру, содержащую ограниченное количество цветов. Например, для 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.

После сохранения содержимого сх и в регистр 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. При ее выполнении суммируется содержимое регистров и ai и в регистр ai копируется содержимое байта, расположенного по вычисленному адресу. Остальные действия, выполняемые в примере, уже неоднократно обсуждались, поэтому мы не будем повторяться.

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

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

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

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

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

Остальные 236 строк системной палитры отведены для цветов заставок и для нужд прикладных задач. У многоцветных рисунков часть цветов просто изменяется. Разумеется, при сокращении цветов рисунок несколько отличается от оригинала, но что остается делать, если в вашем распоряжении есть только ограниченное число регистров цвета.

 
Назад Начало