Page 1 of 1

Directdraw Library - Antialiasing; Restore Background; etc..

Posted: Sun May 17, 2009 4:39 pm
by jayagopal
please use this if you like. i've gotten so much support from this forum that i am really happy to give something. i hope it is at all useful to someone.

please give any feedback for optimization or additions. (this is just a start)

Code: Select all

;=========================================;
;                                         ;
;            C I N T A M A N I            ;
;                                         ;
;       Directdraw Graphics Library       ;
;                                         ;
;_________________________________________;
;                                         ;
;      jayagopal.das -- May 15, 2009      ;
;                                         ;
;=========================================;

;-Constants
;#screenW  =1280: #screenH = 800
;#screenW  =1024: #screenH = 768
#screenW  = 800: #screenH = 600
;#screenW  = 640: #screenH = 480
;#screenW  = 320: #screenH = 240

#RD=0
#GR=1
#BL=2

#Background=-2
#Foreground=-1

;-Procedure Declarations
;Graphics
Declare redraw()                                                     ; draw the virtual-screen on the real one
Declare clear(x1=0,y1=0,x2=#screenW-1,y2=#screenH-1)                 ; restore the background (or a portion of it)
Declare addPage()                                                    ; add a screen page
Declare deletePage(pageNumber) ;NOT TESTED YET!!!
Declare copyPageFore(pageNumber)                                     ; copy a screen page to the foreground
Declare copyPageBack(pageNumber)                                     ; copy a screen page to the background
Declare.c virtualScreen(x,y,channel,intensity=-1)                    ; read/write the appropriate screen page
Declare mLine(x1,y1,x2,y2,r=255,g=255,b=255,a=255,antialias=0)       ; manual line drawing routine
Declare mCircle(x,y,rad,r=255,g=255,b=255,a=255,antialias=0)         ; manual circle drawing routine
Declare pixel(x,y,r,g,b,a=255)                                       ; write a pixel to the virtual screen

;Calculation
Declare.f frac(num.f)                                                ; return the fractional part of a number
Declare.f radius(dx,dy)                                              ; return a value from the radius table
Declare sign(n)                                                      ; return the sign of a number

;-Global arrays and variables
Global Dim radiusTable.f(#screenW,#screenH)                          ; to speed up distance calculations
Global *foreground=AllocateMemory(#screenW*#screenH*4)               ; the foreground screen page
Global *background=AllocateMemory(#screenW*#screenH*4)               ; the background screen page
Global Dim *page(0)                                                  ; used for storing additional screen pages
Global pageCount=0
Global Buffer                                                        ; starting address for screen memory
Global Pitch                                                         ; bytes in one screen-line
Global drawPage                                                      ; the page to draw to



;==========STARTING FROM HERE, this can be in another file==================
;-Initialization
If InitSprite()=0 Or InitKeyboard()=0 Or InitMouse()=0: End: EndIf
If OpenScreen(#screenW, #screenH, 32, "PB DirectGraphics Library Example")

  ;- Main Loop
  ;{ contains examples of drawing using these procedures

  ;draw some static on a gradient on 4 different pages
  For i=1 To 4
    addPage()
    drawPage=i ;this is the page-number to draw to (index starts from 1)
    For y=0 To #screenH-1: For x=0 To #screenW-1
      pixel(x,y,Int(x/#screenW*255),Int(y/#screenH*255),Int((x+y)/(#screenW+#screenH)*255)): pixel(x,y,Random(64),Random(64),Random(64),128)
    Next: Next
  Next
  
  ;some variables for drawing the thingy
  rad=#screenH*0.14: circumference=rad*2*#PI: t.f: ti.f
  
  Repeat
    ExamineKeyboard()
    ExamineMouse()

    drawPage=#Foreground
      ;draw a weird thingy that spins And follows the mouse
      ;mx=MouseX(): my=MouseY()
      mx=DesktopMouseX(): my=DesktopMouseY()
      mLine(#screenW/2,#screenH/2,mx,my,255,255,255,155,1)
      t=ti: ti+0.05
      For i=0 To 23
        t+(#PI/12): px=mx+rad*Cos(t): py=my+rad*Sin(t)
        mLine(mx,my,px,py,255,255,255,155,1)
        mCircle(px,py,#screenH*0.02,255,255,255,155,1)
      Next
          
      ;draw some random lines, circles and pixels
      For i=1 To 3000: pixel(Random(#screenW),Random(#screenH), Random(255),Random(255),Random(255), Random(155)): Next
      For i=1 To 10
        mLine(Random(#screenW),Random(#screenH), Random(#screenW),Random(#screenH), Random(255),Random(255),Random(255), Random(55),1)
        mCircle(Random(#screenW),Random(#screenH), Random(#screenW), Random(255),Random(255),Random(255), Random(55),1)
      Next
      mCircle(#screenW/2,#screenH/2,#screenH*0.3, 255,255,255,155,0) ;standing circle
      mCircle(#screenW/2,#screenH/2,#screenH*0.4, 255,255,255,155,1) ;standing antialiased circle
      
      mLine(#screenW*0.375,#screenH,#screenW,#screenH*0.375, 255,255,255,155,0) ;standing line
      mLine(0,#screenH/2,#screenW/2,0, 255,255,255,155,1)                       ;standing antialiased line
      
    ;cycle the background through all the extra pages (this will not effect the foreground, but WILL overwrite the background)
    If q=pageCount: q=1: EndIf: copyPageBack(q): q+1
    
    drawPage=#Background
      ;draw directly onto the background (even after the foreground is drawn) -- this will be updated after clear() is called
      For r =1 To 50: mCircle(100,100, r, Random(255),Random(255),Random(255), Random(55),1): Next          
      
    redraw()
    clear() ;full clear
    ;halfDim=rad+#screenH*0.02+1: clear(mx-halfDim,my-halfDim,mx+halfDim,my+halfDim) ;example of clearing just a portion
    
  Until KeyboardPushed(#PB_Key_Escape) ;}  
  
Else
  MessageRequester("Error","Sorry, some problem drawing the screen?",0)
EndIf

End
;==========AND ENDING HERE, this can be in another file==================



;-Graphics procedures      
Procedure redraw()
  restoredrawPage=drawPage
  drawPage=#Foreground
  If StartDrawing(ScreenOutput())
    Buffer = DrawingBuffer()                                     ; Get the start address of the screen buffer.
    Pitch  = DrawingBufferPitch()                                ; Get the length (in bytes) of one horizontal line.  
    
    For y=0 To #screenH-1
      *Line.l = Buffer+Pitch*y
      For x=0 To #screenW-1
        PokeC(*Line, virtualScreen(x,y,#BL)): *Line+1
        PokeC(*Line, virtualScreen(x,y,#GR)): *Line+1
        PokeC(*Line, virtualScreen(x,y,#RD)): *Line+2
      Next
    Next
    StopDrawing()
    FlipBuffers() ; Page flipping
  EndIf
  drawPage=restoredrawPage
EndProcedure
Procedure clear(x1=0,y1=0,x2=#screenW-1,y2=#screenH-1)
  If x1=0 And y1=0 And x2=#screenW-1 And y2=#screenH-1
    CopyMemory(*background,*foreground,#screenW*#screenH*4)
  Else
    ;restore the specified portion
    If x1>x2: Swap x1, x2: EndIf
    If y1>y2: Swap y1, y2: EndIf
    If x1<0: x1=0: EndIf: If x2>#screenW-1: x2=#screenW-1: EndIf
    If y1<0: y1=0: EndIf: If y2>#screenH-1: y2=#screenH-1: EndIf
    
    width=(x2-x1)*4
    For y=y1 To y2
      offset=y*#screenW*4+x1*4
      CopyMemory(*background+offset,*foreground+offset,width)
    Next
  EndIf
EndProcedure
Procedure addPage()
  pageCount=ArraySize(*page())+1
  ReDim *page(pageCount)
  *page(pageCount)=AllocateMemory(#screenW*#screenH*4)
EndProcedure
Procedure deletePage(pageNumber)
  FreeMemory(*page(pageNumber))
  For i=pageNumber To pageCount: *page(i)=*page(i+1): Next
  pageCount-1
EndProcedure
Procedure copyPageFore(pageNumber)
  If pageNumber>0 And pageNumber<=pageCount
    CopyMemory(*page(pageNumber),*foreground,#screenW*#screenH*4)
  EndIf
EndProcedure
Procedure copyPageBack(pageNumber)
  If pageNumber>0 And pageNumber<=pageCount
    CopyMemory(*page(pageNumber),*background,#screenW*#screenH*4)
  EndIf
EndProcedure
Procedure.c virtualScreen(x,y,channel,intensity=-1)
  Select drawPage
    Case #Foreground: *pageAddress=*foreground
    Case #Background: *pageAddress=*background
    Default: *pageAddress=*page(drawPage)
  EndSelect  
  
  If intensity=-1
    intensity=PeekC(*pageAddress+y*#screenW*4+x*4+channel)
    ProcedureReturn intensity
  Else
    PokeC(*pageAddress+y*#screenW*4+x*4+channel,intensity)
    ProcedureReturn 1
  EndIf
EndProcedure
Procedure mLine(x1,y1,x2,y2,r=255,g=255,b=255,a=255,antialias=0)
  If antialias: gradient.f: y.f: C1.f: C2.f: xend.f: yend.f: xgap.f: deltax.f: deltay.f: EndIf        ;initialize floats
  
  If Abs(y2-y1) > Abs(x2-x1): steep=1: EndIf  ;detect slopes>.5
  If steep: Swap x1, y1: Swap x2, y2: EndIf
  If x1>x2: Swap x1, x2: Swap y1, y2: EndIf
  
  ;ix1=x1:ix2=x2
  deltax=x2-x1
  deltay=Abs(y2-y1)
  error=deltax/2
  y=y1
  
  If antialias And deltax<>0
    gradient=deltay/deltax+0.0010001 ;get slope    
    
    ;generate antialiased endpoints
    xend=Int(x1+0.5)
    yend=y1+gradient*(xend-x1)
    xgap=1-frac(x1)
    ix1=Int(xend)
    iy1=Int(yend)
    C2=frac(yend+0.5)*xgap
    C1=1-C2
    ;If steep: pixel(iy1,ix1,r,g,b,a*C1): pixel(iy1+1,ix1,r,g,b,a*C2)
    ;Else: pixel(ix1,iy1,r,g,b,a*C1): pixel(ix1,iy1+1,r,g,b,a*C2): EndIf
    
    ;y=yend+gradient
    
    xend=Int(x2+0.5)
    yend=y2+gradient*(xend-x2)
    xgap=1-frac(x2)
    ix2=Int(xend)
    iy2=Int(yend)
    C2=frac(yend+0.5)*xgap
    C1=1-C2
    ;If steep: pixel(iy2,ix2,r,g,b,a*C1): pixel(iy2+1,ix2,r,g,b,a*C2)
    ;Else: pixel(ix2,iy2,r,g,b,a*C1): pixel(ix2,iy2+1,r,g,b,a*C2): EndIf
  EndIf
  
  If y1<y2: ystep = 1: Else: ystep = -1: gradient*-1: EndIf
  For x=x1 To x2
    If Not antialias ;bresenham's algorithm
      If steep: pixel(y,x,r,g,b,a): Else: pixel(x,y,r,g,b,a): EndIf
      error-deltay
      If error<0: y+ystep: error+deltax: EndIf
    Else ;wu's algorithm
      C2=frac(y+0.5)
      C1=1-C2
      If steep: pixel(y+0,x,r,g,b,a*C1): Else: pixel(x,y+0,r,g,b,a*C1): EndIf
      If steep: pixel(y+1,x,r,g,b,a*C2): Else: pixel(x,y+1,r,g,b,a*C2): EndIf      
      y+gradient
    EndIf
  Next
EndProcedure
Procedure mCircle(x,y,rad,r=255,g=255,b=255,a=255,antialias=0)
  If rad>0
    ys.f
    If antialias: C1.f: C2.f: Else: C1=1: EndIf
    For xs = -rad*0.7071 To -1
      ys=Sqr(rad*rad-xs*xs)
      If antialias: C2=frac(ys+0.5): C1=1-C2: EndIf
      pixel(x+xs,y+ys,r,g,b,a*C1)
      pixel(x-xs,y+ys,r,g,b,a*C1)
      pixel(x+xs,y-ys,r,g,b,a*C1)
      pixel(x-xs,y-ys,r,g,b,a*C1)
      
      pixel(x+ys,y+xs,r,g,b,a*C1)
      pixel(x-ys,y+xs,r,g,b,a*C1)
      pixel(x+ys,y-xs,r,g,b,a*C1)
      pixel(x-ys,y-xs,r,g,b,a*C1)
      
      If antialias
        pixel(x+xs,y+ys+1,r,g,b,a*C2)
        pixel(x-xs,y+ys+1,r,g,b,a*C2)
        pixel(x+xs,y-ys-1,r,g,b,a*C2)
        pixel(x-xs,y-ys-1,r,g,b,a*C2)
        
        pixel(x+ys+1,y+xs,r,g,b,a*C2)
        pixel(x-ys-1,y+xs,r,g,b,a*C2)
        pixel(x+ys+1,y-xs,r,g,b,a*C2)
        pixel(x-ys-1,y-xs,r,g,b,a*C2)
      EndIf
    Next
    If antialias: C2=frac(rad+0.5): C1=1-C2: Else: C1=1: EndIf
    pixel(x,y+rad,r,g,b,a*C1)
    pixel(x,y-rad,r,g,b,a*C1) 
    pixel(x+rad,y,r,g,b,a*C1) 
    pixel(x-rad,y,r,g,b,a*C1) 
    
    If antialias
      pixel(x,y+rad+1,r,g,b,a*C2)
      pixel(x,y-rad-1,r,g,b,a*C2)
      pixel(x+rad+1,y,r,g,b,a*C2)
      pixel(x-rad-1,y,r,g,b,a*C2)
    EndIf
    
    ;circumference=rad*2*#PI  
    ;;generic calculation:
    ;t.f
    ;For i = 1 To circumference
    ;  t+2*#PI/circumference
    ;  pixel(x+rad*Cos(t), y+rad*Sin(t), r,g,b,a)
    ;Next
  EndIf
EndProcedure
Procedure pixel(x,y,r=255,g=255,b=255,a=255)
  If r<0 : r=0 : EndIf
  If g<0 : g=0 : EndIf
  If b<0 : b=0 : EndIf
  If a<0 : a=0 : EndIf  
  
  If r>255 : r=255 : EndIf
  If g>255 : g=255 : EndIf
  If b>255 : b=255 : EndIf
  If a>255 : a=255 : EndIf
  
  If x>=0 And x<#screenW And y>=0 And y<#screenH
    If a<255
      ar=255-a
      r=virtualScreen(x,y,#RD)*ar/255+b*a/255
      g=virtualScreen(x,y,#GR)*ar/255+g*a/255
      b=virtualScreen(x,y,#BL)*ar/255+r*a/255
    EndIf
    virtualScreen(x,y,#RD,r)
    virtualScreen(x,y,#GR,g)
    virtualScreen(x,y,#BL,b)
  EndIf
EndProcedure

;-Calculation procedures
Procedure.f frac(num.f)
    num=num-Int(num)
    ProcedureReturn num
  EndProcedure
Procedure.f radius(dx,dy)  
  ProcedureReturn radiusTable(Int(Abs(dx)),Int(Abs(dy)))
EndProcedure
Procedure sign(n)
  ProcedureReturn n/Abs(n)
EndProcedure

Posted: Sun May 17, 2009 5:11 pm
by NoahPhense
Sweet..

- np

an OPTIMIZATION strategy...

Posted: Sun May 17, 2009 5:31 pm
by jayagopal
:)

there is a way to seriously optimize this... but i need help figuring out how to structure the screen pages to match the actual drawing buffer. then you can do a single memory copy of the whole page directly into the drawing buffer (right now i'm doing a byte at a time with poke).

the example below is WAY FASTER... but it is seriously glitchy (means flickery) and only works 'cuz of some bizarre magic number (which may not work at all on other machines... let me know). i'm posting it just to demonstrate what i'm hinting at in the hope that someone can help me make it work properly (sorry to make this so long by pasting the whole thing again with the changes... but it's just easier):

WARNING: THE FIRST POST IS THE MORE FUNCTIONAL VERSION, THIS VERSION IS JUST TO DEMONSTRATE AN OPTIMIZATION BUT IT DOES NOT FULLY WORK YET

Code: Select all

;=========================================;
;                                         ;
;            C I N T A M A N I            ;
;                                         ;
;       Directdraw Graphics Library       ;
;                                         ;
;_________________________________________;
;                                         ;
;      jayagopal.das -- May 15, 2009      ;
;                                         ;
;=========================================;

;-Constants
;#screenW  =1280: #screenH = 800: #magic=448
;#screenW  =1024: #screenH = 768: #magic=-256
#screenW  = 800: #screenH = 600: #magic=24
;#screenW  = 640: #screenH = 480: #magic=224
;#screenW  = 320: #screenH = 240: #magic=624

#RD=0
#GR=1
#BL=2

#Background=-2
#Foreground=-1

;-Procedure Declarations
;Graphics
Declare redraw()                                                     ; draw the virtual-screen on the real one
Declare clear(x1=0,y1=0,x2=#screenW-1,y2=#screenH-1)                 ; restore the background (or a portion of it)
Declare addPage()                                                    ; add a screen page
Declare deletePage(pageNumber) ;NOT TESTED YET!!!
Declare copyPageFore(pageNumber)                                     ; copy a screen page to the foreground
Declare copyPageBack(pageNumber)                                     ; copy a screen page to the background
Declare.c virtualScreen(x,y,channel,intensity=-1)                    ; read/write the appropriate screen page
Declare mLine(x1,y1,x2,y2,r=255,g=255,b=255,a=255,antialias=0)       ; manual line drawing routine
Declare mCircle(x,y,rad,r=255,g=255,b=255,a=255,antialias=0)         ; manual circle drawing routine
Declare pixel(x,y,r,g,b,a=255)                                       ; write a pixel to the virtual screen

;Calculation
Declare.f frac(num.f)                                                ; return the fractional part of a number
Declare.f radius(dx,dy)                                              ; return a value from the radius table
Declare sign(n)                                                      ; return the sign of a number

;-Global arrays and variables
Global Dim radiusTable.f(#screenW,#screenH)                          ; to speed up distance calculations
Global pageSize=4*#screenH*(2*#screenW+#magic) ;!!!!!!!!!!!!!!!!!!!!!!!!
;Global pageSize=4*#screenH*#screenW
Global *foreground=AllocateMemory(pageSize)               ; the foreground screen page
Global *background=AllocateMemory(pageSize)               ; the background screen page
Global Dim *page(0)                                                  ; used for storing additional screen pages
Global pageCount=0
Global Buffer                                                        ; starting address for screen memory
Global Pitch                                                         ; bytes in one screen-line
Global drawPage                                                      ; the page to draw to


;==========STARTING FROM HERE, this can be in another file==================
;-Initialization
If InitSprite()=0 Or InitKeyboard()=0 Or InitMouse()=0: End: EndIf
If OpenScreen(#screenW, #screenH, 32, "PB DirectGraphics Library Example")

  ;- Main Loop
  ;{ contains examples of drawing using these procedures

  ;draw some static on a gradient on 4 different pages
  For i=1 To 4
    addPage()
    drawPage=i ;this is the page-number to draw to (index starts from 1)
    For y=0 To #screenH-1: For x=0 To #screenW-1
      pixel(x,y,Int(x/#screenW*255),Int(y/#screenH*255),Int((x+y)/(#screenW+#screenH)*255)): pixel(x,y,Random(64),Random(64),Random(64),128)
    Next: Next
  Next
  
  ;some variables for drawing the thingy
  rad=#screenH*0.14: circumference=rad*2*#PI: t.f: ti.f
  
  Repeat
    ExamineKeyboard()
    ExamineMouse()

    drawPage=#Foreground
      ;draw a weird thingy that spins And follows the mouse
      ;mx=MouseX(): my=MouseY()
      mx=DesktopMouseX(): my=DesktopMouseY()
      mLine(#screenW/2,#screenH/2,mx,my,255,255,255,155,1)
      t=ti: ti+0.05
      For i=0 To 23
        t+(#PI/12): px=mx+rad*Cos(t): py=my+rad*Sin(t)
        mLine(mx,my,px,py,255,255,255,155,1)
        mCircle(px,py,#screenH*0.02,255,255,255,155,1)
      Next
          
      ;draw some random lines, circles and pixels
      For i=1 To 3000: pixel(Random(#screenW),Random(#screenH), Random(255),Random(255),Random(255), Random(155)): Next
      For i=1 To 10
        mLine(Random(#screenW),Random(#screenH), Random(#screenW),Random(#screenH), Random(255),Random(255),Random(255), Random(55),1)
        mCircle(Random(#screenW),Random(#screenH), Random(#screenW), Random(255),Random(255),Random(255), Random(55),1)
      Next
      mCircle(#screenW/2,#screenH/2,#screenH*0.3, 255,255,255,155,0) ;standing circle
      mCircle(#screenW/2,#screenH/2,#screenH*0.4, 255,255,255,155,1) ;standing antialiased circle
      
      mLine(#screenW*0.375,#screenH,#screenW,#screenH*0.375, 255,255,255,155,0) ;standing line
      mLine(0,#screenH/2,#screenW/2,0, 255,255,255,155,1)                       ;standing antialiased line
      
    ;cycle the background through all the extra pages (this will not effect the foreground, but WILL overwrite the background)
    If q=pageCount: q=1: EndIf: copyPageBack(q): q+1
    
    drawPage=#Background
      ;draw directly onto the background (even after the foreground is drawn) -- this will be updated after clear() is called
      For r =1 To 50: mCircle(100,100, r, Random(255),Random(255),Random(255), Random(55),1): Next          
      
    redraw()
    clear() ;full clear
    ;halfDim=rad+#screenH*0.02+1: clear(mx-halfDim,my-halfDim,mx+halfDim,my+halfDim) ;example of clearing just a portion
    
  Until KeyboardPushed(#PB_Key_Escape) ;}  
  
Else
  MessageRequester("Error","Sorry, some problem drawing the screen?",0)
EndIf

End
;==========AND ENDING HERE, this can be in another file==================



;-Graphics procedures      
Procedure redraw()
  restoredrawPage=drawPage
  drawPage=#Foreground
  If StartDrawing(ScreenOutput())
    Buffer = DrawingBuffer()                                     ; Get the start address of the screen buffer.
    CopyMemory(*foreground,Buffer,pageSize) ;!!!!!!!!!!!!!!!!!!!!!!!!
    ;Pitch  = DrawingBufferPitch()                                ; Get the length (in bytes) of one horizontal line.  
    ;
    ;For y=0 To #screenH-1
    ;  *Line.l = Buffer+Pitch*y
    ;  For x=0 To #screenW-1
    ;    PokeC(*Line, virtualScreen(x,y,#BL)): *Line+1
    ;    PokeC(*Line, virtualScreen(x,y,#GR)): *Line+1
    ;    PokeC(*Line, virtualScreen(x,y,#RD)): *Line+2
    ;  Next
    ;Next
    StopDrawing()
    FlipBuffers() ; Page flipping
  EndIf
  drawPage=restoredrawPage
EndProcedure
Procedure clear(x1=0,y1=0,x2=#screenW-1,y2=#screenH-1)
  If x1=0 And y1=0 And x2=#screenW-1 And y2=#screenH-1
    CopyMemory(*background,*foreground,pageSize)
  Else
    ;restore the specified portion
    If x1>x2: Swap x1, x2: EndIf
    If y1>y2: Swap y1, y2: EndIf
    If x1<0: x1=0: EndIf: If x2>#screenW-1: x2=#screenW-1: EndIf
    If y1<0: y1=0: EndIf: If y2>#screenH-1: y2=#screenH-1: EndIf
    
    width=(x2-x1)*4
    For y=y1 To y2
      offset=y*#screenW*4+x1*4
      CopyMemory(*background+offset,*foreground+offset,width)
    Next
  EndIf
EndProcedure
Procedure addPage()
  pageCount=ArraySize(*page())+1
  ReDim *page(pageCount)
  *page(pageCount)=AllocateMemory(pageSize)
EndProcedure
Procedure deletePage(pageNumber)
  FreeMemory(*page(pageNumber))
  For i=pageNumber To pageCount: *page(i)=*page(i+1): Next
  pageCount-1
EndProcedure
Procedure copyPageFore(pageNumber)
  If pageNumber>0 And pageNumber<=pageCount
    CopyMemory(*page(pageNumber),*foreground,pageSize)
  EndIf
EndProcedure
Procedure copyPageBack(pageNumber)
  If pageNumber>0 And pageNumber<=pageCount
    CopyMemory(*page(pageNumber),*background,pageSize)
  EndIf
EndProcedure
Procedure.c virtualScreen(x,y,channel,intensity=-1)
  Select drawPage
    Case #Foreground: *pageAddress=*foreground
    Case #Background: *pageAddress=*background
    Default: *pageAddress=*page(drawPage)
  EndSelect  
  
  If y<#screenH
  offset=(#screenW+#magic*4)*y
  
  If intensity=-1
    intensity=PeekC(*pageAddress+y*#screenW*4+x*4+channel+offset)
    ProcedureReturn intensity
  Else
    PokeC(*pageAddress+y*#screenW*4+x*4+channel+offset,intensity)
    ProcedureReturn 1
  EndIf
  EndIf
EndProcedure
Procedure mLine(x1,y1,x2,y2,r=255,g=255,b=255,a=255,antialias=0)
  If antialias: gradient.f: y.f: C1.f: C2.f: xend.f: yend.f: xgap.f: deltax.f: deltay.f: EndIf        ;initialize floats
  
  If Abs(y2-y1) > Abs(x2-x1): steep=1: EndIf  ;detect slopes>.5
  If steep: Swap x1, y1: Swap x2, y2: EndIf
  If x1>x2: Swap x1, x2: Swap y1, y2: EndIf
  
  ;ix1=x1:ix2=x2
  deltax=x2-x1
  deltay=Abs(y2-y1)
  error=deltax/2
  y=y1
  
  If antialias And deltax<>0
    gradient=deltay/deltax+0.0010001 ;get slope    
    
    ;generate antialiased endpoints
    xend=Int(x1+0.5)
    yend=y1+gradient*(xend-x1)
    xgap=1-frac(x1)
    ix1=Int(xend)
    iy1=Int(yend)
    C2=frac(yend+0.5)*xgap
    C1=1-C2
    ;If steep: pixel(iy1,ix1,r,g,b,a*C1): pixel(iy1+1,ix1,r,g,b,a*C2)
    ;Else: pixel(ix1,iy1,r,g,b,a*C1): pixel(ix1,iy1+1,r,g,b,a*C2): EndIf
    
    ;y=yend+gradient
    
    xend=Int(x2+0.5)
    yend=y2+gradient*(xend-x2)
    xgap=1-frac(x2)
    ix2=Int(xend)
    iy2=Int(yend)
    C2=frac(yend+0.5)*xgap
    C1=1-C2
    ;If steep: pixel(iy2,ix2,r,g,b,a*C1): pixel(iy2+1,ix2,r,g,b,a*C2)
    ;Else: pixel(ix2,iy2,r,g,b,a*C1): pixel(ix2,iy2+1,r,g,b,a*C2): EndIf
  EndIf
  
  If y1<y2: ystep = 1: Else: ystep = -1: gradient*-1: EndIf
  For x=x1 To x2
    If Not antialias ;bresenham's algorithm
      If steep: pixel(y,x,r,g,b,a): Else: pixel(x,y,r,g,b,a): EndIf
      error-deltay
      If error<0: y+ystep: error+deltax: EndIf
    Else ;wu's algorithm
      C2=frac(y+0.5)
      C1=1-C2
      If steep: pixel(y+0,x,r,g,b,a*C1): Else: pixel(x,y+0,r,g,b,a*C1): EndIf
      If steep: pixel(y+1,x,r,g,b,a*C2): Else: pixel(x,y+1,r,g,b,a*C2): EndIf      
      y+gradient
    EndIf
  Next
EndProcedure
Procedure mCircle(x,y,rad,r=255,g=255,b=255,a=255,antialias=0)
  If rad>0
    ys.f
    If antialias: C1.f: C2.f: Else: C1=1: EndIf
    For xs = -rad*0.7071 To -1
      ys=Sqr(rad*rad-xs*xs)
      If antialias: C2=frac(ys+0.5): C1=1-C2: EndIf
      pixel(x+xs,y+ys,r,g,b,a*C1)
      pixel(x-xs,y+ys,r,g,b,a*C1)
      pixel(x+xs,y-ys,r,g,b,a*C1)
      pixel(x-xs,y-ys,r,g,b,a*C1)
      
      pixel(x+ys,y+xs,r,g,b,a*C1)
      pixel(x-ys,y+xs,r,g,b,a*C1)
      pixel(x+ys,y-xs,r,g,b,a*C1)
      pixel(x-ys,y-xs,r,g,b,a*C1)
      
      If antialias
        pixel(x+xs,y+ys+1,r,g,b,a*C2)
        pixel(x-xs,y+ys+1,r,g,b,a*C2)
        pixel(x+xs,y-ys-1,r,g,b,a*C2)
        pixel(x-xs,y-ys-1,r,g,b,a*C2)
        
        pixel(x+ys+1,y+xs,r,g,b,a*C2)
        pixel(x-ys-1,y+xs,r,g,b,a*C2)
        pixel(x+ys+1,y-xs,r,g,b,a*C2)
        pixel(x-ys-1,y-xs,r,g,b,a*C2)
      EndIf
    Next
    If antialias: C2=frac(rad+0.5): C1=1-C2: Else: C1=1: EndIf
    pixel(x,y+rad,r,g,b,a*C1)
    pixel(x,y-rad,r,g,b,a*C1) 
    pixel(x+rad,y,r,g,b,a*C1) 
    pixel(x-rad,y,r,g,b,a*C1) 
    
    If antialias
      pixel(x,y+rad+1,r,g,b,a*C2)
      pixel(x,y-rad-1,r,g,b,a*C2)
      pixel(x+rad+1,y,r,g,b,a*C2)
      pixel(x-rad-1,y,r,g,b,a*C2)
    EndIf
    
    ;circumference=rad*2*#PI  
    ;;generic calculation:
    ;t.f
    ;For i = 1 To circumference
    ;  t+2*#PI/circumference
    ;  pixel(x+rad*Cos(t), y+rad*Sin(t), r,g,b,a)
    ;Next
  EndIf
EndProcedure
Procedure pixel(x,y,r=255,g=255,b=255,a=255)
  If r<0 : r=0 : EndIf
  If g<0 : g=0 : EndIf
  If b<0 : b=0 : EndIf
  If a<0 : a=0 : EndIf  
  
  If r>255 : r=255 : EndIf
  If g>255 : g=255 : EndIf
  If b>255 : b=255 : EndIf
  If a>255 : a=255 : EndIf
  
  If x>=0 And x<#screenW And y>=0 And y<#screenH
    If a<255
      ar=255-a
      r=virtualScreen(x,y,#RD)*ar/255+b*a/255
      g=virtualScreen(x,y,#GR)*ar/255+g*a/255
      b=virtualScreen(x,y,#BL)*ar/255+r*a/255
    EndIf
    virtualScreen(x,y,#RD,r)
    virtualScreen(x,y,#GR,g)
    virtualScreen(x,y,#BL,b)
  EndIf
EndProcedure

;-Calculation procedures
Procedure.f frac(num.f)
    num=num-Int(num)
    ProcedureReturn num
  EndProcedure
Procedure.f radius(dx,dy)  
  ProcedureReturn radiusTable(Int(Abs(dx)),Int(Abs(dy)))
EndProcedure
Procedure sign(n)
  ProcedureReturn n/Abs(n)
EndProcedure