allasm.ru

    Меню

 

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

ТЕОРИЯ

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

       #define IDB_MYBITMAP   100
       IDB_MYBITMAP  BITMAP  "c:\project\example.bmp"

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

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

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

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

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

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

                 LoadBitmap proto hInstance:HINSTANCE, lpBitmapName:LPSTR
    • Функция возвращает хэндл битмэпа. hInstance есть хэндл инстанции вашей программы. lpBitmapName - это указатель на строку, содержащую имя битмэпа (необходимо при использовании 2-го метода). Если для обращения к битмэпу вы используете константу (например, IDB_MYBITMAP), то её значение вы и должны сюда поместить (в нашем случае это было бы значение 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, куда копирует. Для простоты здесь мы будем использова 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 это координаты левого верхнего угла исходной области

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

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

       Вот и всё! Если коротко, то сначала добавьте битмэп в файл ресурсов. После этого загрузите его из ресурсов с помощью LoadBitmap. Вы получите хэндл битмэпа. Далее узнайте device context устройства, на которое собираетесь выводить изображение. Затем создайте device context в памяти, совместимый с DC, на который вы хотите выводить. "Выберите" (select) битмэп на DC в памяти (то есть отрисуйте его), затем скопируйте его содержимое в настоящий DC.

    ПРИМЕР

       .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.bmp" и располагается в той же папке, что и файл ресурсов.

          .if uMsg==WM_CREATE
             invoke LoadBitmap,hInstance,IDB_MAIN
             mov hBitmap,eax

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

          .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.

            .elseif uMsg==WM_DESTROY
             invoke DeleteObject,hBitmap
             invoke PostQuitMessage,NULL

    По окончанию работы удаляем битмэп посредством DeleteObject.