|
Манипуляции с палитрой цветов При разработке графических программ сравнительно часто приходится решать следующую задачу. На экране находится исходное изображение, при построении которого использованы некоторые регистры цвета видеокарты. К нему добавляется новый рисунок, закрывающий только часть исходной картинки. Цвета не затронутой новым рисунком части экрана должны сохраниться. Поэтому при установке палитры добавляемого рисунка нельзя изменять содержимое уже используемых регистров цвета видеокарты. В данном разделе будет показан способ записи новых цветов в свободные регистры видеокарты с исключением повторения одинаковых цветов. Он может быть полезен, например, при оформлении "рабочего стола", когда задача выводит на экран заставку, различные рамки, линейки, элементы меню, значки ярлыков и прочие оформительские атрибуты. Необходимые для этого рисунки обычно используют общую палитру, содержащую ограниченное количество цветов. Например, для Windows 3.X и ее. приложений создано много рисунков различных значков, хранящихся в виде вмр-файлов и пиктограмм, использующих стандартную (для Windows) 16-цветную палитру. Системная палитра Для того чтобы каждый раз при добавлении нового рисунка не считывать в оперативную память текущее содержимое регистров цвета видеокарты, имеет смысл постоянно хранить в оперативной памяти копию их текущего состояния. Эту копию мы будем называть системной палитрой. При установке палитры добавляемого рисунка предпринимается попытка разместить ее в системной палитре и если она окажется успешной, то дополненная системная палитра, или только ее дополнение, копируется в регистры цвета видеокарты. Большинство вспомогательных рисунков, применяемых для оформления рабочей области экрана, хранится в файлах форматов BMP и ico. Поэтому мы выберем следующее расположение кодов базовых цветов в каждой строке системной палитры: синий, зеленый, красный, пустой байт (формат b, g, r, 0). Такой вариант системной палитры не является точной копией содержимого регистров цвета видеокарты. Однако хранящиеся в ней коды цветов строго соответствуют кодам, находящимся в регистрах видеокарты. Учитывая достаточно большой размер палитры, ее не целесообразно хранить в разделе данных задачи. Лучше выделить отдельный сегмент, а в разделе данных зарезервировать следующие два слова: Syspal dw 0 ; смещение палитры от начала сегмента (обычно 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 в памяти В большинстве случаев нулевой регистр цвета видеокарты уже содержит код черного цвета (его байты очищены). Если это не так, то к перечисленным командам надо добавить очистку нулевого регистра цвета. Содержимое остальных регистров цвета не имеет значения, они считаются свободными, поскольку задача работает только с системной палитрой. Внимание! Пополнение системной палитры Вместо простой установки палитры добавляемого рисунка она размешается в системной палитре. При этом код каждого добавляемого цвета последовательно сравнивается с кодами всех цветов, находящихся в системной палитре. Отсутствующий цвет помещается в первую свободную строку системной палитры, а значение переменной numcol увеличивается на 1. В результате будут добавлены только те цвета, которых не было раньше. Если в добавляемой палитре повторится один и тот же цвет (такое бывает), то в системную палитру он будет включен только один раз. Рассмотрим конкретный пример. Предположим, что после инициализации системной палитры на экран выводится рисунок, использующий стандартную для Windows ЗХ 16-цветную палитру. При ее добавлении в системной палитре появятся 14 новых цветов, поскольку черный и белый там уже находились. Теперь при добавлении на экран любого количества рисунков, использующих 16-цветную палитру Windows, системная палитра не изменяется. В этом случае эффективность описанного алгоритма очевидна. Новые номера цветов Коды точек образа рисунка являются номерами строк палитры, хранящейся в том же файле. При использовании описанного алгоритма порядок расположения части или всех исходных цветов может измениться. Поэтому при обработке каждого цвета добавляемого рисунка надо запоминать его расположение в системной палитре, а при построении рисунка выполнять перекодировку точек. Для хранения новых значений кодов точек рисунка в памяти резервируется пространство размером в 256 байтов (максимальное количество цветов палитры). Небольшой размер этого пространства позволяет расположить его в сегменте данных программы, например, так: Index db 256 dup (?); выделение 256 байтов для массива Index Массив index имеет следующую структуру. Номера байтов соответствуют номерам строк палитры добавляемого рисунка, а содержимое байтов является номерами строк системной палитры. При построении рисунка по коду точки выбирается соответствующий байт массива index и его содержимое записывается в видеопамять. Если при добавлении новых цветов произойдет переполнение системной палитры, то перед возвратом из подпрограммы будет установлен С-разряд и восстановлено исходное значение переменной numcoi. Пример 4.7. Установка палитры добавляемого рисунка формата BMP AnlsPal pusha сохранение "всех" регистров Выполнение примера 4.7 начинается с сохранения в стеке содержимого всех используемы.фегистров. Команда pusha не работает с сегментными регистрами, поэтом, содержимое gs сохраняет макрос pushReg. Он же помещает в стек исходноезначение переменной numcol для того, чтобы его можно было восстановить) случае переполнения системной палитры. Третья команда очищает регистр bx, который используется как счетчик байтов массива Index. Основные действия выполняются в двух вложенных циклах. Внешний имеет Внешний цш! повторяется столько раз, сколько цветов (строк) содержит палитра добашемого рисунка. Эта величина указывается в регистре сх при вызове подпрограммы. Оба цикла используют общий регистр сх для хранения счетчикаювторов, поэтому выполнение внешнего цикла начинается с сохранения в:теке его содержимого. Кроме того, в стеке сохраняется содержимое 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 И второе изменение. Строка палитры формата 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 "Изюминкой" примера 4.9 является перекодировка точек рисунка с помощью команды xlat. При ее выполнении суммируется содержимое регистров bх и ai и в регистр ai копируется содержимое байта, расположенного по вычисленному адресу. Остальные действия, выполняемые в примере, уже неоднократно обсуждались, поэтому мы не будем повторяться. Недостаточно места в системной палитре Ограниченное количество цветов, которые можно одновременно вывести на экран, является "Ахиллесовой пятой" видеорежимов PPG. Вместе с тем, именно по этой причине код точки занимает всего 1 байт, и манипуляции с графическими объектами выполняются достаточно просто и быстро. В общем случае задача одновременного вывода на экран двух рисунков, использующих разные палитры цветов, неразрешима. Ее можно решить только при определенных ограничениях на размеры палитр и разнообразие описанных в них цветов. Например, при разработке простых компьютерных игр все рисунки для конкретной игры подготавливаются с использованием цветов единой палитры, которая устанавливается один раз, и в дальнейшем не изменяется. В семействе Windows 3X применяется комбинированное решение. Рисунки, предназначенные для оформления рабочего стола, как уже говорилось, используют стандартную 16-цветную палитру, которая мало чем отличается от палитры CGA, приведенной в табл. 4.2. Для хранения этих (статических) цветов выделено 20 строк системной палитры и столько же регистров цвета видеокарты. Остальные 236 строк системной палитры отведены для цветов заставок и для нужд прикладных задач. У многоцветных рисунков часть цветов просто изменяется. Разумеется, при сокращении цветов рисунок несколько отличается от оригинала, но что остается делать, если в вашем распоряжении есть только ограниченное число регистров цвета. |