Выбрать главу

 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");

  delete[] buf;

  surf->Release();

  return 0;

 }

 delete[] buf;

 return surf;

}

Сначала эта функция определяет размеры изображения из BMP-файла с помощью функции GetBmpDimensions() — простой функции класса DirectDrawWin, которая открывает BMP-файл и извлекает из заголовка ширину и высоту изображения. На основании полученных данных создается новая поверхность с использованием версии CreateSurface(), которая создает поверхность по ее размерам. Новая поверхность заполнена случайными пикселями, но мы не стираем ее, потому что вскоре значение каждого пикселя будет задано в соответствии с содержимым BMP-файла.

Затем мы открываем BMP-файл с помощью класса ifstream и извлекаем из него данные заголовка. Далее проверяется сигнатура файла; если проверка дает отрицательный результат, BMP-файл может содержать неверную информацию, поэтому функция завершает работу.

Дополнительные данные заголовка извлекаются с помощью структуры BITMAPINFOHEADER. Обратите внимание: после заполнения структуры текущая позиция в файле ifstream изменяется в соответствии со значением поля biSize. Это сделано для того, чтобы в будущем, при увеличении размера структуры BITMAPINFOHEADER, наша программа нормально работала с новыми BMP-файлами.

Ширина и высота изображения уже известны, поэтому читать значения полей biWidth и biHeight структуры BITMAPINFOHEADER не нужно. Функция CreateSurface() считывает глубину пикселей (biBitCount) и размер изображения (biSizeImage). Как упоминалось выше, поле biSizeImage часто равно нулю, поэтому мы проверяем его значение. Снова приведу соответствующий фрагмент кода:

int imagesize=bmpinfohdr.biSizeImage;

if (imagesize==0) imagesize=((imagew*(imagebitdepth/8)+3) & ~3)*imageh;

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

И последняя проверка: по содержимому поля biCompression мы убеждаемся, что BMP-файл не содержит сжатых данных, не поддерживаемых нами. Для сжатых файлов функция возвращает ноль, код неудачного завершения.

Если изображение является палитровым, мы загружаем палитру. Количество элементов палитры в файле определяется полем biClrUsed, но это поле также может быть равно нулю. В этом случае предполагается, что присутствуют все 256 элементов палитры. Палитра загружается лишь в том случае, если параметр installpalette равен TRUE; тогда вызывается функция CreatePalette(). Вскоре мы рассмотрим код этой функции.

Следующий этап — чтение графических данных, которое в нашем случае выполняется одним вызовом функции ifstream::read(). Графические данные передаются функции Copy_Bmp_Surface(), отвечающей за пересылку данных новой поверхности. После возврата из функции Copy_Bmp_Surface() буфер с графическими данными освобождается. BMP-файл автоматически закрывается при возвращении из функции CreateSurface() (поскольку локальный объект ifstream выходит из области видимости). 

Функция CreatePalette() 

Если второй аргумент функции CreateSurface() равен TRUE, CreatePalette() создает и заполняет объект DirectDrawPalette данными, полученными из BMP-файла. Функция CreatePalette() выглядит так:

BOOL DirectDrawWin::CreatePalette(RGBQUAD* quad, int ncolors){

 if (palette) palette->Release(), palette=0;

 PALETTEENTRY pe[256];

 ZeroMemory(pe, sizeof(pe));

 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;

 }

 HRESULT r=ddraw2->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256, pe, &palette, 0);

 if (r!=DD_OK) {

  TRACE("failed to reate DirectDraw palette\n");

  return FALSE;

 }

 primsurf->SetPalette(palette);

 return TRUE;

}

Палитры DirectDraw создаются функцией CreatePalette() интерфейса DirectDraw, которой передается массив структур PALETTEENTRY. Чтобы выполнить это требование, приходится преобразовывать массив структур RGBQUAD, извлеченный из BMP-файла, во временный массив (структуры PALETTEENTRY и RGBQUAD очень похожи, поэтому такое преобразование оказывается тривиальным). Затем созданный массив передается функции CreatePalette(). Флаг DDPCAPS_ALLOW256 сообщает, что мы намерены задать значения всех 256 элементов палитры. Если вы пропустили главу 4 (конечно же, нет!), вернитесь к ней и ознакомьтесь с возможными аспектами использования этого флага.

Наконец, функция SetPalette() интерфейса DirectDrawSurface() присоединяет палитру к поверхности. Обратите внимание на то, что палитра присоединяется к первичной поверхности. Хотя палитры можно присоединять и к другим поверхностям, на системную палитру влияет только палитра, присоединенная к первичной поверхности. 

Передача графических данных