Основы графического вывода

Глобальные системы координат GDI. Отображение основных графических объектов. Основные и дополнительные средства для рисования линий. Растровые изображения и метафайлы. Обзор и создание зависимых и независимых от графического устройства битмапов.

Рубрика Программирование, компьютеры и кибернетика
Вид лекция
Язык русский
Дата добавления 24.06.2009
Размер файла 498,8 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Информация об используемых битмапом цветах размещается сразу после заголовка битмапа в виде массива от 2 до 256 записей типа RGBQUAD или пропущена вовсе, если битмап представлен в истинных цветах. Структура RGBQUAD отличается от RGBTRIPLE только тем, что она дополнена неиспользуемым байтом до границы двойного слова Иногда возникает непроизвольная ошибка при попытке создать палитру (как объект GDI): для создания палитры необходимо в функцию CreatePalette передать массив структур типа PALETTENTRY, который имеет схожий с RGBQUAD формат. Естественное желание -- просто использовать палитру битмапа в качестве массива структур PALETEENTRY. Неприятность связана с тем, что компоненты красного, синего и зеленого цветов в этих структурах перечислены в разном порядке; таким образом необходимо все-таки создавать собственный массив структур PALETENTRY для создания палитры..

Аналогично формату OS/2 вводится дополнительная объединяющая структура BITMAPINFO, по смыслу эквивалентная структуре BITMAPCOREINFO.

typedef struct tagBITMAPINFO {

BITMAPINFOHEADER bmiHeader;

RGBQUAD bmiColors[1];

} BITMAPINFO;

Полный размер структуры BITMAPINFO можно определить исходя из размера заголовка (обязательно надо брать значение поля biSize, а не sizeof (BITMAPIFOHEADER)) и размера палитры, вычисляемого с учетом поля biClrUsed:

UINT uSizeDibInfo;

LPBITMAPINFOHEADER lpbmih;

uSizeDibInfo = lpbmih->biSize + (

lpbmih->biClrUsed ? lpbmih->biClrUsed : (

lpbmih->biBitCount > 8 ? 0 : (1 << lpbmih->biBitCount))

) * sizeof (RGBQUAD);

Теоретически, этот фрагмент кода лишь относительно корректен -- поле biClrUsed может отсутствовать в структуре BITMAPINFOHEADER. По идее надо сначала проверить значение поля biSize, и только если поле biClrUsed присутствует в структуре, использовать его значение. Однако этот фрагмент может оказаться совершенно корректным, если осуществлять загрузку заголовка битмапа в специально выделенную для этого структуру BITMAPINFOHEADER, с предварительным обнулением всех полей (примерно так, как в примере на странице 56).

Следует еще раз напомнить, что битмапы с неполным заголовком -- современными системами не поддерживаются, так что в принципе не будет ошибки, если посчитать заголовок присутствующим полностью. В то же время битмапы с неполной палитрой -- почти типичный случай; например обои Windows-95 часто представлены именно в таком виде, поэтому учитывать возможность задания поля biClrUsed необходимо.

Иногда бывает удобно воспользоваться собственным заменителем структуры BITMAPINFO:

struct {

BITMAPINFOHEADER bmiHeader;

RGBQUAD bmiColors[ 256 + 3 ];

} 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, Windows NT 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 бита на пиксель) первоначально (Windows NT 3.x) требовали нескольких одновременных изменений в битмапе:

палитра отсутствует, так как изображение сохраняется практически в истинных цветах (даже 16 бит на пиксель дает возможность описать 65 536 разных цветов);

вместо палитры записываются три двойных слова, представляющего соответственно маски красной, зеленой и синей компонент (эти маски позволяют GDI разобраться, какие биты в 16ти или 32х битовом номере цвета передают соответствующую компоненту цвета);

поле biCompression задается равным BI_BITFIELDS, что бы подчеркнуть отсутствие палитры и наличие вместо нее масок цветов. Это значение позволяет старым приложениям распознать неподдерживаемый формат битмапа до того, как возникнет ошибка, связанная с попыткой прочитать палитру.

Все три изменения осуществлялись одновременно и были обязательны для HiColor битмапа. Однако по мере развития в этот формат были внесены некоторые изменения. Так, существенно упрощенный GDI в Windows-95 потребовал задания фиксированных масок цветов, работать как в Windows NT с произвольными масками было чересчур сложно В случае Windows NT требуется только чтобы биты для каждой компоненты образовывали непрерывную группу, а число бит для задания каждой компоненты и порядок задания этих групп не фиксированы..

В Windows-95 разрешено применять следующие маски цветов:

Формат

Красный

Зеленый

Синий

16 бит/пиксель, 5-5-5 (32 768 цветов):

0x00007C00L

0x000003E0L

0x0000001FL

16 бит/пиксель, 5-6-5 (65 536 цветов) Формат 5-6-5 является альтернативным, для его задания обязательно задание масок цветов; в то время как для задания формата 5-5-5 маски можно опустить -- он считается стандартным.:

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 бита/пиксель). На практике эти расширения большинством приложений не поддерживаются.

Для проверки использовался стандартный MS Paint, который сам по возможности не выполняет анализа изображений и всю работу старается передать GDI. Это позволяет использовать его в качестве теста на возможности GDI. Для разных платформ Windows были получены следующие результаты:

Windows 3.11

Поддерживает режимы 1, 4, 8, 16, 24, 32 бит/пиксель для BI_RGB;

Режим BI_BITFIELDS не поддерживается Очень может быть, что это зависит от видеоадаптера и драйверов -- на тестовой машине был установлен Diamond Stealth 64 PCI с чипом S3 Vision968. Стандартные драйвера для Windows 3.x вообще не поддерживают режимов 16 и 32 бит/пиксель..

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 требуется, что бы таблица определения цветов содержала записи RGBQUAD Или RGBTRIPLE, если битмап соответствует формату OS/2, если битмап сохранен в виде файла, в виде ресурсов приложения, или если он каким-либо способом передается другому приложению. Таким образом 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 выполняется для ускорения процесса вывода. Так, например, если битмап применяется для закраски фона окна (в качестве «обоев»), то особую важность приобретает скорость вывода, в то время как качество цветопередачи должно только лишь соответствовать возможностям аппаратуры и применяемому режиму.

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

Для этого надо воспользоваться функцией:

HBITMAP CreateDIBitmap (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. Типичный пример использования зависимого битмапа для отображения приведен ниже. В этом примере предполагается, что:

в созданном окне будет отображаться битмап, находящийся в файле с именем C:\TEST\MY.BMP

используются распаковщики сообщений из windowsx.h

для загрузки битмапа из файла применяется функция LoadDIBfromFile, приведенная на странице 52, а для освобождения занятых им ресурсов -- функция FreeDIB (см. страницу 53)

Загрузка битмапа осуществляется однократно при создании окна (при обработке сообщения WM_CREATE, в примере -- в функции Cls_OnCreate), тогда же DIB преобразуется в зависимый от устройства и, так как он больше не нужен, уничтожается. Хендл зависимого от устройства битмапа сохраняется в структуре описания окна, в двойном слове со смещением 0 (см. функции GetWindowLong и SetWindowLong).

Далее, при необходимости перерисовать битмап (сообщение WM_PAINT, функция-обработчик Cls_OnPaint) зависимый от устройства битмап отображается с помощью функции BitBlt.

Когда окно закрывается (сообщение WM_DESTROY, функция Cls_OnDestroy) находящийся в памяти зависимый от устройства битмап уничтожается, так как все занятые ресурсы GDI должны быть обязательно освобождены.

// включаемые файлы и описание структур см. в примере на странице 52

HBITMAP ConvertDIBtoDDB (LP_DIB lpDib)

{HDC hdc;

HBITMAP hbmp;

// для преобразования DIB в обычный битмап нужен контекст устройства,

// на котором будет осуществляться отображение (окно, дисплей, принтер)

hdc = GetWindowDC (NULL); // используем контекст всего дисплея

// создаем зависимый битмап и запоминаем его хендл

hbmp = CreateDIBitmap (

hdc, lpDib->lpDibHdr, CBM_INIT, lpDib->lpImage,

(LPBITMAPINFO) (lpDib->lpDibHdr), DIB_RGB_COLORS);

ReleaseDC (NULL, hdc); // освобождаем контекст дисплея

return hbmp;}

BOOL Cls_OnCreate (HWND hwnd, LPCREATESTRUCT lpCreateStruct)

{_DIB dib;

// загружаем битмап из файла

if (LoadDIBfromFile (&dib, "C:\\TEST\\MY.BMP")) {

// создаем зависимый битмап и запоминаем его хендл

// предположим, что при регистрации класса окон мы зарезервировали

// 4 байта в структуре описания окна; там мы сохраним хендл DDB,

// который будет отображаться в окне.

SetWindowLong (hwnd, 0, (LONG)ConvertDIBtoDDB (&dib));

FreeDIB (&dib); // освобождаем память, занятую DIB}

return TRUE;}

void Cls_OnPaint (HWND hwnd)

{PAINTSTRUCT ps;

BITMAP bmp;

HBITMAP hBmp;

BeginPaint (hwnd, &ps);

// получаем хендл битмапа и узнаем его характеристики

hBmp = (HBITMAP)GetWindowLong (hwnd, 0);

GetObject (hBmp, sizeof (bmp), &bmp);

// создаем совместимый контекст и выбираем в него битмап

hCDC = CreateCompatibleDC (ps.hdc);

SelectBitmap (hCDC, hBmp);

// отображаем битмап в окне

BitBlt (ps.hdc, 0,0, bmp.bmWidth,bmp.bmHeight, hCDC, 0,0, SRCCOPY);

// удаляем совместимый контекст (битмап при этом сохранится)

DeleteDC (hCDC);

EndPaint (hwnd, &ps);}

void Cls_OnDestroy (HWND hwnd)

{HBITMAP hBmp;

// получаем хендл битмапа

hBmp = (HBITMAP)GetWindowLong (hwnd, 0);

if (hBmp) {

// если битмап существует, удаляем его

DeleteBitmap (hBmp);

SetWindowLong (hwnd, 0, 0L);}}

Обратное преобразование DDB в DIB

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

int GetDIBits (hDC, hBmp, nStartScan, nCountScan, lpImage, lpDib, nColorUse);

Параметры и возвращаемое этой функцией значение такое же, как и для функции SetDIBits.

Параметр lpImage может быть NULL, тогда эта функция не переносит данных битмапа, а только лишь заполняет структуру BITMAPINFO. Эта особенность очень часто используется на практике -- при необходимости получить информацию о битмапе структура BITMAPINFOHEADER обнуляется, в нее записывается самая необходимая информация (размер структуры biSize, размеры изображения biWidth и biHeight, информация о формате битмапа biPlanes, biBitCount и biCompression), после чего вызывается функция GetDIBits с нулевым указателем на область данных изображения. GDI при этом просто заполняет структуру BITMAPINFOHEADER остальными данными, в том числе вычисляет размер области, необходимой для хранения данных (поле biSizeImage). В дальнейшем очень просто выделить блок памяти необходимого размера и повторным вызовом функции GetDIBits получить непосредственно само изображение.

// включаемые файлы и описание структур см. в примере на странице 52

BOOL ConvertDDBtoDIB (LP_DIB lpDib, HBITMAP hbmp)

{HDC hdc;

DWORD dwSize;

BITMAPFILEHEADER bmfh;

struct {

BITMAPINFOHEADER bmih;

RGBQUAD palette[ 256 + 3 ];

} bmh;

BITMAP bm;

// инициализируем возвращаемые данные:

lpDib->hglbDib = NULL;

lpDib->lpDibHdr = (LPBITMAPINFOHEADER)NULL;

lpDib->lpImage = (LPSTR)NULL;

lpDib->uDibFlags = 0;

// для преобразования DDB в DIB нужен контекст устройства

hdc = GetWindowDC (NULL); // используем контекст всего дисплея

// получаем информацию об обычном битмапе

GetObject ( (HGDIOBJ)hbmp, sizeof (bm), (LPVOID)&bm);

bmh.bmih.biSize = sizeof (bmih);

bmh.bmih.biWidth = bm.bmWidth;

bmh.bmih.biHeight = bm.bmHeight;

bmh.bmih.biPlanes = (WORD)1;

bmh.bmih.biBitCount = bm.bmPlanes * bm.bmBitsPixel;

// определяем формат битмапа

bmh.bmih.biCompression = ( (bmh.bmih.biBitCount == 16) || (bmh.bmih.biBitCount == 32)) ?

BI_BITFIELDS : BI_RGB;

// обнуляем остальные поля

bmh.bmih.biSizeImage = bmh.bmih.biXPelsPerMeter = bmh.bmih.biYPelsPerMeter =

bmh.bmih.biClrUsed = bmh.bmih.biClrImportant = 0L;

// определяем все остальные данные

GetDIBits (

hdc, hbmp, 0, abs (bmh.bmih.biHeight),

(LPVOID)0L, (LPBITMAPINFO)&bmh, DIB_RGB_COLORS);

// выделяем необходимое пространство

dwSize = sizeof (BITMAPFILEHEADER) +

( (bmh.bmih.biCompression == BI_BITFIELDS) ? sizeof (DWORD)*3 : 0L) +

sizeof (RGBQUAD) * (bmh.bmih.biClrUsed ? bmh.bmih.biClrUsed :

(bmh.bmih.biBitCount <= 8 ? 1<<bmh.bmih.biBitCount : 0));

lpDib->lpDibHdr = (LPBITMAPINFOHEADER)GlobalAllocPtr (GHND, dwSize+bmh.bmih.biSizeImage);

if (lpDib->lpDibHdr != (LPBITMAPINFOHEADER)NULL) {

// уточняем информацию

bmh.bmih.biXPelsPerMeter = (LONG) (

(GetDeviceCaps (hdc, HORZRES) * 1000UL) / GetDeviceCaps (hdc, HORZSIZE));

bmh.bmih.biYPelsPerMeter = (LONG) (

(GetDeviceCaps (hdc, VERTRES) * 1000UL) / GetDeviceCaps (hdc, VERTSIZE));

// копируем заголовок битмапа

_memcpy_ (lpDib->lpDibHdr, &bmh, dwSize);

// получаем изображение

lpDib->lpImage = (LPSTR) (lpDib->lpDibHdr) + dwSize;

GetDIBits (

hdc, hbmp, 0, abs (bmh.bmih.biHeight),

(LPVOID) (lpDib->lpImage), (LPBITMAPINFO) (lpDib->lpDibHdr), DIB_RGB_COLORS);

// и устанавливаем остальные поля структуры _DIB:

lpDib->hglbDib = GlobalPtrHandle (lpDib->lpDibHdr);

lpDib->uDibFlags = DIB_FILE; // выделенное пространство должно быть освобождено}

ReleaseDC (NULL, hdc); // освобождаем контекст дисплея

return lpDib->uDibFlags ? TRUE : FALSE;}

Создание ассоциаций DIB с контекстом устройства

Эти операции надо выполнять различными способами, в зависимости от API:

1) Для Windows API удобно использование так называемого DIB драйвера. В том виде, в каком было формально описано применение DIB драйвера, этот путь практически ничего не дает -- слишком много ограничений на использование этого драйвера и на использование получаемого контекста устройства.

2) Для Win32 API можно создать специальную «DIB секцию», которая описывает DIB, но с помощью хендла HBITMAP, применяемого для описания DDB. Это позволяет связать DIB с контекстом устройства и выполнить рисование непосредственно на нем. Этот путь является во многих случаях предпочтительным, особенно с учетом несколько неудачной реализации функции CreateDIBitmap в некоторых версиях Win32 (CreateDIBitmap на некоторых платформах выделяет пространство для хранения битмапа в куче, используемой GDI, в то время как для DIB-секций пространство всегда выделяется в куче, используемой по умолчанию).

DIB драйвер (Windows API)

Как видно из предыдущего материала, самая сложная операция -- сохранение измененного независимого от устройства битмапа. При этом необходимо выполнить значительный объем операций и, кроме этого, приходится анализировать заголовки битмапа, различающиеся в разных версиях. Операция загрузки битмапа из файла или из ресурсов осуществляется качественно проще и, что очень удобно, алгоритм не зависит от версии битмапа.

По-видимому это подтолкнуло разработчиков Microsoft включить в состав распространяемых с компилятором компонент специальный DIB-драйвер (DIB.DRV) Если вы будете его использовать, то вы должны включить файл драйвера DIB.DRV в установочный комплект вашего приложения -- в стандартной системе этот драйвер не содержится; обычно он включен в redistribution kit, сопровождающий компиляторы., который позволяет модифицировать существующий DIB, не анализируя его заголовок. Используя этот драйвер вы можете легко получить хендл контекста устройства, связанного с указанным вами упакованным DIB. Далее все операции рисования на этом контексте устройства будут сопровождаться изменениями DIB. После уничтожения созданного контекста устройства в упакованном DIB сохранится измененное изображение.

Во всем этом есть два существенных но:

-- созданный таким образом контекст не может быть использован для операций передачи растровых изображений. То есть отобразить данный DIB с помощью операций BitBlt или StretchBlt невозможно -- для этого необходимо осуществить отображение битмапа, обращаясь к его упакованному представлению. Однако самая сложная операция -- получение измененного изображения в виде упакованного DIB -- осуществляется без вашего участия.

-- DIB драйвер не является компонентом операционной системы, так что получить его можно только в составе redistribution kit, сопровождавшем компиляторы во времена Windows 3.x; к сожалению в современные компиляторы он не включен и мне пока не попадалось его версий для Win32 API.

Для того, что бы создать контекст устройства, ассоциированный с DIB, Вы должны использовать функцию CreateDC (см. также раздел «Получение хендла контекста устройства», стр. 5):

HDC hDibDC = CreateDC ("DIB", NULL, NULL, lpPackedDib);

// рисуем на контексте, после чего его освобождаем

DeleteDC (hDibDC);

Параметр со значением "DIB" задает имя драйвера (DIB.DRV), два другие должны быть NULL, а последний параметр указывает на данные, передаваемые драйверу при создании контекста устройства. Для DIB.DRV он должен указывать на упакованный DIB, то есть на заголовок битмапа BITMAPINFOHEADER (BITMAPCOREHEADER), сопровождаемый всеми необходимыми данными (палитрой и данными изображения). После использования такого контекста вы должны его уничтожить с помощью функции DeleteDC.

При работе с DIB-драйвером необходимо учитывать, что обрабатываемые битмапы не должны быть сжатыми (BI_RLE4 или BI_RLE8), так как:

а) драйвер не может осуществлять сжатие битмапа заново после каждой операции рисования,

б) необходимый для хранения битмапа объем памяти может увеличиваться в процессе рисования, так как он зависит от реально достигнутой степени сжатия, разной для разных изображений, а размер блока памяти, содержащего упакованный DIB, определяется вами и не может изменяться в процессе рисования на контексте.

DIB-cекция (Win32 API)

Win32 API содержит специальную функцию CreateDIBSection, которая создает хендл DDB битмапа (HBITMAP), ассоциированный не с DDB, а с независимым от устройства битмапом. Таким образом существует возможность выполнения над DIB всех операций, типичных для DDB. Так, например, можно получить HBITMAP, соответствующий DIB и выбрать его в контекст устройства.

HBITMAP CreateDIBSection (hdc, lpbmi, nColorUse, ppvBits, hSection, dwOffset); 1

Параметр hdc задает хендл контекста устройства, информация о цветах и палитре которого используется когда параметр nColorUse равен DIB_PAL_COLORS.

Параметр lpbmi является указателем на структуру BITMAPINFO, содержащую заголовок битмапа и, при необходимости, палитру или маски (для HiColor режимов). BITMAPINFOHEADER, являющийся частью BITMAPINFO содержит информацию об организации битмапа и его размерах.

С помощью параметра nColorUse задается способ использования палитры. Значение DIB_RGB_COLORS указывает, что палитра битмапа содержит таблицу записей RGBQUAD (RGBTRIPLE), а значение DIB_PAL_COLORS указывает, что вместо палитры битмапа размещен массив целых чисел, являющихся индексами цветов в системной палитре.

Параметр ppvBits является указателем на переменную типа LPVOID. В эту переменную будет записан указатель на начало данных изображения.

Два последних параметра: hSection и dwOffset используются, если битмап содержится в проецируемом в память файле. В этом случае hSection является хендлом проецирования, возвращенном функцией CreateFileMapping, а dwOffset -- смещение от начала файла до данных изображения. В описании указывается, что функция CreateDIBSection требует, что бы значение dwOffset было кратно 4 (длина строки растра в DIB всегда выравнивается на границу двойного слова). Если в проецируемом файле содержится так называемый «Packed DIB», то есть битмап без заголовка файла, то смещение до начала данных изображения само собой будет кратно 4 байтам Разве что кроме форматов OS/2, использующих для задания палитры записи RGBTRIPLE по 3 байта каждая; тогда необходимо следить, что бы размер палитры обеспечивал выравнивание начала изображения на границу, кратную 4 байтам..

Однако в нормальных файлах битмапов заголовок файла присутствует. Он описывается структурой BITMAPFILEHEADER, которая имеет размер 14 байт. Очевидно, что 14 не кратно 4. И, как следствие, для большинства битмапов суммарный размер заголовка файла, заголовка битмапа и данных о цветах (палитры или масок) не может быть кратен 4 (!). Размер структуры BITMAPINFOHEADER равен 40. Суммарный размер обоих заголовков равен 54 и не кратен 4. Палитра, состоящая из записей RGBAUAD по 4 байта каждая, либо маски цветов -- три двойных слова никак не могут выровнять конец заголовка по границе двойного слова. В тоже время, если при вызове функции CreateDIBSection задать величину dwOffset не кратную 4, то функция вернет NULL, хотя код ошибки (возвращаемый функцией GetLastError) не будет установлен. Как результат -- обычные битмапы в виде файлов нельзя спроецировать в память и передать функции CreateDIBSection.

В итоге функция CreateDIBSection может легко применяться для создания нового DIB -- в этом случае hSection и dwOffset следует задать равными 0. Тогда GDI сам создаст необходимое проецирование и вернет хендл битмапа. При необходимости сохранения DIB в виде файла можно с помощью функции GetObject прочитать информацию о DIB-секции:

DIBSECTION ds; // 1

int GetObject (hbmpDIBSection, sizeof (DIBSECTION), &ds);

Структура DIBSECTION содержит следующую информацию:

typedef struct _DIBSECTION {// 1

BITMAP dsBm;

BITMAPINFOHEADER dsBmih;

DWORD dsBitfields[ 3 ];

HANDLE dshSection;

DWORD dsOffset;

} DIBSECTION;

Поле dsBm содержит структуру BITMAP, описывающую секцию как DDB; В этой структуре можно прочитать поле bmBits (в примере выше это будет ds.dsBm.bmBits), которое является указателем на данные изображения DIB-секции. Этот указатель совпадает с тем, который возвращается в параметре ppvBits при вызове функции CreateDIBSection и может быть использован функциями работы с DIB.

Поле dsBmih описывает секцию как DIB; В основном значения полей этой структуры совпадают с теми, которые были указаны при создании секции. Однако, если вы не вычисляли сами размер данных изображения перед вызовом CreateDIBSection, то GDI сам вычислит нужное значение и возвратит его в поле biSizeImage (в примере выше это будет ds.dsBmih.biSizeImage).

Массив dsBitfields содержит маски цветов; они заполняются в зависимости от числа цветов и установленного режима сжатия. Подробнее о масках см. «Формат Win32 (Windows NT 3.x)», стр.58.

Поля dshSection и dsOffset повторяют значения, указанные при вызове функции CreateDIBSection. Если вы указали нулевые значения, то и эти поля также будут нулевыми, несмотря на то, что система сама создает проецирование.

При использовании структуры DIBSECTION нужно следить за тем, что бы вы не создавали DIB-секцию для битмапов в формате OS/2. Непосредственно GDI эту работу выполнит без затруднений, но при этом функция GetObject возвратит в структуре DIBSECTION не BITMAPINFOHEADER, а BITMAPCOREHEADER, другого размера и с другими полями. Если вам придется все-же работать с битмапами OS/2, то заодно придется описать и собственную структуру, аналогичную DIBSECTION; лучше всего просто превратить ее в union, содержащий вариант для Windows и для OS/2.

Если же вы собираетесь использовать CreateDIBSection для редактирования уже существующего DIB, то стоит воспользоваться одним из двух возможных способов: а) создать временное проецирование и скопировать в него битмап, пропустив заголовок файла или б) загрузить DIB обычным образом, создать пустую DIB-секцию, скопировать в нее изображение (скажем, с помощью SetDIBitsToDevice) и освободить первоначально загруженный DIB.

Внимание! Если вы вызывали CreateDIBSection с нулевыми значениями hSection и dwOffset, то при удалении созданной секции с помощью DeleteObject система сама удалит созданное проецирование (вам оно недоступно, так как GetObject возвращает также нули в полях dshSection и dsOffset). Но если вы сами создали проецирование, то вы обязаны сами его удалить.

При работе с DIB-секциями часто возникает необходимость получить палитру, используемую этой секцией. Типичный случай -- сохранение DIB-секции в виде файла: если число цветов меньше или равно 256, то такая секция обязательно содержит палитру. Причем в этом случае нужна палитра не в виде структуры LOGPALETTE или массива записей PALETTENTRY, а в виде массива записей RGBQUAD. Для этого предназначена пара функций:

UINT GetDIBColorTable (hdc, uStartIndex, cEntries, lprgbColors); 1

UINT SetDIBColorTable (hdc, uStartIndex, cEntries, lprgbColors); 1

Обратите внимание -- функции используют не хендл DIB-секции, а хендл совместимого контекста устройства, в который должна быть выбрана DIB-секция.

Практические примеры:

1) Создание пустой DIB-секции 1:

struct {// не `BITMAPINFO bmi' - нам надо зарезервировать место под палитру

BITMAPINFOHEADER bmiHeader;

RGBQUAD bmiColors[ 256+3 ]; // в BITMAPINFO используется bmiColors[1]

} bmi;

LPVOID lpData;

HDC hdcDisplay;

HDC hdcMem;

HBITMAP hbmpDibSection;

int nFirstCol;

int nColors; // число цветов в системной палитре

PALETTEENTRY pe[ 256 ]; // системная палитра

int i;

hdcDisplay = GetWindowDC ( (HWND)0L);

// создаем DIB-секцию, для чего полностью заполняем bmi, включая маски и палитру

bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);

bmi.bmiHeader.biWidth = GetDeviceCaps (hdcDisplay, HORZRES); // пусть размер DIB

bmi.bmiHeader.biHeight = GetDeviceCaps (hdcDisplay, VERTRES); // совпадает с экраном

bmi.bmiHeader.biPlanes = 1;

bmi.bmiHeader.biBitCount =

GetDeviceCaps (hdcDisplay, BITSPIXEL) * GetDeviceCaps (hdcDisplay, PLANES);

bmi.bmiHeader.biCompression = BI_RGB;

bmi.bmiHeader.biSizeImage = 0L; // а это пусть GDI вычисляет

bmi.bmiHeader.biXPelsPerMeter =

GetDeviceCaps (hdcDisplay, HORZRES)*1000 / GetDeviceCaps (hdcDisplay, HORZSIZE);

bmi.bmiHeader.biYPelsPerMeter =

GetDeviceCaps (hdcDisplay, VERTRES)*1000 / GetDeviceCaps (hdcDisplay, VERTSIZE);

bmi.bmiHeader.biClrUsed = 0;

bmi.bmiHeader.biClrImportant = 0;

// обнулим палитру

ZeroMemory ( (LPVOID)bmi.bmiColors, sizeof (bmi.bmiColors));

// решим, будем-ли мы задавать маски цветов для режимов 16 и 32 bpp

// если задавать, то только стандартные 5-5-5, 5-6-5 или 8-8-8 и указать BI_BITFIELDS

// (даже на Windows NT 4.0 CreateDIBSection работала только со стандартными масками_

// можно и не задавать; битмапы 16 и 32 bpp можно создать и как BI_RGB

// В ДАННОМ ПРИМЕРЕ БУДЕМ ЗАДАВАТЬ РЕЖИМ BI_BITFIELDS

nFirstCol = 0;

switch (bmi.bmiHeader.biBitCount) {

case 16:

bmi.bmiColors[0].rgbGreen = 124; // red: 0x7C00

bmi.bmiColors[1].rgbGreen = 3; // green: 0x03E0

bmi.bmiColors[1].rgbBlue = 224;

bmi.bmiColors[2].rgbBlue = 31; // blue: 0x001F

bmi.bmiHeader.biCompression = BI_BITFIELDS;

nFirstCol = 3;

break;

case 32:

bmi.bmiColors[0].rgbRed = 255; // red: 0x00FF0000

bmi.bmiColors[1].rgbGreen = 255; // green: 0x0000FF00

bmi.bmiColors[2].rgbBlue = 255; // blue: 0x000000FF

bmi.bmiHeader.biCompression = BI_BITFIELDS;

nFirstCol = 3;

break;}

// проверим, нужно-ли назначать битмапу палитру?

nColors = GetDeviceCaps (hdcDisplay, SIZEPALETTE);

// для 16, 24 и 32 nColors будет равен 0

if (nColors) {

GetSystemPaletteEntries (hdcDisplay, 0, nColors, pe);

for (i =0; i < nColors; i++) {

bmi.bmiColors[ i + nFirstCol ].rgbRed = pe[i].peRed;

bmi.bmiColors[ i + nFirstCol ].rgbGreen = pe[i].peGreen;

bmi.bmiColors[ i + nFirstCol ].rgbBlue = pe[i].peBlue;}

bmi.bmiHeader.biClrUsed = nColors;}

// создаем секцию по полученному описанию

hbmpDibSection = CreateDIBSection (

hdcDisplay, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS, &lpData, (HANDLE)0L, 0);

// заполним секцию каким-либо изображением

if (hbmpDibSection) {

hdcMem = CreateCompatibleDC (hdcDisplay);

SelectObject (hdcMem, hbmpDibSection);

// собственно здесь и выполняется редактирование DIB-секции

BitBlt (

hdcMem, 0,0, bmi.bmiHeader.biWidth,bmi.bmiHeader.biHeight,

hdcDisplay, 0,0,

SRCCOPY);

DeleteDC (hdcMem);}

ReleaseDC ( (HWND)0L, hdcDisplay);

// hbmpDibSection оставляем для использования в дальнейшем

2) Сохранение DIB-секции в виде .bmp файла 1:

HBITMAP hbmpDibSection; // этот хендл мы получаем из предыдущего примера

BITMAPFILEHEADER bmfh; // заголовок файла битмапа

DIBSECTION ds; // информация о битмапе

HANDLE hf; // хендл файла в котором будет записан DIB

int nColors; // число цветов в палитре битмапа

RGBQUAD rgbs[ 256 ]; // палитра, заполняется если nColors != 0

HDC hdcDisplay;

HDC hdcMem;

DWORD dwWritten;

// получаем кое-какую информацию о записанном битмапе

GetObject (hbmpDibSection, sizeof (ds), &ds);

// определяем размер палитры

hdcDisplay = GetWindowDC ( (HWND)0L);

hdcMem = CreateCompatibleDC (hdcDisplay);

SelectObject (hdcMem, hbmpDibSection);

ReleaseDC ( (HWND)0L, hdcDisplay);

nColors = ds.dsBmih.biClrUsed ? ds.dsBmih.biClrUsed :

(ds.dsBmih.biBitCount <= 8 ? 1<<ds.dsBmih.biBitCount : 0);

if (nColors) {

// палитра присутствует

nColors = GetDIBColorTable (hdcMem, 0, nColors, rgbs);

ds.dsBmih.biClrUsed = nColors;}

DeleteDC (hdcMem);

// сохраняем в файле

hf = CreateFile (

"TestDIB.bmp", GENERIC_READ, 0,0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

if (hf != INVALID_HANDLE_VALUE) {

// заполняем и записываем заголовок файла

bmfh.bfType = 'MB';

bmfh.bfSize = bmfh.bfReserved1 = bmfh.bfReserved2 = 0L;

bmfh.bfOffBits =

sizeof (BITMAPFILEHEADER) +

ds.dsBmih.biSize +

(ds.dsBmih.biCompression == BI_BITFIELDS ? sizeof (ds.dsBitfields): 0) +

nColors * sizeof (RGBQUAD);

WriteFile (hf, (LPVOID)&bmfh, sizeof (bmfh), &dwWritten, (LPOVERLAPPED)0L);

// записываем полученный от GDI заголовок битмапа

WriteFile (hf, (LPVOID)&ds.dsBmih,ds.dsBmih.biSize,&dwWritten, (LPOVERLAPPED)0L);

// проверяем наличие масок цветов

if (ds.dsBmih.biCompression == BI_BITFIELDS) {

// пишем маски

WriteFile (

hf, (LPVOID) (ds.dsBitfields), sizeof (ds.dsBitfields),

&dwWritten, (LPOVERLAPPED)0L);}

// пишем палитру при ее наличии

if (nColors) {

// палитра присутствует

WriteFile (

hf, (LPVOID) (rgbs), sizeof (RGBQUAD) * nColors,

&dwWritten, (LPOVERLAPPED)0L);}

// записываем полученное от GDI изображение

// строго говоря, надо убедиться, что это не битмап OS/2 по ds.dsBmih.biSize

// и либо использовать ds.dsBmih.biSizeImage, либо вычислять самим, если это

// битмап OS/2

WriteFile (

hf, (LPVOID) (ds.dsBm.bmBits), ds.dsBmih.biSizeImage,

&dwWritten, (LPOVERLAPPED)0L);

// вместо ds.dsBm.bmBits можно воспользоваться lpData из предыдущего примера

CloseHandle (hf);}

// считаем, что больше DIB-секция нам не нужна

DeleteObject (hbmpDibSection);

3) Загрузка DIB-файла с помощью DIB-секции 1:

HWND hwnd; // предположим, что мы будем отображать

// DIB в этом окне

HANDLE hfDib; // данные для проецирования DIB файла

HANDLE hmapDib;

BY_HANDLE_FILE_INFORMATION finfo;

LPBITMAPFILEHEADER lpbmfh;

HANDLE hmapTemp; // packed DIB

LPBITMAPINFO lpbmi;

HBITMAP hbmpDibSection;

HDC hdcMem;

HDC hdcDisplay;

LONG dwOffBits; // смещение до данных изображения

LPVOID lpData;

lpbmi = (LPBITMAPINFO)0L; // инициализируем указатель -- дальше проверим

// скопируем временное проецирование для "Packed DIB"

hfDib = CreateFile (

"TestDIB.bmp", GENERIC_READ, 0, 0,

OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

if (hfDib != INVALID_HANDLE_VALUE) {

hmapDib = CreateFileMapping (hfDib, 0, PAGE_READ|SEC_COMMIT, 0,0, 0);

lpbmfh = (LPBITMAPFILEHEADER)MapViewOfFile (hmapDib, FILE_MAP_READ, 0,0, 0);

// вычислим смещение данных изображения в "Packed DIB" для использования позже

dwOffBits = lpbmfh->bfOffBits - sizeof (BITMAPFILEHEADER);

// создадим временное проецирование

GetFileInformationByHandle (hfDib, &finfo);

finfo.nFileSizeLow -= sizeof (BITMAPFILEHEADER);

hmapTemp = CreateFileMapping (

INVALID_HANDLE_VALUE, 0, PAGE_READWRITE|SEC_COMMIT, 0,finfo.nFileSizeLow, 0);

lpbmi = (LPBITMAPINFO)MapViewOfFile (hmapTemp, FILE_MAP_WRITE, 0,0, 0);

if (lpbmi && lpbmfh) {

// собственно копирование файла...

CopyMemory (

(LPVOID)lpbmi,

(LPVOID) (lpbmfh + 1),

finfo.nFileSizeLow);}

UnmapViewOfFile ( (LPVOID)pbmfh);

CloseHandle (hmapDib);

CloseHandle (hfDib);}

// если проецирование создано и файл скопирован, то отобразим его в окне

if (lpbmi) {

hdcDisplay = GetWindowDC (hwnd);

hbmpDibSection = CreateDIBSection (

hdcDisplay, lpbmi, DIB_RGB_COLORS, &lpData, hmap, dwOffBits);

hdcMem = CreateCompatibleDC (hdcDisplay);

SelectObject (hdcMem, hbmpDibSection);

BitBlt (

hdcDisplay, 0,0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight,

hdcMem, 0,0,

SRCCOPY);

ReleaseDC (hwnd, hdcDisplay);

DeleteDC (hdcMem);

DeleteObject (hbmpDibSection);

UnmapViewOfFile ( (LPVOID)lpbmi);

CloseHandle (hmapTemp);}

Метафайлы

Сохранять изображения можно не только в виде битмапов. Альтернативный, причем достаточно интересный, метод представлен с помощью так называемых метафайлов (metafile).

Когда мы рассматривали способы получения контекста устройства, то отмечали, что один из перечисленных методов предназначен для создания метафайлов. Это довольно специфичное графическое устройство, которому не соответствует ровным счетом никакого реального устройства. Более того, при работе с метафайлом не создается никакого изображения.


Подобные документы

  • Разработка графического редактора для рисования двухмерной и трехмерной графики, используя язык программирования Java и интерфейсы прикладного программирования Java 2D и Java 3D. Создание графического редактора 3D Paint. Основные методы класса Graphics.

    курсовая работа [197,5 K], добавлен 19.11.2009

  • Сущность, задачи и особенности объектно-ориентированного программирования. Создание и редактирование графических файлов при помощи различных инструментов рисования. Основные требования к аппаратному и программному обеспечению. Руководство пользователя.

    курсовая работа [270,9 K], добавлен 09.03.2009

  • Структура организации графического интерфейса, объявление и создание слушателей событий с помощью анонимных классов. Представление данных для таблицы – класс AbstractTableModel. Визуализация ячеек таблицы. Два основных типа потоков ввода-вывода в Java.

    лекция [685,3 K], добавлен 01.05.2014

  • Разработка графического интерфейса для ввода начальных значений, отображения результатов и тестирования методов собственного класса на языке программирования С++. Подсветка цветом выбранных операндов в процессе их инициализации и вывода на дисплей.

    курсовая работа [234,6 K], добавлен 27.12.2014

  • Изучение особенностей растровых и векторных графических редакторов. Создание графического редактора: выбор языка программирования, разработка структуры программы и алгоритма работы. Описание интерфейса программы. Руководство программиста и пользователя.

    курсовая работа [1,3 M], добавлен 28.07.2013

  • Растровая и векторная графика. Растровые графические редакторы. Масштабирование растрового изображения. Средства хранения высокоточных графических объектов. Изменение масштаба без потери качества и практически без увеличения размеров исходного файла.

    презентация [652,8 K], добавлен 11.03.2015

  • Растровая графика, составление графических изображений из отдельных точек (пикселей). Растровые графические редакторы. Векторная графика - построение изображения из простых объектов. Достоинства, недостатки и применение растровой и векторной графики.

    презентация [7,8 K], добавлен 06.01.2014

  • Определение понятия "видеокарта". Рассмотрение режима работы устройства по преобразованию цифрового сигнала в аналоговый электрический. Изучение особенностей изображения графических и текстовых знаков. Описание графического ускорителя и акселератора.

    презентация [1,2 M], добавлен 20.12.2015

  • Методы вывода графических примитивов в программе Delphi. Основные методы, объявленные в классе TCanvas. Использование объектов Brush, Pen, Front. Примеры применения функции Round Rect. Отличия способов рисования прямоугольника Polyline и Polygon.

    курсовая работа [834,1 K], добавлен 17.09.2014

  • Создание с помощью графического редактора логотипа и баннера для образовательного сайта "Областные математические олимпиады". Типы логотипов, баннер как графический элемент страницы сайта. Обзор инструментов графического редактора Adobe Illustrator.

    курсовая работа [2,9 M], добавлен 08.02.2014

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.