Если мы применяем изгибание наружу, изгибание внутрь и совсем не изгибаемся, индивидуально к каждому из 4-х углов прямоугольника получаем 81 вариант:
Код реализует следующую команду BASIC:
BOX X1%, Y1%, X2%, Y2% RADII Radii%
BOX
рисует прямоугольник между противоположными углами в точках (X1%, Y1%) и (X2%, Y2%). Первый байт 32-битного значения в Радиусы% определяет угол первого квадранта, второй байт Радиусы% определяет угол второго квадранта и так далее. Эти байты, независимые друг от друга, выражают процент от половины наименьшего размера прямоугольника. Положительное значение округляется наружу (выпукло), отрицательное значение округляется внутрь (вогнутое), нулевое значение не округляется вообще.
Дуги нарисованы с помощью 8-точечного симметричного кода Брезенхема. Это означает, что для каждой дуги 90 ° одновременно рисуются точки, движущиеся к биссектрисе.
Если бы цель заключалась в том, чтобы нарисовать весь закругленный прямоугольник с использованием некоторого пунктирного стиля линий, тогда было бы очень желательно нарисовать все эти дуги полностью только против часовой стрелки. Как это можно сделать, используя алгоритм Брезенхема или аналогичный?
Кроме того, можно ли еще оптимизировать существующий код (размер кода и / или скорость выполнения)?
; 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