Win32 API. Урок 25. Простой битмэп

Дата публикации 25 май 2002

Win32 API. Урок 25. Простой битмэп — Архив WASM.RU

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

ТЕОРИЯ

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

Код (Text):
  1.  
  2.        #define IDB_MYBITMAP   100
  3.        IDB_MYBITMAP  BITMAP  "c:\project\example.bmp"

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

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

Код (Text):
  1.  
  2.        MyBitMap  BITMAP "c:\project\example.bmp"

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

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

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

      Код (Text):
      1.  
      2.            LoadBitmap proto hInstance:HINSTANCE, lpBitmapName:LPSTR
    • Функция возвращает хэндл битмэпа. hInstance есть хэндл инстанции вашей программы. lpBitmapName - это указатель на строку, содержащую имя битмэпа (необходимо при использовании 2-го метода). Если для обращения к битмэпу вы используете константу (например, IDB_MYBITMAP), то её значение вы и должны сюда поместить (в нашем случае это было бы значение 100). Не помешает небольшой пример:
      • Первый метод:

        Код (Text):
        1.  
        2.            .386
        3.            .model flat, stdcall
        4.            ................
        5.            .const
        6.  
        7.            IDB_MYBITMAP    equ 100
        8.            ...............
        9.            .data?
        10.            hInstance  dd ?
        11.  
        12.            ..............
        13.            .code
        14.            .............
        15.                invoke GetModuleHandle,NULL
        16.  
        17.                mov hInstance,eax
        18.            ............
        19.                invoke LoadBitmap,hInstance,IDB_MYBITMAP
        20.            ...........
      • Второй метод:

        Код (Text):
        1.  
        2.            .386
        3.            .model flat, stdcall
        4.            ................
        5.  
        6.            .data
        7.            BitmapName  db "MyBitMap",0
        8.            ...............
        9.            .data?
        10.  
        11.            hInstance  dd ?
        12.            ..............
        13.            .code
        14.            .............
        15.  
        16.                invoke GetModuleHandle,NULL
        17.                mov hInstance,eax
        18.            ............
        19.                invoke LoadBitmap,hInstance,addr BitmapName
        20.  
        21.            ...........
    • Получите хэндл device context'a (DC). Это можно сделать либо вызовом функции BeginPaint в ответ на сообщение WM_PAINT, либо вызовом GetDC в любое время.

      Создайте device context в памяти (memory DC) с теми же аттрибутами, что и device context, полученный на предыдущем шаге. Идея в том, чтобы создать некоторую "невидимую" поверхность, на которой мы можем отрисовать битмэп. После этого мы просто копируем содержимое невидимой поверхности в текущий device context с помощью вызова одной-единственной функции. Этот приём называется двойной буферизацией (double buffering) и используется для быстрого вывода изображений на экран. Создать "невидимую" поверхность можно вызовом CreateCompatibleDC:

      Код (Text):
      1.  
      2.            CreateCompatibleDC  proto  hdc:HDC

      При успешном завершении функция возвращает через регистр eax хэндл device context'a в памяти. hdc - это хэндл device context'a, с которым должен быть совместим DC в памяти.

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

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

      Код (Text):
      1.  
      2.            BitBlt  proto  hdcDest:DWORD, nxDest:DWORD, nyDest:DWORD, \
      3.            nWidth:DWORD, nHeight:DWORD, hdcSrc:DWORD, nxSrc:DWORD, \
      4.            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):
    1.  
    2.    .386
    3.    .model flat,stdcall
    4.    option casemap:none
    5.    include \masm32\include\windows.inc
    6.    include \masm32\include\user32.inc
    7.    include \masm32\include\kernel32.inc
    8.    include \masm32\include\gdi32.inc
    9.    includelib \masm32\lib\user32.lib
    10.    includelib \masm32\lib\kernel32.lib
    11.    includelib \masm32\lib\gdi32.lib
    12.  
    13.    WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
    14.    IDB_MAIN   equ 1
    15.  
    16.  
    17.    .data
    18.    ClassName db "SimpleWin32ASMBitmapClass",0
    19.    AppName  db "Win32ASM Simple Bitmap Example",0
    20.  
    21.    .data?
    22.    hInstance HINSTANCE ?
    23.    CommandLine LPSTR ?
    24.    hBitmap dd ?
    25.  
    26.  
    27.    .code
    28.    start:
    29.     invoke GetModuleHandle, NULL
    30.     mov    hInstance,eax
    31.     invoke GetCommandLine
    32.     mov    CommandLine,eax
    33.     invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
    34.     invoke ExitProcess,eax
    35.  
    36.    WinMain proc
    37.    hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
    38.     LOCAL wc:WNDCLASSEX
    39.     LOCAL msg:MSG
    40.     LOCAL hwnd:HWND
    41.     mov   wc.cbSize,SIZEOF WNDCLASSEX
    42.     mov   wc.style, CS_HREDRAW or CS_VREDRAW
    43.     mov   wc.lpfnWndProc, OFFSET WndProc
    44.     mov   wc.cbClsExtra,NULL
    45.     mov   wc.cbWndExtra,NULL
    46.     push  hInstance
    47.     pop   wc.hInstance
    48.     mov   wc.hbrBackground,COLOR_WINDOW+1
    49.     mov   wc.lpszMenuName,NULL
    50.     mov   wc.lpszClassName,OFFSET ClassName
    51.     invoke LoadIcon,NULL,IDI_APPLICATION
    52.     mov   wc.hIcon,eax
    53.     mov   wc.hIconSm,eax
    54.     invoke LoadCursor,NULL,IDC_ARROW
    55.     mov   wc.hCursor,eax
    56.     invoke RegisterClassEx, addr wc
    57.     INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
    58.               WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
    59.               CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
    60.               hInst,NULL
    61.     mov   hwnd,eax
    62.     invoke ShowWindow, hwnd,SW_SHOWNORMAL
    63.     invoke UpdateWindow, hwnd
    64.     .while TRUE
    65.      invoke GetMessage, ADDR msg,NULL,0,0
    66.      .break .if (!eax)
    67.      invoke TranslateMessage, ADDR msg
    68.      invoke DispatchMessage, ADDR msg
    69.     .endw
    70.     mov     eax,msg.wParam
    71.     ret
    72.    WinMain endp
    73.  
    74.  
    75.    WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    76.       LOCAL ps:PAINTSTRUCT
    77.       LOCAL hdc:HDC
    78.       LOCAL hMemDC:HDC
    79.       LOCAL rect:RECT
    80.       .if uMsg==WM_CREATE
    81.          invoke LoadBitmap,hInstance,IDB_MAIN
    82.          mov hBitmap,eax
    83.       .elseif uMsg==WM_PAINT
    84.          invoke BeginPaint,hWnd,addr ps
    85.          mov    hdc,eax
    86.          invoke CreateCompatibleDC,hdc
    87.          mov    hMemDC,eax
    88.          invoke SelectObject,hMemDC,hBitmap
    89.          invoke GetClientRect,hWnd,addr rect
    90.          invoke BitBlt,hdc,0,0,rect.right,rect.bottom,hMemDC,0,0,SRCCOPY
    91.          invoke DeleteDC,hMemDC
    92.          invoke EndPaint,hWnd,addr ps
    93.     .elseif uMsg==WM_DESTROY
    94.      invoke DeleteObject,hBitmap
    95.      invoke PostQuitMessage,NULL
    96.     .ELSE
    97.      invoke DefWindowProc,hWnd,uMsg,wParam,lParam
    98.      ret
    99.     .ENDIF
    100.     xor eax,eax
    101.     ret
    102.    WndProc endp
    103.    end start
    104.  
    105.    ;---------------------------------------------------------------------
    106.    ;                            Файл ресурсов
    107.    ;---------------------------------------------------------------------
    108.    #define IDB_MAIN 1
    109.    IDB_MAIN BITMAP "tweety78.bmp"

    АНАЛИЗ

    Собственно, на этом уроке и анализировать нечего :smile3:

    Код (Text):
    1.  
    2.        #define IDB_MAIN 1
    3.        IDB_MAIN BITMAP "tweety78.bmp"

    Определите константу IDB_MAIN, присвоив ей значение 1. Затем используйте эту константу при определении битмэп-ресурса. Файл, который будет включен в ресурсы, называется "tweety78.bmp" и располагается в той же папке, что и файл ресурсов.

    Код (Text):
    1.  
    2.       .if uMsg==WM_CREATE
    3.          invoke LoadBitmap,hInstance,IDB_MAIN
    4.          mov hBitmap,eax

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

    Код (Text):
    1.  
    2.       .elseif uMsg==WM_PAINT
    3.          invoke BeginPaint,hWnd,addr ps
    4.          mov    hdc,eax
    5.          invoke CreateCompatibleDC,hdc
    6.          mov    hMemDC,eax
    7.          invoke SelectObject,hMemDC,hBitmap
    8.          invoke GetClientRect,hWnd,addr rect
    9.          invoke BitBlt,hdc,0,0,rect.right,rect.bottom,hMemDC,0,0,SRCCOPY
    10.          invoke DeleteDC,hMemDC
    11.          invoke EndPaint,hWnd,addr ps

    Мы решили отрисовывать битмэп в ответ на сообщение WM_PAINT. Для этого мы сначала вызываем BeginPaint и получаем хэндл DC. Затем создаем совместимый memory DC вызовом CreateCompatibleDC. Далее "выбираем" битмэп в память с помощью SelectObject. Определяем размеры клиентской области окна через GetClientRect. Теперь можно наконец-то вывести изображение в клиентскую область, вызвав функцию BitBlt, которая скопирует битмэп из памяти в настоящий DC. По завершению рисования мы удаляем DC в памяти вызовом DeleteDC, так как он нам больше не нужен. Подаём сигнал о завершении отрисовки окна с помощью EndPaint.

    Код (Text):
    1.  
    2.         .elseif uMsg==WM_DESTROY
    3.          invoke DeleteObject,hBitmap
    4.          invoke PostQuitMessage,NULL

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


    0 2.289
    archive

    archive
    New Member

    Регистрация:
    27 фев 2017
    Публикаций:
    532