Win32 API. Урок 25. Простой битмэп — Архив WASM.RU
На этом уроке мы научимся использовать битмэпы в своих программах. Если быть более точным, мы научимся отображать битмэп в клиентской области нашей программы. Скачайте пример здесь.
ТЕОРИЯ
Битмэпы можно представлять себе как изображения, хранимые в компьютере. На компьютерах используется множество различных форматов изображений, но Windows естественным образом поддерживает только формат растровых изображений Windows (.bmp). На этом уроке, когда речь будет идти о битмэпах, будет подразумеваться именно этот формат. Самый простой способ использовать битмэп - это использовать его как ресурс. Есть два способа это выполнить. Можно включить битмэп в файл определения ресурсов (.rc) следующим образом:
Код (Text):
#define IDB_MYBITMAP 100 IDB_MYBITMAP BITMAP "c:\project\example.bmp"В этом методе для представления битмэпа используется константа. В первой строчке просто задаётся константа с именем IDB_MYBITMAP и значением 100. По этому имени мы и будем обращаться к битмэпу в нашей программе. В следующей строке объявляется битмэп-ресурс. Таким образом, компилятор узнаёт, где ему искать собственно сам .bmp файл.
В другом методе для представления битмэпа используется имя, делается это следующим образом:
Код (Text):
MyBitMap BITMAP "c:\project\example.bmp"При использовании этого метода, в вашей программе вам придётся ссылаться на битмэп по строке "MyBitMap", а не по значению.
Оба метода прекрасно работают, главное - определиться, какой именно вы будете использовать. После включения битмэпа в файл ресурсов, можно приступить к его отображению в клиентской области нашего окна:
- Вызовите LoadBitmap чтобы узнать хэндл битмэпа. Функция LoadBitmap имеет следующий прототип:
Код (Text):
LoadBitmap proto hInstance:HINSTANCE, lpBitmapName:LPSTR- Функция возвращает хэндл битмэпа. hInstance есть хэндл инстанции вашей программы. lpBitmapName - это указатель на строку, содержащую имя битмэпа (необходимо при использовании 2-го метода). Если для обращения к битмэпу вы используете константу (например, IDB_MYBITMAP), то её значение вы и должны сюда поместить (в нашем случае это было бы значение 100). Не помешает небольшой пример:
- Первый метод:
Код (Text):
.386 .model flat, stdcall ................ .const IDB_MYBITMAP equ 100 ............... .data? hInstance dd ? .............. .code ............. invoke GetModuleHandle,NULL mov hInstance,eax ............ invoke LoadBitmap,hInstance,IDB_MYBITMAP ...........- Второй метод:
Код (Text):
.386 .model flat, stdcall ................ .data BitmapName db "MyBitMap",0 ............... .data? hInstance dd ? .............. .code ............. invoke GetModuleHandle,NULL mov hInstance,eax ............ invoke LoadBitmap,hInstance,addr BitmapName ...........- Получите хэндл device context'a (DC). Это можно сделать либо вызовом функции BeginPaint в ответ на сообщение WM_PAINT, либо вызовом GetDC в любое время.
Создайте device context в памяти (memory DC) с теми же аттрибутами, что и device context, полученный на предыдущем шаге. Идея в том, чтобы создать некоторую "невидимую" поверхность, на которой мы можем отрисовать битмэп. После этого мы просто копируем содержимое невидимой поверхности в текущий device context с помощью вызова одной-единственной функции. Этот приём называется двойной буферизацией (double buffering) и используется для быстрого вывода изображений на экран. Создать "невидимую" поверхность можно вызовом CreateCompatibleDC:
Код (Text):
CreateCompatibleDC proto hdc:HDCПри успешном завершении функция возвращает через регистр eax хэндл device context'a в памяти. hdc - это хэндл device context'a, с которым должен быть совместим DC в памяти.
- После создания невидимой поверхности вы можете отобразить на ней битмэп с помощью вызова SelectObject, передав ей в качестве первого параметра хэндл DC в памяти, а в качестве второго - хэндл битмэпа. Прототип этой функции следующий:
Код (Text):
SelectObject proto hdc:HDC, hGdiObject:DWORD- Теперь битмэп отображен на device context'e в памяти. Единственное, что осталось сделать - это скопировать его на на устройство вывода, то есть на настоящий device context. Этого можно добиться с помощью таких функций, как BitBlt и StretchBlt. BitBlt просто копирует содержимое одного DC в другой, поэтому она работает быстро; StretchBlt может сжимать или растягивать изображение по размерам того DC, куда копирует. Для простоты здесь мы будем использова BitBlt, имеющую следующий прототип:
Код (Text):
BitBlt proto hdcDest:DWORD, nxDest:DWORD, nyDest:DWORD, \ nWidth:DWORD, nHeight:DWORD, hdcSrc:DWORD, nxSrc:DWORD, \ nySrc:DWORD, dwROP:DWORD \
- hdcDest это хэндл device context'a, в который копируется битмэп
- nxDest, nyDest это координаты верхнего левого угла области вывода
- nWidth, nHeight это ширина и высота области вывода
- hdcSrc это хэндл device context'a, из которого копируется битмэп
- nxSrc, nySrc это координаты левого верхнего угла исходной области
- dwROP это код растровой операции (raster-operation code, ROP), указывающий, как совмещать пиксели исходного и конечного изображений. Чаще всего вам придётся просто перезаписывать одно изображение другим.
- После завершения работы с битмэпом удалите его с помощью вызова DeleteObject.
Вот и всё! Если коротко, то сначала добавьте битмэп в файл ресурсов. После этого загрузите его из ресурсов с помощью LoadBitmap. Вы получите хэндл битмэпа. Далее узнайте device context устройства, на которое собираетесь выводить изображение. Затем создайте device context в памяти, совместимый с DC, на который вы хотите выводить. "Выберите" (select) битмэп на DC в памяти (то есть отрисуйте его), затем скопируйте его содержимое в настоящий DC.
ПРИМЕР
Код (Text):
.386 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\gdi32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\gdi32.lib WinMain proto :DWORD,:DWORD,:DWORD,:DWORD IDB_MAIN equ 1 .data ClassName db "SimpleWin32ASMBitmapClass",0 AppName db "Win32ASM Simple Bitmap Example",0 .data? hInstance HINSTANCE ? CommandLine LPSTR ? hBitmap dd ? .code start: invoke GetModuleHandle, NULL mov hInstance,eax invoke GetCommandLine mov CommandLine,eax invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT invoke ExitProcess,eax WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD LOCAL wc:WNDCLASSEX LOCAL msg:MSG LOCAL hwnd:HWND mov wc.cbSize,SIZEOF WNDCLASSEX mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInstance pop wc.hInstance mov wc.hbrBackground,COLOR_WINDOW+1 mov wc.lpszMenuName,NULL mov wc.lpszClassName,OFFSET ClassName invoke LoadIcon,NULL,IDI_APPLICATION mov wc.hIcon,eax mov wc.hIconSm,eax invoke LoadCursor,NULL,IDC_ARROW mov wc.hCursor,eax invoke RegisterClassEx, addr wc INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\ WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\ CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\ hInst,NULL mov hwnd,eax invoke ShowWindow, hwnd,SW_SHOWNORMAL invoke UpdateWindow, hwnd .while TRUE invoke GetMessage, ADDR msg,NULL,0,0 .break .if (!eax) invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg .endw mov eax,msg.wParam ret WinMain endp WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM LOCAL ps:PAINTSTRUCT LOCAL hdc:HDC LOCAL hMemDC:HDC LOCAL rect:RECT .if uMsg==WM_CREATE invoke LoadBitmap,hInstance,IDB_MAIN mov hBitmap,eax .elseif uMsg==WM_PAINT invoke BeginPaint,hWnd,addr ps mov hdc,eax invoke CreateCompatibleDC,hdc mov hMemDC,eax invoke SelectObject,hMemDC,hBitmap invoke GetClientRect,hWnd,addr rect invoke BitBlt,hdc,0,0,rect.right,rect.bottom,hMemDC,0,0,SRCCOPY invoke DeleteDC,hMemDC invoke EndPaint,hWnd,addr ps .elseif uMsg==WM_DESTROY invoke DeleteObject,hBitmap invoke PostQuitMessage,NULL .ELSE invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret .ENDIF xor eax,eax ret WndProc endp end start ;--------------------------------------------------------------------- ; Файл ресурсов ;--------------------------------------------------------------------- #define IDB_MAIN 1 IDB_MAIN BITMAP "tweety78.bmp"АНАЛИЗ
Собственно, на этом уроке и анализировать нечего
Код (Text):
#define IDB_MAIN 1 IDB_MAIN BITMAP "tweety78.bmp"Определите константу IDB_MAIN, присвоив ей значение 1. Затем используйте эту константу при определении битмэп-ресурса. Файл, который будет включен в ресурсы, называется "tweety78.bmp" и располагается в той же папке, что и файл ресурсов.
Код (Text):
.if uMsg==WM_CREATE invoke LoadBitmap,hInstance,IDB_MAIN mov hBitmap,eaxПосле получения сообщения WM_CREATE мы вызываем LoadBitmap для загрузки битмэпа из файла ресурсов, передавая идентификатор битмэпа в качестве второго параметра. По завершению работы функции мы получим хэндл битмэпа. Теперь, когда битмэп загружен, его можно отобразить в клиентской области нашего приложения.
Код (Text):
.elseif uMsg==WM_PAINT invoke BeginPaint,hWnd,addr ps mov hdc,eax invoke CreateCompatibleDC,hdc mov hMemDC,eax invoke SelectObject,hMemDC,hBitmap invoke GetClientRect,hWnd,addr rect invoke BitBlt,hdc,0,0,rect.right,rect.bottom,hMemDC,0,0,SRCCOPY invoke DeleteDC,hMemDC invoke EndPaint,hWnd,addr psМы решили отрисовывать битмэп в ответ на сообщение WM_PAINT. Для этого мы сначала вызываем BeginPaint и получаем хэндл DC. Затем создаем совместимый memory DC вызовом CreateCompatibleDC. Далее "выбираем" битмэп в память с помощью SelectObject. Определяем размеры клиентской области окна через GetClientRect. Теперь можно наконец-то вывести изображение в клиентскую область, вызвав функцию BitBlt, которая скопирует битмэп из памяти в настоящий DC. По завершению рисования мы удаляем DC в памяти вызовом DeleteDC, так как он нам больше не нужен. Подаём сигнал о завершении отрисовки окна с помощью EndPaint.
Код (Text):
.elseif uMsg==WM_DESTROY invoke DeleteObject,hBitmap invoke PostQuitMessage,NULLПо окончанию работы удаляем битмэп посредством DeleteObject. © Iczelion, пер. WD-40
Win32 API. Урок 25. Простой битмэп
Дата публикации 25 май 2002