2007. 2. 16. 14:27
DevX Win32 Assembly Tutorial 25: Simple Bitmap

Tutorial 25: Simple Bitmap

이 번장에서는, 비트맵을 어떻게 사용하는지를 설명한다. 정확히 말하면, 윈도우의 작업영역에 어떻게 비트맵을 표시하는지를 설명한다.
 소스    리소스 스크립트    비트 맵    실행 결과  

Theory:

비트맵은 컴퓨터에 저장되어 있는 그림이라고 생각할 수 있다. 그림 관련 포맷은 여러가지가 있지만, Windows의 표준형식은 Windows Bitmap Graphics 파일(. bmp)이다. 이번에 설명하는 「비트맵」은, 이 Windows bitmap graphics이다. 가장 간단히 사용하려면 , 리소스로 사용하면 되지만, 여기에는 2가지 방법이 있다. 하나는 다음과 같이 리소스 정의 파일(. rc)에 비트맵을 저장하는 방식이다.

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

이 방법은 비트맵이란 것을 나타내는 정수를 사용한다. IDB_MYBITMAP라는 이름이 100 이라는 것을 정의하고 있다. 프로그램에서는, 이 이름을 비트맵을 참조할 때에 사용하게 된다. 다음으론 비트맵의 리소스를 정의하고 있다. 리소스 컴파일러에게 비트맵 파일이 실제로 어디에 있는지를 지정해 주고있다.

다른 한가지 방법은 비트맵을 다음과 같이 이름으로 정의하는 것이다.

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

이 방법은, 이전 방법에서 정수로 구별하는 것을, 「MyBitMap」라는 문자열로서 구별하게 된다.

양쪽 방법 모두 정상적로 작동하지만, 예제에서는 전자의 방법(resource file에)으로 비트맵을 저장하는 방법을 사용한다.

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

  2. DC핸들을 얻는다. 얻는방법은 WM_PAINT 메세지에서 BeginPaint 함수를 호출하든지,또는 다른 메세지에서 GetDC 함수를 호출하는 것이다
  3. 이전 단계에서 얻어진 DC핸들과 동일한 속성을 가진 메모리DC를 생성한다.메모리DC는, 비트맵을 그릴수 있는 기능을 가진 논리화면 영역이다.
    이런 처리가 끝나면, 어떤 함수를 호출해서, 메모리DC 표면에 출력한 내용을, 실제의 DC에 복사한다. 이것은, 더블 버퍼링으로 불리는 기술로, 그림을 고속으로 출력시키는 방법이다. 메모리DC는 , CreateCompatibleDC 함수를 호출해서 생성할 수 있다.

    CreateCompatibleDC proto hdc:HDC

    이 함수가 성공적으로 수행되면, eax 레지스터에 메모리DC의 핸들이 저장된다. hdc는 실제DC이며, 이 DC와 동일한 특성을 가진 메모리DC를 생성한다.

  4. 이상태에서, 메모리DC에 출력이 끝났다.
    이번에는,비트맵을 출력할려는 표면을 가진 DC를 선택해주어야 한다. 이것은, SelectObject 함수를 호출해서 할 수 있다. 이 함수는, 첫번째 인수로 메모리DC의 핸들을, 두번째 인수는 표시할려는 비트맵을 지정한다. SelectObject 함수는 다음과 같은 프로토타입으로 되어있다.

    SelectObject  proto hdc:HDC, hGdiObject:DWORD

  5. 메모리DC에 출력이 완료됬을 것이다.
    이제 해야할 것은 실제DC에 메모리DC의 내용을 복사해야하지만, 이는 BitBlt 함수를 호출하는 방법, 또는 StretchBlt 함수를 호출하는 방법 등 몇가지가 있다. BitBlt 함수는 단순히 DC간의 복사작업만 하는 것에 비해, StretchBlt 함수는 크기를 다르게 할 수 있다.하지만 BitBlt 함수보다는 조금더 시간이 더 걸린다. 예제에서는 간단한 BitBlt 함수를 사용한다.

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

    • hdcDest
      비트맵 대상의 DC핸들
    • nxDest, nyDest
      출력 영역의 좌상단의 좌표값
    • nWidth, nHeight
      출력영역의 길이와 폭
    • hdcSrc
      비트맵 원본의 DC핸들
    • nxSrc, nySrc
      원본 비트맵의 좌상단의 좌표값
    • dwROP
      래스터 오퍼레이션 코드(ROP)를 지정한다. 그림데이터를 복사할 때, 원본과 대상의 색상데이터를 어떻게 결합하는지를 지정할 수 있다.
      대부분의 경우, 새로운 데이터로 덮어쓰기하는 것이다.
  6. 비트맵에 대한 처리가 끝나면, DeleteObject 함수를 호출해서 삭제한다.

이를 요약하면 다음과 같다.

  1. 리소스 스크립트에 비트맵을 기술한다
  2. LoadBitmap 함수를 호출해서, 리소스로부터 비트맵을 로드한다
  3. 비트맵 핸들을 얻는다
  4. 비트맵을 출력하고 싶은 DC의 핸들을 얻는다
  5. 그 DC와 동일한 형태의 메모리DC를 생성한다
  6. 그 메모리DC에 비트맵을 출력한다
  7. 메모리DC로부터 실제DC로 복사한다

Example :

.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 file
;---------------------------------------------------------------------
#define IDB_MAIN 1
IDB_MAIN BITMAP "tweety78.bmp"

Analysis:

 

#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 메세지에 대한 처리로서 리소스로부터 비트맵 파일을 로드하기위해, 두번째 인수에 비트맵의 리소스ID를 설정해서 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핸들을 얻는다. 그리고, CreateCompatibleDC 함수를 호출하여 DC와 호환되는 메모리DC를 생성한다. SelectObject 함수로, 메모리DC의 오브젝트에 비트맵을 선택해 준다. GetClientRect 함수를 호출하여,작업영역의 출력크기를 결정하고, 메모리DC로부터 실제 DC로 비트맵을 전송하는 BitBlt 함수를 호출함으로써, 작업영역에 실제 비트맵 출력된다. 출력이 완료되면, 메모리DC는 필요없기 때문에, DeleteDC 함수로 삭제한다. 마지막으로, EndPaint 함수로서 출력세션을 종료한다.

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

비트맵이 필요없다면, DeleteObject 함수로 삭제한다.



Posted by openserver