Introduction to Graphics and Device Contexts

Windows graphics are accomplished using a handle to a device context, or DC. The DC holds the current state of a generalized, or abstract, drawing device. This includes a number of GDI objects and drawing modes. There are currently three types of "devices" for drawing: a video display, a printer/plotter, or a bitmap in memory. Back to Win95 ASM Page

GDI objects

In order to draw, the DC must hold the necessary "drawing tools". Each of these tools exist as GDI objects. You call SelectObject whenever you need to change tools. The DC can hold only one tool of each kind. Because the API uses the tools in the DC, complex draws may involve programming many tool changes.

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 DC holds the state of a drawing area on the video display. Each DC defines a clipping region--everything drawn inside it "clips out" the graphics of any windows "under" that region. Everything drawn outside the region is lost.
    One DC commonly used by Windows programmers is the one that allows drawing in the client area of a window. (Every window is divided into two parts, the client and nonclient areas. The nonclient area contains the title bar, menu, border, and scroll bars.) 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.

WM_PAINT, BeginPaint, and EndPaint

The WM_PAINT message is sent when the client area of a window requires repainting. One such case is when portions of the client area are "uncovered" by closing, moving, or resizing an overlapping window. Because the DC does not hold the image that is supposed to be there, the uncovered portion must be redrawn by the application.
    When handling WM_PAINT, always call both BeginPaint and EndPaint, even if you don't do any graphics. These functions handle the special needs of the WM_PAINT message. If you don't handle this message, DefWindowProc will call these functions for you.
    BeginPaint returns a DC handle, and EndPaint releases it. So it's not necessary to call GetDC and ReleaseDC. The BeginPaint and EndPaint functions should only be used by the WM_PAINT message handler.

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 common DC handle is retrieved when GetDC (or BeginPaint) is called. Every time it's retrieved, it references a new DC initialized with a default set of attributes. Thus to keep memory usage low, the common DC should be released with ReleaseDC (or EndPaint). The common DC is generally used as a temporary DC, acquired and released by each message handler needing a DC.
    A private DC is created when a window is created. Its handle can be retrieved with the GetDC (or BeginPaint) function, and 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.

An example program

The example program, wingdi01.asm, draws small opaque circles when you click in the client area of the "main" window. If you cover all or part of the window with another application window, and then move or remove that window, you'll find the "uncovered" portion of the image is lost. First, the application setup and cleanup.
;
; Application initialization subroutine
; Returns:
;       ESI = address of a CREATEARGS structure, the CreateWindowEx argument list
;       EDI = address of a WNDCLASSEX structure, the RegisterClassEx argument
;       WINMAIN will set the hInstance field of the above structures
;
        .data
wc WNDCLASSEX <size WNDCLASSEX,0,MainWndProc,0,0, 0, \
                        0,0,COLOR_WINDOW+1, 0,mainwndclsname,0>
wndmain CREATEARGS <0,mainwndclsname,maincaption, \
                        WS_OVERLAPPEDWINDOW+WS_VISIBLE,\
                        100,100,400,200, 0,0,0,0>

mainwndclsname db 'WindowGDI',0

maincaption db 'GDI Intro -- click in client area',0

        .code

        public  InitApp,EndApp
        extrn LoadCursor:near

InitApp:
        mov     edi,offset wc
        mov     esi,offset wndmain

        push    large IDC_ARROW
        push    large 0
        call    LoadCursor
        mov     [wc].wcxCursor,eax
        ret
;
; Application cleanup subroutine
; Returns:
;       EAX = application exit code
;
EndApp:
        xor     eax,eax ; assume no errors
        ret
Second, the message dispatch, and the window termination message handling.
;
; 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.
;
        .code

        extrn DefWindowProc:near
        extrn PostQuitMessage:near

MainWndProc:

        mov     eax,[esp+4+4]   ; message ID
        cmp     eax,WM_LBUTTONDOWN
        je      left_mouse_down
        cmp     eax,WM_DESTROY  ; about to start window destruction
        je      start_destroy
        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.
;
start_destroy:
        push    large 0
        call    PostQuitMessage

        xor     eax,eax
        ret     16
Third and, finally, 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 WNDCLASSEX structure "wc" had its style field set with CS_OWNDC, 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.
;
; -----
;
; In response to a click of the left mouse button,
; draw a circle centered on the click point.
;
        .data

wndDC           dd 0            ; DC handle (hDC) of window client area

        .code

        extrn GetDC:near
        extrn ReleaseDC:near
        extrn Ellipse:near

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

        mov     dx,[esp+4+12+2]      ; HIWORD(lParam) = y
        mov     eax,[esp+4+12]       ; LOWORD(lParam) = x
        and     edx,large 0FFFFh
        and     eax,large 0FFFFh
        add     edx,10
        add     eax,10
        push    edx                  ; y, lower right
        push    eax                  ; x
        sub     edx,20
        sub     eax,20
        push    edx                  ; y, upper left
        push    eax                  ; x
        push    [wndDC]              ; hDC
        call    Ellipse

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

        xor     eax,eax
        ret     16

Default DC attributes

When a DC is first created with CreateDC, or a common DC is retrieved with GetDC, it is set to a default state.