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

Графический формат PCX


В индустрии компьютерной графики существует так много стандартов, что само слово «стандарт» уже потеряло свой первоначальный смысл. Сегодня существует несколько наиболее известных стандартов: PCX, GIF, RGB, TGA, TIF и многие другие. Нам интересен формат PCX потому, что сегодня он является самым распространенным.

Файл в формате PCX представляет собой закодированное представление изображения. Кодирование необходимо для уменьшения размера файла, поскольку только один образ 320х200 пикселей уже займет 64К памяти. Рисованные объекты обладают большой цветовой избыточностью, и это обстоятельство используется для сжатия изображения.

Примечание

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

Давайте посмотрим на рисунок 5.5 (файл CH19\WARINTR2.PCX на дискете). Это копия экрана из игры Warlock. Как вы можете заметить, там не слишком много цветов. Более того, на нем присутствует множество больших, одинаково окрашенных областей.

Как правило, в экранных изображениях используется лишь ограниченное число цветов. Так почему бы не подсчитать количество пикселей одинакового цвета и не сохранить их как целые группы, вместе с позицией и цветом. Это можно сделать. В общем, подобная технология, правда, в несколько усовершен­ствованном виде, и применена в PCX-формате. Для сжатия информации этот формат использует так называемое RLE-кодирование. При этом изображение обрабатывается построчно без учета расположения пикселей по вертикали.

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


При этом сохраняется



количество повторений пикселей и значение их цвета. Повторяется это до тех пор, пока все изображение не окажется сжатым.



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

Файл формата PCX состоит из трех секций:

§                                Первая секция PCX-файла длиной 128 байт содержит различную служеб­ную информацию;

§                                Вторая секция — это данные сжатого образа, которые могут оказаться любой длины;

§                                Третья секция размером в 768 байт содержит цветовую палитру, если она есть. В нашем случае она будет присутствовать, поскольку мы используем 256-цветный режим 13h. Эти 768 байт хранят значения RGB от 0 до 255.

Суммируя вышесказанное, можно нарисовать структуру PCX-файла (рис. 5.7).

Получение информации из заголовка несложно: достаточно прочитать первые 128 байт и отформатировать их в соответствии со структурой, представленной в Листинге 5-9.



Листинг 5.9. Структура заголовка PCX-файла.

typedef struct pcx_header_typ

{

char manufacturer; // всегда 10

char version;       // 0 - версия 2.5 Paintbrush

  // 2 - версия 2.8 с палитрой

  // 3 - версия 2.8 без палитры

  // 5 - версия 3.0 или старше

char encoding;      // всегда 1 - RLE кодирование

char bits_per_pixel;// количество бит на пиксель



  // для нашего случая – 8

int x,y; // координаты верхнего левого угла изображения

int width,height; // размеры изображения

int horz_res;      // количество пикселей по горизонтали

int vert_res;      // количество пикселей по вертикали

char ega_palette[48]; // EGA-палитра. Ее можно игнорировать,

char reserved;         // ничего значимого

char num_color_planes; // количество цветовых плоскостей

                       //в изображении

int bytes_per_line;    // количество байт на одну строку

int palette_type;      // не беспокойтесь об этом

char padding[58];      // ссылка на палитру в конце файла

} pcx_header, *pcx_header_ptr;

Последнюю часть PCX-файла также довольно легко обработать:

§          Необходимо установить указатель на конец файла;

§          Передвинуться вверх на 768 байт;

§          Прочитать 768 байт как палитру.

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

§          Если код прочитанного байта принадлежит множеству 192...255, то мы вычитаем из него 192 и используем полученный результат, как количество повторений следующего байта;

§          Если код прочитанного байта лежит в диапазоне от 0 до 191, то мы используем его как байт данных, то есть помещаем его в битовую карту без изменений.

Если вы достаточно внимательны, то можете спросить: «А как же быть с пикселями, имеющими значения от 192 до 255? Интерпретируются ли они как RLE-цепочки?» Да, и гениальное решение этого вопроса состоит в том, что такие значения кодируются не одним, а двумя байтами.


Например, если требуется поместить в файл значение 200, то сначала нужно записать число 193 (192-1) как количество повторений, а потом — 200. Посмотрим на рисунок 5.8, чтобы увидеть пример декомпрессии.

Теперь настало время написать программу, реализующую чтение файл формата PCX. Она получилась весьма неплохой. Листинг 5.10 даст вам возможность убедиться в этом самостоятельно.


Листинг 5.10. Программа чтения файла формата PCX.

// размеры

экрана

#define SCREEN_WIDTH 320

#define SCREEN_HEIGHT 200

// структура для хранения данных PCX

файла

typedef struct pcx_picture_typ

{

pcx_header header; // заголовок файла (длина 128 байт)

RGB_color palette[256]; // палитра

char far *buffer; // буфер для размещения изображения

// после

декомпрессии

} pcx_picture, *pcx_picture_ptr;

void PCX Load(char *filename,

pcx_picture_ptr image,int enable_palette)

{

// функция загружает данные из PCX-файла в структуру pcx picture

// после декомпрессии байты изображения помещаются в буфер.

// Отдельные элементы изображения выделяются позднее. Также

// загружается палитра и заголовок

FILE *fp;

int num_bytes,index;

long count;

unsigned char data;

char far *temp_buffer;

// открыть

файл

fp = fopen(filename,"rb");

// загрузить

заголовок

temp_buffer = (char far*)image;

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

{

temp_buffer[index] = getc(fp);

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

// загрузить данные и декодировать их в буфере

count=0;

while (count<=SCREEN_WIDTH * SCREEN_HEIGHT}

{

// получить первую часть данных

data = getc(fp);

//это  RLE?

if (data>=192 &&data<=255)

{

// подсчитываем, сколько байт сжато

num_bytes = data-192;

//читаем байт цвета

data = getc(fp);

// повторяем байты в буфере num_bytes раз

while(num_bytes-->0)

{

image->buffer[count++] = data;

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

} // конец оператора if

else

{

// помещаем данные в следующую позицию буфера

image->buffer[count++] = data;

} // конец оператора else

} // конец чтения байт изображения



// перейти в позицию, не доходя 768 байт от конца файла

fseek(fp,-768L,SEEK_END);

// читаем палитру и загружаем ее в регистры VGA

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

{

// красная

составляющая

image->palette[index].red   = (getc(fp) >> 2);

// зеленая

составляющая

image->palette[index].green = (getc(fp) >> 2);

// синяя

составляющая

image->palette[index].blue = (getc(fp) >> 2);

} // конец

цикла for

fclose(fp);

// если флаг enable_palette установлен, меняем палитру

// на загруженную из файла

if (enable_palette)

{

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

{

Set_Palette_Register(index,

(RGB_color_ptr)&image->palette[index]);

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

} // конец установки новой палитры

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

Функция PCX_Load() — это сердце всей программы. Она загружает PCX-файл, декодирует его в буфере и загружает палитру. Каждый PCX-файл имеет свою собственную палитру в конце файла и я думаю, что вы сами можете добавить возможность загрузки повой палитры в таблицу соответствия цветов.

Функция выполняет именно те действия, которые-мы с вами уже обсуждали и ничего больше:

§          Открывает PCX-файл;

§          Читает заголовок;

§          Загружает PCX-файл и декомпрессирует все 64000 пикселей;

§          Загружает цветовую палитру.                    

В общем, все это несложно. А вот что делать с картинками, которые больше, чем целый экран? Ответ прост: можно декодировать только маленький кусочек, скажем 24 на 24 пикселя.

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

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

Возникает вопрос: «Как редактировать PCX-файлы в режиме 320х200х 256?» Для этого можно воспользоваться такими условно-бесплатными программами как VGA-Paint или Pro-Paint. Тем не менее, я надеюсь, что самые расторопные читатели уже давно пользуются копией Electronic Art's Deluxe Paint & Animation. Это одна из самых классных программ для рисования на ПК. Она корректно работает с режимом 320х200х256 и имеет множество полезных функций для преобразования и анимации изображения.


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