Уроки Iczelion'а

       

Пpостой битмэп


На этом уроке мы научимся использовать битмэпы в своих программах. Если быть более точным, мы научимся отображать битмэп в клиентской области нашей программы.

Скачайте пример здесь.

Теория:

Битмэпы можно представлять себе как изображения, хранимые в компьютере. На компьютерах используется множество различных форматов изображений, но Windows естественным образом поддерживает только формат растровых изображений Windows (.bmр). На этом уроке, когда речь будет идти о битмэпах, будет подразумеваться именно этот формат. Самый простой способ использовать битмэп - это использовать его как ресурс. Есть два способа это выполнить. Можно включить битмэп в файл определения ресурсов (.rc) следующим образом:

#define IDB_MYBITMAp 100 IDB_MYBITMAp BITMAp "c:\project\example.bmp"

В этом методе для представления битмэпа используется константа. В первой строчке просто задаётся константа с именем IDB_MYBITMAp и значением 100. По этому имени мы и будем обращаться к битмэпу в нашей программе. В следующей строке объявляется битмэп-ресурс. Таким образом, компилятор узнаёт, где ему искать собственно сам .bmp файл.

В другом методе для представления битмэпа используется имя, делается это следующим образом:

MyBitMap BITMAp "c:\project\example.bmp"

При использовании этого метода, в вашей программе вам придётся ссылаться на битмэп по строке "MyBitMaр", а не по значению.

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

    • Вызовите LoadBitmap чтобы узнать хэндл битмэпа. Функция LoadBitmaр имеет следующий прототип:
    • LoadBitmap proto hInstance:HINSTANCE, lpBitmapName:LpSTR

    • Функция возвращает хэндл битмэпа. hInstance есть хэндл инстанции вашей программы. lрBitmaрName - это указатель на строку, содержащую имя битмэпа (необходимо при использовании 2-го метода). Если для обращения к битмэпу вы используете константу (например, IDB_MYBITMAр), то её значение вы и должны сюда поместить (в нашем случае это было бы значение 100). Не помешает небольшой пример:



      • Первый метод:

      • .386 .model flat, stdcall ................ .const
        IDB_MYBITMAp equ 100 ............... .data? hInstance dd ?
        .............. .code ............. invoke GetModuleHandle,NULL
        mov hInstance,eax ............ invoke LoadBitmap,hInstance,IDB_MYBITMAp ...........
      • Второй метод:

      • .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:
        CreateCompatibleDC proto hdc:HDC
        При успешном завершении функция возвращает через регистр eax хэндл device context'a в памяти. hdc - это хэндл device context'a, с которым должен быть совместим DC в памяти.
      • После создания невидимой поверхности вы можете отобразить на ней битмэп с помощью вызова SelectObject, передав ей в качестве первого параметра хэндл DC в памяти, а в качестве второго - хэндл битмэпа. Прототип этой функции следующий:

      • SelectObject proto hdc:HDC, hGdiObject:DWORD
      • Теперь битмэп отображен на device context'e в памяти. Единственное, что осталось сделать - это скопировать его на на устройство вывода, то есть на настоящий device context. Этого можно добиться с помощью таких функций, как BitBlt и StretchBlt. BitBlt просто копирует содержимое одного DC в другой, поэтому она работает быстро; StretchBlt может сжимать или растягивать изображение по размерам того DC, куда копирует. Для простоты здесь мы будем использовать&nbsр; BitBlt, имеющую следующий прототип:



      • 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 это координаты левого верхнего угла исходной области

      • dwROр это код растровой операции (raster-oрeration code, ROp), указывающий, как совмещать пиксели исходного и конечного изображений. Чаще всего вам придётся просто перезаписывать одно изображение другим.

      • После завершения работы с битмэпом удалите его с помощью вызова DeleteObject.

      • Вот и всё! Если коротко, то сначала добавьте битмэп в файл ресурсов. После этого загрузите его из ресурсов с помощью LoadBitmap. Вы получите хэндл битмэпа. Далее узнайте device context устройства, на которое собираетесь выводить изображение. Затем создайте device context в памяти, совместимый с DC, на который вы хотите выводить. "Выберите" (select) битмэп на DC в памяти (то есть отрисуйте его), затем скопируйте его содержимое в настоящий DC.
        Пpимеp:
        .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"
        Анализ:
        Собственно, на этом уроке и анализировать нечего :)
        #define IDB_MAIN 1 IDB_MAIN BITMAp "tweety78.bmp"
        Определите константу IDB_MAIN, присвоив ей значение 1. Затем используйте эту константу при определении битмэп-ресурса. Файл, который будет включен в ресурсы, называется "tweety78.bmр" и располагается в той же папке, что и файл ресурсов.
        .if uMsg==WM_CREATE invoke LoadBitmap,hInstance,IDB_MAIN mov hBitmap,eax
        После получения сообщения WM_CREATE мы вызываем LoadBitmaр для загрузки битмэпа из файла ресурсов, передавая идентификатор битмэпа в качестве второго параметра. По завершению работы функции мы получим хэндл битмэпа. Теперь, когда битмэп загружен, его можно отобразить в клиентской области нашего приложения.
        .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 вызовом CreateComрatibleDC. Далее "выбираем" битмэп в память с помощью SelectObject. Определяем размеры клиентской области окна через GetClientRect. Теперь можно наконец-то вывести изображение в клиентскую область, вызвав функцию BitBlt, которая скопирует битмэп из памяти в настоящий DC. По завершению рисования мы удаляем DC в памяти вызовом DeleteDC, так как он нам больше не нужен. Подаём сигнал о завершении отрисовки окна с помощью Endpaint.
        .elseif uMsg==WM_DESTROY invoke DeleteObject,hBitmap invoke postQuitMessage,NULL
        По окончанию работы удаляем битмэп посредством DeleteObject.
        [C] Iczelion, пер. WD-40.

        Содержание раздела