Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / GDI

Clock Frame Design Using Assembly

4.67/5 (3 votes)
14 Oct 2015CPOL3 min read 15.6K   578  
Here, we will design a clock frame with Assembly Language and GDI32

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:

ASM
extern BeginPaint
extern EndPaint
extern Rectangle

; More functions
;.................
;.........

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:

ASM
; case WM_PAINT
cmp dword[ebp+12], WM_PAINT
jne L2 ; Look for next message
;{     begin WM_PAINT

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:

ASM
mov  ebx,  dword[ebp+8] ; hWnd              
push dword PaintSt                     ; Pass pointer of instance to PAINTSTRUCT
push dword ebx                             ; Pass the window handle 
call BeginPaint                           ; Update area set by previous API CALL
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:

ASM
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:

ASM
RGB 255, 184, 211
push eax ; Value for color
push 2    ; Pen width
push 0     ; 0 is PS_SOLID
call CreatePen
mov dword[ebp-12], eax ; Here we need a local variable at offset 12

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:

ASM
push dword[ebp-12]
push dword[WindowDC]
call SelectObject

We need a pink brush that will fill our background:

ASM
RGB 255, 184, 211
push eax ; Value for color
call CreateSolidBrush
mov dword[ebp-8], eax ; Here we need a local variable at offset 8

Select the brush with Window DC:

ASM
push dword[ebp-8]
push dword[WindowDC]
call SelectObject

Now draw a rectangle and delete the pen and brush from memory:

ASM
push 382
push 382
push 0
push 0
push dword[WindowDC]
call Rectangle

; delete the brush
push dword[ebp-8]
call DeleteObject

; delete the pen
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:

ASM
; Create a pen
RGB 0, 128, 128
push eax ; Return value of RGB macro
push 2    ; Pen width
push 0     ; 0 is PS_SOLID
call CreatePen
mov dword[ebp-12], eax ; Here we need a local variable at offset 12
; . . . . . . .  .        . ..      ..

; Create a brush
RGB 0, 166, 166
push eax ; Return value of RGB macro
call CreateSolidBrush
mov dword[ebp-8], eax ; Here we need a local variable at offset 8
; . . .  . .      ..          ...

In the above codes, a user-defined macro called RGB is used for calculating the rgb value.

ASM
; Now draw the ellipse
push 310
push 310
push 40
push 40
push dword[WindowDC]
call Ellipse

; Create pen and brush with different colors
; . . . . . .
;  . . ...

; And draw another 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.

ASM
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:

ASM
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:

ASM
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:

ASM
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:

ASM
; ------------------------------
mov  ebx,  dword[ebp+8] ; hWnd      
push DWORD PaintSt                 ; PAINTSTRUCT 
push DWORD ebx                     ; Window handle
call EndPaint                    ; End the paint operation  

;}     end WM_PAINT

Conclusion

I hope this tip has given you knowledge on graphics programming in assembly language.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)