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