Introduction
This tip describes the use of GDI32 in assembly programming language. As an example project, we will create a simple graphical analog clock frame. As assembler, we will use the NASM assembler, And for drawing the graphics, we will use the Windows GDI32 library.
Why Assembly Language?
Studying and learning assembly language will give a better idea about how a program actually executes in a computer. And also, you can learn assembly language to enhance your skill on programming.
Using the Code
We know that every Windows program contains a main window. So our clock frame project also has a main window. But in this tip, I will not explain the code used for creating the main window. Instead, I will describe the GDI32 codes used in the project.
However, if you want know about how to create a Window in Assembly language, then you might want to take a look at this CodeProject article of mine:
Let's start:
To use GDI32 functions in assembly, first you have to declare the function name as an external symbol. In NASM, you can do it as follows:
extern BeginPaint
extern EndPaint
extern Rectangle
Now, you can use those GDI32 functions to draw what you want.
Since all drawings are done on WM_PAINT
message or event, we will handle the WM_PAINT
message to draw our clock frame. We will handle the message inside the main Window Procedure function. The name of the main window procedure function is WindowProc
.
The following codes handle the WM_PAINT
message to draw our clock frame in main window:
cmp dword[ebp+12], WM_PAINT
jne L2
In the above codes, CMP
instruction compares the Message
parameter of WindowProc
function with WM_PAINT
constant and stores the result in CPU_FLAGS
. If the comparison result is not equal, then JNE
instruction will skip our drawing codes since our window doesn't want to paint anything.
Let's begin to write drawing codes.
GDI32 drawing begins through calling BeginPaint
function:
mov ebx, dword[ebp+8]
push dword PaintSt
push dword ebx
call BeginPaint
mov dword[WindowDC], eax
The BeginPaint
function returns device context handle of window. We have stored the result into the WindowDC
variable. We will use this variable many times to do various drawing operations.
Note that WindowDC
is a variable declared in the .data
section and PaintSt
is also a variable declared in .bss
section.
Now, we will call a user-defined function named FillBackground
to fill the background of the window with pink color:
call FillBackground
The FillBackground
function creates a pink color Pen
and a pink color Brush
. Then it draws a fill rectangle using Rectangle
function of GDI32:
Since our background color will be pink, so create a pink pen:
RGB 255, 184, 211
push eax
push 2
push 0
call CreatePen
mov dword[ebp-12], eax
In the above codes, we create a pen via calling CreatePen
function of GDI32 and we store the pen object handle to the local stack.
Select the pen object with Window DC through calling SelectObject
function. The SelectObject
function selects a GDI object into a specified device context:
push dword[ebp-12]
push dword[WindowDC]
call SelectObject
We need a pink brush that will fill our background:
RGB 255, 184, 211
push eax
call CreateSolidBrush
mov dword[ebp-8], eax
Select the brush with Window DC:
push dword[ebp-8]
push dword[WindowDC]
call SelectObject
Now draw a rectangle and delete the pen and brush from memory:
push 382
push 382
push 0
push 0
push dword[WindowDC]
call Rectangle
push dword[ebp-8]
call DeleteObject
push dword[ebp-12]
call DeleteObject
Here, we will draw two ellipses as our clock frame and will create a Pen
and a Brush
for giving color to ellipses:
RGB 0, 128, 128
push eax
push 2
push 0
call CreatePen
mov dword[ebp-12], eax
RGB 0, 166, 166
push eax
call CreateSolidBrush
mov dword[ebp-8], eax
In the above codes, a user-defined macro called RGB
is used for calculating the rgb value.
push 310
push 310
push 40
push 40
push dword[WindowDC]
call Ellipse
push 300
push 300
push 50
push 50
push dword[WindowDC]
call Ellipse
Now, we will call a user-defined function named DrawDigits
to draw digit-numbers of our clock frame.
call DrawDigits
The DrawDigits
function uses DrawTextA
function of GDI32 to draw text and SetTextColor
function of GDI32 to set color of text.
And finally, we draw our clock hands using GDI32 MoveToEx
and LineTo
functions.
First, we draw the hour hand of the clock:
push 0
push 175
push 175
push dword[WindowDC]
call MoveToEx
push 115
push 175
push dword[WindowDC]
call LineTo
Then we draw the minute hand:
push 0
push 175
push 175
push dword[WindowDC]
call MoveToEx
push 175
push 260
push dword[WindowDC]
call LineTo
And at last, we draw the second hand:
push 0
push 155
push 190
push dword[WindowDC]
call MoveToEx
push 250
push 120
push dword[WindowDC]
call LineTo
GDI32 drawing ends through calling EndPaint
function:
mov ebx, dword[ebp+8]
push DWORD PaintSt
push DWORD ebx
call EndPaint
Conclusion
I hope this tip has given you knowledge on graphics programming in assembly language.