47952 (597372), страница 18
Текст из файла (страница 18)
Еще позже в документации появились указания о том, что возможно одновременное задание масок цветов и палитры — после заголовка сначала размещаются маски цветов и только затем палитра, размер которой определяется полем biClrUsed заголовка битмапа. Помимо этого, также можно найти замечание о том, что маски могут быть заданы для всех режимов, в которых число бит/пиксель равно или превышает 16, включая TrueColor (24 бита/пиксель). На практике эти расширения большинством приложений не поддерживаются.
Для проверки использовался стандартный MS Paint, который сам по возможности не выполняет анализа изображений и всю работу старается передать 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–98 Windows NT 4.0 | сверх того, что может Windows–95: Поддерживает режимы 16 и 32 бит/пиксель для BI_BITFIELDS; Для режима 24 бита/пиксель задание масок (и BI_BITFIELDS) не поддерживается. Для режимов 16, 24, 32 возможно задание палитры как в BI_RGB так и в BI_BITFIELDS; |
Однако, при работе в 256ти цветном режиме осталось впечатление, что необязательная для 16ти, 24х и 32х бит/пиксель битмапов палитра просто игнорируется, даже если присутствует. Однако это особенность MS Paint, а не GDI. К сожалению, остальные проверенные приложения (например, Photo Shop 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, то палитра размещается непосредственно после масок.
Сохранение независимого от устройства битмапа
Для сохранения битмапа необходимо разобраться со всеми необходимыми структурами данных, заполнить их, а затем записать в файл. Задачу можно существенно упростить, если считать, что битмап загружен в виде «Packed DIB», что существенно позволяет сохранение битмапа свести, аналогично чтению, к нескольким функциям.
В ранее приводимых примерах я использовал собственную структуру _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;
bmfh.bfOffBits =
sizeof (BITMAPFILEHEADER) +
(LPSTR) (lpDib->lpImage) - (LPSTR) (lpDib->lpDibHdr);
// в нашем случае это всегда «Packed DIB», поэтому разница двух указателей
// возвратит расстояние между ними.
if (lpDib->lpDibHdr.biSize == sizeof (BITMAPCOREHEADER)) {
// определяем размер изображения
#define lpbmch ((LPBITMAPCOREHEADER) (lpDib->lpDibHdr))
dwSize = ( (lpbmch->bcWidth * lpbmch->bcBitCount + 31) >> 3) & ~3L;
dwSize *= lpbmch->bcHeight;
#undef lpbmch
// прибавляем размер заголовков и палитры
dwSize += bmfh.bfOffBits;
} else {
// размер изображения можно получить из BITMAPINFOHEADER
dwSize = bmfh.bfOffBits + lpDib->lpDibHdr.biSizeImage;}
_hwrite (hf, (LPVOID)&bmfh, sizeof (bmfh));
// записываем собственно DIB
_hwrite (hf, (LPVOID) (lpDib->lpDibHdr), dwSize - sizeof (BITMAPFILEHEADER));
_lclose (hf);
a = TRUE;}
return a;}
Большая часть трудностей, связанных с анализом информации о битмапе, переносится на функции, осуществляющие загрузку битмапа (LoadDIBfromFile, стр. 52, LoadDIBfromResources, стр. 52) и преобразование из DDB в DIB (ConvertDDBtoDIB, стр. 64)
Отображение независимого от устройства битмапа
Для отображения DIB существует несколько возможных способов. Два из них аналогичны функциям BitBlt и StretchBlt, но используют в качестве исходных данных не контекст устройства, а независимый от устройства битмап.
Эти функции используют указатель на структуру BITMAPINFO (или BITMAPCOREINFO), задающую характеристики битмапа и таблицу определения цветов, а также указатель на данные изображения. Эти указатели могут указывать на разные части одного блока данных, содержащего весь битмап, или вообще на разные блоки данных, если вам так удобнее.
Сначала мы рассмотрим функцию, осуществляющую перенос изображения из битмапа на устройство, не изменяющее размеров изображения.
int SetDIBitsToDevice (
hDC, nDestX, nDestY, nDestWidth, nDestHeight,
nSrcX, nSrcY, nStartScan, nCountScan, lpImage, lpDibInfo, nColorUse);
Параметры hDC, nDestX, nDestY, nDestWidth и nDestHeight указывают устройство, на котором осуществляется отображение битмапа, положение и размер выводимого изображения. Существенно отметить, что в данном случае используется система координат устройства.
Параметры nSrcX и nSrcY задают положение исходного прямоугольного изображения в битмапе. Если вы задаете эти координаты не 0, то помните, что у независимых от устройства битмапов начало отсчета системы координат помещается в левый–нижний угол, что несколько необычно.
Параметр lpDibInfo является указателем на структуру BITMAPINFO (BITMAPCOREINFO), определяющую характеристики битмапа, а параметр lpImage указывает на область памяти, содержащую данные изображения.
Еще два параметра nStartScan и nCountScan указывают на фрагмент битмапа, определенный в области lpImage. nStartScan указывает номер строки развертки, с которой начинается изображение, а nCountScan указывает число строк, отображаемых этой операцией. С помощью этих параметров можно разбить процесс вывода одного большого битмапа на несколько вызовов функции SetDIBitsToDevice, каждый из которых перенесет только небольшую часть строк изображения. Это может существенно сократить требуемое для отображения битмапа количество памяти (полная картинка 1280 x 1024, 24 бита/пиксель занимает более 3M).
Последний параметр nColorUse определяет применение цветов битмапом. Он может быть DIB_RGB_COLORS, если таблица определения цветов содержит записи RGBQUAD; или он может быть DIB_PAL_COLORS, если таблица определения цветов содержит массив 16ти разрядных номеров цветов в текущей палитре.
В Windows требуется, что бы таблица определения цветов содержала записи RGBQUAD10, если битмап сохранен в виде файла, в виде ресурсов приложения, или если он каким–либо способом передается другому приложению. Таким образом DIB_PAL_COLORS может применяться, только если вы сами создаете и используете DIB, не сохраняя его и никому не передавая, и при этом текущая системная палитра полностью удовлетворяет вашим требованиям, что весьма и весьма редкий случай. Более того, при использовании DIB_PAL_COLORS вы обязаны проследить, что бы количество цветов, определяемых индексами было четным. Это связано с тем, что строки растра в DIB выравниваются на границу двойного слова и, одновременно, должны начинаться на такой границе. Размер заголовка (BITMAPINFOHEADER) кратен 4, одна запись RGBQUAD тоже имеет длину 4 байта; таким образом при использовании DIB_RGB_COLORS строка растра всегда начнется на границе двойного слова. А вот в случае DIB_PAL_COLORS одна запись в таблице определения цветов — 16ти разрядное число, тогда вам необходимо проследить, что бы таблица содержала четное число записей и ее длина была бы кратна 4 байтам.
Функция возвращает число строк развертки, перенесенных данной операцией.
Следующая рассматриваемая нами функция осуществляет перенос изображения с одновременным изменением размеров изображения:
int StretchDIBits (
hDC, nDestX, nDestY, nDestWidth, nDestHeight,
nSrcX, nSrcY, nSrcWidth, nSrcHeight, lpImage, lpDibInfo,
nColorUse, dwROP);
Параметры hDC, nDestX, nDestY, nDestWidth, nDestHeight задают устройство, на котором осуществляется отображение, положение и размеры изображения, как оно должно быть отображено.
Параметры nSrcX, nSrcY, nSrcWidth, nSrcHeight задают положение и размеры исходного изображения в битмапе. Начало отсчета системы координат битмапа находится в левом–нижнем углу битмапа, единицы соответствуют пикселям битмапа.
Параметр lpDibInfo указывает на структуру BITMAPINFO (BITMAPCOREINFO), а lpImage на буфер, содержащий данные изображения.
Параметр nColorUse указывает на способ задания таблицы определения цветов, обычно DIB_RGB_COLORS, а параметр dwROP задает индекс растровой операции, выполняемой при переносе изображения.
Так как при переносе может изменяться размер изображения, то функция использует текущий режим сжатия изображения, заданный функцией SetStretchBltMode.
Функция возвращает число строк развертки, перенесенных данной операцией.
Использование промежуточного DDB при работе с DIB
Надо отметить, что функции, отображающие DIB, заметно уступают в скорости функциям, копирующим обычный битмап из одного контекста в другой. Поэтому, если вы многократно осуществляете отображение одного и того же битмапа, то часто удобнее использовать вместо функций SetDIBitsToDevice и StretchDIBits функции по отображению обычного, зависимого от устройства, битмапа, который должен быть предварительно создан из DIB.
Этот способ имеет еще одно достоинство — он позволят изменить характеристики битмапа: размеры, цветовой формат, реорганизовать палитру.
Основа способа проста:
сначала надо загрузить DIB (обычно в виде «Packed DIB »)
преобразовать DIB в DDB с помощью функции CreateDIBitmap
выполнить требуемые операции над DDB (см. «Работа с зависимым от устройства битмапом», стр. 45)
осуществить обратное преобразование DDB в DIB с помощью функции GetDIBits
Первая операция — загрузка DIB — уже подробно рассмотрена в разделе «Загрузка независимых от устройства битмапов.», на ней останавливаться сейчас не будем. Вторая операция позволяет получить хендл DDB (HBITMAP), который можно использовать для выполнения требуемых операций над битмапом. Так, например, такой битмап может быть выбран в совместимый контекст устройства или использован для создания кисти и т.д.
Следует отметить, что определяя свойства преобразования DIB в DDB надо правильно определить цель такого преобразования. Если вы планируете выполнить редактирование DIB, то важно не испортить хранимое изображение; часто может быть целесообразно назначить цветовое разрешение DDB достаточно высоким, скажем TrueColor, даже если он реально будет отображаться на устройстве, имеющем существенно меньшее цветовое разрешение. Либо воспользоваться DIB–секциями, которые также возвращают HBITMAP. Однако еще чаще встречается другой вариант — преобразование DIB в DDB выполняется для ускорения процесса вывода. Так, например, если битмап применяется для закраски фона окна (в качестве «обоев»), то особую важность приобретает скорость вывода, в то время как качество цветопередачи должно только лишь соответствовать возможностям аппаратуры и применяемому режиму.












