anti-aliased lines

Just starting out? Need help? Post your questions and find answers here.
User avatar
griz
Enthusiast
Enthusiast
Posts: 167
Joined: Sun Jun 29, 2003 7:32 pm
Location: Canada

anti-aliased lines

Post by griz »

Hi guys

Here is a bit of code I ported from Blitz a while ago. It draws anti-aliased lines. It's FAR slower than the built-in PB lineXY() command.

I was hoping someone had a faster or better way to do this?

Code: Select all

; combine two pixels at specified w=weight=0-255
Procedure MergePixel(x,y,r,g,b,w)
  col = Point(x,y) : r2 = Red(col)
  g2 = Green(col)  : b2 = Blue(col)
  rnew = ((r * w) >> 8) + ((r2 * (255 - w)) >> 8) 
  gnew = ((g * w) >> 8) + ((g2 * (255 - w)) >> 8)
  bnew = ((b * w) >> 8) + ((b2 * (255 - w)) >> 8)  
  Plot(x,y,RGB(rnew,gnew,bnew))
EndProcedure

; draw anti aliased line on current drawing surface
Procedure AntiLineXY(x1,y1,x2,y2,col)
  r = Red(col)
  g = Green(col)
  b = Blue(col)
  FrontColor(r,g,b)
  Plot(x1,y1,col)
  Plot(x2,y2,col)
  xd = x2 - x1
  yd = y2 - y1
  If (xd = 0 Or yd = 0)
    LineXY(x1,y1,x2,y2)
    ProcedureReturn
  EndIf
  If Abs(xd) > Abs(yd)
    If (x1>x2)
      tmp = x1: x1 = x2: x2 = tmp
      tmp = y1: y1 = y2: y2 = tmp
      xd = x2-x1
      yd = y2-y1
    EndIf
    grad = yd * 65536 / xd
    yf = y1 * 65536
    For x = x1 + 1 To x2 - 1
      yf = yf + grad 
      w = (yf >> 8) & $FF
      y = yf >> 16
      MergePixel(x,y,r,g,b,255-w)
      MergePixel(x,y+1,r,g,b,w)
    Next
  Else
    If (y1 > y2)
      tmp = x1: x1 = x2: x2 = tmp
      tmp = y1: y1 = y2: y2 = tmp
      xd = x2 - x1
      yd = y2 - y1
    EndIf
    grad = xd * 65536 / yd
    xf = x1 * 65536
    For y = y1 + 1 To y2 - 1
      xf = xf + grad 
      w = (xf >> 8) & $FF
      x = xf >> 16
      MergePixel(x,y,r,g,b,255-w)
      MergePixel(x+1,y,r,g,b,w)
    Next
  EndIf
EndProcedure
User avatar
griz
Enthusiast
Enthusiast
Posts: 167
Joined: Sun Jun 29, 2003 7:32 pm
Location: Canada

demonstration

Post by griz »

Here is a quick demonstration of the code above. The rays drawn on the right of the screen are anti-aliased, the ones on the left are not. You can enable the screenshot flag (see code) to save the results to 'out.bmp' when you hit ESCape to exit. If I comment out the Anti-Aliased line rays I get 1650 fps, but only 24 fps when they are drawn. No debugger enabled. This is on an Athlon XP 2400/Radeon 9700 system. Faster/better techniques? Please share them here.

Code: Select all

; Anti -Aliased line demo by Griz Nov 2004

ExamineDesktops()

Global Done               : Done = #False
Global ScreenWidth        : ScreenWidth = DesktopWidth(0)
Global screenheight       : ScreenHeight=DesktopHeight(0)
Global ScreenDepth        : ScreenDepth = DesktopDepth(0)
Global Radius             : Radius = ScreenWidth / 4
Global CircleAX           : CircleAX = Radius
Global CircleAY           : CircleAY = Radius+2
Global CircleBX           : CircleBX = Radius*3-2
Global CircleBY           : CircleBY = Radius+2
Global Angle              : Angle = 180
Global Lcol               : Lcol = RGB(255,255,255)
Global ScreenShot         : Screen_Shot = #false
Global Fps.f              : Fps = 0.0
Global FpsTimerCount      : FpsTimerCount = 0
Global CurrentFps.f       : CurrentFps = 0.0
Global FpsTimer           : FpsTimer = ElapsedMilliseconds()

Procedure Mod(a,b) 
  If b = 0: b = 1:EndIf 
  ProcedureReturn a-a/b*b 
EndProcedure

Procedure.f DegToRad(deg.f)
  ProcedureReturn deg/57.29578
EndProcedure

Procedure.f RadToDeg(rad.f)
  ProcedureReturn rad*57.29578
EndProcedure

Procedure.f WrapAngle(value.f)
  value.f=mod(value.f,360.0)
  If value.f<0.0
    value.f=value.f+360.0
  EndIf
  ProcedureReturn value.f
EndProcedure

Procedure MergePixel(x,y,r,g,b,w)
  col = Point(x,y) : r2 = Red(col)
  g2 = Green(col)  : b2 = Blue(col)
  rnew = ((r * w) >> 8) + ((r2 * (255 - w)) >> 8) 
  gnew = ((g * w) >> 8) + ((g2 * (255 - w)) >> 8)
  bnew = ((b * w) >> 8) + ((b2 * (255 - w)) >> 8)  
  Plot(x,y,RGB(rnew,gnew,bnew))
EndProcedure

Procedure AntiLineXY(x1,y1,x2,y2,col)
  r = Red(col)
  g = Green(col)
  b = Blue(col)
  FrontColor(r,g,b)
  Plot(x1,y1,col)
  Plot(x2,y2,col)
  xd = x2 - x1
  yd = y2 - y1
  If (xd = 0 Or yd = 0)
    LineXY(x1,y1,x2,y2)
    ProcedureReturn
  EndIf
  If Abs(xd) > Abs(yd)
    If (x1>x2)
      tmp = x1: x1 = x2: x2 = tmp
      tmp = y1: y1 = y2: y2 = tmp
      xd = x2-x1
      yd = y2-y1
    EndIf
    grad = yd * 65536 / xd
    yf = y1 * 65536
    For x = x1 + 1 To x2 - 1
      yf = yf + grad 
      w = (yf >> 8) & $FF
      y = yf >> 16
      MergePixel(x,y,r,g,b,255-w)
      MergePixel(x,y+1,r,g,b,w)
    Next
  Else
    If (y1 > y2)
      tmp = x1: x1 = x2: x2 = tmp
      tmp = y1: y1 = y2: y2 = tmp
      xd = x2 - x1
      yd = y2 - y1
    EndIf
    grad = xd * 65536 / yd
    xf = x1 * 65536
    For y = y1 + 1 To y2 - 1
      xf = xf + grad 
      w = (xf >> 8) & $FF
      x = xf >> 16
      MergePixel(x,y,r,g,b,255-w)
      MergePixel(x+1,y,r,g,b,w)
    Next
  EndIf
EndProcedure

; initialize direct X
If InitSprite()=0 Or InitKeyboard()=0
  MessageRequester("Error", "Can't Open Direct X!", 0)
  End
EndIf

; open full screen
If OpenScreen(screenwidth, screenheight, screendepth, "Antialiased Line Demo")=0
  MessageRequester("Error", "Can't Open Screen!", 0)
  End
EndIf

Repeat
  ExamineKeyboard()
  If KeyboardReleased(#pb_key_escape)
    Done = #true
  EndIf

  ClearScreen(0,0,0)
  StartDrawing(ScreenOutput())
    ; draw lines
    For a2 = 0 To 36
      a3=WrapAngle(a2*10+angle)
      LineXY(CircleAX, CircleAY, CircleAX-Radius*Cos(DegToRad(a3)),CircleAY-Radius*Sin(DegToRad(a3)),lcol)
      AntiLineXY(CircleBX, CircleBY, CircleBX-Radius*Cos(DegToRad(a3)),CircleBY-Radius*Sin(DegToRad(a3)),lcol)
    Next
    ; draw frames per second
    DrawingMode(1)
    FrontColor(128,128,128)    
    Status$="fps = "+Str(CurrentFps)
    Locate((CircleBX - CircleAX)-(TextLength(status$)/2), CircleAY*2)
    DrawText(status$)
  StopDrawing()
  
  If Done = #True And ScreenShot = #True
    g=GrabSprite(#pb_any,0,0,ScreenWidth, ScreenHeight)
    SaveSprite(g, "out.bmp")
    FreeSprite(g)
  EndIf
  
  FlipBuffers(0)
  
  Angle + 1
  If Angle > 359
    Angle = 0
  EndIf   
  
  Fps+1
  FpsTimerCount = ElapsedMilliseconds()
  If FpsTimerCount - FpsTimer => 1000
    CurrentFps = Fps
    Fps = 0
    FpsTimer = ElapsedMilliseconds()
  EndIf  
Until done=#true
End
El_Choni
TailBite Expert
TailBite Expert
Posts: 1007
Joined: Fri Apr 25, 2003 6:09 pm
Location: Spain

Post by El_Choni »

Just one thing: use radians, forget degrees. The FPU talks in radians, so it's always faster to do the calculations directly in radians.

Another thing: you don't need that Mod() procedure either, PureBasic obtains the module using "a%b" (doesn't work with floats, anyway, AFAIK)

My version:

Code: Select all

; Anti -Aliased line demo by Griz Nov 2004

ExamineDesktops()

Global Done               : Done = #False
Global ScreenWidth        : ScreenWidth = DesktopWidth(0)
Global screenheight       : ScreenHeight = DesktopHeight(0)
Global ScreenDepth        : ScreenDepth = DesktopDepth(0)
Global Radius             : Radius = ScreenWidth/4
Global CircleAX           : CircleAX = Radius
Global CircleAY           : CircleAY = Radius+2
Global CircleBX           : CircleBX = Radius*3-2
Global CircleBY           : CircleBY = Radius+2
Global Angle.f            : Angle = 3.1416
Global Lcol               : Lcol = $FFFFFF
Global ScreenShot         : Screen_Shot = #false
Global Fps.f              : Fps = 0.0
Global FpsTimerCount      : FpsTimerCount = 0
Global CurrentFps.f       : CurrentFps = 0.0
Global FpsTimer           : FpsTimer = ElapsedMilliseconds()

Procedure MergePixel(x, y, r, g, b, w)
  col = Point(x, y):r2 = col&$FF
  g2 = (col&$FF00)>>8:b2 = (col&$FF0000)>>16
  rnew = ((r*w)>>8)+((r2*(255-w))>>8)
  gnew = ((g*w)>>8)+((g2*(255-w))>>8)
  bnew = ((b*w)>>8)+((b2*(255-w))>>8) 
  Plot(x, y, (rnew&$FF)|(gnew<<8)|(bnew<<16))
EndProcedure

Procedure AntiLineXY(x1, y1, x2, y2, col)
  r = col&$FF
  g = (col&$FF00)>>8
  b = (col&$FF0000)>>16
  Plot(x1, y1, col)
  Plot(x2, y2, col)
  xd = x2-x1
  yd = y2-y1
  If (xd=0 Or yd=0)
    LineXY(x1, y1, x2, y2)
    ProcedureReturn
  EndIf
  If Abs(xd)>Abs(yd)
    If (x1>x2)
      tmp = x1: x1 = x2: x2 = tmp
      tmp = y1: y1 = y2: y2 = tmp
      xd = x2-x1
      yd = y2-y1
    EndIf
    grad = yd<<16/xd
    yf = y1<<16
    For x=x1+1 To x2-1
      yf+grad
      w = (yf>>8)&$FF
      y = yf>>16
      MergePixel(x, y, r, g, b, 255-w)
      MergePixel(x, y+1, r, g, b, w)
    Next
  Else
    If (y1>y2)
      tmp = x1: x1 = x2: x2 = tmp
      tmp = y1: y1 = y2: y2 = tmp
      xd = x2-x1
      yd = y2-y1
    EndIf
    grad = xd<<16/yd
    xf = x1<<16
    For y=y1+1 To y2-1
      xf+grad
      w = (xf>>8)&$FF
      x = xf>>16
      MergePixel(x , y, r, g, b, 255-w)
      MergePixel(x+1, y, r, g, b, w)
    Next
  EndIf
EndProcedure

; initialize direct X
If InitSprite()=0 Or InitKeyboard()=0
  MessageRequester("Error", "Can't Open Direct X!", 0)
  End
EndIf

; open full screen
If OpenScreen(screenwidth, screenheight, screendepth, "Antialiased Line Demo")=0
  MessageRequester("Error", "Can't Open Screen!", 0)
  End
EndIf

#speed.f = 0.03
#radia = 32
#inter.f = 6.2832/#radia

Repeat
  ExamineKeyboard()
  If KeyboardReleased(#pb_key_escape)
    Done = #true
  EndIf
  FlipBuffers()
  ClearScreen(0, 0, 0)
  StartDrawing(ScreenOutput())
    ; draw lines
    a2.f = 0
    While a2<6.2832
      a3.f = a2+Angle
      LineXY(CircleAX, CircleAY, CircleAX-Radius*Cos(a3), CircleAY-Radius*Sin(a3), Lcol)
      AntiLineXY(CircleBX, CircleBY, CircleBX-Radius*Cos(a3), CircleBY-Radius*Sin(a3), Lcol)
      a2+#inter
    Wend
    ; draw frames per second
    DrawingMode(1)
    FrontColor(128, 128, 128)   
    Status$="fps = "+Str(CurrentFps)
    Locate((CircleBX-CircleAX)-(TextLength(status$)/2), CircleAY*2)
    DrawText(status$)
  StopDrawing()
  If Done=#True And ScreenShot=#True
    g = GrabSprite(#pb_any, 0, 0, ScreenWidth, ScreenHeight)
    SaveSprite(g, "out.bmp")
    FreeSprite(g)
  EndIf
  Angle+#speed
  If Angle>6.2832:Angle-6.2832:EndIf   
  Fps+1
  FpsTimerCount = ElapsedMilliseconds()
  If FpsTimerCount-FpsTimer=>1000
    CurrentFps = Fps
    Fps = 0
    FpsTimer = ElapsedMilliseconds()
  EndIf 
Until done=#true
End  
Last edited by El_Choni on Wed Nov 24, 2004 4:14 am, edited 1 time in total.
El_Choni
Max.²
Enthusiast
Enthusiast
Posts: 175
Joined: Wed Jul 28, 2004 8:38 am

Re: demonstration

Post by Max.² »

griz wrote:Here is a quick demonstration of the code above. The rays drawn on the right of the screen are anti-aliased, the ones on the left are not. You can enable the screenshot flag (see code) to save the results to 'out.bmp' when you hit ESCape to exit. If I comment out the Anti-Aliased line rays I get 1650 fps, but only 24 fps when they are drawn. No debugger enabled. This is on an Athlon XP 2400/Radeon 9700 system. Faster/better techniques? Please share them here.
Seems that the Point and the Plot functions take a lot of time. I tried to replace with GetPixel and SetPixel API functions, but that made matters worse. .-/

Maybe this can help you?

viewtopic.php?t=11297
User avatar
griz
Enthusiast
Enthusiast
Posts: 167
Joined: Sun Jun 29, 2003 7:32 pm
Location: Canada

Post by griz »

Seems that the Point and the Plot functions take a lot of time.
You're right Max.², When I use Stefan Möbius's great little E2D lib (download here : http://www.purearea.net/pb/english/index.htm) and replace point() with pointfast() lines are drawn nearly three times as fast. Thanks to Stefan for the E2D lib! :)
Post Reply