
FB II Compiler
PG PRO
Debugging
Memory
System
Mathematics
Resources
Disk I/O
Windows
Controls
Menus
Mouse
Keyboard
Text
Fonts
Drawing
Sound
Clipboard
Printing
Communication
ASM |
DRAWING
Set GWorld pixels directly
I have some experience doing this with 32 bit Gworlds. The code below is an example of poking values directly into a Gworlds pixel map. FN QD32Support and
FN copyBits are from PGs GRFX.FLTR. The FN build32bitGWorld function is a modifyed version of FN buildGWorld also from the GRFX.FLTR.
'========
LOCAL FN QD32Support
'---------------------------------------
' Return _zTrue if 32 bit Color
' QuickDraw is available. From PG GRFX.FLTR
'---------------------------------------
DIM QD32Support
QD32Support = _false
LONG IF FN GESTALT(_"qd ") => _gestalt32BitQD
LONG IF SYSERROR = _noErr
QD32Support = _zTrue
END IF
END IF
END FN = QD32Support
'========
LOCAL FN copyBits(srcWrld&,dstWrld&,@srcRect&,@dstRect&,cMode)
'---------------------------------------
' Copy bits from one world to another. From PG GRFX.FLTR
'---------------------------------------
DIM hasQD32
DIM oldWorld&,oldDevice&,oldPort&
hasQD32 = FN QD32Support
LONG IF hasQD32
CALL GETGWORLD(oldWorld&,oldDevice&)
CALL SETGWORLD(dstWrld&,oldDevice&)
XELSE
CALL GETPORT(oldPort&)
CALL SETPORT(dstWrld&)
IF cMode = _transparent THEN cMode = _srcOR
END IF
CALL COPYBITS(#srcWrld& + 2,#dstWrld& + 2,#srcRect&,#dstRect&,cMode,0)
LONG IF hasQD32
CALL SETGWORLD(oldWorld&,oldDevice&)
XELSE
CALL SETPORT(oldPort&)
END IF
END FN
'-----------
LOCAL FN build32bitGWorld(@rectPtr&)
'---------------------------------------
' Adapted from PG GRFX.FLTR
'---------------------------------------
DIM t,l,b,r
DIM QDErr,t$
DIM theWorld&,clipRgnRect&,visrgnRect&
t;8 = rectPtr&
'Note 32 in line below creates 32 bit gworld
QDErr = FN NEWGWORLD(theWorld&,32,#@t,0,0,0)
LONG IF QDErr
t$ = "Insufficient memory for GWorld"
CALL PARAMTEXT(t$,"","","")
controlNum%=FN NOTEALERT(128,0)
END IF
END FN = theWorld&
'-----------------------------------------------------
LOCAL FN pokeIntoGworld(Wd,Ht)
DIM rect.0,T,L,B,R
CALL SETRECT(rect,0,0,Wd,Ht)
'----------Make Gworld
gPrevGworld&=FN build32bitGWorld(rect.top%)
LONG IF gPrevGworld& <>_false
colorMapHand& = FN GETGWORLDPIXMAP(gPrevGworld&)
LONG IF colorMapHand&<>_false
locked = FN LOCKPIXELS(colorMapHand&)
LONG IF locked<>_false
colorPixMapAdr&=FN GETPIXBASEADDR(colorMapHand&)
LONG IF colorPixMapAdr&<>_false
'Note the width of 32 bit gworlds in pixels is evenly divisible by 4
remainderOfDivide = Wd MOD 4
FOR y=0 TO Ht-1 'do coloring
FOR x=0 TO Wd-1
'--------------Calculate address of pixel
'Need to add remainderOfDivide to make evenly divisible by 4
wdNum=Wd+4-remainderOfDivide
adr1&=colorPixMapAdr&+((wdNum<<2)*y)+(x<<2)
'example colors poked into pixmap
POKE adr1&+1, (y*x)>>1 'Red
POKE adr1&+2, (y*x)>>2 'Green
POKE adr1&+3, (y*x)>>3 'Blue
NEXT x
NEXT y
END IF
END IF
CALL UNLOCKPIXELS(colorMapHand&)
END IF
END IF
END FN=gPrevGworld&
'------------
LOCAL FN TestGworld
DIM rect.8
x=480
y=360
CALL SETRECT(rect,0,0,x,y)
WINDOW 1, "Poke Gworld",@rect
CALL GETGWORLD(currPort&,currDevice&) ' store the current graphics port and device
gWorld&=FN pokeIntoGworld(x,y)
LONG IF gWorld&
FN copyBits(gWorld&,currPort&,rect.top%,rect.top%,0)
DO
UNTIL LEN(INKEY$) OR FN BUTTON
CALL DISPOSEGWORLD(theWorld&)
END IF
END FN
FN TestGworld
The interest of this technique for many people is the speed. Here's a demo that compares SETCPIXEL with a custom FN SetCPixel in FB. An assembler version of FN SetCPixel, if anyone wants it, is 2-3 times faster again.
The GWorld is 8 bits deep (as commonly used for high speed graphics and animation). If you need to work with 16 or 32 bits, it's not too hard to modify FN SetCPixel. The demo itself works with any colour depth.
COMPILE ,_dimmedvarsOnly
DIM gRowBytes,gBaseAddr&,gGWRect.8
END GLOBALS
LOCAL FN SetCPixel(x,y,pixelVal&) 'fast replacement for CALL SETCPIXEL
LONG IF (x>=0) AND (x<gGWRect.right) ' Clip to gGWRect which must have...
LONG IF (y>=0) AND (y<gGWRect.bottom) '...its origin at (0,0).
POKE y*gRowBytes+gBaseAddr&+x,pixelVal&
END IF
END IF
END FN
LOCAL FN LockGWAndGetBaseAddr(offPort&)
DIM pmHand&,err
err=FN LOCKPIXELS(FN GETGWORLDPIXMAP(offPort&))
pmHand&=FN GETGWORLDPIXMAP(offPort&)
gRowBytes=pmHand&..RowBytes% AND &3FFF 'For use by SetCPixel to...
gBaseAddr&=FN GETPIXBASEADDR(pmHand&) '...compute pixel addresses.
END FN
LOCAL FN UnLockGW(offPort&)
CALL UNLOCKPIXELS(FN GETGWORLDPIXMAP(offPort&))
END FN
LOCAL FN CopyGW2GW(sPort&,dPort&,sRectPtr&,dRectPtr&)
CALL COPYBITS(#sPort&+2,#dPort&+2,#sRectPtr&,#dRectPtr&,_srcCopy,0)
END FN
LOCAL FN SetUpGW&(hSize,vSize)
DIM offPort&
CALL SETRECT(gGWRect,0,0,hSize,vSize)' GW's rect with origin (0,0)
LONG IF FN NEWGWORLD(offPort&,8,#@gGWRect,0,0,0)<>_noErr'8-bit depth
offPort&=_nil ' signal error
END IF
END FN=offPort&
LOCAL FN SetRandomRGB(rgbPtr&) ' for pretty colours
rgbPtr&.red%=RND(32767)*2
rgbPtr&.green%=RND(32767)*2
rgbPtr&.blue%=RND(32767)*2
END FN
DIM currPort&,currDevice&,offPort&,j,k,rgb.6,pixelVal&,tk1&,tk2&
WINDOW 1,"Compare",(50,50)-(410,430),_docNoGrow
offPort&=FN SetUpGW&(WINDOW(_width),WINDOW(_height))
IF offPort&=_nil THEN STOP
DO
tk1&=FN TICKCOUNT
FOR j=0 TO gGWRect.right-1 'Paint the window...
FN SetRandomRGB(@rgb) '...in coloured stripes...
FOR k=0 TO gGWRect.bottom-1 '...a pixel at a time...
CALL SETCPIXEL(j,k,rgb) '...by a slow method.
NEXT : NEXT
tk1&=FN TICKCOUNT-tk1&
tk2&=FN TICKCOUNT
CALL GETGWORLD(currPort&,currDevice&) ' save screen world for restore
CALL SETGWORLD(offPort&,0) ' set to write to GWorld
FN LockGWAndGetBaseAddr(offPort&) 'lock down before using
FOR j=0 TO gGWRect.bottom-1
FN SetRandomRGB(@rgb)
pixelVal&=FN COLOR2INDEX(rgb) ' convert rgb to pixel value
FOR k=0 TO gGWRect.right-1 'Paint the GWorld...
FN SetCPixel(k,j,pixelVal&) '...by a much faster method.
NEXT : NEXT
CALL SETGWORLD(currPort&,currDevice&) 'restore screen world
FN CopyGW2GW(offPort&,currPort&,@gGWRect,@gGWRect)'copy to wnd
FN UnLockGW(offPort&) 'unlock after use
tk2&=FN TICKCOUNT-tk2&
CALL TEXTMODE(_srcCopy)
PRINT%(210,20) tk1& "CALL SETCPIXEL" ' show times
PRINT%(210,40) tk2& "FN SetCPixel"
UNTIL FN BUTTON OR (INKEY$<>"")
CALL DISPOSEGWORLD(offPort&)
I'm having real trouble trying to convert this to run with either 16 bit and 32 bit.
for 16 bit:
POKE WORD y*gRowBytes+gBaseAddr& + (x*2), pixelVal&
and 32 bit:
POKE LONG y*gRowBytes+gBaseAddr& + (x*4), pixelVal&
From what I can tell though, if you're pokeing into an 8-bit gworld, you can easily then copybits to a 32 bit world - just make sure you specify "8" in depth when you call NEWGWORLD, and simply "POKE" away with the original function, and use CopyBits from world --> on screen when you're ready.
Of course, for optimum speed, keep everything in 8-bit mode(screen too).
Here is the assembler version of FN SetCPixel, in response to requests from graphics speed freaks.
LOCAL FN SetCPixel(x,y,pixelVal&)'fast replacement for CALL SETCPIXEL
REGISTER(a2)=@gGWRect
REGISTER(a1)=gBaseAddr&
REGISTER(d2)=gRowBytes
` move.w ^x,d0
` bmi.s done ; Clip to gGWRect which must have...
` cmp.w right(a2),d0
` bge.s done
` adda.w d0,a1
` move.w ^y,d0
` bmi.s done
` cmp.w bottom(a2),d0
` bge.s done ; ...its origin at (0,0).
` muls d2,d0
` move.l ^pixelVal&,d1
` move.b d1,(a1,d0.l)
`done
END FN
And here is an even faster version which you should use with caution. The plotted point x,y must lie within gGWRect -- otherwise it's crashville :-(
LOCAL FN SetCPixelDangerous(x,y,pixelVal&)'(no test for outside gGWRect)
REGISTER(a1)=gBaseAddr&
REGISTER(d0)=gRowBytes
` add.w ^x,a1
` muls ^y,d0
` move.l ^pixelVal&,d1
` move.b d1,(a1,d0.l)
`done
END FN
These are both usable in the demo program for GWorld direct-pixmap-writing, posted yesterday.
|