If we apply outward curving, inward curving, and no curving at all, individually to each of the 4 corners of the rectangle, we get next 81 variations:
The code implements next BASIC command:
BOX X1%, Y1%, X2%, Y2% RADII Radii%
BOX
draws the rectangle between the opposite corners at (X1%, Y1%) and (X2%, Y2%).
The first byte of the 32-bit value in Radii% defines the first quadrant corner, the second byte of Radii% defines the second quadrant corner, and so on. These bytes, independent of each other, express a percentage of the half of the smallest dimension of the rectangle. A positive value rounds outwards (convex), a negative value rounds inwards (concave), a zero doesn't round at all.
The arcs are drawn via an 8-point-symmetrical Bresenham code. This means that for each 90° arc, points are drawn simultaneously moving towards the bisector.
If the intent were to draw the whole rounded rectangle using some dotted linestyle, then it would be very desirable to draw all of these arcs entirely in an anti-clockwise fashion only. How can this be done, using either the Bresenham algorithm or a similar one ?
Also, can the present code be optimized any further (codesize and/or execution speed) ?
; Rounded Box (c) 2021 Sep Roland
; Assemble with FASM
ORG 256
mov ax, 0013h ; BIOS.SetVideoMode 320x200
int 10h
mov [Color], 4 ; Red
push dword 64643232h ; Q4=100% Q3=100% Q2=50% Q1=50%
push 89 189 10 130
call Box ; 'BOX 130,10,189,89 RADII Bytes(50,50,100,100)'
mov [Color], 14 ; Yellow
push dword 00006464h ; Q4=0% Q3=0% Q2=100% Q1=100%
push 189 189 110 130
call Box ; 'BOX 130,110,189,189 RADII Bytes(100,100,0,0)'
mov [Color], 1 ; Blue
push dword 9C00329Ch ; Q4=-100% Q3=0% Q2=50% Q1=-100%
push 129 149 70 10
call Box ; 'BOX 10,70,149,129 RADII Bytes(-100,50,0,-100)'
mov [Color], 2 ; Green
push dword 009C9C32h ; Q4=0% Q3=-100% Q2=-100% Q1=50%
push 129 309 70 170
call Box ; 'BOX 170,70,309,129 RADII Bytes(50,-100,-100,0)'
mov ah, 00h ; BIOS.WaitKey
int 16h ; -> AX
mov ax, 0003h ; BIOS.SetVideoMode 80x25
int 10h
mov ax, 4C00h ; DOS.Terminate
int 21h
; -----------------------------
; Plots single pixel on the 256-color screen
; IN (ax,bx,es) OUT ()
PT: push ax di ; AX=Y BX=X
mov di, ax
shl di, 6
xchg al, ah
add di, ax
mov al, [Color]
mov [es:di+bx], al
pop di ax
ret
; -----------------------------
; Draws rectangle with optional, individually outward or inward, curving corners
; IN () OUT ()
Box: push es
pushad
mov bp, sp ; Args start at BP+36
mov ax, 0A000h
mov es, ax
mov ecx, [bp+36+8] ; Radii
; SI = Min(Width,Height)-1
mov si, [bp+36+6] ; Y2
sub si, [bp+36+2] ; Y1 -> SI is DeltaY
mov ax, [bp+36+4] ; X2
sub ax, [bp+36+0] ; X1 -> AX is DeltaX
cmp si, ax
jbe .Q1
mov si, ax
; Q4 Q3 Q2 Q1
; | | | | <-- Convex --> <-- Concave -> |
; | " | " | " | Xc Yc Arc Xc Yc Arc R % |
; | | | | ---- ---- ---- ---- ---- ---- ---- ---- |
; ^ ^
; SP <-------------------- 4 * 8 words ---------------------> BP
.Q1: push cx
call .Radius ; -> AX ECX (BX DX)
push ax
mov dx, [bp+36+2] ; Y1
mov bx, [bp+36+4] ; X2
push .Q3Arc dx bx
add dx, ax
sub bx, ax
push .Q1Arc dx bx
.Q2: push cx
call .Radius ; -> AX ECX (BX DX)
push ax
mov dx, [bp+36+2] ; Y1
mov bx, [bp+36+0] ; X1
push .Q4Arc dx bx
add dx, ax
add bx, ax
push .Q2Arc dx bx
.Q3: push cx
call .Radius ; -> AX ECX (BX DX)
push ax
mov dx, [bp+36+6] ; Y2
mov bx, [bp+36+0] ; X1
push .Q1Arc dx bx
sub dx, ax
add bx, ax
push .Q3Arc dx bx
.Q4: push cx
call .Radius ; -> AX ECX (BX DX)
push ax
mov dx, [bp+36+6] ; Y2
mov bx, [bp+36+4] ; X2
push .Q2Arc dx bx
sub dx, ax
sub bx, ax
push .Q4Arc dx bx
mov si, sp ; DS=SS
add si, 4*16
.Arcs: sub si, 16
mov ax, [si+12] ; Current radius
test ax, ax ; No curving if radius=0
jz .f
push si ; (1)
xor bx, bx ; Start at (0,R)
cmp [si+14], bl ; BL=0
jg .a ; Convex
add si, 6 ; Concave
.a: mov di, 3 ; Decision variable D=3-2*R
sub di, ax
sub di, ax
jmp .c ; Skip all tops (belong to sides)
.b: push ax bx ; (2)
call word [si+4] ; Plot arc
pop bx ax ; (2)
.c: mov cx, bx
test di, di ; If D<0 then D=D+6+4*X
jns .d ; If D>=0 then D=D+10+4*(X-Y)
add di, 6 ; Y=Y-1
jmp .e
.d: sub cx, ax
add di, 10
dec ax ; Y=Y-1
.e: shl cx, 2
add di, cx
inc bx ; X=X+1
cmp bx, ax
jbe .b ; While X<=Y
pop si ; (1)
.f: cmp si, sp
jne .Arcs
.North: mov ax, [si+32+8] ; Y1, Going from East to West
mov bx, [si+48] ; Q1Xc
@@: call PT
dec bx
cmp bx, [si+32] ; Q2Xc
jge @b
.West: mov bx, [si+16+6] ; X1, going from North to South
mov ax, [si+32+2] ; Q2Yc
@@: call PT
inc ax
cmp ax, [si+16+2] ; Q3Yc
jle @b
.South: mov ax, [si+8] ; Y2, going from West to East
mov bx, [si+16] ; Q3Xc
@@: call PT
inc bx
cmp bx, [si] ; Q4Xc
jle @b
.East: mov bx, [si+6] ; X2, going from South to North
mov ax, [si+2] ; Q4Yc
@@: call PT
dec ax
cmp ax, [si+48+2] ; Q1Yc
jge @b
mov sp, bp
popad
pop es
ret 12
; - - - - - - - - - - - - - - -
; IN (ecx,si) OUT (ax,ecx) MOD (bx,dx)
.Radius:movsx ax, cl ; Inputted percentage [-128,+127]
; AX = Min(Abs(Percentage),100)
test ax, ax
jns @f
neg ax
@@: cmp ax, 100
jna @f
mov ax, 100
; AX = (SI/2)*AX/100
@@: mul si ; AX is percentage [0,100]
mov bx, 200
div bx ; -> AX is radius 0+
; Prepare for the next quadrant's percentage
shr ecx, 8
ret
; - - - - - - - - - - - - - - -
.Q1Arc: neg ax
jmp .PT___
; - - - - - - - - - - - - - - -
.Q2Arc: neg bx
neg ax
jmp .PT__
; - - - - - - - - - - - - - - -
.Q3Arc: neg bx
.PT___: call .PT
neg bx
neg ax
jmp .PT_
; - - - - - - - - - - - - - - -
.Q4Arc: xchg ax, bx
.PT__: call .PT
.PT_: xchg ax, bx
; --- --- --- --- ---
.PT: add bx, [si] ; X plus current Xc
add ax, [si+2] ; Y plus current Yc
call PT
sub bx, [si] ; Restore X
sub ax, [si+2] ; Restore Y
ret
; ------------------------------
Color rb 1