# Tutorial 25: Simple Bitmap

In this tutorial, we will learn how to use bitmap in our program. To be exact, we will learn how to display a bitmap in the client area of our window. Download the example.

### Theory

Bitmaps can be thought of as pictures stored in computer. There are many picture formats used with computers but Windows only natively supports Windows Bitmap Graphics files (.bmp). The bitmaps I'll refer to in this tutorial are Windows bitmap graphics files. The easiest way to use a bitmap is to use it as a resource. There are two ways to do that. You can include the bitmap in the resource definition file (.rc) as follows:

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

This method uses a constant to represent the bitmap. The first line just creates a constant named IDB_MYBITMAP which has the value of 100. We will use this label to refer to the bitmap in the program. The next line declares a bitmap resource. It tells the resource compiler where to find the actual bmp file.
The other method uses a name to represent the bitmap as follows:

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

This method requires that you refer to the bitmap in your program by the string "MyBitMap" instead of a value.
Either method works fine as long as you know which method you're using.
Now that we put the bitmap in the resource file, we can go on with the steps in displaying it in the client area of our window.

1. call LoadBitmap to get the bitmap handle. LoadBitmap has the following definition:

LoadBitmap proto hInstance:HINSTANCE, lpBitmapName:LPSTR

This function returns a bitmap handle. hInstance is the instance handle of our program. lpBitmapName is a pointer to the string that is the name of the bitmap (incase you use the second method to refer to the bitmap). If you use a constant to refer to the bitmap (like IDB_MYBITMAP), you can put its value here. (In the example above it would be 100). A short example is in order:

First Method:

.386
.model flat, stdcall
................
.const
IDB_MYBITMAP equ 100
...............
.data?
hInstance dd ?
..............
.code
.............
invoke GetModuleHandle,NULL
mov hInstance,eax
............
invoke LoadBitmap,hInstance,IDB_MYBITMAP
...........

Second Method:

.386
.model flat, stdcall
................
.data
BitmapName db "MyBitMap",0
...............
.data?
hInstance dd ?
..............
.code
.............
invoke GetModuleHandle,NULL
mov hInstance,eax
............
invoke LoadBitmap,hInstance,addr BitmapName
...........

1. Obtain a handle to device context (DC). You can obtain this handle by calling BeginPaint in response to WM_PAINT message or by calling GetDC anywhere.
2. Create a memory device context which has the same attribute as the device context we just obtained. The idea here is to create a kind of "hidden" drawing surface which we can draw the bitmap on. When we are finished with the operation, we just copy the content of the hidden drawing surface to the actual device context in one function call. It's an example of double-buffer technique used for fast display of pictures on the screen. You can create this "hidden" drawing surface by calling CreateCompatibleDC.

CreateCompatibleDC proto hdc:HDC

If this function succeeds, it returns the handle of the memory device context in eax. hdc is the handle to the device context that you want the memory DC to be compatible with.

1. Now that you got a hidden drawing surface, you can draw on it by selecting the bitmap into it. This is done by calling SelectObject with the handle to the memory DC as the first parameter and the bitmap handle as the second parameter. SelectObject has the following definition:

SelectObject proto hdc:HDC, hGdiObject:DWORD

1. The bitmap is drawn on the memory device context now. All we need to do here is to copy it to the actual display device, namely the true device context. There are several functions that can perform this operation such as BitBlt and StretchBlt. BitBlt just copies the content of one DC to another so it's fast while StretchBlt can stretch or compress the bitmap to fit the output area. We will use BitBlt here for simplicity. BitBlt has the following definition:

BitBlt proto hdcDest:DWORD, nxDest:DWORD, nyDest:DWORD, nWidth:DWORD, nHeight:DWORD, hdcSrc:DWORD, nxSrc:DWORD, nySrc:DWORD, dwROP:DWORD

hdcDest is the handle of the device context that serves as the destination of bitmap transfer operation
nxDest, nyDest are the coordinate of the upper left corner of the output area
nWidth, nHeight are the width and height of the output area
hdcSrc is the handle of the device context that serves as the source of bitmap transfer operation
nxSrc, nySrc are the coordinate of the upper left corner of the source rectangle.
dwROP is the raster-operation code (hence the acronym ROP) that governs how to combine the color data of the bitmap to the existing color data on the output area to achieve the final result. Most of the time, you only want to overwrite the existing color data with the new one.
1. When you're done with the bitmap, delete it with DeleteObject API call.

That's it! To recapitulate, you need to put the bitmap into the resource scipt. Then load it from the resource with LoadBitmap. You'll get the bitmap handle. Next you obtain the handle to the device context of the area you want to paint the bitmap on. Then you create a memory device context that is compatible with the device context you just obtained. Select the bitmap into the memory DC then copy the content of the memory DC to the real DC.

### Example Code:

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

`;---------------------------------------------------------------------`
`; The resource script`
`;---------------------------------------------------------------------`
`#define IDB_MAIN 1`
`IDB_MAIN BITMAP "tweety78.bmp"`

### Analysis:

There is not much to analyze in this tutorial ;)

> `#define IDB_MAIN 1`
> `IDB_MAIN BITMAP "tweety78.bmp"`

Define a constant named IDB_MAIN, assign 1 as its value. And then use that constant as the bitmap resource identifier. The bitmap file to be included in the resource is "tweety78.bmp" which resides in the same folder as the resource script.

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

In response to WM_CREATE, we call LoadBitmap to load the bitmap from the resource, passing the bitmap's resource identifier as the second parameter to the API. We get the handle to the bitmap when the function returns.
Now that the bitmap is loaded, we can paint it in the client area of our main window.

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

We choose to paint the bitmap in response to WM_PAINT message. We first call BeginPaint to obtain the handle to the device context. Then we create a compatible memory DC with CreateCompatibleDC. Next select the bitmap into the memory DC with SelectObject. Determine the dimension of the client area with GetClientRect. Now we can display the bitmap in the client area by calling BitBlt which copies the bitmap from the memory DC to the real DC. When the painting is done, we have no further need for the memory DC so we delete it with DeleteDC. End painting session with EndPaint.

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

When we don't need the bitmap anymore, we delete it with DeleteObject

[Iczelion's Win32 Assembly HomePage]