Смекни!
smekni.com

Основы графического вывода (стр. 23 из 26)

в созданном окне будет отображаться битмап, находящийся в файле с именем 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)

{_DIBdib;

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

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) Для WindowsAPI удобно использование так называемого DIB драйвера. В том виде, в каком было формально описано применение DIB драйвера, этот путь практически ничего не дает — слишком много ограничений на использование этого драйвера и на использование получаемого контекста устройства.

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

DIB драйвер (WindowsAPI)

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

По–видимому это подтолкнуло разработчиков Microsoft включить в состав распространяемых с компилятором компонент специальный DIB–драйвер (DIB.DRV)[11], который позволяет модифицировать существующий 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, определяется вами и не может изменяться в процессе рисования на контексте.

DIBcекция (Win32 API)