I'd like to draw a perfectly filled (1) circle (aka disc) in assembly in 320x200 mode with a 100-pixel radius. What's the quickest/easiest way?
(1): I mean a disc is perfectly filled when its color is e.g. constant white and has no black pixels in it.
If by quickest you mean the quickest to write, here a simple solution for DOS.
It doesn't use any DOS service but the exit one.
It is meant to generate a COM file (raw output with NASM is fine, just rename it with COM extension).
BITS 16
ORG 100h
push 0a000h ;Video memory graphics segment
pop es
mov ax, 0013h ;320x200@8bpp
int 10h
push 09h ;Blue
push 159 ;cX
push 99 ;cY
push 60 ;Radius
call drawFilledCircle
;Wait for a key
xor ah, ah
int 16h
;Restore text mode
mov ax, 0003h
int 10h
;Return
mov ax, 4c00h
int 21h
;Color
;cX
;cY
;R
drawFilledCircle:
push bp
mov bp, sp
sub sp, 02h
mov cx, WORD [bp+04h] ;R
mov ax, cx
mul ax ;AX = R^2
mov WORD [bp-02h], ax ;[bp-02h] = R^2
mov ax, WORD [bp+06h]
sub ax, cx ;i = cY-R
mov bx, WORD [bp+08h]
sub bx, cx ;j = cX-R
shl cx, 1
mov dx, cx ;DX = Copy of 2R
.advance_v:
push cx
push bx
mov cx, dx
.advance_h:
;Save values
push bx
push ax
push dx
;Compute (i-y) and (j-x)
sub ax, WORD [bp+06h]
sub bx, WORD [bp+08h]
mul ax ;Compute (i-y)^2
push ax
mov ax, bx
mul ax
pop bx ;Compute (j-x)^2 in ax, (i-y)^2 is in bx now
add ax, bx ;(j-x)^2 + (i-y)^2
cmp ax, WORD [bp-02h] ;;(j-x)^2 + (i-y)^2 <= R^2
;Restore values before jump
pop dx
pop ax
pop bx
ja .continue ;Skip pixel if (j-x)^2 + (i-y)^2 > R^2
;Write pixel
push WORD [bp+0ah]
push bx
push ax
call writePx
.continue:
;Advance j
inc bx
loop .advance_h
;Advance i
inc ax
pop bx ;Restore j
pop cx ;Restore counter
loop .advance_v
add sp, 02h
pop bp
ret 08h
;Color
;X
;Y
writePx:
push bp
mov bp, sp
push ax
push bx
mov bx, WORD [bp+04h]
mov ax, bx
shl bx, 6
shl ax, 8
add bx, ax ;320 = 256 + 64
add bx, WORD [bp+06h]
mov ax, WORD [bp+08h]
;TODO: Clip
mov BYTE [es:bx], al
pop bx
pop ax
pop bp
ret 06h
It simply a common technique for writing plane figures given some parameters, it's called rasterization.
Given the center C(x, y) and the radius R of the circle, the algorithm is as follow
1. For i = y-R to y+R
1.1 For j = x-R to x+R
1.1.1 If (i-y)^2 + (j-x)^2 <= R^2 Then
1.1.1.1 DrawPixel(j, i)
1.1.1 End if
1.1 End For
1. End For
This is not optimized for speed, at all!
I indeed create multiple routine for the sake of clarity.
Also I use the stack a lot.
Note writePx
does not clip the coordinates!
If you want to speed up the things you need to be more specific on your requirement.
For example is the radius always fixed? If yes you can use a block of memory that encode a quarter of circle, something like this
0 0 0 0 0 1 1 1
0 0 0 1 1 1 1 1
0 0 1 1 1 1 1 1
0 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
Where each digits may be a bit or a byte depending on your speed vs memory constraints.
You can then copy this block directly into video memory or use it as a template (kind of chroma key technique).
For printing the other three quarters of the circle simple play with the counters.
If the radius is not fixed you can boost the code above by