} bitmapheader;
В этой структуре резервируется достаточное пространство для удержания заголовка битмапа и палитры плюс еще некоторая информация (3 дополнительные записи RGBQUAD = 12 байт), о которой будет рассказано ниже, в разделе «Формат Win32 (Windows NT 3.x)».
Определение размера области данных для хранения изображения осуществляется точно также, как и в случае OS/2, за небольшой оговоркой — полученный размер является максимальным. Если используются сжатые битмапы (biCompression равно BI_RLE4 или BI_RLE8), то реальное изображение может оказаться существенно меньшим. Вообще говоря, определение размера сжатого изображения возможно только после того, как это изображение полностью построено — так как возможность сжатия данных и степень сжатия очень сильно зависят от характера самих данных. Таким образом, при выделении пространства под вновь создаваемые битмапы стоит выделять максимально необходимый объем пространства, а при сохранении в сжатом виде этот размер вам вернет GDI, так как собственно сжатие осуществляется именно им.
DWORD dwSizeImage;
LPBITMAPCOREHEADER lpbmih; // считаем, что указатель на заголовок нам дан
dwSizeImage = ( (lpbmih->biWidth * lpbmih->biBitCount + 31) >> 3) & ~3L;
dwSizeImage *= lpbmih->biHeight;
Формат Win32 (Windows NT 3.x)
При расширении возможностей битмапов, реализованных в Win32 API (ранние версии Windows–95, WindowsNT 3.x) удалось обойтись без изменения размера заголовка битмапа; изменения коснулись только способов задания некоторых полей и описания цветов. Всего можно перечислить несколько новшеств:
перечисление строк развертки как снизу–вверх, так и сверху–вниз;
добавление двух новых цветовых форматов: 16 и 32 бита на пиксель (так называемые HiColor);
в случае форматов 16 и 32 бита на пиксель новый способ описания цветов — вместо палитры задаются маски цветов.
Рассмотрим эти новшества подробнее.
Во–первых, для того, что бы разобраться в том, какой порядок перечисления строк развертки используется, надо обратить внимание на поле biHeight. Если строки развертки перечисляются сверху–вниз, то это поле будет представлено отрицательной величиной. В связи с этим при определении размеров изображения необходимо использовать абсолютную величину поля biHeight.
DWORD dwSizeImage;
LPBITMAPCOREHEADER lpbmih; // считаем, что указатель на заголовок нам дан
dwSizeImage = ( (lpbmih->biWidth * lpbmih->biBitCount + 31) >> 3) & ~3L;
dwSizeImage *= abs (lpbmih->biHeight); // 1
Во–вторых, новые цветовые форматы (16 и 32 бита на пиксель) первоначально (WindowsNT 3.x) требовали нескольких одновременных изменений в битмапе:
палитра отсутствует, так как изображение сохраняется практически в истинных цветах (даже 16 бит на пиксель дает возможность описать 65 536 разных цветов);
вместо палитры записываются три двойных слова, представляющего соответственно маски красной, зеленой и синей компонент (эти маски позволяют GDI разобраться, какие биты в 16ти или 32х битовом номере цвета передают соответствующую компоненту цвета);
поле biCompression задается равным BI_BITFIELDS, что бы подчеркнуть отсутствие палитры и наличие вместо нее масок цветов. Это значение позволяет старым приложениям распознать неподдерживаемый формат битмапа до того, как возникнет ошибка, связанная с попыткой прочитать палитру.
Все три изменения осуществлялись одновременно и были обязательны для HiColor битмапа. Однако по мере развития в этот формат были внесены некоторые изменения. Так, существенно упрощенный GDI в Windows–95 потребовал задания фиксированных масок цветов, работать как в WindowsNT с произвольными масками было чересчур сложно[7].
В Windows–95 разрешено применять следующие маски цветов:
Формат | Красный | Зеленый | Синий |
16 бит/пиксель, 5–5–5 (32 768 цветов): | 0x00007C00L | 0x000003E0L | 0x0000001FL |
16 бит/пиксель, 5–6–5 (65 536 цветов)[8]: | 0x0000F800L | 0x000007E0L | 0x0000001FL |
32 бит/пиксель, 8–8–8 (16 777 216 цветов): | 0x00FF0000L | 0x0000FF00L | 0x000000FFL |
Таким образом для 16ти и 32х битовых битмапов появились стандартные маски цветов, которые будут использоваться по умолчанию, если в самом битмапе эти маски не определены; в этом случае поле biCompression задается равным BI_RGB, а не BI_BITFIELDS. Теперь режим BI_BITFIELDS не обязательно должен устанавливаться для 16ти и 32х битовых битмапов, он используется только в том случае, если заголовок битмапа содержит маски.
Если маски присутствуют, то они перечисляются сразу за заголовком битмапа (BITMAPINFOHEADER) в приведенном в таблице порядке — красный, зеленый и синий цвета.
Кроме того, в случае 16ти, 24х или 32х бит на пиксель и режима BI_RGB появилась возможность задавать палитру (поле biClrUsed должно быть ненулевым — палитра для максимально допустимого числа цветов в этих форматах чересчур громоздка). Смысл включения палитры теперь связан не с необходимостью задавать соответствие номеров цветов реальным цветам, а с возможностью оптимизировать процесс отображения битмапа, задавая рекомендуемую для него палитру. Это реально может иметь место при воспроизведении HiColor или TrueColor битмапов на устройствах, поддерживающих палитру — для повышения качества цветопередачи такому устройству целесообразно назначить палитру, оптимизированную для этого битмапа. Если этого не сделать, то все множество цветов битмапа будет приводится к той палитре, которая уже используется устройством — скорее всего это будет системная палитра.
Еще позже в документации появились указания о том, что возможно одновременное задание масок цветов и палитры — после заголовка сначала размещаются маски цветов и только затем палитра, размер которой определяется полем biClrUsed заголовка битмапа. Помимо этого, также можно найти замечание о том, что маски могут быть заданы для всех режимов, в которых число бит/пиксель равно или превышает 16, включая TrueColor (24 бита/пиксель). На практике эти расширения большинством приложений не поддерживаются.
Для проверки использовался стандартный MSPaint, который сам по возможности не выполняет анализа изображений и всю работу старается передать GDI. Это позволяет использовать его в качестве теста на возможности GDI. Для разных платформ Windows были получены следующие результаты:
Windows 3.11 | Поддерживает режимы 1, 4, 8, 16, 24, 32 бит/пиксель для BI_RGB;Режим BI_BITFIELDS не поддерживается[9]. |
Windows–95 | Поддерживает режимы 1, 4, 8, 16, 24, 32 бит/пиксель для BI_RGB; |
Windows–98Windows NT 4.0 | сверх того, что может Windows–95:Поддерживает режимы 16 и 32 бит/пиксель для BI_BITFIELDS;Для режима 24 бита/пиксель задание масок (и BI_BITFIELDS) не поддерживается.Для режимов 16, 24, 32возможно задание палитры как в BI_RGB так и в BI_BITFIELDS; |
Однако, при работе в 256ти цветном режиме осталось впечатление, что необязательная для 16ти, 24х и 32х бит/пиксель битмапов палитра просто игнорируется, даже если присутствует. Однако это особенность MSPaint, а не GDI. К сожалению, остальные проверенные приложения (например, PhotoShop 5.0) вообще отказались работать с HiColor форматами (16 и 32 бит/пиксель).
Это значит, что для экспорта изображений приложение должно использовать по возможности старые, проверенные и широко распространенные форматы 1, 4, 8 бит/пиксель с полной палитрой (в случае OS/2 приходилось наблюдать ошибки при чтении битмапов с сокращенной палитрой); либо TrueColor в стандартном варианте — без палитры и без масок цветов. А вот при чтении битмапа целесообразно допускать все эти возможные варианты, что обеспечит совместимость с битмапами, создаваемыми другими приложениями, даже в ближайшем будущем.
Таким образом для битмапов Win32 надо обращать внимание на:
возможно отрицательную высоту битмапа;
режим сжатия BI_BITFILEDS — если он задан, то после заголовка есть 3 двойных слова с масками цветовых компонент; если же задан режим BI_RGB, BI_RLE4 или BI_RLE8, то масок нет (предполагаются стандартные маски 5–5–5 или 8–8–8);
для форматов 1, 4 и 8 бит на пиксель палитра обязательна, а для 16, 24 и 32 бит на пиксель палитра может отсутствовать (то есть нулевое значение biClrUsed интерпретируется либо как максимальный размер палитры, либо как ее отсутствие — смотря по числу бит на пиксель). Для HiColor или TrueColor режимов палитра является лишь рекомендуемой, облегчающей процесс отображения полноцветного битмапа на устройстве, поддерживающем палитры. Именно поэтому в примере на странице 57 при определении размера палитры значение поля biBitCount сравнивалось с 8, а не проверялось строгое равенство 24 битам на пиксель — максимальный размер палитры определен только для 2х, 16ти и 256ти цветных битмапов, а для форматов с 16тью, 24мя и 32мя битами на пиксель для задания палитры необходимо задать поле biClrUsed. По умолчанию в HiColor и TrueColor битмапах палитра отсутствует. Если задан и режим BI_BITFIELDS, и biClrUsed не равен 0, то палитра размещается непосредственно после масок.
Сохранение независимого от устройства битмапа
Для сохранения битмапа необходимо разобраться со всеми необходимыми структурами данных, заполнить их, а затем записать в файл. Задачу можно существенно упростить, если считать, что битмап загружен в виде «PackedDIB», что существенно позволяет сохранение битмапа свести, аналогично чтению, к нескольким функциям.
В ранее приводимых примерах я использовал собственную структуру _DIB, описывающую загруженный в память DIB. Она определена в примере на странице 52, вместе с включением необходимых заголовочных файлов и определением вспомогательных символов. Помимо этого она будет применяться в данном примере, а также с ее помощью будут выполнены операции по преобразованию DIB в DDB (стр. 63) и наоборот — DDB в DIB (стр. 64).
Поскольку формат независимого битмапа в предыдущих разделах был рассмотрен достаточно подробно, то сейчас можно прямо перейти к исходному тексту.
BOOL StoreDIBtoFile (LPSTR lpszFileName, LP_DIB lpDib) {
BITMAPFILEHEADER bmfh; // заголовокфайлабитмапа
HFILE hf;
BOOL a = FALSE;
DWORD dwSize;
hf = _lcreat (lpszFileName, 0);
if (hf != HFILE_ERROR) {
// заполняем и записываем заголовок файла
bmfh.bfType = DIB_SIGNATURE;
bmfh.bfSize = 0L;
bmfh.bfReserved1 = bmfh.bfReserved2 = 0;