Rotozoom function...

Game development specific discussions.
Dr_D
Posts: 2345
Joined: May 27, 2005 4:59
Contact:

Rotozoom function...

Postby Dr_D » Jan 30, 2009 23:39

I've been posting this thing in other threads, but I thought I might as well post it in it's own thread. I made this for fbext originally, but most people don't want to download that whole library just to use a zoom function. Anyway, here it is. Rotozoomer

EDIT: Archive updated with asm version and bug fix.
Last edited by Dr_D on Feb 09, 2009 3:23, edited 3 times in total.
vdecampo
Posts: 2980
Joined: Aug 07, 2007 23:20
Location: Maryland, USA
Contact:

Postby vdecampo » Jan 30, 2009 23:52

This is a really nice piece of code, but there are lots of places you could speed this up....

1. Pre Compute Sin/Cos lookup tables
2. Many variables don't change, so compute them one time. (ie: Screen Width, Pitch, BPP, etc...)

I think this would make a good OOP object too. I will add this to my collection. :)

-Vince
Dr_D
Posts: 2345
Joined: May 27, 2005 4:59
Contact:

Postby Dr_D » Jan 30, 2009 23:57

Thanks. I hope you can use it... and I'm all for speeding it up. If you think you can, go for it. :)

The thing is, with trig lokup tables, that wouldn't really help. It only calls sin/cos once per frame. In the inner loops it just uses the var, not a function call. Also, pitch, bpp are already pre-computed. I might mess around and see if I can get it working with asm. That really seems like it would be fun, ya know? ;)
vdecampo
Posts: 2980
Joined: Aug 07, 2007 23:20
Location: Maryland, USA
Contact:

Postby vdecampo » Jan 31, 2009 3:28

I rolled the function up in OOP cause I like OOP! :) and tried to make some simple optimizations. I must say your code was already very fast. I tried to unroll some loops and some of the redundant calculations, but I think I only gained <5 fps. I wish there was an alternative to the sorting of the coords. I guess the next step would be to slowly convert sections to pure ASM.

rotozoom.bi

Code: Select all

/'
   RotoZoomer by Dave Stanley(Dr_D)
   OOP by Vincent DeCampo
'/

#Include Once "fbgfx.bi"

Type _RotoZoom
   Declare Constructor()
   Declare Sub RZ(dst As FB.IMAGE Ptr = 0,src As FB.IMAGE Ptr, dstX As Integer,dstY As Integer,angle As Integer,zoom As Single )
Private:
   As Double   pi = Atn(1)*4
   As Double   pi_180
   As UInteger transcol = &hFF00FF
   As Integer  SCR_W
   As Integer  SCR_H
   As Integer  SCR_W2
   As Integer  SCR_H2
   As Integer  BPP
   As Integer  PITCH
   As Double   _Cos(359)
   As Double   _Sin(359)
   As Single   nxtc, nxts, nytc, nyts
   As Single   tc,ts,sw2z,sh2z,sw2z_ts,sw2z_tc,sh2z_tc,sh2z_ts
   As UInteger Ptr dstptr, srcptr
   As UByte Ptr srcbyteptr, dstbyteptr
   As Integer  xput, yput, startx, endx, starty, endy, temp
   As Integer  x(3), y(3), xa, xb, ya, yb
   As Integer  srcpitch, srcbpp, srcwidth, srcheight, dstpitch,sw2,sh2,dw,dh,dstbpp
   As Integer  nx, ny, mx, my, col   ', lx, ly, i, j
End Type

Constructor _RotoZoom

   ScreenInfo (SCR_W,SCR_H,BPP,,PITCH)
   SCR_W2 = SCR_W\2
   SCR_H2 = SCR_H\2
   
   pi_180 = pi / 180
   
   For ang As Integer = 0 To 359
      _Cos(ang)=Cos(ang*pi_180)
      _Sin(ang)=Sin(ang*pi_180)
   Next

End Constructor

Sub _RotoZoom.RZ(dst As FB.IMAGE Ptr = 0,src As FB.IMAGE Ptr, dstX As Integer,dstY As Integer,angle As Integer,zoom As Single )

   If dst = 0 Then
      dstptr = ScreenPtr
      dw     = SCR_W
      dh     = SCR_H
      dstbpp = BPP
   Else
      dstptr = Cast( UInteger Ptr, dst + 1 )
      dw     = dst->Width
      dh     = dst->height
      dstbpp = dst->bpp
   End If

   srcptr      = Cast(UInteger Ptr, src + 1 )
   srcbyteptr  = Cast(UByte Ptr, srcptr )
   dstbyteptr  = Cast(UByte Ptr, dstptr )

   'Get Source Image Attributes
   sw2 = src->Width\2
   sh2 = src->height\2
   srcbpp = src->bpp
   srcpitch = src->pitch
   srcwidth = src->Width
   srcheight = src->height

   tc = _cos(angle)
   ts = _sin(angle)

   sw2z = (sw2 * zoom)
   sh2z = (sh2 * zoom)
   sw2z_ts = sw2z*ts
   sw2z_tc = sw2z*tc
   sh2z_tc = sh2z*tc
   sh2z_ts = sh2z*ts

   xa = (sw2z_tc + sh2z_ts)
   ya = (sh2z_tc - sw2z_ts)

   xb = (sh2z_ts - sw2z_tc)
   yb = (sw2z_ts + sh2z_tc)

   tc/=zoom
   ts/=zoom

   x(0) = sw2-xa
   x(1) = sw2+xa
   x(2) = sw2-xb
   x(3) = sw2+xb

   y(0) = sh2-ya
   y(1) = sh2+ya
   y(2) = sh2-yb
   y(3) = sh2+yb

   'This section eats about 5fps
   For i As Integer = 0 To 3
      For j As Integer = i+1 To 3
         If x(i) > x(j) Then
            temp = x(i)
            x(i) = x(j)
            x(j) = temp
         End If
         If y(i) > y(j) Then
            temp = y(i)
            y(i) = y(j)
            y(j) = temp
         End If
      Next
   Next

   startx   = x(0)
   endx     = x(3)
   starty   = y(0)
   endy     = y(3)

   If dstY+starty < 0  Then starty = -dstY
   If dstX+startx < 0  Then startx = -dstX
   If dstX+endx>(dw-1) Then endx = (dw-1)-dstX
   If dstY+endy>(dh-1) Then endy = (dh-1)-dstY

   Select Case As Const dst
      Case 0
         For ly As Integer = starty To endy

            yput = ly + dstY
            ny = ly - sh2
            dstpitch = (yput * dw )
            nytc = (ny * tc)
            nyts = (ny * ts)

            For lx As Integer = startx To endx

               nx = lx - sw2
               nxtc = (nx * tc)
               nxts = (nx * ts)
               mx = (nxtc - nyts) + sw2
               my = (nytc + nxts) + sh2

               If mx>-1 Then
                  If my>-1 Then
                     If mx<srcwidth Then
                        If my<srcheight Then
                           xput = lx + dstX
                           col = *Cast(UInteger Ptr, srcbyteptr + my * srcpitch + mx * srcbpp )
                           If col <> transcol Then
                              dstptr[ dstpitch + xput ] = col
                           End If
                        End If
                     End If
                  End If
               End If

            Next
         Next

      Case Else

         For ly As Integer = starty To endy

            yput = ly + dstY
            ny = ly - sh2
            dstpitch = yput * dst->pitch
            nytc = (ny * tc)
            nyts = (ny * ts)

            For lx As Integer = startx To endx

               nx = lx - sw2
               nxtc = (nx * tc)
               nxts = (nx * ts)
               mx = (nxtc - nyts) + sw2
               my = (nytc + nxts) + sh2

               If mx>-1 Then
                  If my>-1 Then
                     If mx<srcwidth Then
                        If my<srcheight Then
                           xput = lx + dstX
                           col = *Cast(UInteger Ptr, srcbyteptr + my * srcpitch + mx * srcbpp )
                           If col <> transcol Then
                              *Cast(UInteger Ptr, dstbyteptr + dstpitch + xput * dstbpp) = col
                           End If
                        End If
                     End If
                  End If
               End If

            Next
         Next

   End Select

End Sub


rotozoom.bas

Code: Select all

#Include "rotozoom.bi"

Dim As Integer SCR_W  = 640
Dim As Integer SCR_H  = 480
Dim As Integer SCR_W2 = SCR_W/2
Dim As Integer SCR_H2 = SCR_H/2

ScreenRes SCR_W,SCR_H,32,,FB.GFX_HIGH_PRIORITY

Dim As String filename
Dim As Integer iw, ih, fpscount, bpp
Dim As fb.image Ptr image, background
Dim As String mString, fpsString
Dim As Double this_time, last_loop, last_toggle, fpsTime
Dim As Integer angle
Dim As Single zoom = 1

   filename = "rectangle.bmp"
   Open filename For Binary As #1
   Get #1,19,iw
   Get #1,23,ih
   Close #1
   
   image = ImageCreate(iw, ih)
   BLoad filename, image
   
   'background = ImageCreate(SCR_W, SCR_H)
   
   Dim As _RotoZoom PutRZ

Do
    last_loop = Timer-this_time
    this_time = Timer
   
    fpsCount+=1
    If this_time>fpsTime Then
        fpsTime = this_time+1
        fpsString = "FPS: " & fpsCount
        fpsCount = 0
    End If
       
    If MultiKey(FB.SC_UP) Then
        zoom *=.995
        If zoom<.01 Then zoom = .01
    End If
   
    If MultiKey(FB.SC_DOWN) Then
        zoom *=1.005
        If zoom>200 Then zoom = 200
    End If
   
    If MultiKey(FB.SC_LEFT) Then
        angle -=1
        If angle<0 Then angle = 359
    End If
   
    If MultiKey(FB.SC_RIGHT) Then
        angle +=1
        If angle>359 Then angle = 0
    End If
 
   'render to screen
   ScreenLock
   Line (0,0)-(640,480),&hFF,bf
   PutRZ.RZ(0,image, SCR_W2-image->Width/2, SCR_H2-image->height/2, angle, zoom )
   
   'render to buffer
   'Line background,(0,0)-(640,480),&hFF,bf
   'PutRZ.RZ(background,image, SCR_W2-image->Width\2, SCR_H2-image->height\2, angle, zoom )
   
   'ScreenLock
   'Put (0,0),background,PSet
   
   Color(&HFFFFFF)
   Locate 1,1
   Print fpsString, angle
   
   'screensync
   ScreenUnLock
   
   Sleep 1,1
   
Loop Until MultiKey(FB.SC_ESCAPE)


-Vince
vdecampo
Posts: 2980
Joined: Aug 07, 2007 23:20
Location: Maryland, USA
Contact:

Postby vdecampo » Jan 31, 2009 3:33

Also, I'm not sure if SWAP is optimized by the compiler to swap pointers or if it calls a routine so I changed it to use a temp variable. If swap is optimized, I would switch it back. I was trying to eliminate the overhead of calling into another function.

-Vince
Dr_D
Posts: 2345
Joined: May 27, 2005 4:59
Contact:

Postby Dr_D » Jan 31, 2009 3:35

Cool man. :)
recycled
Posts: 8
Joined: Feb 16, 2006 0:08
Location: recycled bin

Postby recycled » Feb 01, 2009 6:17

I am having 3 problems using rotozoom in my experimental game.

can you move image position to center of image just like multiput did?
image rotates on its center wonderfully, but drawing position is upper left corner.

can you add ZoomX and ZoomY?
it will be more useful stretching the imges than just Zoom it.

It seems rotation angle is backward too(counter-clockwise).
Dr_D
Posts: 2345
Joined: May 27, 2005 4:59
Contact:

Postby Dr_D » Feb 01, 2009 6:47

Here's a version that uses the center as the location of "put." Counter clockwise for positive rotation anlge is actually the standard, so I'm going to keep that. You can modify it if you want though. All you have to do is this:

Code: Select all

tc = cos( -angle * pi_180 )
ts = sin( -angle * pi_180 )


As for adding x/y zooming... I have another version, but it isn't nearly as fast as this one. I can post it up here, if you want, but I really wouldn't recommend using it in a game... unless we can get it ported to fast asm. As DJ mentioned before, this one uses sin/cos on every single pixel, so it looks a bit better, but lacks the speed. It would really be better to use opengl or directx. ;)


EDIT: you'll need to change the function declaration as well...

Code: Select all

declare sub rotozoom( byref dst as FB.IMAGE ptr = 0, byref src as FB.IMAGE ptr, byval positx as integer, byval posity as integer, byref angle as integer, byref zoom as single )



Code: Select all

sub rotozoom( byref dst as FB.IMAGE ptr = 0, byref src as FB.IMAGE ptr, byval positx as integer, byval posity as integer, byref angle as integer, byref zoom as single )

    'rotozoomer for 32 bpp by Dave Stanley(Dr_D)
    static as integer nx, ny, mx, my, col, lx, ly, i, j
    static as single nxtc, nxts, nytc, nyts
    static as integer sw2, sh2, dw, dh
    static as single tc,ts
    static as uinteger ptr dstptr, srcptr
    static as ubyte ptr srcbyteptr, dstbyteptr
    static as integer xput, yput, startx, endx, starty, endy
    static as integer x(3), y(3), xa, xb, ya, yb
    static as integer dstpitch, srcpitch, srcbpp, dstbpp, srcwidth, srcheight
    static as uinteger transcol = rgb( 255, 0, 255 )
   
    if dst = 0 then
        dstptr = screenptr
        screeninfo dw,dh
    else
        dstptr = cast( uinteger ptr, dst + 1 )
        dw = dst->width
        dh = dst->height
        dstbpp = dst->bpp
    end if
   
    srcptr = cast( uinteger ptr, src + 1 )
    srcbyteptr = cast( ubyte ptr, srcptr )
    dstbyteptr = cast( ubyte ptr, dstptr )

    sw2 = src->width\2
    sh2 = src->height\2
    srcbpp = src->bpp
    srcpitch = src->pitch
    srcwidth = src->width
    srcheight = src->height

    tc = cos( angle * -pi_180 )
    ts = sin( angle * -pi_180 )
   
    xa = ((sw2 * zoom)*tc + (sh2  * zoom)*ts)
    ya = ((sh2 * zoom)*tc - (sw2  * zoom)*ts)
   
    xb = ((sh2 * zoom)*ts - (sw2  * zoom)*tc)
    yb = ((sw2 * zoom)*ts + (sh2  * zoom)*tc)
   
    tc/=zoom
    ts/=zoom
   
    x(0) = sw2-xa
    x(1) = sw2+xa
    x(2) = sw2-xb
    x(3) = sw2+xb
    y(0) = sh2-ya
    y(1) = sh2+ya
    y(2) = sh2-yb
    y(3) = sh2+yb
   
    for i = 0 to 3
        for j = i to 3
            if x(i)>=x(j) then
                swap x(i), x(j)
            end if
        next
    next
    startx = x(0)
    endx = x(3)
   
    for i = 0 to 3
        for j = i to 3
            if y(i)>=y(j) then
                swap y(i), y(j)
            end if
        next
    next
    starty = y(0)
    endy = y(3)
   
    positx-=sw2
    posity-=sh2
    if posity+starty<0 then starty = -posity
    if positx+startx<0 then startx = -positx
    if positx+endx>(dw-1) then endx = (dw-1)-positx
    if posity+endy>(dh-1) then endy = (dh-1)-posity
   
    select case as const dst
        case 0
            for ly = starty to endy
               
                yput = ly + posity
                ny = ly - sh2
                dstpitch = (yput * dw )
                nytc = (ny * tc)
                nyts = (ny * ts)
               
                for lx = startx to endx
                   
                    nx = lx - sw2
                    nxtc = (nx * tc)
                    nxts = (nx * ts)
                    mx = (nxtc - nyts) + sw2
                    my = (nytc + nxts) + sh2
                   
                    if mx>-1 then
                        if my>-1 then
                            if mx<srcwidth then
                                if my<srcheight then
                                   
                                    xput = lx + positx
                                    col = *cast(uinteger ptr, srcbyteptr + my * srcpitch + mx * srcbpp )
                                   
                                    if col <> transcol then
                                        dstptr[ dstpitch + xput ] = col
                                    end if
                                   
                                end if
                            end if
                        end if
                    end if
                   
                next
               
            next
           
        case else
           
            for ly = starty to endy
               
                yput = ly + posity
                ny = ly - sh2
                dstpitch = yput * dst->pitch
                nytc = (ny * tc)
                nyts = (ny * ts)
               
                for lx = startx to endx
                   
                    nx = lx - sw2
                    nxtc = (nx * tc)
                    nxts = (nx * ts)
                    mx = (nxtc - nyts) + sw2
                    my = (nytc + nxts) + sh2
                   
                    if mx>-1 then
                        if my>-1 then
                            if mx<srcwidth then
                                if my<srcheight then
                                   
                                    xput = lx + positx
                                    col = *cast(uinteger ptr, srcbyteptr + my * srcpitch + mx * srcbpp )
                                   
                                    if col <> transcol then
                                        *cast(uinteger ptr, dstbyteptr + dstpitch + xput * dstbpp) = col
                                    end if
                                   
                                end if
                            end if
                        end if
                    end if
                   
                next
               
            next
           
    end select
   
end sub
recycled
Posts: 8
Joined: Feb 16, 2006 0:08
Location: recycled bin

Postby recycled » Feb 02, 2009 4:47

l would like to see XY stretch version too.
not every game uses realtime zooming.

I am working on asteroid clone.
Dr_D
Posts: 2345
Joined: May 27, 2005 4:59
Contact:

Postby Dr_D » Feb 02, 2009 6:12

Cool! Do you have a demo? I'd like to see it. :)

Anyway, here's the stretching/skewing code:


EDIT: replaced the code with this... found a great optimization. I dunno how I missed it before. It's almost as fast as the non-skewing version now... maybe 1 fps difference. ;)

Code: Select all

sub rotozoomS( byref dst as FB.IMAGE ptr = 0, byref src as const FB.IMAGE ptr, byval positx as integer, byval posity as integer, byref angle as integer, byref zoomx as single, byref zoomy as single )
   
    static as integer nx, ny, mx, my, col
    static as single nxtc, nxts, nytc, nyts
    static as single tcdzx, tcdzy, tsdzx, tsdzy
    static as integer sw2, sh2, dw, dh
    static as single tc, ts
    static as uinteger ptr dstptr, srcptr
    static as integer xput, yput, startx, endx, starty, endy
    static as uinteger transcol
    static as integer x(3), y(3), xa, xb, ya, yb, lx, ly
    static as ubyte ptr srcbyteptr, dstbyteptr
    static as integer dstpitch, srcpitch, srcbpp, dstbpp, srcwidth, srcheight
   
    if zoomx <= 0 or zoomy <= 0 then exit sub
   
    transcol = rgb(255,0,255)
   
    if dst = 0 then
        dstptr = screenptr
        screeninfo dw,dh
    else
        dstptr = cast( uinteger ptr, dst + 1 )
        dw = dst->width
        dh = dst->height
        dstbpp = dst->bpp
    end if
   
    srcptr = cast( uinteger ptr, src + 1 )
    srcbyteptr = cast( ubyte ptr, srcptr )
    dstbyteptr = cast( ubyte ptr, dstptr )
   
    sw2 = src->width\2
    sh2 = src->height\2
    srcbpp = src->bpp
    srcpitch = src->pitch
    srcwidth = src->width
    srcheight = src->height
   
    tc = cos( angle * pi_180 )
    ts = sin( angle * pi_180 )
    tcdzx = tc/zoomx
    tcdzy = tc/zoomy
    tsdzx = ts/zoomx
    tsdzy = ts/zoomy

    xa = sw2 * tc * zoomx + sh2  * ts * zoomx
    ya = sh2 * tc * zoomy - sw2  * ts * zoomy
   
    xb = sh2 * ts * zoomx - sw2  * tc * zoomx
    yb = sw2 * ts * zoomy + sh2  * tc * zoomy
   
    x(0) = sw2-xa
    x(1) = sw2+xa
    x(2) = sw2-xb
    x(3) = sw2+xb
    y(0) = sh2-ya
    y(1) = sh2+ya
    y(2) = sh2-yb
    y(3) = sh2+yb
   
    for i as integer = 0 to 3
        for j as integer = i to 3
            if x(i)>=x(j) then
                swap x(i), x(j)
            end if
        next
    next
    startx = x(0)
    endx = x(3)
   
    for i as integer = 0 to 3
        for j as integer = i to 3
            if y(i)>=y(j) then
                swap y(i), y(j)
            end if
        next
    next
    starty = y(0)
    endy = y(3)
   
    positx-=sw2
    posity-=sh2
    if posity+starty<0 then starty = -posity
    if positx+startx<0 then startx = -positx
    if positx+endx>(dw-1) then endx = (dw-1)-positx
    if posity+endy>(dh-1) then endy = (dh-1)-posity
   
   
    select case as const dst
        case 0

            for ly = starty to endy
               
                yput = ly + posity
                ny = ly - sh2
               
                dstpitch = (yput * dw )
               
                nytc = (ny * tcdzy)
                nyts = (ny * tsdzy)
               
                for lx  = startx to endx
                   
                    nx = lx - sw2
                   
                    nxtc = (nx * tcdzx)
                    nxts = (nx * tsdzx)
                   
                    mx = (nxtc - nyts) + sw2
                    my = (nytc + nxts) + sh2
                   
                    if mx>-1 then
                        if my>-1 then
                            if mx<srcwidth then
                                if my<srcheight then
                                   
                                    xput = lx+positx
                                    col = *cast(uinteger ptr, srcbyteptr + my * srcpitch + mx * srcbpp )
                                   
                                    if col<>transcol then
                                        dstptr[ dstpitch + xput ] = col
                                    end if
                                   
                                end if
                            end if
                        end if
                    end if
                   
                next
               
            next
           
        case else

            for ly = starty to endy
               
                yput = ly + posity
                ny = ly - sh2
               
                dstpitch = (yput * dst->pitch )
               
                nytc = (ny * tcdzy)
                nyts = (ny * tsdzy)
               
                for lx  = startx to endx
                   
                    nx = lx - sw2
                   
                    nxtc = (nx * tcdzx)
                    nxts = (nx * tsdzx)
                   
                    mx = (nxtc - nyts) + sw2
                    my = (nytc + nxts) + sh2
                   
                    if mx>-1 then
                        if my>-1 then
                            if mx<srcwidth then
                                if my<srcheight then
                                   
                                    xput = lx+positx
                                    col = *cast(uinteger ptr, srcbyteptr + my * srcpitch + mx * srcbpp )
                                   
                                    if col<>transcol then
                                        *cast(uinteger ptr, dstbyteptr + dstpitch + xput * dstbpp) = col
                                    end if
                                   
                                end if
                            end if
                        end if
                    end if
                   
                next
               
            next
           
    end select

end sub
Dr_D
Posts: 2345
Joined: May 27, 2005 4:59
Contact:

Postby Dr_D » Feb 07, 2009 3:55

*slam*

Hey guys... just letting you know that there is an asm version in the archive in my first post now. If yetifoot wouldn't have stepped in and helped, there is no way it would be this good. Mucho praiso el foot! :)

If anyone sees anything they think they can improve, please do so!
ShawnLG
Posts: 80
Joined: Dec 25, 2008 20:21

Postby ShawnLG » Feb 07, 2009 8:05

Why was there a Motly Crue youtube video in the code. It had nothing to do with the program. This would be more appropriate http://www.youtube.com/watch?v=c46KPeCh5ec
vdecampo
Posts: 2980
Joined: Aug 07, 2007 23:20
Location: Maryland, USA
Contact:

Postby vdecampo » Feb 07, 2009 13:55

Is the zoom function only working in the x-axis, or has it always been like this and I just didnt notice?

-Vince
Dr_D
Posts: 2345
Joined: May 27, 2005 4:59
Contact:

Postby Dr_D » Feb 07, 2009 17:39

vdecampo, you can scale each axis independently now. if you only want the normal rotozoom effect, just send the same value to zoomx and zoomy.

nobody knows how that motley crue video got there...
Dr_D
Posts: 2345
Joined: May 27, 2005 4:59
Contact:

Postby Dr_D » Feb 07, 2009 22:33

Archive updated again with bounds checking bugfix and some more optimizations. I think this is now the fastest native fb rotozoomer there is.

Return to “Game Dev”

Who is online

Users browsing this forum: No registered users and 0 guests