Introduction to Graphics

Ultimately all graphics under Windows are accomplished via a device context or DC. The DC is the information about a drawing environment. This includes a reference to a drawing surface (a so-called "device") some assigned GDI objects (drawing tools) and a few drawing modes.  There are currently three types of devices: a video display device a hardcopy device (printer or plotter) and a virtual device (a bitmap in memory). [ Back to Win32 ASM Page ]

Display device context (video display DC)

The DC used with a video display is also known as a display device context. It allows you to draw images on the video display.
    Each display DC holds the state of a drawing area on the video display. Each DC defines a clipping region--everything "drawn" outside the region is ignored and effectively removed (or "clipped out").
    One DC commonly used by Windows programmers is the one that allows drawing in the client area of a window. When needed a handle to this DC is normally retrieved in a message handler after it is invoked and released before the message handler terminates. If the message is WM_PAINT the two functions used are BeginPaint and EndPaint. Otherwise the two functions are GetDC and ReleaseDC.

Common and private DC

The two primary types of display device contexts are common and private. (The third supported type class is obsolete.) When retrieving the DC handle the DC will be private if the window class was registered with the CS_OWNDC class style otherwise it will be common. A DC handle is retrieved by calling GetDC. (When responding to WM_PAINT we must call BeginPaint.)
    If the DC is common it references a new DC initialized with a default set of drawing tools and modes. This DC should be released by calling ReleaseDC before exiting the window procedure. (When responding to WM_PAINT we must call EndPaint.)
    If a private DC is created when a window is created it's not necessary to call ReleaseDC. Unlike the common DC it is not reinitialized on retrieval. As a result you avoid the need to reset all your drawing tools and modes with every message requiring graphics. However if you display a lot of windows simultaneously giving every window a private DC will use a lot of memory.

An example program

The example program windraw1.asm draws a circle when you click in the client area of the "main" window.

Creation loop and dispatch

.386
.model  flat

; If using TLINK32
don't include vclib.inc
include vclib.inc    ; Microsoft VC++ .lib link names

include win32hst.inc ; constants
structures
and entry names

.data
align   4
wcx     dd      size WNDCLASSEX           ; cbSize
dd      CS_VREDRAW or CS_HREDRAW  ; style
dd      WndProc                   ; lpfnWndProc
dd      0
0                       ; cbClsExtra
cbWndExtra
dd      0                         ; hInstance
dd      0                         ; hIcon
dd      0                         ; hCursor
dd      COLOR_WINDOW+1            ; hbrBackground
dd      0                         ; lpszMenuName
dd      wndclsname                ; lpszClassName
dd      0                         ; hIconSm

wndclsname db 'windraw'
0

.code

public _start
extrn   GetModuleHandle:near
extrn   LoadIcon:near
LoadCursor:near
extrn   RegisterClassEx:near

_start:
push    large 0         ; NULL string pointer means
call    GetModuleHandle ; get HINSTANCE/HMODULE of EXE file
mov     [wcx.wcx_hInstance]
eax

push    large IDI_WINLOGO
push    large 0         ; hInstance
0 = stock icon
call    LoadIcon
mov     [wcx.wcx_hIcon]
eax

push    large IDC_ARROW
push    large 0         ; hInstance
0 = stock cursor
call    LoadCursor
mov     [wcx.wcx_hCursor]
eax

push    offset wcx
call    RegisterClassEx

.data
align   4
cwargs   dd   0                  ; dwExStyle
dd   wndclsname         ; lpszClass
dd   wnd_title          ; lpszName
dd   WS_VISIBLE or WS_OVERLAPPED or WS_SYSMENU or WS_THICKFRAME \
or WS_MINIMIZEBOX or WS_MAXIMIZEBOX  ; style
dd   100                ; x
dd   100                ; y
dd   200                ; cx (width)
dd   200                ; cy (height)
dd   0                  ; hwndParent
dd   0                  ; hMenu
dd   0                  ; hInstance
dd   0                  ; lpCreateParams

msgbuf MSG      <>

wnd_title db 'Draw a circle'
0

.code

extrn   CreateWindowEx:near
extrn   GetMessage:near
DispatchMessage:near
extrn   ExitProcess:near

sub     esp
48            ; allocate argument list
mov     esi
offset cwargs ; set block move source
mov     edi
esp           ; set block move destination
mov     ecx
12            ; number of arguments
rep movsd
mov     eax
[wcx.wcx_hInstance]
mov     [esp+40]
eax      ; set hInstance argument in stack
call    CreateWindowEx

msg_loop:
push    large 0         ; uMsgFilterMax
push    large 0         ; uMsgFilterMin
push    large 0         ; hWnd (filter)
0 = all windows
push    offset msgbuf   ; lpMsg
call    GetMessage      ; returns FALSE if WM_QUIT
or      eax
eax
jz      end_loop

push    offset msgbuf
call    DispatchMessage

jmp     msg_loop

end_loop:
push    large 0 ; (error) return code
call    ExitProcess
;
; The window procedure...where messages for one class of windows
;   are processed.
;
; Parameters are hWnd
message
wParam
lParam.
;   hWnd is the window receiving this message.
;   message is the message ID.
;   wParam and lParam depend on the message ID.
;
; Must preserve EBX
ESI
and EDI.
;
extrn   DefWindowProc:near
PostQuitMessage:near

.code
WndProc:
mov     eax
[esp+4+4]   ; message ID
cmp     eax
WM_DESTROY  ; about to start window destruction
je      on_destroy
cmp     eax
WM_LBUTTONDOWN      ; left mouse button is clicked
je      on_left_mouse_button_down
jmp     DefWindowProc   ; delegate other message processing
;
; Process WM_DESTROY.  Sent after window is removed from screen
but
; before any destruction begins.
;
; Return zero if processed.
;
; Must preserve EBX
ESI
and EDI.
;
on_destroy:
push    large 0
call    PostQuitMessage

xor     eax
eax
ret     16

Drawing

Here is the heart of this example -- graphics at the click of a mouse button. It shows the acquisition (GetDC) and release (ReleaseDC) of a common DC. If the WNDCLASSEX structure "wc" had CS_OWNDC or'ed into the style field the GetDC would get the handle of a private DC and ReleaseDC would do nothing -- in other words the call to ReleaseDC could be eliminated.
    To draw a circle the Ellipse function is called with the coordinates of the enclosing rectangle.
; Process WM_LBUTTONDOWN.  Left mouse button has been pressed.
;
; wParam is mouse flags.
; lParam is y:x (client coordinates).
;
; Return zero if processed.
;
; Must preserve EBX
ESI
and EDI.
;
.data
align   4
wndDC   dd 0            ; DC handle (hDC) of window client area

.code

extrn GetDC:near
ReleaseDC:near
extrn Ellipse:near

on_left_mouse_button_down:
push    dword ptr [esp+4+0]     ; hWnd
call    GetDC
mov     [wndDC]
eax             ; hDC for window

push    large 150               ; y
lower right
push    large 150               ; x
push    large 50                ; y
upper left
push    large 50                ; x
push    [wndDC]                 ; hDC
call    Ellipse

push    [wndDC]
push    dword ptr [esp+4+0]     ; hWnd
call    ReleaseDC

xor     eax
eax
ret     16

end     _start