Программирование графики с использованием Direct3D

         

Функции работы с палитрой


Класс RMWin предоставляет две функции, которые содействуют использованию палитр. Функция UsePalette() является защищенной функцией, позволяющей классу RMWin использовать палитру, хранящуюся в файле BMP. Функция UsePalette() определена в объявлении класса RMWin:

void UsePalette(CString filename) { palettefile = filename; }

Функция просто сохраняет полученное имя файла. Файл будет использован классом RMWin когда необходимо будет создать палитру.

Вспомните, что функция OnCreate() вызывала закрытую функцию InstallPalette(). Функция InstallPalette() использует имя файла, предоставленное функцией UsePalette() для извлечения и установки палитры, основываясь на содержимом файла BMP. Код функции InstallPalette() показан в листинге 10.6.

Листинг 10.6. Функция InstallPalette()

BOOL RMWin::InstallPalette() { BITMAPFILEHEADER bmpfilehdr; BITMAPINFOHEADER bmpinfohdr; RGBQUAD quad[256]; PALETTEENTRY pe[256]; int ncolors; if (palettefile.GetLength() <= 0) return FALSE; if (modedepth != 8) return FALSE; if (palette) { palette->Release(); palette = 0; } ifstream bmp(palettefile, ios::binary | ios::nocreate); bmp.read((char*)&bmpfilehdr, sizeof(bmpfilehdr)); bmp.read((char*)&bmpinfohdr, sizeof(bmpinfohdr));

char* ptr = (char*)&bmpfilehdr.bfType; if (*ptr != 'B' || *++ptr != 'M') { TRACE("invalid bitmap\n"); return FALSE; } if (bmpinfohdr.biBitCount != 8) { TRACE("not 8 bit file!\n"); return FALSE; } if (bmpinfohdr.biClrUsed == 0) ncolors = 256; else ncolors = bmpinfohdr.biClrUsed; bmp.read((char*)quad, sizeof(RGBQUAD) * ncolors); for(int i = 0; i < ncolors; i++) { pe[i].peRed = quad[i].rgbRed; pe[i].peGreen = quad[i].rgbGreen; pe[i].peBlue = quad[i].rgbBlue; pe[i].peFlags = D3DPAL_READONLY; } HRESULT r = ddraw->CreatePalette(DDPCAPS_8BIT, pe, &palette, 0); if (r != DD_OK) { TRACE("failed to load palette data from file\n"); return FALSE; } primsurf->SetPalette(palette); backsurf->SetPalette(palette); return TRUE; }

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

Сначала в функции InstallPalette() объявляется несколько локальных переменных:

BITMAPFILEHEADER bmpfilehdr; BITMAPINFOHEADER bmpinfohdr; RGBQUAD quad[256]; PALETTEENTRY pe[256];

Структура BITMAPFILEHEADER присутствует в начале каждого файла BMP. Мы используем эту структуру для загрузки параметров конкретного файла BMP. В частности, структура BITMAPFILEHEADER содержит сигнатуру, позволяющую идентифицировать файл BMP. Определение структуры BITMAPFILEHEADER выглядит следующим образом:

typedef struct tagBITMAPFILEHEADER { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER;

В корректном файле BMP поле bfType содержит символы «BM». Если эта сигнатура отсутствует, мы будем знать, что загруженный файл имеет неверный формат.

Структура BITMAPINFOHEADER располагается в файле BMP сразу после структуры BITMAPFILEHEADER. Эта структура используется для загрузки параметров хранящегося в файле изображения. Определение структуры BITMAPINFOHEADER выглядит следующим образом:

typedef struct tagBITMAPINFOHEADER { DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER;

Поля biWidth и biHeight содержат размеры изображения. Поле biBitCount указывает глубину цвета изображения. Оно используется, чтобы убедиться, что загружаемый файл содержит 8-битовое изображение.

Объявление массива структур RGBQUAD включено потому, что файл BMP хранит данные палитры в виде элементов RGBQUAD. Объявленный массив содержит 256 элементов, поскольку это максимально возможное количество цветов, которое может храниться в файле.

Затем объявлен массив структур PALETTEENTRY. Мы используем этот массив для создания палитры DirectDraw. Структуры RGBQUAD и PALETTEENTRY очень похожи. Их определение выглядит так:

typedef struct tagRGBQUAD { BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; } RGBQUAD; typedef struct tagPALETTEENTRY { BYTE peRed; BYTE peGreen; BYTE peBlue; BYTE peFlags; } PALETTEENTRY;

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

Сначала функция InstallPalette() проверяет строку palettefile:

if (palettefile.GetLength() <= 0) return FALSE;

Вспомните, что значение строки palettefile устанавливается в функции UsePalette(). Если класс, производный от RMWin не использует UsePalette() для объявления имени файла BMP, палитра не создается и функция InstallPalette() возвращает FALSE. Это не так плохо, как может показаться, поскольку палитра требуется только для 8-разрядных видеорежимов. Приложения, использующие только 16-, 24- и 32-разрядные видеорежимы будут правильно выполняться и без использования функции UsePalette().

Затем проверяется глубина цвета в текущем видеорежиме:

if (modedepth != 8) return FALSE;

Палитра не требуется для тех видеорежимов, глубина цвета которых отличается от восьми, и в этом случае функция InstallPalette() завершает работу.

Потом функция InstallPalette() освобождает любые существующие палитры:

if (palette) { palette->Release(); palette = 0; }

Далее открывается файл BMP и загружаются структуры, содержащие параметры файла и изображения:

ifstream bmp(palettefile, ios::binary | ios::nocreate); bmp.read((char*)&bmpfilehdr, sizeof(bmpfilehdr)); bmp.read((char*)&bmpinfohdr, sizeof(bmpinfohdr));

Загруженные данные используются при проверке сигнатуры файла BMP и глубине цвета изображения, содержащегося в файле:

char* ptr = (char*)&bmpfilehdr.bfType; if (*ptr != 'B' || *++ptr != 'M') { TRACE("invalid bitmap\n"); return FALSE; } if (bmpinfohdr.biBitCount != 8) { TRACE("not 8 bit file!\n"); return FALSE; }

Если отсутствует сигнатура «BM», значит, файл поврежден, или это вообще не файл BMP. В этом случае выводится сообщение об ошибке, и функция возвращает FALSE.

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

Затем вычисляется количество цветов в палитре:

if (bmpinfohdr.biClrUsed == 0) ncolors = 256; else ncolors = bmpinfohdr.biClrUsed;

Бывает, что значение поля biClrUsed равно нулю. Это указывает, что файл содержит максимально возможное для данной глубины количество цветов. Если значение поля biClrUsed равно нулю, мы присваиваем переменной ncolors значение 256. В противном случае мы просто присваиваем значение поля biClrUsed переменной ncolors.

На следующем шаге функция InstallPalette() выполняет загрузку палитры с диска и инициализирует массив структур PALETTEENTRY:

bmp.read((char*)quad, sizeof(RGBQUAD) * ncolors ); for( int i = 0; i < ncolors; i++) { pe[i].peRed = quad[i].rgbRed; pe[i].peGreen = quad[i].rgbGreen; pe[i].peBlue = quad[i].rgbBlue; pe[i].peFlags = D3DPAL_READONLY; }

Палитра загружается в массив quad. Затем в цикле элементы массива quad копируются в массив pe. Константа D3DPAL_READONLY используется для указания, что цвета в массиве не должны изменяться.

Теперь мы можем создать палитру DirectDraw:

HRESULT r = ddraw->CreatePalette(DDPCAPS_8BIT, pe, &palette, 0); if (r != DD_OK) { TRACE("failed to load palette data from file\n"); return FALSE; }

Палитра создается функцией CreatePalette() интерфейса DirectDraw. Константа DDPCAPS_8BIT указывает DirectDraw, что данные предоставленной палитры являются 8-разрядными. Массив pe передается во втором аргументе. Третий аргумент функции CreatePalette() — это адрес указателя на новую палитру.

В заключение новая палитра присоединяется к первичной и вторичной поверхностям:

primsurf->SetPalette(palette); backsurf->SetPalette(palette);

На этом работа функции InstallPalette() завершается. Файл BMP закрывается автоматически при выходе из функции, поскольку объект ifstream, используемый для открытия файла, выходит из области видимости.



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