Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Графический интерфейс GDI в Microsoft Windows

© Александр Фролов, Григорий Фролов
Том 4, М.: Диалог-МИФИ, 1993, 288 стр.

[Назад] [Содеожание] [Дальше]

4.4. Рисование изображений DIB

Процесс рисования изображений DIB включает в себя несколько этапов.

Сначала необходимо загрузить bmp-файл в оперативную память и убедиться в том, что этот файл действительно содержит изображение DIB. Ваше приложение может полностью проигнорировать bmp-файлы в формате Presentation Manager (как это делает, например, приложение Paintbrush в Windows версии 3.1) или выполнить их преобразование в формат Windows, что намного лучше. Следует также проверить формат заголовка BITMAPINFOHEADER.

Затем нужно определить размер таблицы цветов (если она есть). Если в DIB есть таблица цветов, ее следует преобразовать в палитру. Непосредственно перед рисованием изображения DIB созданная палитра должна быть выбрана в контекст отображения и реализована. Если bmp-файл содержит изображение с высоким цветовым разрешением, в файле нет таблицы цветов. В этом случае нет необходимости создавать палитру.

После создания палитры следует определить адрес битов изображения. Напомним, что смещение битов изображения находится в поле bfOffBits структуры BITMAPFILEHEADER. Если содержимое этого поля равно нулю, можно вычислить адрес битов изображения исходя из размера заголовков и размера таблицы цветов.

В заключение считанное и проверенное изображение DIB можно нарисовать, использовав один из двух способов.

Первый способ заключается в преобразовании изображения DIB в изображение DDB с помощью функции SetDIBits. Полученное таким образом изображение DDB может быть выбрано в контекст памяти и нарисовано обычным способом при помощи функции BitBlt или StretchBlt.

Второй способ основан на использовании функции StretchDIBits, которая сама выполняет необходимые преобразования, однако в некоторых случаях работает медленнее функции BitBlt.

Если изображение DIB содержит таблицу цветов и устройство вывода способно работать с цветовыми палитрами, ваше приложение должно обрабатывать сообщения WM_PALETTECHANGED и WM_QUERYNEWPALETTE. Для обработки этих сообщений можно использовать алгоритм, описанный в главе "Цвет и цветовые палитры".

Загрузка bmp-файла и проверка заголовков

Вы можете загрузить в оперативную память весь bmp-файл сразу или вначале только заголовки, а затем таблицу цветов и биты изображений. В приложении BMPINFO, рисующем изображения DIB в своем окне, мы использовали первый способ, отведя для загрузки bmp-файла один сплошной блок глобальной памяти.

Составляя программу чтения bmp-файла в память, не следует забывать о том, что размер файла, а следовательно и размер нужного для его загрузки блока памяти, практически всегда превышает 64 Кбайт. Поэтому для чтения такого файла лучше всего использовать функцию _hread , позволяющую прочитать сразу весь файл в один блок памяти любого (теоретически) размера.:

_hread(hfDIBFile, lpBuf, *dwFileSize);

Мы уже пользовались этой функцией для перекодировки файла из OEM в ANSI.

Прочитав файл в память, следует убедиться, что его первые два байта содержат значение 0x4d42 ("BM"). Если это так, нужно определить формат bmp-файла. Для этого следует проанализировать содержимое поля biSize, расположенное сразу после заголовка BITMAPFILEHEADER. Для файлов в формате Windows в этом поле должно быть значение 40, что соответствует размеру структуры BITMAPINFOHEADER. Для файлов в формате Presentation Manager в этом поле должно находиться значение 12 (размер структуры BITMAPCOREHEADER).

Ваше приложение может отвергнуть файл в формате Presentation Manager, и это не будет большим недостатком для приложения Windows. В случае необходимости bmp-файлы Presentation Manager могут быть преобразованы в формат Windows, например, с помощью приложения Paintbrush.

Убедившись в том, что вы загрузили bmp-файл в формате Windows, следует проверить содержимое полей структуры BITMAPINFOHEADER.

Следует проверить поля biPlanes, biBitCount и biCompression. Вы можете использовать для проверки следующие критерии:

Поле Критерии проверки
biPlanes Должно содержать значение 1
biBitCount Может быть равно 1, 4, 8 или 24.Вы можете столкнуться с новыми 16- и 32-битовыми форматами файлов DIB, используемых в Windows NT. Для них в этом поле могут находиться также значения 16 и 32. Если ваше приложение не умеет обрабатывать такие файлы, данную ситуацию следует рассматривать как ошибочную
biCompression Может принимать одно из следующих значений: BI_RGB, BI_RLE4, BI_RLE8.При использовании метода компрессии BI_RLE4 содержимое поля biBitCount должно быть равно 4. При использовании метода компрессии BI_RLE8 содержимое поля biBitCount должно быть равно 8.Ваше приложение может ограничиться обработкой bmp-файлов в формате BI_RGB, как это делает, например, приложение Paintbrush

Можно было бы проверить содержимое и других полей структуры BITMAPINFOHEADER, однако это необязательно, так как они не содержат критической информации. Проверка "с пристрастием" может привести к тому, что пользователи будут думать, будто ваше приложение не умеет читать такие файлы, с которыми легко справляются другие приложения.

Итак, подводя итоги, можно выдать следующие рекомендации:

смело игнорируйте bmp-файлы в формате Presentation Manager, а если вы не можете так поступить, преобразуйте их в формат Windows;

в структуре BITMAPINFOHEADER проверяйте только поля biPlanes, biBitCount и biCompression;

так как метод компрессии RLE4 и RLE8 используются редко и не приводит к значительной экономии памяти, ваше приложение может не поддерживать компрессованные bmp-файлы.

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

Процесс создания цветовой палитры несложен. Вначале надо убедиться в том, что bmp-файл содержит таблицу цветов. Если размер таблицы цветов не равен нулю, следует заказать память для структуры LOGPALETTE , заполнить соответствующим образом заголовок и переписать в палитру цвета из таблицы цветов:

lpPal->palVersion = 0x300;
lpPal->palNumEntries = wNumColors;
for (i = 0; i < wNumColors; i++)
{
  lpPal->palPalEntry[i].peRed  =lpbmi->bmiColors[i].rgbRed;
  lpPal->palPalEntry[i].peGreen=lpbmi->bmiColors[i].rgbGreen;
  lpPal->palPalEntry[i].peBlue =lpbmi->bmiColors[i].rgbBlue;
  lpPal->palPalEntry[i].peFlags = 0;
}

Палитра создается с помощью функции CreatePalette:

hPal = CreatePalette(lpPal);

Рисование DIB

Если отображаемый bmp-файл содержит таблицу цветов, и на предыдущем этапе была создана палитра, ее следует выбрать в контекст отображения и реализовать:

hOldPal = SelectPalette(hdc, hPal, FALSE);
RealizePalette(hdc);

После этого вы можете нарисовать DIB одним из двух способов.

Первый способ рисования заключается в предварительном преобразовании изображения DIB в изображение DDB с последующим рисованием изображения DDB. Вы уже умеете рисовать изображение DDB, для этого его следует выбрать в специально созданный контекст памяти и затем отобразить функцией BitBlt или StretchBlt.

Для преобразования DIB в DDB вы должны использовать функцию SetDIBits :

int WINAPI SetDIBits(
  HDC hdc,                 // контекст отображения
  HBITMAP hbmp,            // изображение DDB
  UINT uStartScan,         // номер первой строки
  UINT uScanLines,         // количество строк
  const void FAR* lpvBits, // биты изображения
  BITMAPINFO FAR* lpbmi,   // заголовок изображения
  UINT fuColorUse);        // содержимое таблицы цветов

Параметр hdc должен содержать идентификатор контекста отображения, в котором будет отображаться полученное изображение DDB.

Через параметр hbmp следует передать идентификатор битового изображения DDB, совместимого с контекстом hdc. Его можно создать при помощи функции CreateCompatibleBitmap. После преобразования это изображение можно будет использовать для рисования функциями BitBlt или StretchBlt.

Параметр uStartScan задает номер строки сканирования битового изображения, начиная с которого будет выполняться преобразование. Если вам нужно нарисовать все изображение целиком, для этого параметра следует задать значение 0.

Параметр uScanLines определяет количество строк сканирования, участвующих в преобразовании. Если нужно преобразовать все изображение, для этого параметра следует указать высоту изображения, взятую из заголовка BITMAPINFOHEADER.

Через параметр lpvBits следует передать указатель на область памяти, содержащую биты изображения в формате DIB.

В процессе преобразования функция SetDIBits использует заголовок bmp-файла BITMAPINFO, указатель на который следует передать через параметр lpbmi.

Последний параметр fuColorUse указывает функции на содержимое таблицы цветов, которая расположена сразу после структуры BITMAPINFOHEADER. Возможны два значения - DIB_RGB_COLORS и DIB_PAL_COLORS.

Если указано значение DIB_RGB_COLORS , таблица цветов содержит RGB-цвета, которые можно использовать для создания палитры. Если же указано значение DIB_PAL_COLORS , таблица цветов содержит 16-битовые ссылки на элементы системной палитры.

Если вы загрузили bmp-файл в память, таблица цветов обычно содержит именно RGB-цвета, поэтому для преобразования и последующего рисования изображения вы должны указать значение DIB_RGB_COLORS.

Возвращаемое функцией SetDIBits значение равно количеству преобразованных строк сканирования или нулю при ошибке.

Поясним процесс рисования на простом примере.

Пусть мы загрузили изображение DIB с шириной wWidth и высотой wHeight. Создаем изображение DDB, совместимое с контекстом отображения hdc и имеющее те же размеры. Для этого воспользуемся функцией CreateCompatibleBitmap :

hbmp = CreateCompatibleBitmap(hdc, wWidth, wHeight);

Создадим также контекст памяти, совместимый с контекстом отображения:

hMemDC = CreateCompatibleDC(hdc);

Далее вызываем функцию SetDIBits, которая преобразует биты изображения DIB и запишет их в созданное нами изображение DDB с идентификатором hbmp:

SetDIBits(hdc, hbmp, 0, wHeight,
  lpDibBits, (LPBITMAPINFO)lpih, DIB_RGB_COLORS);

Теперь нам нужно нарисовать полученное изображение DDB. Для этого выбираем его в контекст памяти и переносим в контекст отображения, например, функцией BitBlt:

hbmp = (HBITMAP)SelectObject(hMemDC, hbmp);
BitBlt(hdc, x, y, wWidth, wHeight,
       hMemDC, 0, 0, SRCCOPY);

Все! Изображение нарисовано. Теперь можно удалить контекст памяти, не забыв перед этим выбрать в него старое битовое изображение (размером 1х1 пиксел):

DeleteObject(SelectObject(hMemDC, hbmp));
DeleteDC(hMemDC);

Второй способ нарисовать DIB немного проще:

StretchDIBits(hdc,
   x, y, wWidth, wHeight,
   0, 0, wWidth, wHeight,
   lpDibBits, (LPBITMAPINFO)lpih,
   DIB_RGB_COLORS, SRCCOPY);

Прототип функции StretchDIBits выглядит несколько громоздко, однако эта функция дополнительно позволяет масштабировать рисуемое изображение. Функция имеет параметры, аналогичные параметрам функций StretchBlt и SetDIBits:

BOOL WINAPI StretchDIBits(
  HDC hdc,          // контекст для рисования
  int nXDest,       // x-координата верхнего левого угла
                    //    области рисования
  int nYDest,       // y-координата верхнего левого угла
                    //    области рисования
  int nWidthDest,   // новая ширина изображения
  int nHeightDest,  // новая высота изображения
  int nXSrc,        // x-координата верхнего левого угла
                    //    исходной области
  int nYSrc,        // y-координата верхнего левого угла
                    //    исходной области
  int nWidthSrc,    // ширина исходного изображения
  int nHeightSrc,   // высота исходного изображения
  const void FAR* lpvBits, // биты изображения
  BITMAPINFO FAR* lpbmi,   // заголовок изображения
  UINT fuColorUse,         // содержимое таблицы цветов
  DWORD dwRop);            // код растровой операции

Возвращаемое значение равно количеству преобразованных строк сканирования или нулю при ошибке.

Преобразование DDB в DIB

Если перед вами встанет задача преобразования DDB в DIB (например, для последующей записи изображения в bmp-файл), вам не обойтись без функции GetDIBits :

int WINAPI GetDIBits(
  HDC hdc,                 // контекст отображения
  HBITMAP hbmp,            // изображение DDB
  UINT uStartScan,         // номер первой строки
  UINT uScanLines,         // количество строк
  void FAR* lpvBits,       // биты изображения
  BITMAPINFO FAR* lpbmi,   // заголовок изображения
  UINT fuColorUse);        // содержимое таблицы цветов

Параметры этой функции полностью аналогичны параметрам функции SetDIBits, однако действие прямо противоположное. Функция преобразует биты изображения в формат DIB и записывает их по адресу, заданному параметром lpvBits. Дополнительно заполняется заголовок lpbmi (если параметр lpvBits указан как NULL, функция ограничивается заполнением заголовка изображения)

Если вы собираетесь сохранить изображение DIB в bmp-файле, вы должны самостоятельно сформировать заголовок файла BITMAPFILEHEADER.

[Назад] [Содеожание] [Дальше]