bmfh.bfOffBits =
sizeof (BITMAPFILEHEADER) +
(LPSTR) (lpDib->lpImage) - (LPSTR) (lpDib->lpDibHdr);
// в нашем случае это всегда «PackedDIB», поэтому разница двух указателей
// возвратит расстояние между ними.
if (lpDib->lpDibHdr.biSize == sizeof (BITMAPCOREHEADER)) {
// определяем размер изображения
#definelpbmch ((LPBITMAPCOREHEADER) (lpDib->lpDibHdr))
dwSize = ( (lpbmch->bcWidth * lpbmch->bcBitCount + 31) >> 3) & ~3L;
dwSize *= lpbmch->bcHeight;
#undeflpbmch
// прибавляем размер заголовков и палитры
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), задающую характеристики битмапа и таблицу определения цветов, а также указатель на данные изображения. Эти указатели могут указывать на разные части одного блока данных, содержащего весь битмап, или вообще на разные блоки данных, если вам так удобнее.
Сначала мы рассмотрим функцию, осуществляющую перенос изображения из битмапа на устройство, не изменяющее размеров изображения.
intSetDIBitsToDevice (
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, каждый из которых перенесет только небольшую часть строк изображения. Это может существенно сократить требуемое для отображения битмапа количество памяти (полная картинка 1280x1024, 24 бита/пиксель занимает более 3M).
Последний параметр nColorUse определяет применение цветов битмапом. Он может быть DIB_RGB_COLORS, если таблица определения цветов содержит записи RGBQUAD; или он может быть DIB_PAL_COLORS, если таблица определения цветов содержит массив 16ти разрядных номеров цветов в текущей палитре.
В Windows требуется, что бы таблица определения цветов содержала записи RGBQUAD[10], если битмап сохранен в виде файла, в виде ресурсов приложения, или если он каким–либо способом передается другому приложению. Таким образом DIB_PAL_COLORS может применяться, только если вы сами создаете и используете DIB, не сохраняя его и никому не передавая, и при этом текущая системная палитра полностью удовлетворяет вашим требованиям, что весьма и весьма редкий случай. Более того, при использовании DIB_PAL_COLORS вы обязаны проследить, что бы количество цветов, определяемых индексами было четным. Это связано с тем, что строки растра в DIB выравниваются на границу двойного слова и, одновременно, должны начинаться на такой границе. Размер заголовка (BITMAPINFOHEADER) кратен 4, одна запись RGBQUAD тоже имеет длину 4 байта; таким образом при использовании DIB_RGB_COLORS строка растра всегда начнется на границе двойного слова. А вот в случае DIB_PAL_COLORS одна запись в таблице определения цветов — 16ти разрядное число, тогда вам необходимо проследить, что бы таблица содержала четное число записей и ее длина была бы кратна 4 байтам.
Функция возвращает число строк развертки, перенесенных данной операцией.
Следующая рассматриваемая нами функция осуществляет перенос изображения с одновременным изменением размеров изображения:
intStretchDIBits (
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 выполняется для ускорения процесса вывода. Так, например, если битмап применяется для закраски фона окна (в качестве «обоев»), то особую важность приобретает скорость вывода, в то время как качество цветопередачи должно только лишь соответствовать возможностям аппаратуры и применяемому режиму.
Преобразование DIB в DDB
Для этого надо воспользоваться функцией:
HBITMAPCreateDIBitmap (hDC, lpDibInfoHdr, dwInit, lpImage, lpDibInfo, nColorUse);
которая создает вовсе не независимый от устройства битмап, как можно решить, глядя на название. Эта функция создает самый обыкновенный, зависимый от устройства битмап по указанному независимому от устройства.
Параметр hDC задает контекст устройства, с которым будет совместим создаваемый битмап.
Параметр lpDibInfoHdr является указателем на структуру BITMAPINFOHEADER (BITMAPCOREHEADER), а lpDibInfo — на структуру BITMAPINFO (BITMAPCOREINFO). Так как BITMAPINFO начинается с заголовка BITMAPINFOHEADER, то оба указателя обычно одинаковы.
Параметр dwInit указывает, надо ли осуществлять инициализацию изображения обычного битмапа данными изображения DIB. Если надо, то он должен быть равен CBM_INIT, иначе 0. Нулевое значение dwInit применяется только если битмап создается в несколько приемов: сначала создается битмап как объект GDI, а затем на него переносится изображение.
Параметр lpImage указывает на данные изображения DIB, а nColorUse задает тип таблицы определения цветов.
Если вы решили при создании обычного битмапа не инициализировать его изображение, то можете выполнить эту операцию позже, применяя функцию:
int SetDIBits (hDC, hBmp, nStartScan, nCountScan, lpImage, lpDibInfo, nColorUse);
Параметр hDC задает контекст устройства, а hBmp — обычный битмап, который инициализируется этой функцией. Надо следить, что бы данный битмап не был выбран в контекст устройства в момент вызова функции SetDIBits.
Параметры nStartScan и nCountScan задают переносимую за одну операцию полосу независимого от устройства битмапа. С помощью этих параметров можно разбить перенос одного большого битмапа на несколько операций, аналогично функции SetDIBitsToDevice.
Параметр lpImage указывает на буфер данных, lpDibInfo является указателем на структуру BITMAPINFO, содержащую заголовок и таблицу определения цветов, а nColorUse указывает на тип применяемой таблицы определения цветов (обычно DIB_RGB_COLORS).
Функция возвращает число перенесенных этой операцией строк развертки.
Использовать зависимый от устройства битмап вместо DIB имеет смысл при многократном отображении битмапа, например, если битмап отображается в окне: в этом случае перерисовка будет осуществляться при обработке каждого сообщения WM_PAINT. Типичный пример использования зависимого битмапа для отображения приведен ниже. В этом примере предполагается, что: