[futurebasic] "just" rotation... (help)

Message: < previous - next > : Reply : Subscribe : Cleanse
Home   : January 2001 : Group Archive : Group : All Groups

From: "Peter Dempsey" <theviron@...>
Date: Mon, 08 Jan 2001 18:30:18 -0600
My tower of things I didn't know I didn't know just keeps growing...

I've been looking around for rotating a picture for a while (as said 
below... not much effort).  With the long string about fancy picture 
rotation, I decided to renew my quest (hey, it would fractionalize the size 
of starships!).  I did find the FBII function (RotatePIX).  However, when I 
tried to put it into FB3 it started whining a bunch about tons of things.  
So I started changing whatever errors there were, and it looks like it 
should work.  But alas, no, things are not so simple (because it was the 
perfect routine... well almost).  For some very odd it reason, it only 
copies the lower right corner, which it rotates.  Stop reading now if you 
know of an FB3 update (well.. it'll save me some embarassment from my 
guesses at whats wrong...).

Covington sent me an FB3 rotate picture function, but he thinks I'm smarter 
than I am ;).  It doesn't have convenient input variables for rects & 
gworlds... just weird addresses and byte moving stuff which I am very shaky 
on (but still, thanks!). Figured I should explain my switch over.

Of course assuming I get this working I will need more help because it is in 
a rectangle (most ships aren't rectangles)... so I'd have to get rid of a 
color or use a mask or something... (i copied the ships using the loop tool, 
and magically the empty space between the ship and its rect doesn't 
appear... no clue how/why though... but that magical benefit will be lost 
once I use rect rotations)

As always, thanks a bunch to everyone!

-Peter Dempsey

Heres what I have: (from Function Junction II's RotatePIX)

'----------------------------------
' FN rotatePix.BAS
' by Ross W. Lambert
' Copyright � 1995
' Ariel Publishing, Inc.
'----------------------------------

' Comments: This version of the pixmap rotation code requires 32-bit
' QuickDraw.

' Note also that the offscreen GWorlds are hardcoded to 256 colors.
' This is important because the size of the "chunk" that the functions
' must move around are dependent on the number of colors. If you need
' to support thousdands of colors, the chunk size is a word, not
' a byte, so you change the GWorlds to 16 bits of color information
' e.g. qdErr% = FN NEWGWORLD(GW&,16,#@bRect,0,0,_keepLocal).

' Next you change all PEEKs and POKEs to PEEK WORD and POKE WORD.

' Likewise, if you must support millions of colors (full 24 bit color),
' change the GWorlds to support 32 bits of color information and the
' PEEKs and POKEs to PEEK LONG and POKE LONG.

' Because so much more data is being shuffled about, it takes a little
' longer and requires more RAM. It is still surprisingly fast. I hope
' it does not sound arrogant to say it, but these routines are state
' of the art. Most paint programs shipping today do not move pixels
' around this fast!  - Ross

'COMPILE 0,_dimmedVarsOnly_noRedimVars
DEFSTR LONG
begin globals
DIM gSrcRect.8
DIM gDstRect.8
DIM h&, curr&, cDev&, qdErr%, longSide%, deg%
END GLOBALS

GOTO "demo"

'---------------------------------- Function Definitions

' FB's integer sine and cosine are misdocumented, but they are one
' of the keys to the raw speed of the routines. They do NOT take
' an angle (0-360), but a binary-ized angle (0-255). The routines
' below let you pass a normal angle and they binary-ize it on
' their own.

' FN intCos provides the integer cosine of the angle you pass.

' OK for Miniruntime: NO

' INPUTS:      angle%    -    INTEGER: an angle in the range 0�-359�
' OUTPUTS:     intCos%   -    INTEGER: an integerized cosine (0-255)
' GLOBALS:     none

DEF FN intCos(angle%) = USR _cosine(angle%*256\360)

'----------------------------------

' FN intSin provides the integer sine of the angle you pass.

' OK for Miniruntime: NO

' INPUTS:      angle%    -    INTEGER: an angle in the range 0�-359�
' OUTPUTS:     intCos%   -    INTEGER: an integerized cosine (0-255)
' GLOBALS:     none

DEF FN intSin(xangle%) = USR _sine(xangle%*256\360)

'----------------------------------

' FN fndBounds takes any four points and determines the smallest
' rectangle that will enclose them ALL. The toolbox has a similar
' routine for any two points, but this was handier for my purposes.

' INPUTS:      ptr& -    LONGINT: ptr to 4 contiguous point records
'                        (FB passes ptr automatically due to @)
'              rPtr& -   LONGINT: ptr to result rectangle (VAR)
'                        (FB passes ptr automatically due to @)
' OUTPUT:      none
' GLOBALS:     none

LOCAL MODE
LOCAL FN fndBounds(@ptra&,@rPtr&)
  DIM pt.4(3)                                     ' four point records
  DIM arect.8                                      ' a rectangle
  DIM i

  pt(0);16     = ptra&                             ' copy 4 sets of points 
from caller
  arect.top     = pt.v(0)                          ' arbitrary starting data 
for rect
  arect.left    = pt.h(0)
  arect.bottom  = pt.v(0)
  arect.right   = pt.h(0)

  FOR i = 1 TO 3                                  ' scan each remaining 
point
    IF pt.h(i) < arect.left THEN arect.left = pt.h(i)' is it left bnds?
    IF pt.h(i) > arect.right THEN arect.right = pt.h(i)' or right bnds?
    IF pt.v(i) < arect.top THEN arect.top = pt.v(i)' or upper bnds?
    IF pt.v(i) > arect.bottom THEN arect.bottom = pt.v(i)' or lower?
  NEXT

  BLOCKMOVE @arect,rPtr&,8                         ' return bounds rect
END FN

'-----------------------------------------

' FN cntrRect centers the second rectangle inside (or around)
' the first. This FN is useful for centering windows and other rects.

' INPUTS:      cPtr& - LONGINT: pointer to first rect
'              tPtr& - LONGINT: pointer to second rect
' OUTPUT:      none
' GLOBALS:     none

LOCAL MODE
LOCAL FN cntrRect(@cPtr&,@tPtr&)
  DIM cRect.8                                     ' rect to center
  DIM tRect.8                                     ' target rect (center 
within this rect)
  DIM dX%, dY%
  cRect;8    = cPtr&                              ' copy the rectangles
  tRect;8    = tPtr&
  CALL OFFSETRECT(cRect,-cRect.left,-cRect.top)   ' normalize
  dX% = ((tRect.right-tRect.left)-(cRect.right-cRect.left))/2
  dY% = ((tRect.bottom-tRect.top)-(cRect.bottom-cRect.top))/2
  CALL OFFSETRECT(cRect,dX%+tRect.left,dY%+tRect.top)' offset into position
  BLOCKMOVE @cRect,cPtr&,8                        ' copy back to caller
END FN

'----------------------------------

' FN rotatePix will quickly rotate pixels through any arbitrary angle.

' OK for Miniruntime: NO

' INPUTS:      rPtr&     -    LONGINT: pointer to source rectangle
'                             (FB passes ptr automatically due to @)
'              dPtr&     -    LONGINT: pointer to destination rect
'                             (FB passes ptr automatically due to @)
'              srcGW&    -    LONGINT: the source GWorld
'              dstGW&    -    LONGINT: the destination GWorld
'              angle%    -    INTEGER: angle of rotation

' OUTPUTS:     qdErr%    -    INTEGER: QuickDraw or memory error
'                             (0 = _noErr)
' GLOBALS:     none

LOCAL MODE
LOCAL FN rotatePix(@rPtr&,@dPtr&,srcGW&,dstGW&,angle%)
  DIM sRect.8                                     ' source rect
  DIM dRect.8                                     ' destination rect
  DIM bRect.8                                     ' bounding rect of rotated 
pixmap
  DIM nRect.8                                     ' normalized rect
  DIM pt1.4                                       ' four rectangle points�
  DIM pt2.4                                       ' �for figuring a bounds 
rect
  DIM pt3.4
  DIM pt4.4
  DIM newY%,newX%                                 ' for arbitrary angles
  DIM tx%, ty%, qdErr%, copyGW&, oldGW&, oldDev&, bWd%, bHt%, rotGW&, dest&
  DIM pmH&, ok%, rowBytes%, dRowBytes%, pixPtr&, sinTheta%, cosTheta%, x%, 
y%, b%
  DIM pixSize&, xOS%

  dRect;8      = dPtr&                            ' copy destination rect
  sRect;8      = rPtr&                            ' copy source rect
  bRect;8      = rPtr&                            ' bounds rect = source 
rect to start

  CALL GETGWORLD(oldGW&,oldDev&)                  ' save current GWorld

  angle%       = angle% MOD 360                   ' adjust angle for range = 
0�-359�
  IF angle% < 0 THEN angle% = 360 + angle%

  tx%  = (sRect.right-sRect.left)/2               ' distances to center
  ty%  = (sRect.bottom-sRect.top)/2

  CALL OFFSETRECT(bRect,-bRect.left-tx%,-bRect.top-ty%)' ctr rect @0,0
  nRect;8 = @bRect                                ' copy of centered rect

  qdErr% = FN NEWGWORLD(copyGW&,8,#@bRect,0,oldDev&,_keepLocal)
  LONG IF qdErr% = _noErr

    '----- Copy source rect offscreen
    CALL SETGWORLD(copyGW&,oldDev&)
    CALL FORECOLOR(_blackColor)
    CALL BACKCOLOR(_whiteColor)
    CALL COPYBITS(#srcGW&+2,#copyGW&+2,sRect,bRect,_patCopy,_nil)

    pmH&         = [copyGW&+_portpixmap]
    ok%          = FN LOCKPIXELS (pmH&)
    rowBytes%    = {[pmH&]+_pmrowBytes} AND &H3FFF
    pixPtr&      = FN GETPIXBASEADDR(pmH&)

    sinTheta%    = FN intSin(angle%)
    cosTheta%    = FN intCos(angle%)

    '------------- rotate source rect to find destination rect
    pt1.h   = (nRect.left*cosTheta% - nRect.top*sinTheta%) >> 8
    pt1.v   = (nRect.left*sinTheta% + nRect.top*cosTheta%) >> 8

    ' lower left
    pt2.h   = (nRect.left*cosTheta% - nRect.bottom*sinTheta%) >> 8
    pt2.v   = (nRect.left*sinTheta% + nRect.bottom*cosTheta%) >> 8

    ' lower right
    pt3.h   = (nRect.right*cosTheta% - nRect.bottom*sinTheta%) >> 8
    pt3.v   = (nRect.right*sinTheta% + nRect.bottom*cosTheta%) >> 8

    ' upper right
    pt4.h   = (nRect.right*cosTheta% - nRect.top*sinTheta%) >> 8
    pt4.v   = (nRect.right*sinTheta% + nRect.top*cosTheta%) >> 8

    FN fndBounds(pt1,nRect)                       ' find rotated bounding 
rect

    bWd%         = nRect.right - nRect.left       ' figure width of bounds 
rect
    bHt%         = nRect.bottom - nRect.top       ' figure ht of bounds rect

    qdErr% = FN NEWGWORLD(rotGW&,8,#@nRect,0,oldDev&,_keepLocal)

    LONG IF qdErr% = _noErr

      '----- Acquire ptr to actual pixel data in 2nd GWorld
      pmH&         = [rotGW&+_portPixMap]         ' pmH& is temp var
      dRowBytes%   = {[pmH&]+_pmRowBytes} AND &H3FFF
      ok%          = FN LOCKPIXELS (pmH&)
      dest&        = FN GETPIXBASEADDR(pmH&)
      '------
      CALL SETGWORLD(rotGW&,0)
      CALL FORECOLOR(_blackColor)
      CALL BACKCOLOR(_whiteColor)
      CALL ERASERECT(nRect)

      pixSize& = (nRect.bottom-nRect.top)*dRowBytes%

      angle%       = 360 - angle%                 ' we're reversing the 
process now!
      sinTheta%    = FN intSin(angle%)
      cosTheta%    = FN intCos(angle%)

      ' This algorithm scans each pixel in the destination pixmap,
      ' caculates which "unrotated" pixel is its source in the
      ' source pixmap, and copies the source to the destination.

      FOR y% = nRect.top TO nRect.bottom-1
        FOR x% = nRect.left TO nRect.right-1
          newX%   = ((x% * cosTheta% - y% *sinTheta%) >> 8)
          newY%   = ((x% * sinTheta% + y% *cosTheta%) >> 8)
          LONG IF newY% => bRect.top% AND newY% < bRect.bottom%
            LONG IF newX% => bRect.left% AND newX% < bRect.right%
              POKE 
dest&+xOS%,PEEK(pixPtr&+(newX%-bRect.left)+((newY%-bRect.top)*rowBytes%))
            END IF                                ' { END IF in horz bounds 
}
          END IF                                  ' { END IF in vert bounds 
}
          INC(xOS%)
        NEXT
        xOS% = 0                                  ' x offset
        dest& = dest& + dRowBytes%
      NEXT

      bRect;8 = @nRect
      FN cntrRect(bRect,dRect)                    ' centers bounds rect in 
destination
      b% = FN SECTRECT(bRect,dRect,bRect)         ' clip bounds to dest

      CALL SETGWORLD(dstGW&,oldDev&)
      CALL COPYBITS(#rotGW&+2,#dstGW&+2,nRect,bRect,_patCopy,_nil)

      CALL DISPOSEGWORLD(rotGW&)
    XELSE
      qdErr% = -1
    END IF                                        ' { END IF qdErr% for 2nd 
GWorld}
    CALL DISPOSEGWORLD(copyGW&)
  XELSE
    qdErr% = -1                                   ' our OOM code
  END IF                                          ' { END IF qdErr }
  CALL SETGWORLD(oldGW&,oldDev&)                  ' restore output to oldGW&
END FN = qdErr%


'---------------------------------- Demonstration
"demo"
RESOURCES "RotatePix.RSRC"

WINDOW 1,"FN rotatePix Demo"
TEXT _Monaco,9
DIM num

LONG IF SYSTEM(_maxColors) = 32                   ' QD32 available
PRINT
COLOR _zBlack
BOX FILL 0,0 TO 300,300
h& = FN GETPICTURE(128)                         ' ariel pict
LONG IF h& <> _nil
CALL GETGWORLD(curr&,cDev&)                   ' save current gWorld
gSrcRect;8 = @h&..picFrame%          ' get PICT rect
CALL DRAWPICTURE(h&,gSrcRect)

gDstRect        = gSrcRect                 ' copy the rect for destinations

longSide = SQR(gSrcRect.right*gSrcRect.right + 
gSrcRect.bottom*gSrcRect.bottom)
'revise pictRect so it will hold all rotated views
gDstRect.left = 0
gDstRect.top = 0
gDstRect.right = longSide
gDstRect.bottom = longSide

gDstRect.left = gDstRect.left + 50
gDstRect.top = gDstRect.top + 150
gDstRect.right = gDstRect.right + 50
gDstRect.bottom = gDstRect.bottom + 150
deg% = 15                                     ' starting degrees

DO
qdErr% = FN rotatePix(gSrcRect,gDstRect,curr&,curr&,deg%)
deg% = deg% + 1
UNTIL FN BUTTON OR LEN(INKEY$) OR qdErr%

CALL MOVETO  (10,240)


XELSE                                           ' ----- handle errors here

PRINT "Couldn't find PICT resource."
END IF

XELSE
PRINT "Sorry, this demo requires 32-bit QuickDraw."
END IF
END


_________________________________________________________________
Get your FREE download of MSN Explorer at http://explorer.msn.com