Last year I posted a snippet showing
how to draw anti-aliased lines using a method by Xiaolin Wu.
In "Graphics Gems 2",
http://books.google.com/books?id=ZQQssYF3i7wC&pg=PA446&lpg=PA446&dq=Fast+Anti-Aliased+Circle+Generation&source=web&ots=3tEqjTmXRM&sig=8Yalwrv81RcW5QPg46_fjgmRgZU&hl=en&sa=X&oi=book_result&resnum=2&ct=result#v=onepage&q=Fast%20Anti-Aliased%20Circle%20Generation&f=false
Wu describes how to do the same with circles. Between that article and a php implementation I found
here I was able to create the code below. It's pretty identical to the php implementation, hence why I felt the need to at least document my source, but I had to make a few minor changes to the alpha pixel order.
The below code also includes several other functions I've come up with while drawing my iTunes interface as posted recently in WIP. OBese87 wrote a rounded box routine which I've adapted into my iTunes clone, but now as of last night I have an anti-aliased rounded box routine.
There's a few different versions of the same functions, the only difference being how they're drawn to screen. To use true alpha, I have only the box command to use, which does slow it down quite a bit, so at this time the alpha version isn't ideal for real-time use but maybe for drawing new images saved as sprites. It would be much quicker if I could set alpha values for the color when writing directly to the back buffer using the pDot() function.
REM **************************************************
REM Various drawing functions
REM Author: Phaelax (unless otherwise noted)
REM **************************************************
sync on
hide mouse
do
cls
mx = mousex()
my = mousey()
wuRoundedBox(mx, my, mx+300, my+100, 24, rgb(255,255,255))
`roundedBoxOutline(mx, my, mx+300, my+100, 24)
`fillCircleGradient(mx,my,18, rgb(255,0,0),rgb(0,0,255))
`circ(50,100, 50)
`fillCircle(200,100,50)
`ink rgb(255,255,255),0
`roundedBoxFill(mx, my, mx+400,my+50,10)
`roundedBoxFillGradient(mx, my, mx+400,my+50,10,0,0)
`ink rgb(255,0,0),0
`roundedBoxOutline(mx, my, mx+400,my+50,10)
`set cursor 0,0
`print " "
sync
loop
REM **************************************************
REM Draws an outline of a box with anti-aliased
REM rounded corners as based on Xialon Wu's method
REM **************************************************
function wuRoundedBox(cx as integer, cy as integer, x2 as integer, y2 as integer, r as integer, color as dword)
local alpha1 as dword
local alpha2 as dword
cx = cx+r
cy = cy+r
ow = x2-cx-r
oh = y2-cy-r
x = r
xx = r
y = -1
yy = -1
t# = 0
ink color,0
box cx, cy-r, x2-r, cy-r+1
box cx, y2, x2-r, y2+1
box cx-r, cy, cx-r+1, y2-r
box x2, cy, x2+1, y2-r
lock pixels
while x > y
inc y
cur_dist# = distance#(r, y)
if cur_dist# < t# then dec x
c = 127*cur_dist# : alpha1 = rgb(c,c,c)
c = 127 - c : alpha2 = rgb(c,c,c)
rem octant 1
pDot(cx-y, cy-x, color)
if cy-x > cy-r then pDot(cx-y, cy-x-1, alpha2)
pDot(cx-y, cy-x+1, alpha1)
rem octant 2
pDot(cx-x, cy-y, color)
if cx-x > cx-r then pDot(cx-x-1, cy-y, alpha2)
pDot(cx-x+1, cy-y, alpha1)
rem octant 3
pDot(cx-x, cy+y+oh, color)
if cx-x > cx-r then pDot(cx-x-1, cy+y+oh, alpha2)
pDot(cx-x+1, cy+y+oh, alpha1)
rem octant 4
pDot(cx-y, cy+x+oh, color)
if cy+x+oh < y2 then pDot(cx-y, cy+x+1+oh, alpha2)
pDot(cx-y, cy+x-1+oh, alpha1)
rem octant 5
pDot(cx+y+ow, cy+x+oh, color)
if cy+x+oh < y2 then pDot(cx+y+ow, cy+x+1+oh, alpha2)
pDot(cx+y+ow, cy+x-1+oh, alpha1)
rem octant 6
pDot(cx+x+ow, cy+y+oh, color)
if cx+x+ow < x2 then pDot(cx+x+1+ow, cy+y+oh, alpha2)
pDot(cx+x-1+ow, cy+y+oh, alpha1)
rem octant 7
pDot(cx+x+ow, cy-y, color)
if cx+x+ow < x2 then pDot(cx+x+1+ow, cy-y, alpha2)
pDot(cx+x-1+ow, cy-y, alpha1)
rem octant 8
pDot(cx+y+ow, cy-x, color)
if cy-x > cy-r then pDot(cx+y+ow, cy-x-1, alpha2)
pDot(cx+y+ow, cy-x+1, alpha1)
t# = cur_dist#
endwhile
unlock pixels
endfunction
REM **************************************************
REM Draws an anti-aliased circle
REM Based on Xiaolin Wu method
REM **************************************************
function wuCircle(cx as integer, cy as integer, r as integer)
local alpha1 as dword
local alpha2 as dword
color = rgb(255,255,255)
x = r
xx = r
y = -1
yy = -1
t# = 0
lock pixels
while x > y
inc y
cur_dist# = distance#(r, y)
if cur_dist# < t# then dec x
c = 127*cur_dist# : alpha1 = rgb(c,c,c)
c = 127 - c : alpha2 = rgb(c,c,c)
rem octant 1
pDot(cx-y, cy-x, color)
pDot(cx-y, cy-x-1, alpha2)
pDot(cx-y, cy-x+1, alpha1)
rem octant 2
pDot(cx-x, cy-y, color)
pDot(cx-x-1, cy-y, alpha2)
pDot(cx-x+1, cy-y, alpha1)
rem octant 3
pDot(cx-x, cy+y, color)
pDot(cx-x-1, cy+y, alpha2)
pDot(cx-x+1, cy+y, alpha1)
rem octant 4
pDot(cx-y, cy+x, color)
pDot(cx-y, cy+x+1, alpha2)
pDot(cx-y, cy+x-1, alpha1)
rem octant 5
pDot(cx+y, cy+x, color)
pDot(cx+y, cy+x-1, alpha1)
pDot(cx+y, cy+x+1, alpha2)
rem octant 6
pDot(cx+x, cy+y, color)
pDot(cx+x-1, cy+y, alpha1)
pDot(cx+x+1, cy+y, alpha2)
rem octant 7
pDot(cx+x, cy-y, color)
pDot(cx+x-1, cy-y, alpha1)
pDot(cx+x+1, cy-y, alpha2)
rem octant 8
pDot(cx+y, cy-x, color)
pDot(cx+y, cy-x-1, alpha2)
pDot(cx+y, cy-x+1, alpha1)
t# = cur_dist#
endwhile
unlock pixels
endfunction
REM **************************************************
REM Draws an anti-aliased circle with alpha values
REM Based on Xiaolin Wu method
REM **************************************************
function wuCircleAlpha(cx as integer, cy as integer, r as integer)
local alpha1 as dword
local alpha2 as dword
local color as dword
color = rgb(255,255,255)
x = r
xx = r
y = -1
yy = -1
t# = 0
while x > y
inc y
cur_dist# = distance#(r, y)
if cur_dist# < t# then dec x
c = 127*cur_dist# : alpha1 = argb(c,255,255,255)
c = 127 - c : alpha2 = argb(c,255,255,255)
rem octant 1
box cx-y, cy-x,cx-y+1, cy-x+1, color, color, color, color
box cx-y, cy-x-1,cx-y+1, cy-x, alpha2, alpha2, alpha2, alpha2
box cx-y, cy-x+1,cx-y+1, cy-x+2, alpha1, alpha1, alpha1, alpha1
rem octant 2
box cx-x, cy-y,cx-x+1, cy-y+1, color, color, color, color
box cx-x-1, cy-y, cx-x, cy-y+1, alpha2, alpha2, alpha2, alpha2
box cx-x+1, cy-y,cx-x+2, cy-y+1, alpha1, alpha1, alpha1, alpha1
rem octant 3
box cx-x, cy+y,cx-x+1, cy+y+1, color, color, color, color
box cx-x-1, cy+y, cx-x, cy+y+1, alpha2, alpha2, alpha2, alpha2
box cx-x+1, cy+y,cx-x+2, cy+y+1, alpha1, alpha1, alpha1, alpha1
rem octant 4
box cx-y, cy+x, cx-y+1, cy+x+1, color, color, color, color
box cx-y, cy+x+1,cx-y+1, cy+x+2, alpha2, alpha2, alpha2, alpha2
box cx-y, cy+x-1,cx-y+1, cy+x, alpha1, alpha1, alpha1, alpha1
rem octant 5
box cx+y, cy+x,cx+y+1, cy+x+1, color, color, color, color
box cx+y, cy+x-1,cx+y+1, cy+x, alpha1, alpha1, alpha1, alpha1
box cx+y, cy+x+1,cx+y+1, cy+x+2, alpha2, alpha2, alpha2, alpha2
rem octant 6
box cx+x, cy+y,cx+x+1, cy+y+1, color, color, color, color
box cx+x-1, cy+y,cx+x, cy+y+1, alpha1, alpha1, alpha1, alpha1
box cx+x+1, cy+y,cx+x+2, cy+y+1, alpha2, alpha2, alpha2, alpha2
rem octant 7
box cx+x, cy-y, cx+x+1, cy-y+1, color, color, color, color
box cx+x-1, cy-y,cx+x, cy-y+1, alpha1, alpha1, alpha1, alpha1
box cx+x+1, cy-y,cx+x+2, cy-y+1, alpha2, alpha2, alpha2, alpha2
rem octant 8
box cx+y, cy-x,cx+y+1, cy-x+1, color, color, color, color
box cx+y, cy-x-1,cx+y+1, cy-x, alpha2, alpha2, alpha2, alpha2
box cx+y, cy-x+1,cx+y+1, cy-x+2, alpha1, alpha1, alpha1, alpha1
t# = cur_dist#
endwhile
endfunction
REM **************************************************
REM Returns fractional part of distance
REM * Needed for Xiaolin Wu functions
REM **************************************************
function distance#(a, b)
real# = sqrt(a^2 - b^2)
d# = ceil(real#) - real#
endfunction d#
REM **************************************************
REM Draws a point to screen
REM **************************************************
function pDot(x as integer, y as integer, color as dword )
if x > 0 and x < screen width() and y > 0 and y < screen height()
offset = get pixels pointer()
pitch = get pixels pitch()
bitsPerPixel = bitmap depth(0)/8
pointer = offset + y*pitch + x*bitsPerPixel
*pointer = color
endif
endfunction
REM **************************************************
REM Gets the color drawn on screen at [x,y]
REM **************************************************
function ppick(x as integer, y as integer)
pointer = get pixels pointer() + y*get pixels pitch() + x*(bitmap depth(0)/8)
c = *pointer
endfunction c
REM **************************************************
REM Draws a filled-in circle (slower)
REM **************************************************
function fillCircle2(h,k,r)
for y = -r to r
n = r^2 - y^2
x = sqrt(n)
box h-x, k+y, h+x, k+y+1
next y
endfunction
REM **************************************************
REM Draws a filled-in circle
REM **************************************************
function fillCircle(x,y,r)
for yy = y-r to y
for xx = x-r to x
d# = (xx-x)*(xx-x) + (yy-y)*(yy-y)
if d# <= r*r
box xx,yy,x+(x-xx)+1,yy+1
if yy <> y then box xx,y+(y-yy),x+(x-xx)+1,y+(y-yy)+1
exit
endif
next xx
next yy
endfunction
REM **************************************************
REM Draws a filled-in circle with a vertical gradient
REM **************************************************
function fillCircleGradient(x0,y0,r, color1 as dword, color2 as dword)
x1 = x0-r
y1 = y0-r
x2 = x0+r
y2 = y0+r
diameter# = r+r
for y = y1 to y2
for x = x1 to x0
d# = (x-x0)^2 + (y-y0)^2
if d# <= r*r
p# = (y-y0 + r) / diameter#
c = getTransitionalColor(color1, color2, p#)
w = x0 + x0-x
box x,y,w,y+1, c,c,c,c
exit
endif
next x
next y
endfunction
REM **************************************************
REM Original function: OBese87 [Aug. 19 2009]
REM Draws an outline of a box with rounded corners
REM **************************************************
function roundedBoxOutline(ax,ay,bx,by,r)
width = 1
box ax+r,ay,bx-r+1,ay+width
box bx,ay+r,bx+width,by-r
box ax+r,by,bx-r,by+width
box ax,ay+r,ax+width,by-r
dots = r*6.28
deg2dot# = 360 / (dots*1.0)
dotsperqtr# = dots / 4.0
L = bx-ax
for corner = 0 to 3
select corner
case 0:x=bx-r:y=by-r:endcase
case 1:x=bx-r:y=ay+r:endcase
case 2:x=ax+r:y=ay+r:endcase
case 3:x=ax+r:y=by-r:endcase
endselect
qtr = corner*dotsperqtr#
for o=qtr to qtr+dotsperqtr#
u = sin(o*deg2dot#)*r
v = cos(o*deg2dot#)*r
box x+u, y+v, x+u+width, y+v+width
next o
next corner
endfunction
REM **************************************************
REM Original function: OBese87 [Aug. 19 2009]
REM Edited: Phaelax [June 11 2010]
REM Draws a box with rounded corners and filled
REM with a vertical gradient
REM **************************************************
function roundedBoxFillGradient(ax,ay,bx,by,r, color1 as dword, color2 as dword)
rem variables for drawing the bar
L = abs(bx-ax)-r*2
H# = abs(by-ay)
local color as dword
dots = r*6.28
deg2dot# = 360/(dots*1.0)
dotsperqtr# = dots/4.0
for corner = 2 to 3
select corner
case 0:x=bx-r:y=by-r:endcase
case 1:x=bx-r:y=ay+r:endcase
case 2:x=ax+r:y=ay+r:endcase
case 3:x=ax+r:y=by-r:endcase
endselect
qtr = corner*dotsperqtr#
for o=qtr to qtr+dotsperqtr#
u = sin(o*deg2dot#)*r
v = cos(o*deg2dot#)*r
x1 = L - u*2
c# = (y+v - ay) / H#
color = getTransitionalColor(rgb(122,179,253),rgb(46,91,190), c#)
ink color, 0
box x+u, y+v, x+u+x1, y+v+1
next o
next corner
c1 = getTransitionalColor(rgb(122,179,253),rgb(46,91,190), r / H#)
c2 = getTransitionalColor(rgb(122,179,253),rgb(46,91,190), (H#-r) / H#)
box ax,ay+r,bx,by-r,c2,c1,c2,c1
endfunction
REM **************************************************
REM Original function: OBese87 [Aug. 19 2009]
REM Edited: Phaelax [June 11 2010]
REM Draws a box with rounded corners and filled with
REM the current drawing color
REM **************************************************
function roundedBoxFill(ax,ay,bx,by,r)
dots = r*6.28
deg2dot# = 360 / (dots*1.0)
dotsperqtr# = dots / 4.0
L = abs(bx-ax)-r*2
H = abs(by-ay)
for corner = 2 to 3
select corner
case 0:x=bx-r:y=by-r:endcase
case 1:x=bx-r:y=ay+r:endcase
case 2:x=ax+r:y=ay+r:endcase
case 3:x=ax+r:y=by-r:endcase
endselect
qtr = corner*dotsperqtr#
for o=qtr to qtr+dotsperqtr#
u = sin(o*deg2dot#)*r
v = cos(o*deg2dot#)*r
x2 = L - u*2
box x+u, y+v, x+u+x2, y+v+1
next o
next corner
box ax, ay+r, bx, by-r
endfunction
REM **************************************************
REM Returns a linear interpolated color between base
REM color and target color. Percent ranges from 0 to 1
REM **************************************************
function getTransitionalColor(base, target, percent#)
br = rgbr(base)
bg = rgbg(base)
bb = rgbb(base)
ba = base >> 24
tr = rgbr(target)
tg = rgbg(target)
tb = rgbb(target)
ta = target >> 24
tr = br + (tr-br)*percent#
tg = bg + (tg-bg)*percent#
tb = bb + (tb-bb)*percent#
ta = ba + (ta-ba)*percent#
color = argb(ta,tr,tg,tb)
endfunction color
REM **************************************************
REM Returns a color with an alpha value
REM **************************************************
function argb(a,r,g,b) as dword
c as dword
c = (a*16777216)+(r*65536)+(g*256)+b
endfunction c
"Any sufficiently advanced technology is indistinguishable from magic" ~ Arthur C. Clarke