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

Made with FB

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.

Joe Lertola

'========
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.

Robert

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.

Sean


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).

TJ Grant


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.

Robert