API и FreeBasic. (П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 (hInstance As HINSTANCE, l?Bitma?Name As LPCSTR) As HBITMAP

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

      • Второй метод:
        hInstance = GetModuleHandle(NULL)
        
        ....
        
        hBitmap = LoadBitmap(hInstance,Strptr("MyBitMap"))

    • Получите хэндл device context'a (DC). Это можно сделать либо вызовом функции Beginpaint в ответ на сообщение WM_pAINT, либо вызовом GetDC в любое время.

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

      CreateCompatibleDC(hdc As HDC) As HDC

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

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

    • Теперь битмэп отображен на device context'e в памяти. Единственное, что осталось сделать - это скопировать его на на устройство вывода, то есть на настоящий device context. Этого можно добиться с помощью таких функций, как BitBlt и StretchBlt. BitBlt просто копирует содержимое одного DC в другой, поэтому она работает быстро; StretchBlt может сжимать или растягивать изображение по размерам того DC, куда копирует. Для простоты здесь мы будем использовать&nbsр; BitBlt, имеющую следующий прототип:
      BitBlt (HDC As HDC, 
              nXDest As Integer, 
              nYDest As Integer, 
              nWidth As Integer, 
              nHeight As Integer, 
              hdcSrc As HDC, 
              nXSrc As Integer, 
              nYSrc As Integer, 
              dwRop As DWORD) As BOOL

      • 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:

    #INCLUDE "windows.bi"
    
    Declare Function WinMain(hInst As HINSTANCE , hPrevInst As HINSTANCE,CmdLine As LPSTR,CmdShow As DWORD) As Integer
    Declare Function WndProc(hWnd As HWND, uMsg As UINT, wParam As WPARAM, lParam As LPARAM) As Integer
    
    #DEFINE IDB_MAIN 1
    
    Dim Shared As String ClassName,AppName
    ClassName =  "SimpleWin32ASMBitmapClass"
    AppName = "Win32ASM Simple Bitmap Example"
    
    
    Dim Shared hInstance As HINSTANCE
    Dim Shared CommandLine As LPSTR
    Dim Shared hBitmap As HBITMAP
    
    hInstance = GetModuleHandle(NULL)
    CommandLine = GetCommandLine()
    ExitProcess(WinMain(hInstance,NULL,CommandLine, SW_SHOWDEFAULT))
    
    Function WinMain(hInst As HINSTANCE , hPrevInst As HINSTANCE,CmdLine As LPSTR,CmdShow As DWORD) As Integer
    
        Dim wc As WNDCLASSEX
        Dim msg As MSG
        Dim hwnd As HWND
    
        wc.cbSize = Sizeof(WNDCLASSEX)
        wc.style = CS_HREDRAW Or CS_VREDRAW
        wc.lpfnWndProc = @WndProc
        wc.hInstance = hInstance
        wc.hbrBackground = Cast(HBRUSH, COLOR_WINDOW+1)
        wc.lpszClassName = Strptr(ClassName)
        wc.hIcon = LoadIcon(NULL,IDI_APPLICATION)
        wc.hIconSm  = wc.hIcon
        wc.hCursor = LoadCursor(NULL,IDC_ARROW)
        RegisterClassEx(@wc)
        hwnd = CreateWindowEx(NULL,ClassName,AppName, _
        WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, _
        CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL, _
        hInst,NULL)
        ShowWindow(hwnd,SW_SHOWNORMAL)
        UpdateWindow(hwnd)
        While GetMessage(@msg,NULL,0,0)
            TranslateMessage(@msg)
            DispatchMessage(@msg)
        Wend
        Return msg.wParam
    End Function
    
    Function WndProc(hWnd As HWND, uMsg As UINT, wParam As WPARAM, lParam As LPARAM) As Integer
        Dim ps As PAINTSTRUCT
        Dim hdc As HDC
        Dim hMemDC As HDC
        Dim rect As RECT
        If uMsg=WM_CREATE Then
            hBitmap = LoadBitmap(hInstance, Cast(lpstr, IDB_MAIN))
        Elseif uMsg=WM_PAINT Then
            hdc = BeginPaint(hWnd,@ps)
            hMemDC =  CreateCompatibleDC(hdc)
            SelectObject(hMemDC,hBitmap)
            GetClientRect(hWnd,@rect)
            BitBlt(hdc,0,0,rect.right,rect.bottom,hMemDC,0,0,SRCCOPY)
            DeleteDC(hMemDC)
            EndPaint(hWnd,@ps)
        Elseif uMsg=WM_DESTROY Then
            DeleteObject(hBitmap)
            PostQuitMessage(NULL)
        Else
            Return DefWindowProc(hWnd,uMsg,wParam,lParam)
        Endif
        Return 0
    End Function
    'Файл ресурсов: '#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 Then

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

    Elseif uMsg=WM_PAINT Then
        hdc = BeginPaint(hWnd,@ps)
        hMemDC =  CreateCompatibleDC(hdc)
        SelectObject(hMemDC,hBitmap)
        GetClientRect(hWnd,@rect)
        BitBlt(hdc,0,0,rect.right,rect.bottom,hMemDC,0,0,SRCCOPY)
        DeleteDC(hMemDC)
        EndPaint(hWnd,@ps)

    hBitmap = LoadBitmap(hInstance, Cast(lpstr, IDB_MAIN))

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

    Elseif uMsg=WM_DESTROY Then
        DeleteObject(hBitmap)
        PostQuitMessage(NULL)

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

    Перевод на русский с оригинала: WD-40, адаптация материалов под FreeBasic: Станислав Будинов

    содержание | назад | вперед