typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
В первых трех полях хранятся цветовые RGB-составляющие. На поле rgbReservedмы не будем обращать внимания (предполагается, что оно равно нулю). Как я упоминал выше, количество структур RGBQUADв BMP-файле определяется полем biClrUsed. Тем не менее обычно 8-битные BMP-файлы содержат 256 структур RGBQUAD. В 24-битных RGB-файлах структуры RGBQUADотсутствуют.
Графические данные
Графические данные в основном представляют собой список пикселей, из которых состоит изображение. Однако каждая горизонтальная строка пикселей должна занимать блок памяти, выровненный по границе параграфа. Следовательно, если количество байт, необходимых для хранения строки пикселей, не кратно четырем, в каждую строку включается от одного до трех дополняющих байт.
При этом для работы с графическими данными BMP-файлов используется концепция шага, упоминавшаяся выше в этой главе. Отличие состоит в том, что для графических данных BMP-файлов значение шага вам придется рассчитать самостоятельно. Впрочем, это не так уж сложно, потому что шаг всегда попадает на ближайшую границу параграфа за концом блока памяти, необходимого для хранения строки пикселей.
Изображения хранятся в BMP-файлах в перевернутом виде, так что первая строка пикселей файла на самом деле является нижней строкой настоящего изображения. Чтобы восстановить нормальное изображение, мы начнем чтение файла с последней строки пикселей и будем двигаться к началу.
Организация доступа к поверхностям
В наших программах чтением BMP-файлов занимается класс DirectDrawWin. Впервые эта возможность была использована в главе 3, где в программе Bounce BMP-файл загружался на поверхность. То же самое происходит и в программе BmpView, но сначала давайте рассмотрим соответствующий программный код.
Поддержка работы с BMP-файлами в классе DirectDrawWinобеспечивается функцией CreateSurface(). Существуют две версии CreateSurface(): первая в качестве аргументов получает параметры поверхности, а вторая — имя BMP-файла. Вторая версия CreateSurface()загружает BMP-файл, затем создает новую поверхность, параметры которой совпадают с параметрами изображения, и копирует содержимое файла на поверхность.
Функция CreateSurface()
Функция CreateSurface()требует, чтобы изображение в передаваемом BMP-файле было палитровым или беспалитровым в зависимости от текущего видеорежима. Она не станет загружать палитровые изображения на беспалитровую поверхность, и наоборот. В принципе это возможно, но непрактично. Загрузить палитровое изображение на беспалитровую поверхность довольно просто, но глупо, потому что при этом будет использоваться лишь малая часть возможностей поверхности (всего 256 цветов из 16 миллионов). С другой стороны, загрузка беспалитровых изображений на палитровую поверхность потребует программного сокращения миллионов цветов до 256-цветной палитры.
Давайте посмотрим, как реализована функция CreateSurface()(см. листинг 5.1).
Листинг 5.1. Функция CreateSurface()
LPDIRECTDRAWSURFACE DirectDrawWin::CreateSurface(LPCTSTR filename, BOOL installpalette) {
int imagew, imageh;
GetBmpDimensions(filename, imagew, imageh);
LPDIRECTDRAWSURFACE surf=CreateSurface(imagew, imageh);
if (surf==0) {
TRACE("CreateSurface(filename) failed to create surface\n");
return 0;
}
ifstream bmp(filename, ios::binary | ios::nocreate);
if (!bmp.is_open()) {
TRACE("LoadSurface: cannot open Bmp file\n");
return 0;
}
BITMAPFILEHEADER bmpfilehdr;
bmp.read((char*)&bmpfilehdr, sizeof(bmpfilehdr));
char* ptr=(char*)&bmpfilehdr.bfType;
if (*ptr!='B' || *++ptr!='M') {
TRACE("invalid bitmap\n");
return 0;
}
BITMAPINFOHEADER bmpinfohdr;
bmp.read((char*)&bmpinfohdr, sizeof(bmpinfohdr));
bmp.seekg(sizeof(bmpfilehdr)+bmpinfohdr.biSize, ios::beg);
int imagebitdepth=bmpinfohdr.biBitCount;
int imagesize=bmpinfohdr.biSizeImage;
if (imagesize==0) imagesize=((imagew*(imagebitdepth/8)+3) & ~3)*imageh;
if (bmpinfohdr.biCompression!=BI_RGB) {
TRACE("compressed BMP format\n");
return 0;
}
TRACE("loading '%s': width=%d height=%d depth=%d\n", filename, imagew, imageh, imagebitdepth);
if (imagebitdepth==8) {
int ncolors;
if (bmpinfohdr.biClrUsed==0) ncolors=256;
else ncolors=bmpinfohdr.biClrUsed;
RGBQUAD* quad=new RGBQUAD[ncolors];
bmp.read((char*)quad, sizeof(RGBQUAD)*ncolors);
if (installpalette) CreatePalette(quad, ncolors);
delete[] quad;
}
BYTE* buf=new BYTE[imagesize];
bmp.read(buf, imagesize);
if (!Copy_Bmp_Surface(surf, &bmpinfohdr, buf)) {
TRACE("copy failed\n");
Читать дальше