Программирование игр для Windows. Советы профессионала

Переопределение цветовой палитры


Таблица цветов организована в VGA-карте как регистровый файл. (Я использовал слово регистр, чтобы обозначить значение в таблице соответствия. Каждый регистр палитры — это 24 бита.) Для доступа к значению мы должны произвести некоторые действия. Мы не можем просто сказать; «Изменить компонент красного для значения 123». Мы должны модифицировать все три составляющих цвета, который хотим изменить.

Хотя каждое значение состоит из трех байтов (один для каждой из составляющих), только первые шесть битов каждого байта используются для обозначения цвета. Существует 64 оттенка для каждого цвета, или 2 в 18-й степени различных цветов (это и есть общее количество цветов — 262144). Таким образом, если вы поместите значение, превышающее размер в шесть битов (или 63), то можете нарушить все компоненты, но не изменить цвет.

Итак, нам требуется только три порта ввода-вывода для решения задачи изменения значений в таблице соответствия цветов.

#define PALETTE_MASK        0хЗС6

#define PALETTE_REGISTER_RD 0x3C7

#define PALETTE_REGISTER_WR 0хЗС8

#define PALETTE_DATA        0x3C9

Теперь посмотрим как это реализуется:

§

Порт 0хЗСб называется маской палитры и используется для маскирования битов нужного регистра палитры. Например, если вы поместите в этот порт число 0х00, то получите регистр 0, независимо от того, какой регистр запрашиваете. С другой стороны, если вы запишете в регистр маски значение 0xFF, то получите возможность доступа к любому регистру через индекс регистра палитры 0хЗС8 и 0x3C7 (первый из них используется для записи, а второй — для чтения);

§          Порт 0x3C7, называемый регистром чтения палитры, используется для выбора из таблицы цветов значения, которое вы хотите прочитать;

§          Порт 0хЗС8 называется регистром записи палитры и используется для выбора в таблице соответствия значения, которое вы хотите записать;

§          Наконец, данные красной, зеленой и синей составляющей вы можете записать или прочитать из порта по адресу 0хЗС9, называемого портом данных палитры.






Вы можете спросить: « А как мы прочитаем из одного порта три байта?» На самом деле вы можете прочитать их по очереди. После того как вы выберете необходимый регистр (значение таблицы цветов, к которому вам нужен доступ), то первый записанный в регистр палитры байт будет соответствовать значению красного цвета. Второй байт задаст значение зеленого цвета, ну а третий — синего. Когда вы будете читать, это правило будет так же верно, но. в отличие от записи трех байтов в каждый момент чтения вы будете получать следующий компонент считываемого значения выбранного регистра. Для записи в регистр палитры вы должны:             

§          Выбрать регистр, который хотите изменить;

§          Произвести три записи в порт регистра данных.

Когда вы читаете или пишете в регистр цвета, не забывайте каждый раз предварительно изменять значение маски на OxFF. Листинг 5.1 показывает код, содержащий эти шаги.

Листинг 5.1. Запись в регистр палитры.

void Set_Palette_Register(int index, RGB_color_ptr color)

{

// эта функция устанавливает один из элементов таблицы цветов.

// номер регистра задается параметром index, цвет - структурой color

// указываем, что мы будем обновлять регистр палитры

_outp(PALETTE_MASK,Oxff) ;

// какой из регистров мы хотим обновить

_outp(PALETTE_REGISTER_WR, index);

// теперь обновляем RGB. Обратите внимание,

// что каждый раз используется один и тот же порт

_outp(PALETTE_DATA,color->red) ;

_outp(PALETTE_DATA,color->green) ;

_outp(PALETTE_DATA,color->blue) ;

} // конец функции

Помните, что я использую структуру данных RGB_color. Это структура с тремя полями, каждое из которых предназначено для определенного цвета. Она выглядит так:

typedef struct RGB_color_typ

{

unsigned char red;    // красный

компонент

0-63

unsigned char green; // зеленый

компонент

0-63

unsigned char blue;   // синий

компонент

0-63

} RGB_color, *RGB_color_ptr;

Все походит на то, что следующей операцией должно стать чтение из регистра.


Мы сделаем то же самое, что и в Set_Palette_Register, только вместо записи в порт палитры будем читать из него и пересылать полученные значения в структуру RGB color. Листинг 5.2 содержит необходимый для этого код.

Листинг 5.2 Чтение регистра палитры.

void Get_Palette_Register(int index, RGB_color__ptr color)

{

// функция читает регистры цвета и помещает полученные значения

// в поля структуры color

// установить маску регистра палитры

_outp(PALETTE_MASK,0xff);

// сообщаем VGA, какой из регистров мы будем читать

_outp(PALETTE_REGISTER_RD, index);

// читаем

данные

color->red   = _inp(PALETTE_DATA);

color->green = _inp(palette_data);

color->blue = _inp(PALETTE_DATA);

} конец функции

Теперь, когда мы знаем, как читать и писать в регистр палитры, почему бы нам не создать функцию для получения новой цветовой палитры? Неплохая идея! Напишем для этого функцию, которая строит палитру, имеющую 64 оттенка серого, красного и голубого. Листинг 5.3 содержит ее код.

Листинг 5.3. Создание новой цветовой палитры.

void Create_Cool_ Palette(void)

{

// эта функция создает новую палитру, содержащую по 64 оттенка

// серого, красного, зеленого и синего цветов

RGB_color color;

int index;

// в цикле последовательно создаем цвета и меняем значения регистров

for (index=0; index < 64; index++)

{ // это оттенки серого

color.red   = index;

color.green = index;

color.blue = index;

Set_Palette_Register(index, (RGB_color_ptr)&color);

// это оттенки красного

color.red   = index;

color.green = 0;

color.blue = 0;

Set_Palette_Register(index+64, (RGB_color_ptr)&color) ;

// это оттенки зеленого color.red   = 0;

color.green = index;

color.blue = 0;

Set_Palette_Register(index+128, (RGB_color_ptr)&color) ;

// это оттенки синего

color.red   = 0;

color.green = 0;

color.blue = index;

Set_Palette_Register(index+192, (RGB_color_ptr)&color);

} // конец цикла for

} // конец функции

Наличие возможности изменения цветовой палитры позволяет нам создавать различные интересные эффекты освещения и анимации в наших играх. (Например, как сделаны цветовые эффекты в DOOM'e? Часть цветовой палитры изменяется «на лету» во время игры.) Так достигаются эффекты освещения и стрельбы.Это особенно хорошо для реализации эффекта «отключения света».

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


Содержание раздела