WuLine (Antialiased lines)

Posted by on August 2, 2010

This is a simple implementation of Xiaolin Wu’s line algorithm It doesn’t support colors or blending at all, but it should be a good starting point for anyone willing to implement proper AA lines in PB.

The code:

EnableExplicit
 
Procedure.f  trunc(X.f)
	ProcedureReturn Int(X)
EndProcedure
 
Procedure.f frac(X.f)
	ProcedureReturn (X - trunc(X))
EndProcedure
 
Procedure.f invfrac(X.f)
	ProcedureReturn (1.0 - frac(X))
EndProcedure
 
Procedure.i DrawPixel( x.i, y.i, color.i )
	Plot( x, y, RGB(color, color, color) ) ; you would need to perform bound checking in here, or use your own plotting routine...
EndProcedure
 
Procedure WuLine( x1.f, y1.f, x2.f, y2.f)
 
	Define.i MaxPixelValue = 255
	Define.f grad, xd, yd, length, xm, ym, xgap, ygap, xend, yend, xf, yf, brightness1, brightness2
	Define.i x, y, ix1, ix2, iy1, iy2
	Define.b c1, c2
 
	xd = (x2-x1)
	yd = (y2-y1)
 
	If Abs(xd) > Abs(yd) ;-------------------- Horizontal --------------------
 
		If x1 > x2
			Swap x1, x2
			Swap y1, y2
			xd = (x2-x1)
			yd = (y2-y1)
		EndIf
 
		grad = yd/xd
 
		xend = trunc(x1+0.5)
		yend = y1 + grad*(xend-x1)
		xgap = invfrac(x1+0.5)
		ix1  = Int(xend)
		iy1  = Int(yend)
 
		brightness1 = invfrac(yend) * xgap
		brightness2 =    frac(yend) * xgap
 
		c1 = (brightness1 * MaxPixelValue)
		c2 = (brightness2 * MaxPixelValue)
 
		DrawPixel(ix1,iy1, c1)
		DrawPixel(ix1,iy1+1, c2)
 
		yf = yend+grad
 
		xend = trunc(x2+0.5)
		yend = y2 + grad*(xend-x2)
 
		xgap = invfrac(x2-0.5)
 
		ix2  = Int(xend)
		iy2  = Int(yend)
 
		brightness1 = invfrac(yend) * xgap
		brightness2 =    frac(yend) * xgap
 
		c1 = (brightness1 * MaxPixelValue)
		c2 = (brightness2 * MaxPixelValue)
 
		DrawPixel(ix2,iy2, c1		)
		DrawPixel(ix2,iy2+1, c2)
 
		For x= (ix1+1) To (ix2-1)
 
			brightness1 = invfrac(yf)
			brightness2 =    frac(yf)
 
			c1 = (brightness1 * MaxPixelValue)
			c2 = (brightness2 * MaxPixelValue)
 
			DrawPixel(x,Int(yf), c1	)
			DrawPixel(x,Int(yf)+1, c2)
 
			yf = yf + grad
 
		Next
 
	Else ;-------------------- Vertical --------------------
 
		If y1 > y2
			Swap x1, x2
			Swap y1, y2
			xd = (x2-x1)
			yd = (y2-y1)
		EndIf
 
		grad = xd/yd
		yend = trunc(y1+0.5)
		xend = x1 + grad*(yend-y1)
		ygap = invfrac(y1+0.5)
 
		ix1  = Int(xend)
		iy1  = Int(yend)
 
		brightness1 = invfrac(xend) * ygap
		brightness2 =    frac(xend) * ygap
 
		c1 = (brightness1 * MaxPixelValue)
		c2 = (brightness2 * MaxPixelValue)
 
		DrawPixel(ix1,iy1, c1)
		DrawPixel(ix1,iy1+1, c2)
 
		xf = xend+grad
 
		yend = trunc(y2+0.5)
		xend = x2 + grad*(yend-y2)
		ygap = invfrac(y2-0.5)
		ix2  = Int(xend)
		iy2  = Int(yend)
 
		brightness1 = invfrac(xend) * ygap
		brightness2 =    frac(xend) * ygap
 
		c1 = (brightness1 * MaxPixelValue)
		c2 = (brightness2 * MaxPixelValue)
 
		DrawPixel(ix2,iy2, c1		)
		DrawPixel(ix2,iy2+1, c2)
 
		For y= (iy1+1) To (iy2-1)
 
			brightness1 = invfrac(xf)
			brightness2 =    frac(xf)
 
			c1 = (brightness1 * MaxPixelValue)
			c2 = (brightness2 * MaxPixelValue)
 
			DrawPixel(Int(xf),y, c1	)
			DrawPixel(Int(xf)+1,y, c2)
 
			xf = xf + grad
 
		Next
 
	EndIf
 
EndProcedure

You'll quickly notice there's no coloring and no blending, ie. lines that overlap each other are not blended; we would have to sample the frame buffer to do this and quite frankly, I'd rather use OpenGL if I needed to draw hundredths of AA lines.

There’s at least one issue with this code, sometimes the start/end points are drawn with an offset, etc. I don’t really have time to hunt down the bug though, but heres a quick and dirty example:

Macro RAD2DEG(_n_) 	: (_n_ * 57.295779513082323 ) : EndMacro
Macro DEG2RAD(_n_) 	: (_n_ * 0.0174532925199432 ) : EndMacro
 
Procedure.i render( img.i )
 
	Define.f scalar = 240
	Define.i ot, nt, theta, x, y
	Define.f t, time,frames
 
	Define.i mode = 1
 
	InitKeyboard()
 
	Repeat
 
		If StartDrawing(ImageOutput(img))
 
			Box(0,0,512,512, 0)
 
			If ExamineKeyboard() ; who would've guessed, this works fine without openscreen() if we're not in debug mode...
				If KeyboardReleased( #PB_Key_Space )
					mode = 1 - mode
				EndIf
			EndIf
 
			For theta=0 To 360 Step 8
 
				t = DEG2RAD(theta)+(frames*0.005)
				x = ( Cos(t) * scalar ) + 16
				y = ( Sin(t) * scalar ) + 16
 
				If mode
					WuLine( scalar+y*time*2, scalar+x*time*2, scalar+x, scalar+y )
				Else
					LineXY( scalar+y*time*2, scalar+x*time*2, scalar+x, scalar+y, $ffffff )
				EndIf
 
			Next
 
			time + (Cos(frames*0.01)*0.0045)
			frames + 1
 
			StopDrawing()
			SetGadgetState( 0, ImageID(img) )
 
		EndIf
 
		Delay(8)
	ForEver
 
EndProcedure
 
Define.i img
If OpenWindow(0, 0, 0, 512, 512, "WuLine 4.50 by GuShH", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
	img = CreateImage(#PB_Any, 512, 512)
	If img
		CreateThread(@render(), img)
	EndIf
	ImageGadget(0,  0, 0, 0, 0, ImageID(img))
	Repeat
	Until WaitWindowEvent(16) = #PB_Event_CloseWindow
EndIf

Either way, enjoy.

PS: You may find the complete source in here

0 Comments on WuLine (Antialiased lines)

Respond | Trackback

Respond

Comments

Comments:

*