BBC BASIC for Windows
Programming >> Graphics and Games >> GLib (compact sprite library)
http://bb4w.conforums.com/index.cgi?board=graphics&action=display&num=1463120570

GLib (compact sprite library)
Post by David Williams on May 13th, 2016, 06:22am

Code:
      DEFFNGetGLIBVer:="0.07"
      DEFPROCInitGLIB(RETURN v{}):`v=@vdu%:LOCALS%,Z%:DIMv{a%,w%,h%}
      v.a%=FN`cds:v.w%=`v!208:v.h%=`v!212:Z%=FN`ga(4096,TRUE):S%=FN`ga(2048,TRUE)
      $S%="608B5C2424C703FFFFFFFF8B7C24383B7C24280F8DC70000008B74243C3B74242C0F8DB9"
      $S%+="0000008B4C24308B54243483F9007D03F7D99083FA007D03F7DA90F7D93BF90F8E970000"
      $S%+="00F7DA3BF20F8E8D000000F7D9F7DAC70300000000C7430400000000C7430800000000C7"
      $S%+="430C00000000894B10895314897B1889731C8BEF03E93B6C24287E0E2B6C2428296B10C7"
      $S%+="4304FFFFFFFF83FF007D14297B0C017B10C7431800000000C74304FFFFFFFF8BEE03EA3B"
      $S%+="6C242C7E0E2B6C242C296B14C74304FFFFFFFF83FE007D14297308017314C7431C000000"
      $S%+="00C74304FFFFFFFF61C21C0060":gClip=FN`m(S%,0)
      $S%="608B6C24248B4424288B7D008B4D040FAF4D08FCF3AB61C2080000":Clr=FN`m(S%,0)
      $S%="608BEC81EC800000008B75248BC4FF7538FF7534FF7530FF752CFF7608FF760450E8F6FE"
      $S%+="FFFFF70424FFFFFFFF0F855A0000008B552C89542434C1642434020FAF5424080354240C"
      $S%+="C1E2020355288B7E04897C2438C1642438020FAF7C241C037C2418C1E702033E8B5C2410"
      $S%+="33C98B048A85C0740389048F83C1013BCB7CEF03542434037C2438FF4C241475DF81C480"
      $S%+="00000061C2180060":Plot=FN`m(S%,33)
      $S%="608BEC81EC800000008B75248BC4FF7538FF7534FF7530FF752CFF7608FF760450E85FFE"
      $S%+="FFFFF70424FFFFFFFF0F85570000008B552C89542434C1642434020FAF5424080354240C"
      $S%+="C1E2020355288B7E04897C2438C1642438020FAF7C241C037C2418C1E702033E8BF28B5C"
      $S%+="24108B4424348B5424388B6C241456578BCBFCF3A55F5E03F003FA4D7FF081C480000000"
      $S%+="61C218008B":BPlot=FN`m(S%,33)
      $S%="608BEC83EC409BDBE39BD93C24668B04246625FFF3660D000C66890424D92C248B553483"
      $S%+="FA000F8E480100008B7D3883FF000F8E3C0100006800000100DB0424DB452CDA7534D8C9"
      $S%+="DB5C2404DB4530DA7538DEC9DB5C240883C4048B45248B5D3CF7DA3BDA0F8E090100003B"
      $S%+="58040F8D000100008B4D40F7DF3BCF0F8EF30000003B48080F8DEA000000F7DAF7DF895C"
      $S%+="2408894C240C89542410897C2414C744241800000000C744241C000000008B7004C1E602"
      $S%+="8974242083FB007D10C744240800000000015C2410295C241C8B50042B55343BDA7E0B8B"
      $S%+="70042B7424088974241083F9007D10C744240C00000000014C2414294C24188B50082B55"
      $S%+="383BCA7E0B8B70082B74240C897424148B74240C0FAF700403742408C1E60203308B5D28"
      $S%+="33FF8BCF034C24180FAF4C2404C1E9100FAF4D2CC1E102575055538B4424108B6C242C03"
      $S%+="D98B7C24204F8BD703D50FAFD0C1EA108B1493F7C2FFFFFF0074038914BE4F7DE55B5D58"
      $S%+="5F03742420473B7C24147CAE83C44061C220008B":PlotScaled=FN`m(S%,0)
      $S%="608BEC81EC800000008B75248BC4FF7538FF7534FF7530FF752CFF7608FF760450E85FFE"
      $S%+="FFFFF70424FFFFFFFF0F85610000008B552C89542434C1642434020FAF5424080354240C"
      $S%+="C1E2020355288B7E04897C2438C1642438020FAF7C241C037C2418C1E702033E8B5C2410"
      $S%+="33C98B048A85C0740AD12C8F81248F7F7F7F7F83C1013BCB7CE803542434037C2438FF4C"
      $S%+="241475D881C48000000061C2180060":PlotShadow=FN`m(S%,33)
      $S%="608BEC81EC800000008B75248BC4FF7538FF7534FF7530FF752CFF7608FF760450E8C1FD"
      $S%+="FFFFF70424FFFFFFFF0F856E0000008B552C89542434C1642434020FAF5424080354240C"
      $S%+="C1E2020355288B7E04897C2438C1642438020FAF7C241C037C2418C1E702033E8B5C2410"
      $S%+="33C98B048A85C074178B348F25FEFEFE0081E6FEFEFE00D1E8D1EE03C689048F83C1013B"
      $S%+="CB7CDB03542434037C2438FF4C241475CB81C48000000061C2180060"
      PlotAvg=FN`m(S%,33)
      $S%="8B7424048B4C24088B5E048B54240C3BCB73133B5608730E8B060FAFD38B7C241003D189"
      $S%+="3C90C2100000":PlotPixel=FN`m(S%,0)
      $S%="8B6C24048B45008B5D048B4D088B5424088B7C240C8B742410E81B00000083C201E81300"
      $S%+="000083C701E80B00000083EA01E803000000C21000603BD3730C3BF973080FAFFB03FA89"
      $S%+="34B861C300":PlotPoint=FN`m(S%,0)
      $S%="33C08B6C24048B7C24083B7D0473178B74240C3B7508730E0FAF7504037424088B7D008B"
      $S%+="04B7C20C0000":ReadPixel=FN`m(S%,0)
      $S%="8B6C24048B7C24083B7D0473418B74240C3B750873380FAF7504037424088B7D0033C00F"
      $S%+="B644B7028B5C241089030FB644B7018B5C241489030FB604B78B5C241889030FB644B703"
      $S%+="8B5C241C8903C21C0000":ReadRGBA=FN`m(S%,0)
      $S%="608BEC81EC800000008B75248BC4FF7538FF7534FF7530FF752CFF7608FF760450E85FFE"
      $S%+="FFFFF70424FFFFFFFF0F85670000008B552C89542434C1642434020FAF5424080354240C"
      $S%+="C1E2020355288B7E04897C2438C1642438020FAF7C241C037C2418C1E702033E8B5C2410"
      $S%+="8B753CC1E61833C98B048A85C0740A25FFFFFF000BC689048F83C1013BCB7CE803542434"
      $S%+="037C2438FF4C241475D881C48000000061C21C0060":PlotSetAlpha=FN`m(S%,33)
      $S%="608BEC81EC800000008B75248BC4FF7538FF7534FF7530FF752CFF7608FF760450E8BBFD"
      $S%+="FFFFF70424FFFFFFFF0F85720000008B552C89542434C1642434020FAF5424080354240C"
      $S%+="C1E2020355288B7E04897C2438C1642438020FAF7C241C037C2418C1E702033E8B5C2410"
      $S%+="518B4D3C83C118BE01000000D3E65933C98B048A85C0740C0BC681248F000000FF09048F"
      $S%+="83C1013BCB7CE603542434037C2438FF4C241475D681C48000000061C21C0060"
      PlotSetAlphaBit=FN`m(S%,33)
      $S%="608BEC81EC800000008B75248BC4FF7538FF7534FF7530FF752CFF7608FF760450E80CFD"
      $S%+="FFFFF70424FFFFFFFF0F855F0000008B552C89542434C1642434020FAF5424080354240C"
      $S%+="C1E2020355288B7E04897C2438C1642438020FAF7C241C037C2418C1E702033E8B5C2410"
      $S%+="33F633C98B048A85C074060B348F89048F83C1013BCB7CEC03542434037C2438FF4C2414"
      $S%+="75DC81C480000000C1EE188BC68944241C61C2180060"
      PlotGetAlphaBits=FN`m(S%,33)
      $S%="608BEC81EC800000008B75248BC4FF7538FF7534FF7530FF752CFF7608FF760450E85FFE"
      $S%+="FFFFF70424FFFFFFFF0F85690000008B552C89542434C1642434020FAF5424080354240C"
      $S%+="C1E2020355288B7E04897C2438C1642438020FAF7C241C037C2418C1E702033E8B5C2410"
      $S%+="33C98B048AA9000000FF740F3D000000FF722725FFFFFF0089048F83C1013BCB7CE00354"
      $S%+="2434037C2438FF4C241475D081C48000000061C218008BF0C1EE180FB66C8A020FB6448F"
      $S%+="022BE80FAFEEC1FD0803C588448F020FB66C8A010FB6448F012BE80FAFEEC1FD0803C588"
      $S%+="448F010FB62C8A0FB6048F2BE80FAFEEC1FD0803C588048FC6448F030083C1013BCB0F8C"
      $S%+="6EFFFFFF03542434037C2438FF4C24140F855AFFFFFF81C48000000061C2180060"
      PlotAlphaBlend=FN`m(S%,33)
      Z%=FN`ga(4096,TRUE)
      ENDPROC
      DEFPROCDisplay(W%):PRIVATEI%:IFI%=0 I%=FN`s("InvalidateRect")
      SYSI%,@hwnd%,0,0:IFW%PROC`w
      *REFRESH
      ENDPROC
      DEFFNLoadImg(p$,A%):LOCALK%,X%,Y%,W%,H%,M%:X%=0:Y%=0:K%=FN`l(p$,X%,Y%,0)
      IFK%=0ERROR100,"LoadImg failed"
      A%=FN`g(K%,A%,W%,H%):SYS"DeleteObject",K%:SYS"RtlMoveMemory",A%,A%+54,4*W%*H%
      =A%
      DEFPROCCleanup:PROC`n(3,0):ENDPROC
      DEFFN`m(S%,O%):LOCALA%,I%:A%=FN`ga(LEN$S%,TRUE):FORI%=0TOLEN$S%DIV2-1
        A%?I%=EVAL("&"+MID$($S%,2*I%+1,2)):NEXT
      IFO%>0PROC`d(A%,O%,gClip)
      =A%
      DEFFN`cds:LOCALA%,B%,H%,O%:DIMB%19:!B%=44:B%!4=`v!208:B%!8=`v!212:B%!12=&200001
      SYS"CreateDIBSection",@memhdc%,B%,0,^A%,0,0TOH%:IFH%=0ERROR100,"`cds failed"
      SYS"SelectObject",@memhdc%,H%TOO%:SYS"DeleteObject",O%:CLS:=A%
      DEFPROC`w:PRIVATEB%,G%,I%,S%,W%:LOCALL%,P%,T%:IFS%=0 S%=FN`s("Sleep")
      IFB%=0SYS"timeBeginPeriod",1:B%=1
      IFI%=0PROC`7(I%,G%)
      L%=0:P%=^T%:REPEAT:L%=T%:SYSS%,1:SYSG%,I%,P%:UNTILT%<L%:ENDPROC
      DEFPROC`7(RETURN I%,RETURN G%):LOCALD%,E%,J%,K%,L%
      SYS"LoadLibrary","DDRAW.DLL"TOD%:IFD%=0 ERROR100,"`7 failed"
      SYS"GetProcAddress",D%,"DirectDrawCreateEx"TOE%:DIMJ%19,L%95:J%=J%+3AND-4
      L%=L%+3AND-4:!J%=&15E65EC0:J%!4=&11D23B9C:J%!8=&60002FB9:J%!12=&5BEA9797
      SYSE%,0,^K%,J%,0:L%=!K%:I%=K%:G%=L%!64
      ENDPROC
      DEFFN`l(f$,RETURN X%,RETURN Y%,R%)
      LOCALi{},b{},x%,y%,H%,C%,P%,O%,Q%,T%
      DIMi{a%,b%,c%,d%},T% LOCAL 513
      i.a%=&7BF80980:i.b%=&101ABF32:i.c%=&AA00BB8B:i.d%=&AB0C3000
      SYS"MultiByteToWideChar",0,0,f$,-1,T%,256
      SYS"LoadLibrary","OLEAUT32.DLL"TOO%
      SYS"GetProcAddress",O%,"OleLoadPicturePath"TOQ%:IFQ%=0 THEN=0
      SYSQ%,T%,0,0,0,i{},^P%:IFP%=0 THEN=0
      SYS!(!P%+12),P%,^H%:IFH%=0 THEN=0
      DIMb{T%,W%,H%,B%,P{l&,h&},p{l&,h&},s%}:SYS"GetObject",H%,DIM(b{}),b{}
      x%=b.W%:y%=b.H%:IFX%=0 X%=x%
      IFY%=0 Y%=y%
      IFR%THEN
        IFX%/Y%>x%/y% X%=Y%*x%/y%ELSEY%=X%*y%/x%
      ENDIF
      SYS"CopyImage",H%,0,X%,Y%,0TOC%:SYS"DeleteObject",H%:SYS!(!P%+8),P%:=C%
      DEFFN`g(h%,RETURN D%,RETURN W%,RETURN H%):LOCALB%,S%,R%:DIMB%LOCAL26
      B%=B%+3AND-4:SYS"GetObject",h%,24,B%TOR%
      IFR%=0 ERROR100,"GetObject failed"
      W%=B%!4:H%=B%!8:S%=54+4*W%*H%:IFD%=0DIMD% S%+7:D%=D%+7AND-8
      IFD%=-1D%=FN`ga(54+4*W%*H%,TRUE)
      $$D%=STRING$(53,CHR$0)
      !D%=19778:D%!2=S%:D%!10=54:D%!14=40:D%!18=W%:D%!22=H%:D%!26=&200001
      SYS"GetDIBits",@memhdc%,h%,0,H%,D%+54,D%+14,0TOR%
      IFR%<>H% ERROR100,"GetDIBits failed"
      =D%
      DEFPROC`n(O%,V%):LOCALi,m:m=1000:PRIVATED%
      IFD%=0THEN
        D%=FN`ga(4*m,FALSE):IFD%=0 ERROR100,"GlobalAlloc failed"
        FORi=0TOm-1:D%!(4*i)=0:NEXT
      ENDIF
      CASEO%OF
        WHEN1:i=0:WHILED%!(4*i)<>0ANDi<m:i+=1:ENDWHILE
          IFi>=m ERROR100,"Obj list full"
          D%!(4*i)=V%
        WHEN2:i=0:WHILED%!(4*i)<>V%ANDi<m:i+=1:ENDWHILE
          IFi<m SYS"GlobalFree",V%:D%!(4*i)=0
        WHEN3:FORi=0TOm-1
            IFD%!(4*i)<>0SYS"GlobalFree",D%!(4*i):D%!(4*i)=0
          NEXT:SYS"GlobalFree",D%:D%=0
      ENDCASE
      ENDPROC
      DEFFN`s(f$):LOCALP%:DIMP%LOCAL5:[OPT 0:call f$:]:=P%!-4+P%
      DEFPROC`d(B%,O%,C%):LOCALD%,P%:D%=C%-B%-O%-5:P%=B%+O%+1:!P%=D%:ENDPROC
      DEFFN`ga(S%,F%):LOCALA%:SYS"GlobalAlloc",64,S%TOA%:IFF%PROC`n(1,A%)
      =A%
      DEF FNGrab(v{},A%,X%,Y%,W%,H%)
      LOCALB%,D%,L%,I%,O%
      IFA%=-1A%=FN`ga(4*W%*H%,TRUE)
      D%=A%
      FOR L%=Y% TO Y%+H%-1
        B%=v.a%+4*(L%*v.w%+X%):FORI%=B%TOB%+4*W%-1STEP4:!D%=!I%:D%+=4:NEXT
      NEXT
      =A%
      DEFPROCFWS:LOCALW%:SYS"GetWindowLong",@hwnd%,-16TOW%
      SYS "SetWindowLong",@hwnd%,-16,W%AND&FFFAFFFF
      ENDPROC

 


Re: GLib (compact sprite library)
Post by David Williams on May 13th, 2016, 06:26am

This program ("Wavey Starfish") demonstrates GLib's PlotScaled routine. Copy and paste the following code into the BB4W IDE, then tack-on the GLib library from the first post in this thread (or INSTALL it, if you prefer).

Code:
      REM "Wavey Starfish"
      REM Requires GLIB 0.06 (or later)

      MODE 8 : OFF

      ON ERROR OSCLI"REFRESH ON":ON:CLS:REPORT:PRINT " at line ";ERL:END

      REM If GLIB is an external library file (GLIB.BBC) then
      REM include the following line:
      REM INSTALL @lib$ + "GLIB"

      PROCInitGLIB( g{} )
      ON CLOSE PROC`n(3,0):QUIT

      sprSz% = 64
      nSpokes% = 10
      maxSprSz% = sprSz%
      minSprSz% = 4
      maxR% = 300
      minR% = 50
      cx% = g.w% / 2
      cy% = g.h% / 2

      REM Draw a ball on the screen:
      GCOL 1
      FOR R% = sprSz%/2-1 TO 1 STEP -1
        i = 1-(R%/(sprSz%/2))^4
        COLOUR 1, 255*i, 100*i, 255*i
        CIRCLE FILL sprSz%, sprSz%, 2*R%
      NEXT R%

      REM Copy the ball into a sprite/bitmap:
      ballSpr% = FNGrab(g{}, -1, 0, 0, sprSz%, sprSz%)

      TIME = 0

      *REFRESH OFF

      REPEAT
        SYS Clr, g{}, &204060 : REM dark blue background
        FOR R% = minR% TO maxR% STEP 25
          f = (R%-minR%)/(maxR%-minR%)
          sz% = minSprSz% + maxSprSz%*(1-f)
          angleInc = 100*f*SIN(TIME/150)
          FOR I% = 0 TO nSpokes%-1
            i = 100*SIN(TIME/250)
            angle = I%*(360/nSpokes%) + angleInc + i
            X% = cx% + R%*SINRAD(angle)
            Y% = cy% + R%*COSRAD(angle)
            SYS PlotScaled, g{}, ballSpr%, sprSz%, sprSz%, sz%, sz%, \
            \ X%-sz%/2, Y%-sz%/2
          NEXT I%
        NEXT R%
        PROCDisplay(TRUE)
      UNTIL FALSE

      PROC`n(3,0) : REM free memory
      END
 



Re: GLib (compact sprite library)
Post by DDRM on May 13th, 2016, 11:30am

Hi David,

Ooh, looks like that (particularly the library bit) will repay a bit of study! Are you in the market for providing a bit of documentation for the library functions?

Are your sprites 32 bit bitmaps, with black set to transparent?

Best wishes,

D
Re: GLib (compact sprite library)
Post by David Williams on May 13th, 2016, 11:58am

on May 13th, 2016, 11:30am, DDRM wrote:
Ooh, looks like that (particularly the library bit) will repay a bit of study! Are you in the market for providing a bit of documentation for the library functions?


If anyone is wondering why the GLib code is so cryptic (certainly cryptic enough to cause me problems!), it's because it was originally designed so that it could be employed within the limitations of the Trial version of 'BBC BASIC for Windows' (I can't, off-hand, remember what the memory limitations were). This meant making it as compact as possible, and necessitated the use of short or single-character variable names and functions, and pre-assembled machine code.

I will shortly put up some documentation for it, including some commented example programs.

There's some routines I'd like to add (actually, there's quite a few routines I'd like to add but they would bloat the library, and I won't be able to fit the whole thing into one post!). I like being able to fit it all in one post. (Last time I checked, I only had 34 free characters available! (some more trimming to do, then...))


Quote:
Are your sprites 32 bit bitmaps, with black set to transparent?


Yes, 32bpp (ARGB) bitmaps, although any bitmap header data is ignored (which is why you must specify the height and width of the sprite you wish to plot, and the start address of the sprite's pixel data).

Black pixels are assumed to be transparent by some of the plotting routines. I will document which plotters ignore black pixels, and which ones don't.


David.
--
Re: GLib (compact sprite library)
Post by David Williams on May 15th, 2016, 11:07am

The program listed below will have to function as the rather perfunctory GLib documentation for the time being. It was written in a hurry, sorry. There ought to be enough information in there to put some sprites/bitmaps on the screen (which is GLib's purpose), but I'm willing to respond to any technical questions anyone may have regarding GLib.

Code:
      REM GLib examples

      REM Glossary
      REM ========
      REM
      REM bmAddr ... bitmap address (memory address of ARGB32 bitmap image)
      REM bmW    ... bitmap width (in pixels)
      REM bmH    ... bitmap height (in pixels)
      REM ARGB32 ... 32-bit pixel colour format used by GLib.
      REM            Each pixel in an ARGB32 bitmap image takes up 4 bytes (32 bits per pixel).
      REM            A = 8-bit alpha value (only used by PlotAlphaBlend); range 0 to 255
      REM            R = 8-bit red component (range 0 to 255)
      REM            G = 8-bit green component (range 0 to 255)
      REM            B = 8-bit blue component (range 0 to 255)
      REM            In hexadecimal, ARGB32 has the form &AARRGGBB
      REM            Examples:
      REM              White  &00FFFFFF
      REM              Orange &00FF8000
      REM              Green  &0000FF00
      REM              Blue with 50% alpha &7F0000FF

      REM If GLib is an external file (i.e. in BB4W's standard LIB folder),
      REM then include the following line:
      INSTALL @lib$ + "GLIB"

      REM If not, then tack the GLib library on to the end of this program.

      REM It's good practice to call PROCCleanup upon closing the program,
      REM or if an error is trapped:
      ON CLOSE PROCCleanup : QUIT
      ON ERROR PROCCleanup : CLS : REPORT : PRINT " at line "; ERL : END

      REM Fix program window size (a 'built-in' GLib subroutine):
      PROCFWS

      REM Set up a 640x512 program window:
      MODE 8 : OFF

      WinW% = @vdu%!208
      WinH% = @vdu%!212

      REM Initialise GLib (GLib subroutine):
      PROCInitGLIB( g{} )

      REM The structure returned by PROCInitGLIB has three members:
      REM
      REM   a% ... the 'screen' or 'frame buffer' address (currently a DIB section)
      REM   w% ... screen/frame buffer width
      REM   h% ... screen/frame buffer height

      REM Load 512x512 JPEG image from BB4W's 'GRAPHICS' folder:
      path$ = LEFT$( @lib$, LEN@lib$-4 ) + "EXAMPLES\GRAPHICS\"
      file$ = path$ + "WORLD.JPG"
      worldImg% = FNLoadImg(file$, -1)

      REM FNLoadImg automatically loads the JPEG image, and converts it to
      REM the required raw ARGB32 pixel format required by GLib.
      REM The parameter specified as -1 in this case tells FNLoadImg to allocate
      REM the memory required to hold the ARGB32 bitmap. Otherwise, you must
      REM specify the address at which the ARGB32 bitmap will be placed, having
      REM reserved the memory block (of sufficient size!) yourself.

      REM Create a sprite (a colourful spiral) by drawing it on the screen
      REM using BB4W's native graphics commands, then 'grab' it as a sprite/bitmap:
      DIM spiral{ a%, w%, h% }
      r = 0
      FOR angle = 0 TO 1000
        GCOL RND(15)
        x = WinW% + r*SINRAD(angle)
        y = WinH% + r*COSRAD(angle)
        CIRCLE FILL x, y, 8
        r += 0.1
      NEXT angle
      PROCgetMinMaxRect( g{}, minX%, minY%, maxX%, maxY% )
      spiral.w% = maxX% - minX%
      spiral.h% = maxY% - minY%
      spiral.a% = FNGrab( g{}, -1, minX%, minY%, spiral.w%, spiral.h% )

      REM Create another sprite, but this time one with an alpha mask.
      REM When plotted with PlotAlphaBlend, this sprite will appear as a fuzzy red blob.
      CLS
      DIM ball{ a%, w%, h% }
      GCOL 1 : CIRCLE FILL 200, 200, 80
      PROCgetMinMaxRect( g{}, minX%, minY%, maxX%, maxY% )
      ball.w% = maxX% - minX%
      ball.h% = maxY% - minY%
      ball.a% = FNGrab( g{}, -1, minX%, minY%, ball.w%, ball.h% )
      m = SQR((ball.w%/2)^2 + (ball.h%/2)^2)
      FOR Y% = 0 TO ball.h%-1
        FOR X% = 0 TO ball.w%-1
          d = SQR((X%-ball.w%/2)^2 + (Y%-ball.h%/2)^2)
          ball.a%?(4*(Y%*ball.w%+X%) + 3) = 255*(1 - d/m)^2
        NEXT
      NEXT

      REM Now actually draw stuff...

      REM Clear the program window (fill it with dark blue).
      REM Clr takes parameters g{}, ARGB32
      SYS Clr, g{}, &000040

      REM Plot the 512x512 'world' image at coordinates x=30, y=20.
      REM BPlot takes parameters g{}, bmAddr, bmW, bmH, x, y
      REM BPlot is well-suited to drawing background images. It's very fast.
      REM All pixels (including black ones) in the bitmap image are plotted.
      SYS BPlot, g{}, worldImg%, 512, 512, 30, 20

      REM Use PlotPixel to draw 1000 randomly coloured pixels in the lower half of the window.
      REM PlotPixel takes parameters g{}, x, y, ARGB32
      FOR I% = 1 TO 1000
        SYS PlotPixel, g{}, RND(WinW%), RND(WinH%/2), RND(&FFFFFF)
      NEXT I%

      REM Draw 1000 randomly coloured points in the upper half of the window.
      REM A point is a 2x2 square block of pixels.
      REM PlotPoint takes parameters g{}, x, y, ARGB32
      FOR I% = 1 TO 1000
        SYS PlotPoint, g{}, RND(WinW%), WinH%/2+RND(WinH%/2), RND(&FFFFFF)
      NEXT I%

      REM Draw 10 randomly positioned spiral sprites (the one we created earlier).
      REM Plot takes the same parameters as BPlot - g{}, bmAddr, bmW, bmH, x, y
      REM Black pixels are not plotted (i.e. they are assumed to be transparent)
      FOR I% = 1 TO 10
        SYS Plot, g{}, spiral.a%, spiral.w%, spiral.h%, RND(WinW%), RND(WinH%)
      NEXT I%

      REM Draw 10 randomly positioned, colour-averaged spiral sprites using PlotAvg.
      REM PlotAvg takes parameters g{}, bmAddr, bmW, bmH, x, y
      REM Black pixels are not plotted (i.e. they are considered to be transparent)
      FOR I% = 1 TO 10
        SYS PlotAvg, g{}, spiral.a%, spiral.w%, spiral.h%, RND(WinW%), RND(WinH%)
      NEXT I%

      REM Draw the spiral sprite stretched horizontally and vertically to varying extents.
      REM PlotScaled takes parameters g{}, bmAddr, bmW, bmH, newW, newH, x, y
      REM Black pixels are not plotted.
      FOR I% = 1 TO 10
        newW% = RND(300)
        newH% = RND(300)
        SYS PlotScaled, g{}, spiral.a%, spiral.w%, spiral.h%, newW%, newH%, RND(WinW%), RND(WinH%)
      NEXT I%

      REM Draw alpha-blended sprites.
      REM PlotAlphaBlend takes parameters g{}, bmAddr, bmW, bmH, x, y
      REM Black pixels in the sprite are processed by this routine.
      FOR I% = 1 TO 10
        SYS PlotAlphaBlend, g{}, ball.a%, ball.w%, ball.h%, RND(WinW%), RND(WinH%)
      NEXT I%

      REM Draw translucent shadows (actually 'silhouettes' of the spiral sprite).
      REM PlotShadow takes parameters g{}, bmAddr, bmW, bmH, x, y
      REM Black pixels are not plotted.
      FOR I% = 1 TO 20
        SYS PlotShadow, g{}, spiral.a%, spiral.w%, spiral.h%, RND(WinW%), RND(WinH%)
      NEXT I%

      REM Update the program window:
      PROCDisplay(FALSE)

      PROCCleanup
      END

      DEF PROCgetMinMaxRect(v{}, RETURN minX%, RETURN minY%, RETURN maxX%, RETURN maxY%)
      LOCAL A%, B%, H%, W%, X%, Y%
      minX% = 100000000
      minY% = 100000000
      maxX% = -100000000
      maxY% = -100000000
      A% = v.a%
      W% = v.w%
      H% = v.h%
      FOR Y% = 0 TO H%-1
        B% = A% + 4*Y%*W%
        FOR X% = 0 TO W%-1
          IF B%!(4*X%) THEN
            IF X% < minX% THEN
              minX% = X%
            ENDIF
            IF X% > maxX% THEN
              maxX% = X%
            ENDIF
            IF Y% < minY% THEN
              minY% = Y%
            ENDIF
            IF Y% > maxY% THEN
              maxY% = Y%
            ENDIF
          ENDIF
        NEXT
      NEXT
      ENDPROC
 

Re: GLib (compact sprite library)
Post by David Williams on May 15th, 2016, 12:40pm

If GLib isn't installed in your BB4W LIB folder, then either do that, or append the library (from the first post in this thread) to the end of this program:

Code:
      REM "Ring of Balls"
      REM Requires GLIB 0.07 (or later)

      REM If GLib is in BB4W's LIB folder, then include the following line:
      INSTALL @lib$ + "GLIB"

      REM Otherwise, append the GLib library to the end of this program.

      PROCFWS : REM Fix Window Size

      MODE 8 : REM 640x512
      OFF

      nBalls% = 16

      REM Get window width and height:
      winW% = @vdu%!208
      winH% = @vdu%!212

      PROCInitGLIB( g{} )

      ON ERROR OSCLI"REFRESH ON":ON:PROCCleanup:REPORT:PRINT ERL:END
      ON CLOSE PROCCleanup:QUIT

      REM Create a 32bpp bitmap of dimensions winW% by 10*winH%:
      bgW% = winW%
      bgH% = 10 * winH%
      PRINT "Generating colourful ";bgW%;"x";bgH%;" bitmap..."
      bg% = FN`ga( 4*bgW%*bgH%, TRUE )
      I% = bg%
      k = 3*PI/(bgW%-1)
      FOR Y% = 0 TO bgH%-1
        yf = 16*PI*Y%/(bgH%-1)
        sin_yf = SIN(yf)
        sin_yf_2 = SIN(yf/2)
        FOR X% = 0 TO bgW%-1
          xf = k*X%
          blue& = 128 + 64*(sin_yf+COS(xf))
          red& = 128 + 64*(sin_yf_2+SIN(xf+yf))
          !I% = blue& + &10000*red&
          I% += 4
        NEXT
      NEXT

      REM Create a green ball sprite:
      DIM ball{a%, w%, h%}
      CLS
      GCOL 15
      FOR R% = 64 TO 1 STEP -1
        r = 1 - R%/64
        COLOUR 15, r*10, r*255, r*100
        CIRCLE FILL winW%, winH%, R%
      NEXT R%
      PROCgetMinMaxRect( g{}, minX%, minY%, maxX%, maxY% )
      ball.w% = maxX% - minX% + 1
      ball.h% = maxY% - minY% + 1
      ball.a% = FNGrab( g{}, -1, minX%, minY%, ball.w%, ball.h% )

      h% = bgH% - winH%

      TIME = 0

      *REFRESH OFF

      REPEAT
  
        t = TIME / 120
  
        REM Draw the moving background image:
        y% = -0.5*(h% + h%*SIN(TIME/350))
        SYS BPlot, g{}, bg%, bgW%, bgH%, 0, y%
  
        REM Draw the ring of balls
  
        REM Draw ball shadows:
        FOR I% = 0 TO nBalls%-1
          angle = 2*PI*I%/(nBalls%-1) + t
          x% = winW%/2 + 200*SIN(angle) - ball.w%/2
          y% = winH%/2 + 200*COS(angle) - ball.h%/2
          SYS PlotShadow, g{}, ball.a%, ball.w%, ball.h%, x%-24, y%-32
        NEXT I%
  
        REM Draw the balls proper:
        FOR I% = 0 TO nBalls%-1
          angle = 2*PI*I%/(nBalls%-1) + t
          x% = winW%/2 + 200*SIN(angle) - ball.w%/2
          y% = winH%/2 + 200*COS(angle) - ball.h%/2
          SYS Plot, g{}, ball.a%, ball.w%, ball.h%, x%, y%
        NEXT I%
  
        REM Update the program window
        REM (with VSync synchronisation):
        PROCDisplay( TRUE )
  
      UNTIL FALSE
      END
      :
      :
      :
      :
      DEF PROCgetMinMaxRect(v{}, RETURN minX%, RETURN minY%, RETURN maxX%, RETURN maxY%)
      LOCAL A%, B%, H%, W%, X%, Y%
      minX% = 100000000
      minY% = 100000000
      maxX% = -100000000
      maxY% = -100000000
      A% = v.a%
      W% = v.w%
      H% = v.h%
      FOR Y% = 0 TO H%-1
        B% = A% + 4*Y%*W%
        FOR X% = 0 TO W%-1
          IF B%!(4*X%) THEN
            IF X% < minX% THEN
              minX% = X%
            ENDIF
            IF X% > maxX% THEN
              maxX% = X%
            ENDIF
            IF Y% < minY% THEN
              minY% = Y%
            ENDIF
            IF Y% > maxY% THEN
              maxY% = Y%
            ENDIF
          ENDIF
        NEXT
      NEXT
      ENDPROC
 

Re: GLib (compact sprite library)
Post by David Williams on May 18th, 2016, 09:49am

Code:
      REM Thick antialiased lines using GDI+.
      REM Here, GLib is used in conjunction with GDI+.
      REM Fairly consistent 60 fps for 60 lines on my laptop.
      REM
      REM This program calls GdipDrawLine directly, and performs
      REM 'inline' conversion to 4-byte floats in order to reduce
      REM overheads.

      nLines% = 30

      *ESC OFF

      REM If you don't yet have GLib installed in the BB4W's LIB folder,
      REM either append GLib to the end of this program, or install it
      REM in the LIB folder.
      INSTALL @lib$ + "GLIB"
      PROCInitGLIB( g{} )

      INSTALL @lib$ + "GDIPLIB"

      PROCFWS : REM Fix Window Size

      MODE 8 : OFF

      ON ERROR PROCCleanup : REPORT : PRINT ERL : END
      ON CLOSE PROCCleanup : QUIT

      SetBoundsRect  = FN`s("SetBoundsRect")
      GetBoundsRect  = FN`s("GetBoundsRect")
      OffsetRect     = FN`s("OffsetRect")
      InvalidateRect = FN`s("InvalidateRect")
      GetTickCount   = FN`s("GetTickCount")

      REM The following bit of assembler code
      REM is the work of RTR:
      DIM f4% 10
      P% = f4%
      [OPT 2
      mov esi,[ebp+2]:mov edi,[ebp+7]
      fld qword [esi]:fstp dword [edi]
      ret
      ]

      f32# = 0.0 : pf32% = ^f32# + 4 : !pf32% = &3FF00000

      PROC_gdipinit

      ON ERROR PROCCleanup : PROC_gdipexit : REPORT : PRINT ERL : END
      ON CLOSE PROCCleanup : PROC_gdipexit : QUIT

      DIM rc{l%,t%,r%,b%}

      yellowPen% = FN_gdipcreatepen(&FFFFFF00, 2+&200, 2)
      greenPen%  = FN_gdipcreatepen(&FF00FF00, 2+&200, 4)
      bluePen%   = FN_gdipcreatepen(&FF0080FF, 2+&200, 8)
      orangePen% = FN_gdipcreatepen(&FFFF8000, 2+&200, 6)

      gdipGfxPtr% = FN_gdipg

      SYS SetBoundsRect, @memhdc%, 0, 5
      SYS GetBoundsRect, @memhdc%, rc{}, 0

      DIM line{( nLines%-1 ) x1, y1, x2, y2, dx1, dx2, dy1, dy2, pen%}

      ScrW% = @vdu%!208
      ScrH% = @vdu%!212

      FOR I% = 0 TO nLines%-1
        line{(I%)}.x1 = RND( @vdu%!208 )
        line{(I%)}.y1 = RND( @vdu%!212 )
        line{(I%)}.x2 = RND( @vdu%!208 )
        line{(I%)}.y2 = RND( @vdu%!212 )
        line{(I%)}.dx1 = 2*RND(1)*SGN(RND(1)-0.5)
        line{(I%)}.dx2 = 2*RND(1)*SGN(RND(1)-0.5)
        line{(I%)}.dy1 = 2*RND(1)*SGN(RND(1)-0.5)
        line{(I%)}.dy2 = 2*RND(1)*SGN(RND(1)-0.5)
        CASE RND(4) OF
          WHEN 1 : line{(I%)}.pen% = yellowPen%
          WHEN 2 : line{(I%)}.pen% = greenPen%
          WHEN 3 : line{(I%)}.pen% = bluePen%
          WHEN 4 : line{(I%)}.pen% = orangePen%
        ENDCASE
      NEXT I%

      REM Line endpoints variables:
      x1% = 0 : y1% = 0 : x2% = 0 : y2% = 0

      frame% = 0
      frameRate% = 0

      *REFRESH OFF

      SYS GetTickCount TO time0%
      REPEAT
  
        SYS Clr, g{}, 0
  
        FOR I% = 0 TO nLines%-1
          f64# = line{(I%)}.x1 : f64# *= f32# : CALL f4%,f64#,x1%
          f64# = line{(I%)}.y1 : f64# *= f32# : CALL f4%,f64#,y1%
          f64# = line{(I%)}.x2 : f64# *= f32# : CALL f4%,f64#,x2%
          f64# = line{(I%)}.y2 : f64# *= f32# : CALL f4%,f64#,y2%
          SYS `GdipDrawLine`, gdipGfxPtr%, line{(I%)}.pen%, x1%, y1%, x2%, y2%, @memhdc%
          line{(I%)}.x1 += line{(I%)}.dx1
          line{(I%)}.y1 += line{(I%)}.dy1
          line{(I%)}.x2 += line{(I%)}.dx2
          line{(I%)}.y2 += line{(I%)}.dy2
          IF line{(I%)}.x1 < 0 OR line{(I%)}.x1 >= ScrW% line{(I%)}.dx1 *= -1
          IF line{(I%)}.x2 < 0 OR line{(I%)}.x2 >= ScrW% line{(I%)}.dx2 *= -1
          IF line{(I%)}.y1 < 0 OR line{(I%)}.y1 >= ScrH% line{(I%)}.dy1 *= -1
          IF line{(I%)}.y2 < 0 OR line{(I%)}.y2 >= ScrH% line{(I%)}.dy2 *= -1
        NEXT I%
  
        PRINT TAB(2,1);frameRate%;" fps"
  
        PROCDisplay(TRUE)
  
        frame% += 1
        SYS GetTickCount TO time1%
        IF time1%-time0% >= 1000 THEN
          frameRate% = frame%
          frame% = 0
          SYS GetTickCount TO time0%
        ENDIF
      UNTIL FALSE
      END
 

Re: GLib (compact sprite library)
Post by David Williams on May 22nd, 2016, 08:59am

An unfinished/buggy "Asteroids"-style game:

http://pastebin.com/86rn3bnU

(Scroll down to the bottom of the Pastebin window to the section titled "RAW Paste Data", then click somewhere inside that box and press CTRL+A to select all the code, followed by CTRL+C to copy it into the Windows clipboard, then within the BB4W IDE press CTRL+V to paste in the program listing. Or do it your own way!)

This was never meant to be a game (and really isn't much of a game at all). It was supposed to be a GLib/GDI+ demo with a couple of random 'asteroids' moving across the screen, but then one thing led to another. It's basically is just a demo, not a proper game.

Use the left/right arrow keys to spin around, and use the 'up' arrow key to thrust, and space bar to fire. From level 4 onwards, thicker (and generally faster) asteroids appear which take more hits to destroy.

60 fps on all three of my machines.

For possibly improved performance (if the animation gets jittery), compile the program and then run it.


David.
--




Re: GLib (compact sprite library)
Post by DDRM on May 22nd, 2016, 3:14pm

Handsome, but difficult!

smiley

D
Re: GLib (compact sprite library)
Post by David Williams on Jun 19th, 2016, 3:14pm

Here's a little demo of GLib's capabilities (a work-in-progress game that I had started, but probably won't finish). Nothing much happens yet, sadly.

Use the arrow keys to move around.

Copy and paste the code from Pastebin into the BB4W IDE. The GLib library is already appended at the end of the program, so it doesn't need to reside in BB4W's LIB folder.

http://pastebin.com/Ag9eH66e

(Scroll down to the section titled 'RAW Paste Data' and then left-click somewhere inside the window, press CTRL A to select all the code, then CTRL C to copy it to the clipboard, and then press CTRL V inside the BB4W IDE to paste it.)

The scrolling is very smooth on my desktop PC, but on my laptop it's a bit jerky. I don't know why. Background processes most likely to blame. The program uses less than 15% CPU on my machines.


David.

Re: GLib (compact sprite library)
Post by David Williams on Jun 26th, 2016, 09:11am

Code:
      REM Fern 3D
      REM Requires GLib to run

      *ESC OFF
      *FLOAT 64

      INSTALL @lib$ + "GLIB"

      PROCFWS : MODE 8 : OFF

      PROCInitGLIB( g{} )

      ON ERROR PROCCleanup : REPORT : END
      ON CLOSE PROCCleanup : QUIT

      P% = PlotPixel
      Q% = PlotPoint
      R% = ReadPixel
      G% = g{}

      nPts% = FNgetFernPoints( 12345678, FALSE )
      DIM p(nPts%-1,2), q(nPts%-1,2), m(2,2)
      dummy% = FNgetFernPoints( 12345678, TRUE )

      minX = 10000000
      maxX = -minX
      minY = 10000000
      maxY = -minY

      FOR I% = 0 TO nPts%-1
        IF p(I%,0) < minX THEN minX = p(I%,0)
        IF p(I%,0) > maxX THEN maxX = p(I%,0)
        IF p(I%,1) < minY THEN minY = p(I%,1)
        IF p(I%,1) > maxY THEN maxY = p(I%,1)
      NEXT I%

      FOR I% = 0 TO nPts%-1
        p(I%,0) -= (maxX - minX)/2
        p(I%,1) -= (maxY - minY)/2
        p(I%,2) = 250
      NEXT I%

      *REFRESH OFF
      REPEAT
        T% = TIME
        A = T%/122
        B = T%/176
        C = T%/312
        D = 2*PI*SIN(T%/520)
        E = 3*PI*COS(T%/720)
        sA = SIN(A)
        cA = COS(A)
        sB = SIN(B)
        cB = COS(B)
        sC = SIN(C)
        cC = COS(C)
        m() = (cB*cC), -cB*sC, sB,  cA*sC+sA*sB*cC, cA*cC-sA*sB*sC, -sA*cB, sA*sC-cA*sB*cC, sA*cC+cA*sB*sC, cA*cB
        q() = p().m()
        SYS Clr, G%, &000040
        FOR I% = 0 TO nPts%-1
          SYS Q%, G%, 320+q(I%,0), 256+q(I%,1), &E020
        NEXT
        PROCDisplay(TRUE)
      UNTIL FALSE
      END

      DEF FNgetFernPoints( rndseed%, store_coords% )
      LOCAL A%, C%, I%, S%, r, x, y, newx, newy
      S% = RND(-ABS(rndseed%))
      C% = 0
      x = 0
      y = 0
      SYS Clr, g{}, 0
      FOR I% = 1 TO 80000
        r = RND(1)
        CASE TRUE OF
          WHEN r<=0.1 A=0: B=0: C=0: D=0.16: E=0: F=0
          WHEN r>0.1 AND r<=0.86 A=.85: B=.04: C=-.04: D=.85: E=0: F=1.6
          WHEN r>0.86 AND r<=0.93 A=.2: B=-.26: C=.23: D=.22: E=0: F=1.6
          WHEN r>0.93 A=-.15: B =.28: C=.26: D=.24: E=0: F=.44
        ENDCASE
        newx=A*x+B*y+E
        newy=C*x+D*y+F
        x=newx
        y=newy
        SYS R%, G%, 300+48*x, 16+48*y TO A%
        IF A% = 0 THEN
          IF store_coords% THEN
            SYS P%, G%, 300+48*x, 16+48*y, &E020
            p(C%,0) = 48*x
            p(C%,1) = 48*y
            p(C%,2) = 0
          ELSE
            SYS P%, G%, 300+48*x, 16+48*y, &E020
          ENDIF
          C% += 1
        ENDIF
      NEXT
      = C%
 

Re: GLib (compact sprite library)
Post by David Williams on Jun 27th, 2016, 06:23am

This one's slightly different:

(Reminder: Obtain the GLib library from the first post in this thread and either append it to the end of this program (remove the INSTALL statement in that case), or put it in the usual place (BB4W's LIB folder)).

Code:
      REM Fern 3D
      REM Requires GLib to run

      *ESC OFF
      *FLOAT 64

      INSTALL @lib$ + "GLIB" : REM remove this line if GLib library is appended to end of this program

      PROCFWS : MODE 8 : OFF

      PROCInitGLIB( g{} )

      ON ERROR PROCCleanup : REPORT : END
      ON CLOSE PROCCleanup : QUIT

      P% = PlotPixel
      Q% = PlotPoint
      R% = ReadPixel
      G% = g{}

      REM Create colour-graduated background bitmap:
      WinW% = @vdu%!208
      WinH% = @vdu%!212
      bg% = FN`ga(4*WinW%*WinH%, 1)
      FOR Y% = 0 TO WinH%-1
        A% = 4*Y%*WinW%
        f = 1 - Y%/(WinH%-1)
        C% = INT(255*0.5*f) + &100*INT(255*0.15*f) + &10000*INT(255*0.075*f)
        FOR I% = bg%+A% TO bg%+A%+4*WinW%-1 STEP 4
          !I% = C%
        NEXT I%
      NEXT Y%

      nPts% = FNgetFernPoints( 12345678, FALSE )
      DIM p(nPts%-1,2), q(nPts%-1,2), m(2,2)
      dummy% = FNgetFernPoints( 12345678, TRUE )

      minX = 10000000
      maxX = -minX
      minY = 10000000
      maxY = -minY

      FOR I% = 0 TO nPts%-1
        IF p(I%,0) < minX THEN minX = p(I%,0)
        IF p(I%,0) > maxX THEN maxX = p(I%,0)
        IF p(I%,1) < minY THEN minY = p(I%,1)
        IF p(I%,1) > maxY THEN maxY = p(I%,1)
      NEXT I%

      dx_max = maxX - minX
      dy_max = maxY - minY
      cx = dx_max/2
      cy = dy_max/2
      d_max = SQR(cx^2 + cy^2)

      FOR I% = 0 TO nPts%-1
        x = p(I%,0)
        y = p(I%,1)
        p(I%,2) = 100 + 100*SQR((x-cx)^2 + (y-cy)^2)/d_max
        p(I%,0) -= cx
        p(I%,1) -= cy
      NEXT I%

      k = 255 * 1/700

      *REFRESH OFF

      REPEAT
        T% = TIME
        A = T%/122
        B = T%/176
        C = T%/312
        D = 2*PI*SIN(T%/520)
        E = 3*PI*COS(T%/720)
        sA = SIN(A)
        cA = COS(A)
        sB = SIN(B)
        cB = COS(B)
        sC = SIN(C)
        cC = COS(C)
        m() = (cB*cC), -cB*sC, sB,  cA*sC+sA*sB*cC, cA*cC-sA*sB*sC, -sA*cB, sA*sC-cA*sB*cC, sA*cC+cA*sB*sC, cA*cB
        q() = p().m()
        REMSYS Clr, G%, &000040
        SYS BPlot, G%, bg%, WinW%, WinH%, 0, 0
        FOR I% = 0 TO nPts%-1
          SYS Q%, G%, 320+q(I%,0), 256+q(I%,1), &100*INT(255-k*(350+q(I%,2)))
        NEXT
        PROCDisplay(TRUE)
      UNTIL FALSE
      END

      DEF FNgetFernPoints( rndseed%, store_coords% )
      LOCAL A%, C%, I%, S%, r, x, y, newx, newy
      S% = RND(-ABS(rndseed%))
      C% = 0
      x = 0
      y = 0
      SYS Clr, g{}, 0
      FOR I% = 1 TO 80000
        r = RND(1)
        CASE TRUE OF
          WHEN r<=0.1 A=0: B=0: C=0: D=0.16: E=0: F=0
          WHEN r>0.1 AND r<=0.86 A=.85: B=.04: C=-.04: D=.85: E=0: F=1.6
          WHEN r>0.86 AND r<=0.93 A=.2: B=-.26: C=.23: D=.22: E=0: F=1.6
          WHEN r>0.93 A=-.15: B =.28: C=.26: D=.24: E=0: F=.44
        ENDCASE
        newx=A*x+B*y+E
        newy=C*x+D*y+F
        x=newx
        y=newy
        SYS R%, G%, 300+48*x, 16+48*y TO A%
        IF A% = 0 THEN
          IF store_coords% THEN
            SYS P%, G%, 300+48*x, 16+48*y, &E020
            p(C%,0) = 48*x
            p(C%,1) = 48*y
            p(C%,2) = 0
          ELSE
            SYS P%, G%, 300+48*x, 16+48*y, &E020
          ENDIF
          C% += 1
        ENDIF
      NEXT
      = C%
 

Re: GLib (compact sprite library)
Post by DDRM on Jun 28th, 2016, 08:44am

Hi David,

I get a fail with PROCFWS: where/what is it? It doesn't seem too critical, because simply deleting it results in a working program. grin

I like the way you make the fern flat and then map in the z coordinate by distance, and the nice use of matrix multiplication to the rotation mapping all at once - those matrix functions are blindingly fast, aren't they? A great feature of BB4W.

If you scaled the z mappings in your matrix m() by one of your rotation angles, could you make it "blow in the breeze"?

Best wishes,

D
Re: GLib (compact sprite library)
Post by David Williams on Jun 28th, 2016, 10:34am

on Jun 28th, 2016, 08:44am, DDRM wrote:
I get a fail with PROCFWS: where/what is it? It doesn't seem too critical, because simply deleting it results in a working program. ;D


That was a subroutine I added to a more recent version of GLib than the one you may have in your LIB folder. Version 0.07 is the latest version, and the library code is listed in the first post of this thread. 'FWS' stands for 'Fix Window Size'. The reason for the cryptic name is in keeping with GLib's original need to be usable with the old, memory-limited trial version of BB4W.


Quote:
If you scaled the z mappings in your matrix m() by one of your rotation angles, could you make it "blow in the breeze"?


That's a nice idea, but with 18,907 points to 'process', the frame rate would probably take quite a hit! I may give it a try nevertheless, even if I need to resort to assembler code for speed.

I've included a slightly more efficient version of the program below (it uses a colour table, and hexadecimal constants in the point-plotting loop because the interpreter converts them to binary slightly faster than base 10).

David.
--

Code:
      REM Fern 3D (v3)
      REM Requires GLib to run

      *ESC OFF
      *FLOAT 64

      INSTALL @lib$ + "GLIB" : REM remove this line if GLib library is appended to end of this program

      PROCFWS : MODE 8 : OFF

      PROCInitGLIB( g{} )

      ON ERROR PROCCleanup : REPORT : END
      ON CLOSE PROCCleanup : QUIT

      P% = PlotPixel
      Q% = PlotPoint
      R% = ReadPixel
      G% = g{}

      REM Create colour-graduated background bitmap:
      WinW% = @vdu%!208
      WinH% = @vdu%!212
      bg% = FN`ga(4*WinW%*WinH%, 1)
      FOR Y% = 0 TO WinH%-1
        A% = 4*Y%*WinW%
        f = 1 - Y%/(WinH%-1)
        C% = INT(255*0.5*f) + &100*INT(255*0.15*f) + &10000*INT(255*0.075*f)
        FOR I% = bg%+A% TO bg%+A%+4*WinW%-1 STEP 4
          !I% = C%
        NEXT I%
      NEXT Y%

      nPts% = FNgetFernPoints( 12345678, FALSE )
      DIM p(nPts%-1,2), q(nPts%-1,2), m(2,2)
      dummy% = FNgetFernPoints( 12345678, TRUE )

      minX = 10000000
      maxX = -minX
      minY = 10000000
      maxY = -minY

      FOR I% = 0 TO nPts%-1
        IF p(I%,0) < minX THEN minX = p(I%,0)
        IF p(I%,0) > maxX THEN maxX = p(I%,0)
        IF p(I%,1) < minY THEN minY = p(I%,1)
        IF p(I%,1) > maxY THEN maxY = p(I%,1)
      NEXT I%

      dx_max = maxX - minX
      dy_max = maxY - minY
      cx = dx_max/2
      cy = dy_max/2
      d_max = SQR(cx^2 + cy^2)

      FOR I% = 0 TO nPts%-1
        x = p(I%,0)
        y = p(I%,1)
        p(I%,2) = 50 + 150*SQR((x-cx)^2 + (y-cy)^2)/d_max
        p(I%,0) -= cx
        p(I%,1) -= cy
      NEXT I%

      k = 1/700
      DIM C%( 700 )
      FOR I% = 0 TO 700
        C%(I%) = &100*INT(255*k*I%) + INT(64*k*I%)
      NEXT I%

      *REFRESH OFF

      REPEAT
        T% = TIME
        A = T%/122
        B = T%/176
        C = T%/312
        D = 2*PI*SIN(T%/520)
        E = 3*PI*COS(T%/720)
        sA = SIN(A)
        cA = COS(A)
        sB = SIN(B)
        cB = COS(B)
        sC = SIN(C)
        cC = COS(C)
        m() = cB*cC, -cB*sC, sB,  cA*sC+sA*sB*cC, cA*cC-sA*sB*sC, -sA*cB, sA*sC-cA*sB*cC, sA*cC+cA*sB*sC, cA*cB
        q() = p().m()
        SYS BPlot, G%, bg%, WinW%, WinH%, 0, 0
        FOR I% = 0 TO nPts%-1
          SYS Q%, G%, &140+q(I%,0), &100+q(I%,1), C%(&15E+q(I%,2))
        NEXT
        PROCDisplay(TRUE)
      UNTIL FALSE
      END

      DEF FNgetFernPoints( rndseed%, store_coords% )
      LOCAL A%, C%, I%, S%, r, x, y, newx, newy
      S% = RND(-ABS(rndseed%))
      C% = 0
      x = 0
      y = 0
      SYS Clr, g{}, 0
      FOR I% = 1 TO 80000
        r = RND(1)
        CASE TRUE OF
          WHEN r<=0.1 A=0: B=0: C=0: D=0.16: E=0: F=0
          WHEN r>0.1 AND r<=0.86 A=.85: B=.04: C=-.04: D=.85: E=0: F=1.6
          WHEN r>0.86 AND r<=0.93 A=.2: B=-.26: C=.23: D=.22: E=0: F=1.6
          WHEN r>0.93 A=-.15: B =.28: C=.26: D=.24: E=0: F=.44
        ENDCASE
        newx=A*x+B*y+E
        newy=C*x+D*y+F
        x=newx
        y=newy
        SYS R%, G%, 300+48*x, 16+48*y TO A%
        IF A% = 0 THEN
          IF store_coords% THEN
            SYS P%, G%, 300+48*x, 16+48*y, &E020
            p(C%,0) = 48*x
            p(C%,1) = 48*y
            p(C%,2) = 0
          ELSE
            SYS P%, G%, 300+48*x, 16+48*y, &E020
          ENDIF
          C% += 1
        ENDIF
      NEXT
      = C%
 

Re: GLib (compact sprite library)
Post by DDRM on Jun 28th, 2016, 11:09am

Hi David,

Aha! I now have the updated version (007, I think. I'll be careful not to offend it!), and the problem has gone away.

The point of my "waving" suggestion was that I think you could do it by altering the contents of the m matrix, so there should be essentially 0 time cost. I may be misunderstanding how it works, though, I haven't worked through it carefully. You might need an intermediate array equivalent to p and q, to hold the "blown" XYZ data (i.e with the z coordinates transformed by Cos(angle)), but even that should be fast.

OK, I had a go:
Code:
REM Near the top: probably a sledgehammer! Make space for extra array
HIMEM=LOMEM+4E7
.
REM With the other array declarations (included)
DIM p(nPts%-1,2), q(nPts%-1,2), m(2,2),dt(nPts%-1,2)
.
REM Highjacking your mapping section
m()=1,0,0,0,1,0,0,0,cA
dt()=p().m()
m() = cB*cC, -cB*sC, sB,  cA*sC+sA*sB*cC, cA*cC-sA*sB*sC, -sA*cB, sA*sC-cA*sB*cC, sA*cC+cA*sB*sC, cA*cB
q() = dt().m()
 


Best wishes,

D
Re: GLib (compact sprite library)
Post by David Williams on Jun 28th, 2016, 3:01pm

David, that's efficient and works well! And there's me thinking (or perhaps not thinking) I'd need to do it via a loop.

You probably noticed that darker points (in this case, a point is a 2x2 block of pixels) sometimes overwrite lighter ones, when ideally the points should be plotted in order from dark to light according to their Z co-ordinates. One could sort them (very CPU-expensive given the number of points), or one could write an assembler routine which reads a background pixel and only overwrites it with a new one if the new one is lighter (depending on how 'lightness' is defined!). Doing it in BASIC would really slow things down, I think.


Regards,
David.
--



Re: GLib (compact sprite library)
Post by David Williams on Sep 19th, 2016, 11:09am

Over at the 'BBC BASIC (SDL)' forum, Michael asks the question,

Quote:
The bigger question to ask you Richard, is how is a sprite image held in memory and quickly placed on the screen?


This is exactly what the compact library 'GLib' is for. The library code is posted in the first post of this thread. It plots 32bpp (4 bytes per pixel) sprites, and it's pretty fast (for a software-based renderer).

I've bundled up most of the GLib example programs that I had posted to this thread into a Zip folder containing compiled EXEs and souce code.

Download:

https://drive.google.com/open?id=0B3j5sIQi9SskVnlJdmZuYWI3cTQ


David.
--

Re: GLib (compact sprite library)
Post by David Williams on Sep 19th, 2016, 11:31am

on Sep 19th, 2016, 11:09am, David Williams wrote:
Over at the 'BBC BASIC (SDL)' forum, Michael asks the question,



This is exactly what the compact library 'GLib' is for. The library code is posted in the first post of this thread. It plots 32bpp (4 bytes per pixel) sprites, and it's pretty fast (for a software-based renderer).

I've bundled up most of the GLib example programs that I had posted to this thread into a Zip folder containing compiled EXEs and source code.

Download:

https://drive.google.com/open?id=0B3j5sIQi9SskVnlJdmZuYWI3cTQ


David.
--

Re: GLib (compact sprite library)
Post by michael on Sep 19th, 2016, 11:37pm

I see.. I looked over "Wavey Starfish" and I think I will be able to make it work for the animation overlays..
( what is the status of GLib? is it Open Source or Public Domain or what?)
I am currently working with a DIB section Ric shown me to adapt to my BMPtoCode output code..

The only huge leap is getting all the colors right and then its just a matter of converting my conditional statements to produce the proper hexadecimal value.

Your work is pretty cool. I will definitely tinker with it to see if I can adapt it to my code toon parts tools.

After a certain point, the data base will become too large to post, but hopefully, should allow a person to construct animations easily and control them with commands in code.
Here is an output code image that I would need to modify :

The colors in this program were extracted from windows paint color values on a 256 color BMP file format except for the bottom right color on the default colors.

So if a person were to convert this programs color controls and plotting controls, toon creation work could be more effective.

AND once that was done, I could modify BMPtoCode to automatically generate the code needed for any Code image.

And each image would function like a customizable BBC Basic command in a future library.
**** BMPtoCODE created this entire program from a 256 color BMP image. And the data statements are compressed image data

This cant be compressed any more.. It took me at least a month to work out the compression in LBB.

Code:
      VDU 22,8
      VDU 23,23,1|
      OFF:VDU 5:COLOUR 0:CLG
      PROC_image(100,500)
      WAIT 0:END
      DEF PROC_image(h,v)
      LOCAL DATA
      u=0:r=0:g=0:b=0:a=0:c=0:t=0
      ost$=""
      READ x,y:REPEAT
        READ nst$,t
        IF nst$="0" THEN r=0:g=0:b=0 :REM 'black              0   0   0
        IF nst$="1" THEN r=192:g=128:b=64:REM  ' brown      192 128  64
        IF nst$="2" THEN r=64:g=64:b=192 :REM'dark blue     64  64 192
        IF nst$="3" THEN r=128:g=128:b=128:REM' dark grey 128 128 128
        IF nst$="4" THEN r=128:g=0:b=0:REM'dark red       128   0   0
        IF nst$="5" THEN r=224:g=192:b=0 :REM'dark yellow  224   192   0
        IF nst$="6" THEN r=128:g=160:b=192:REM'flat blue  128 160 192
        IF nst$="7" THEN r=32:g=192:b=64 :REM'green        32 192  64
        IF nst$="8" THEN r=166:g=202:b=240:REM'light blue 166 202 240
        IF nst$="9" THEN r=192:g=192:b=192:REM 'light gray 192 192 192
        IF nst$="a" THEN r=192:g=224:b=0:REM  'light green  192 224   0
        IF nst$="b" THEN r=224:g=32:b=64:REM'light red    224  32  64
        IF nst$="c" THEN r=0:g=160:b=192 :REM'medblue        0 160 192
        IF nst$="d" THEN r=224:g=128:b=64 :REM 'orange       224 128 64
        IF nst$="e" THEN r=224:g=160:b=192:REM'pink       224 160 192
        IF nst$="f" THEN r=160:g=64:b=192:REM'purple      160 64 192
        IF nst$="g" THEN r=192:g=220:b=192:REM'tan        192 220 192
        IF nst$="h" THEN r=255:g=255:b=255:REM'white      255 255 255
        IF nst$="i" THEN r=255:g=255:b=0:REM'yellow       255 255 0
        COLOUR 0,r,g,b:GCOL 0
        FOR u=0 TO t
          a=a+1: MOVE h+c,v-a:DRAW h+c,v-a:IF a>x  THEN c=c+1:a=0
        NEXT u
      UNTIL nst$="100000"
      MOVE 0,0
      DATA 161,161
      DATA 0,9471,3,1,0,159,3,1,0,157,h,3,0,157,h,3,0,155,h,7,0,153,h,7,0,151,h,11,0,149,h,11,0,147,h,13,3,1,0,145,h
      DATA 13,3,1,0,143,h,15,0,145,h,15,0,145,g,1,h,15,0,143,g,1,h,15,0,143,h,17,0,143,h,17,0,141,h,21,0,139,h,21,0,139
      DATA h,21,0,139,h,21,0,137,h,25,0,135,h,25,0,135,h,27,0,133,h,27,0,133,h,27,3,1,0,131,h,27,3,1,0,129,3,1,h,29,0
      DATA 129,3,1,h,29,0,129,g,1,h,29,0,129,g,1,h,29,0,129,h,31,0,129,h,31,0,129,h,31,0,129,h,31,0,129,h,13,9,1,0,1
      DATA h,11,0,131,h,13,9,1,0,1,h,11,0,131,h,11,0,5,9,1,h,7,0,133,h,11,0,5,9,1,h,7,0,133,h,9,3,1,0,7,h
      DATA 5,0,135,h,9,3,1,0,7,h,5,0,139,h,7,0,5,g,1,h,3,0,141,h,7,0,5,g,1,h,3,0,145,g,1,h,7,g,1,0,149
      DATA g,1,h,7,g,1,100000,0
      ENDPROC
 


And here is the current project... extracting the colors and matching them to my custom colors in my output code file:

Any suggestions would help..

It has been just experimentation / trial and error to get the current colors I have posted here..

White found (updated on edit)

If you look at the previous code you will see the colors I need .. (its good to be close to the colors that I made, otherwise the image wont be right.

There seems to be a problem with this program. Seems the graphics draws and screen size are not always the same.. When I run it multiple times the image is not always the same.

Code:
      REM The 3rd and 4th components of the VDU 23,22 statement must be the same as bmi.Header.Width% and bmi.Header.Height%

      VDU 23,22,1024;640;16,16,16,0

      DIM BITMAPINFOHEADER{Size%, Width%, Height%, Planes{l&,h&}, BitCount{l&,h&}, Compression%, SizeImage%, XPelsPerMeter%, YPelsPerMeter%, ClrUsed%, ClrImportant%}
      DIM bmi{Header{} = BITMAPINFOHEADER{}}

      bmi.Header.Size% = DIM(BITMAPINFOHEADER{})
      bmi.Header.Width% = 1024
      bmi.Header.Height% = 640
      bmi.Header.Planes.l& = 1
      bmi.Header.BitCount.l& = 32

      SYS "CreateDIBSection", @memhdc%, bmi{}, 0, ^bits%, 0, 0 TO hbitmap%
      IF hbitmap%=0 ERROR 100, "Couldn't create DIBSection"

      SYS "SelectObject", @memhdc%, hbitmap% TO oldhbm%
      SYS "DeleteObject", oldhbm%
      SYS "InvalidateRect", @hwnd%, 0, 0

      REM The code to this point sets the screen size in pixels and creates bitmap to the same size

      REM To access the bitmap you need to use bit% :-

      REM Each pixel is four bytes

      REM You can access one at a time with  "bits%?(X,Y)=8_bit_number"

      REM Or all four bytes at the same time "bits%!(X,Y)=&aaRRGGBB

      REM when setting X and Y remember they are pixel coordinates not graphics coordinates

      X=100
      Y=100

      bits%?( ( X + ( Y * bmi.Header.Width% ) ) * 4 ) = 255 : REM Will put a blue dot at 100,100

      X=110
      bits%!( ( X + ( Y * bmi.Header.Width% ) ) * 4 ) =&FF0000
      X=115
      bits%!( ( X + ( Y * bmi.Header.Width% ) ) * 4 ) = &00FF00FF : REM Will put a magenta dot at 110,100
      FOR loop = 300 TO 500
  
        bits%!( ( loop + ( loop * bmi.Header.Width% ) ) * 4 ) = &400040 :REM DARK PURPLE
        bits%!( ( loop +8+ ( loop * bmi.Header.Width% ) ) * 4 ) = &FF0000  :REM RED
        bits%!( ( loop +15+ ( loop * bmi.Header.Width% ) ) * 4 ) = &F36E2D7 :REM CYAN
        bits%!( ( loop +16+ ( loop * bmi.Header.Width% ) ) * 4 ) = &F36E2D7 :REM CYAN
        bits%!( ( loop +25+ ( loop * bmi.Header.Width% ) ) * 4 ) = &0011E6380C :REM orange
        bits%!( ( loop +35+ ( loop * bmi.Header.Width% ) ) * 4 ) = &00BEFA717 :REM yellow
        bits%!( ( loop +45+ ( loop * bmi.Header.Width% ) ) * 4 ) = &00F360BFF :REM blue
        bits%!( ( loop +55+ ( loop * bmi.Header.Width% ) ) * 4 ) = &00F36E23C :REM green
        bits%!( ( loop +65+ ( loop * bmi.Header.Width% ) ) * 4 ) = &005F9C57C :REM TAN
        bits%!( ( loop +80+ ( loop * bmi.Header.Width% ) ) * 4 ) = &65497 :REM light blue
        bits%!( ( loop +90+ ( loop * bmi.Header.Width% ) ) * 4 ) = &654951 :REM Greyish purple?
        bits%!( ( loop +91+ ( loop * bmi.Header.Width% ) ) * 4 ) = &654951 :REM greyish purple?
        bits%!( ( loop +92+ ( loop * bmi.Header.Width% ) ) * 4 ) = &654951 :REM greyish purple?
        bits%!( ( loop +93+ ( loop * bmi.Header.Width% ) ) * 4 ) = &654951 :REM greyish purple?
        bits%!( ( loop +99+ ( loop * bmi.Header.Width% ) ) * 4 ) = &6549 :REM grass green
        bits%!( ( loop +109+ ( loop * bmi.Header.Width% ) ) * 4 ) = &65 :REM ? dark blue
        bits%!( ( loop +129+ ( loop * bmi.Header.Width% ) ) * 4 ) = &12632256:REM purple
        bits%!( ( loop +149+ ( loop * bmi.Header.Width% ) ) * 4 ) = &167777215 :REM Dark yellow
        bits%!( ( loop +159+ ( loop * bmi.Header.Width% ) ) * 4 ) = &167777:REM dark cyan
        bits%!( ( loop +169+ ( loop * bmi.Header.Width% ) ) * 4 ) = &167772111:REM dark red
        bits%!( ( loop +179+ ( loop * bmi.Header.Width% ) ) * 4 ) = &FFFFFF: REM BRIGHT WHITE BMPtoCODE
        bits%!( ( loop +189+ ( loop * bmi.Header.Width% ) ) * 4 ) = &C0CFD0: REM GRAY?  BMPtoCODE
        bits%!( ( loop +199+ ( loop * bmi.Header.Width% ) ) * 4 ) = &C0C0C0: REM Light GRAY  BMPtoCODE
        bits%!( ( loop +209+ ( loop * bmi.Header.Width% ) ) * 4 ) = &A0013BF: REM RED BMPtocode? NOPE!! its blue
      NEXT


      bits%!( ( X + ( Y * bmi.Header.Width% ) ) * 4 ) = &400040 :REM DARK PURPLE

      REM The reason for the "* bmi.Header.Width%" is to move the "Y" position up the screen by the correct displacement
      REM and the "*4" is because each position on the bit map is four pixels

      REM A simple line

      REM After you have finished accessing the bit map a "*REFRESH" is needed to update the screen

      *REFRESH
 

Re: GLib (compact sprite library)
Post by David Williams on Sep 21st, 2016, 11:48am

The programs linked-to below work with BB4W and BBCSDL. I get a rather inconsistent 60 to 70 fps on my Core i7 desktop PC with either BB4W and BBCSDL, and the same with BB4W on my Celeron-based laptop (duo core), but not with BBCSDL which returns roughly 30 fps. In this case, omitting the "WAIT 1" statement still isn't enough to raise the average frame rate beyond 50 fps. I have taken steps to ensure that the alpha channel of the 640x480 32bpp bitmap rendered via OSCLI "MDISPLAY ..." is totally clear so that SDL isn't attempting to alpha-blend anything. Furthermore, I have ensured that the address of the start of the bitmap pixel data (i.e. address of the first pixel in the background bitmap) is 'DWORD aligned' (i.e. divisible by 4). This certainly speeds up rendering with BB4W, because usually the pixel data of a 32bpp BMP is misaligned by 2 bytes (header size is 54 bytes, when ideally it ought to be 56!).

Links:

http://pastebin.com/FVRKZNN6

Scroll down to the 'RAW Paste Data' section, press CTRL+A to select all the code, then copy and paste into the BB4W/BBCSDL IDE.


Also, I've just added this (alpha-blended sprites over a colourful background):

http://pastebin.com/aTmyEpVd


Bonus: here's that "wavey starfish" again, compatible with BBCSDL:

http://pastebin.com/EfdUetEL


David.
--

Re: GLib (compact sprite library)
Post by David Williams on Sep 24th, 2016, 9:28pm

Here's another little demo (and definitely the last in the series) which works with both BB4W and BBCSDL. It is not a game as such, although you can use the Z and X keys to move your space ship, and press Enter to fire at the descending aliens.

http://pastebin.com/CWJKMLP4

The program is self-contained. You don't need to install GLib beforehand.

It also runs independent of frame rate.

When run under BBCSDL, I found that I have to clear the entire alpha channel of the 640x480 graphics 'surface' bitmap before calling *MDISPLAY. This is annoying and inefficient. But why is the alpha channel used in the first place? I use it here for fast and easy collision detection, not for alpha blending.


David.
--



Re: GLib (compact sprite library)
Post by DDRM on Sep 26th, 2016, 2:18pm

Hi David,

Ooh, that's an intriguing throw-away! How are you using it for collision detection?

D
Re: GLib (compact sprite library)
Post by David Williams on Sep 26th, 2016, 3:54pm

on Sep 26th, 2016, 2:18pm, DDRM wrote:
Ooh, that's an intriguing throw-away! How are you using it for collision detection?


The 'alien' sprites are plotted using the routine PlotSetAlpha which, apart from merely plotting a sprite, also sets the 'invisible' and normally redundant alpha byte of all overwritten background pixels to a specified value in the range 0 to 255. This value is different for each individual alien, and is in fact the alien's index in the alien{()} structure array. Actually, the indices run from 1 up to the maximum number of aliens that may be plotted (which can't exceed 255) because writing alpha values of zero isn't terribly useful in this scheme. So once we've plotted all the aliens, and the initially-clean alpha channel of the background/surface bitmap has been scribbled all over, when it comes to plotting our missiles/plasma bolts, those are plotted using PlotGetAlphaBits. This routine plots the missile sprites but also reads the background alpha bytes (of all pixels overwritten by the missile sprite) and returns the cumulative alpha value which, if there is no collision, should be zero. If it's nonzero, then a collision of the missile with one of the aliens has taken place. But which alien? The nonzero alpha value will be the actual index of the alien we've just collided with.

I'm pretty sure that I used this collision detection method with my game 'Alien Eliminator':

https://www.youtube.com/watch?v=lgPWQKjLaaw


And with other games, too.

With BB4W, drawing the background/surface bitmap using *MDISPLAY is relatively fast irrespective of the state of the bitmap's alpha channel (whether it is completely clear, or contains nonzero alpha values), because it doesn't attempt to do any alpha blending. But with BBCSDL and *MDISPLAY, the bitmap is 'automatically' scanned for nonzero alpha values prior to rendering. If the alpha channel is clear then the bitmap is drawn fast-ish (not as fast as with BB4W), but if there is so much as one nonzero alpha value, then SDL will alpha blend the entire bitmap (or at least I think that's the case; it certainly seems to be). This is why I clear the alpha channel (when run under BBCSDL) before calling *MDISPLAY. I don't know how SDL is doing the (here unwanted) alpha channel pre-scanning, but I hope it's not using the CPU to do it!


David.
--
Re: GLib (compact sprite library)
Post by DDRM on Sep 27th, 2016, 12:55pm

Hi David,

OK, that's a cute and clever idea, but now I'm struggling a bit... So all the bits where you encode the string $S% as hex to (spaces defined by) variables like PlotGetAlphaBits are essentially pre-assembled functions, which you can then call using SYS, as if they were system commands? Sneaky, if opaque! wink So that's kind of like Michael's BMPtoCode idea? wink

With regard to the use of alpha channel values, Richard posted something on the cross-platform forum in another context that suggests that this is a "feature" of SDL rather than BB4SDL:

"What SDL does is to scan all the alpha bytes; if any of them are non-zero it (sensibly) assumes that you are using the alpha channel and applies it throughout. If none of the alpha bytes are non-zero it ignores the alpha channel entirely."

... so I wouldn't hold your breath for a version which doesn't behave that way...

Best wishes,

D
Re: GLib (compact sprite library)
Post by David Williams on Sep 27th, 2016, 1:52pm

on Sep 27th, 2016, 12:55pm, DDRM wrote:
OK, that's a cute and clever idea, but now I'm struggling a bit... So all the bits where you encode the string $S% as hex to (spaces defined by) variables like PlotGetAlphaBits are essentially pre-assembled functions, which you can then call using SYS, as if they were system commands? Sneaky, if opaque! wink


The intention behind GLib is that it be compact enough to be usable with the pre-v6 trial version of BB4W. So, yes, instead of memory-consuming assembly language, I use strings of pre-assembled machine code to save some space (although each byte of machine code requires 2 bytes for its ASCII representation). I suppose I could've used DATA statements instead which might have saved yet more space, but using strings seemed more convenient to me. I know that it was possible to circumvent (perhaps to a limited extent) the 8K (or was it 12K?) limit of the pre-v6 trial version of BB4W, but I'm not clever enough to figure out how to do it!

Incidentally, the more extensive GLib2 library is DLL-based (uses even less memory than GLib). I wrote it while I was learning C. I found that I was able to write graphics functions sometimes in a matter of minutes (literally!) rather than hours. I know that compiled C can be slower than hand-coded assembler code, but not always, and it depends on the competence of the programmer. Since a C compiler understands the CPU architecture better than most human programmers, and knows how to schedule sequences of instructions for efficient execution, compiled C can be faster. Some of my C-based graphics routines are faster than their largely equivalent assembly language versions, even though they're based on the same algorithms, and this is likely due to the C compiler sequencing (pipelining) the instructions in a more efficient way than I ever possibly could.


Quote:
So that's kind of like Michael's BMPtoCode idea? wink


Hmm.. perhaps, in a sense? Just to be sure, those machine code routines don't encode any image/sprite information.


Quote:
With regard to the use of alpha channel values, Richard posted something on the cross-platform forum in another context that suggests that this is a "feature" of SDL rather than BB4SDL:


I was aware of this, but if there is a way the 'pre-scanning' could be suppressed then it could save some (or rather a lot!) of CPU cycles. For a while, I did wonder why large (e.g. 640x4800 32bpp) BMP images took so long to render via *MDISPLAY (under BBCSDL). SDL was pre-scanning over 3 million pixels, even though for my particular application it wasn't necessary as no alpha blending was taking place.

I won't be able to develop one myself, but an SDL-based graphics library (which uses accelerated SDL graphics surfaces and rendering operations) is highly desirable. (GLib and GfxLib is mostly all CPU-intensive software-based rendering.) Such a library would ideally include an image loader (with appropriate conversion where necessary), and a function for synchronising graphics rendering with screen/monitor's refresh. I gather, from a brief Google search, that VBlank synchronisation is difficult or impossible with SDL in 'windowed mode'.


David.
--
Re: GLib (compact sprite library)
Post by David Williams on Sep 27th, 2016, 7:35pm

Just another experiment with frame rate-independent animation. I'm probably doing this wrong!

Use the Up/Down arrow keys to increase/decrease the number of sprites displayed. The frame rate will drop as the number of sprites increases, but the speed of the sprites should be roughly constant. The frame rate and number of objects are displayed in the window's title bar.

Works with BB4W and BBCSDL:

Code:
      *ESC OFF

      NumSprTypes% = 20
      MaxObjs% = 2000

      M% = 3
      HIMEM = LOMEM + M%*&100000
      HIMEM = (HIMEM + 3) AND -4

      BB4W% = (INKEY(-256) == &57)

      IF BB4W% THEN
        GetTicks$ = "GetTickCount"
        SetWindowText$ = "SetWindowText"
      ELSE
        GetTicks$ = "SDL_GetTicks"
        SetWindowText$ = "SDL_SetWindowTitle"
      ENDIF

      ScrW% = 640
      ScrH% = 480
      VDU 23,22,ScrW%;ScrH%;8,16,16,0 : OFF

      TmpBMPBuf% = FNmalloc(4*128*128)

      REM Create a 640x480 32bpp 'surface' bitmap:
      DIM bmp{a%,w%,h%}
      bmp.w%=ScrW%
      bmp.h%=ScrH%
      bmp.a%=FNcreateBMP32(bmp.w%,bmp.h%)

      PROCInitGLIB(g{},bmp.w%,bmp.h%)
      g.a%=bmp.a%+54

      DIM spr{(NumSprTypes%-1) a%, sz%}
      DIM obj{(MaxObjs%-1) type%, x, y, dx, dy}

      *REFRESH OFF

      GCOL 15
      FOR I%=0 TO NumSprTypes%-1
        spr{(I%)}.sz% = 4*(8+RND(10))
        CLS
        col1% = RND(64)+&100*RND(64)+&10000*RND(64)
        col2% = (32+RND(255-32))+&100*(32+RND(255-32))+&10000*(32+RND(255-32))
        FOR r%=spr{(I%)}.sz%/2-1 TO 1 STEP -1
          f=1-r%/(spr{(I%)}.sz%/2-1)
          PROClerpRGB(col1%,col2%,f,R%,G%,B%)
          COLOUR 15,R%,G%,B%
          CIRCLE FILL spr{(I%)}.sz%, spr{(I%)}.sz%, 2*r%
        NEXT r%
        file$ =  @tmp$ + "spr" + STR$I%
        OSCLI "GSAVE """+file$+""" "+STR$0+","+STR$0+","+STR$(2*spr{(I%)}.sz%)+","+STR$(2*spr{(I%)}.sz%)
      NEXT I%

      CLS : OSCLI "REFRESH" : OSCLI "REFRESH ON"

      FOR I%=0 TO NumSprTypes%-1
        PRINT I%
        file$ =  @tmp$ + "spr" + STR$I% + ".BMP"
        spr{(I%)}.a% = FNLoadBMP(file$,TmpBMPBuf%,4*128*128)
      NEXT I%

      FOR I%=0 TO MaxObjs%-1
        obj{(I%)}.type% = RND(NumSprTypes%)-1
        obj{(I%)}.x = 32+RND(ScrW%-64)
        obj{(I%)}.y = 32+RND(ScrH%-64)
        obj{(I%)}.dx = 0.05 * SGN(RND(1)-0.5) * (0.25 + 3.75*RND(1))
        obj{(I%)}.dy = 0.05 * SGN(RND(1)-0.5) * (0.25 + 3.75*RND(1))
      NEXT I%

      G% = g{}

      NumObjs% = 1

      flushKbBuf% = 0
      frames% = 0
      frameRate% = 0
      SYS GetTicks$ TO time0%

      REPEAT
  
        SYS GetTicks$ TO t1%
  
        SYS Clr,g{},&A0D0
  
        P% = PlotShadow
        FOR I%=0 TO NumObjs%-1
          t%=obj{(I%)}.type%
          a%=spr{(t%)}.a%
          sz%=spr{(t%)}.sz%
          SYS P%,G%,a%,sz%,sz%,obj{(I%)}.x-16,obj{(I%)}.y-16
        NEXT
  
        P% = Plot
        FOR I%=0 TO NumObjs%-1
          t%=obj{(I%)}.type%
          a%=spr{(t%)}.a%
          sz%=spr{(t%)}.sz%
          SYS P%,G%,a%,sz%,sz%,obj{(I%)}.x,obj{(I%)}.y
        NEXT
  
        OSCLI "MDISPLAY "+STR$~bmp.a%
        WAIT 1
  
        SYS GetTicks$ TO t2%
  
        dt%=t2%-t1%
  
        FOR I%=0 TO NumObjs%-1
          obj{(I%)}.x+=dt%*obj{(I%)}.dx
          obj{(I%)}.y+=dt%*obj{(I%)}.dy
          IF obj{(I%)}.x<-32 THEN
            obj{(I%)}.dx*=-1
            obj{(I%)}.x+=obj{(I%)}.dx
          ENDIF
          IF obj{(I%)}.x>ScrW%-32 THEN
            obj{(I%)}.dx*=-1
            obj{(I%)}.x+=obj{(I%)}.dx
          ENDIF
          IF obj{(I%)}.y<-32 THEN
            obj{(I%)}.dy*=-1
            obj{(I%)}.y+=obj{(I%)}.dy
          ENDIF
          IF obj{(I%)}.y>ScrH%-32 THEN
            obj{(I%)}.dy*=-1
            obj{(I%)}.y+=obj{(I%)}.dy
          ENDIF
        NEXT
  
        IF INKEY-58 THEN
          NumObjs%+=1
          IF NumObjs%=MaxObjs% NumObjs%=MaxObjs%
        ENDIF
  
        IF INKEY-42 THEN
          NumObjs%-=1
          IF NumObjs%<1 NumObjs%=1
        ENDIF
  
        frames% += 1
        SYS GetTicks$ TO time1%
        IF time1%-time0% >= 1000 THEN
          frameRate% = frames%
          frames% = 0
          SYS SetWindowText$, @hwnd%, STR$frameRate% + " fps | "+STR$NumObjs%+" objects"
          SYS GetTicks$ TO time0%
        ENDIF
  
        IF flushKbBuf% > 0 THEN
          flushKbBuf% -= 1
        ELSE
          *FX 21, 0
          flushKbBuf% = 250
        ENDIF
  
      UNTIL FALSE
      END

      DEF PROClerpRGB( rgb1%, rgb2%, f, RETURN r%, RETURN g%, RETURN b% )
      LOCAL r1%, g1%, b1%, r2%, g2%, b2%
      r1% = (rgb1% AND &FF0000) >> 16
      g1% = (rgb1% AND &FF00) >> 8
      b1% = rgb1% AND &FF
      r2% = (rgb2% AND &FF0000) >> 16
      g2% = (rgb2% AND &FF00) >> 8
      b2% = rgb2% AND &FF
      r% = r1% + f*(r2%-r1%)
      g% = g1% + f*(g2%-g1%)
      b% = b1% + f*(b2%-b1%)
      ENDPROC

      DEF FNcreateBMP32( W%, H% )
      LOCAL A%, S%
      S% = 54 + 4*W%*H% + 6
      DIM A% S%-1
      A% = ((A% + 3) AND -4) + 2
      A%?0 = ASC"B"
      A%?1 = ASC"M"
      A%!2 = 54 + 4*W%*H%
      A%!6 = 0
      A%!10 = 54
      A%!14 = 40
      A%!18 = W%
      A%!22 = H%
      A%?26 = 1
      A%?28 = 32
      A%!30 = 0
      A%!34 = 4*W%*H%
      A%!38 = 0
      A%!42 = 0
      A%!46 = 0
      A%!50 = 0
      = A%

      DEF FNLoadBMP( file$, buf%, bufSz% )
      LOCAL A%, B%, F%, W%, H%, I%, S%
      F% = OPENIN( file$ )
      IF F% = 0 THEN ERROR 0, "Can't find file " + file$
      S% = EXT#F%
      CLOSE#F%
      OSCLI "LOAD """ + file$ + """ " + STR$~buf%
      IF buf%?0<>ASC"B" OR buf%?1<>ASC"M" THEN
        ERROR 0, file$ + " isn't a BMP file"
      ENDIF
      IF buf%?28 <> 24 AND buf%?28 <> 32 THEN
        ERROR 0, "BMP file has invalid colour depth (must be 24 or 32)"
      ENDIF
      W% = buf%!18
      IF (W% AND 3) <> 0 THEN ERROR 0, "BMP image width must be a multiple of 4"
      H% = buf%!22
      REM if H% is negative then sprite is "upside down" !
      S% = 4 * W%*ABS(H%)
      DIM B% S%+7
      B% = (B% + 7) AND -8
      A% = buf% + buf%!10

      IF SGN(H%) = 1 THEN
        IF buf%?28 = 24 THEN
          FOR I% = B% TO B%+S%-1 STEP 4
            !I% = ?A% OR &100*A%?1 OR &10000*A%?2
            A% += 3
          NEXT
        ENDIF
      ENDIF

      IF SGN(H%)=-1 THEN
        H%=ABS(H%)
        IF buf%?28=24 THEN
          A%+=3*W%*(H%-1)
          FOR Y%=0 TO H%-1
            Z%=A%
            FOR I%=B%+4*W%*Y% TO B%+4*(W%*Y% +(W%-1))-1 STEP 4
              !I%=?Z% OR &100*Z%?1 OR &10000*Z%?2
              Z%+=3
            NEXT
            A%-=3*W%
          NEXT
        ENDIF
      ENDIF
      = B%

      REM ============================================================================
      DEFPROCInitGLIB(RETURN v{},W%,H%):LOCALS%,Z%:DIMv{a%,w%,h%}
      v.w%=W%
      v.h%=H%
      Z%=FNmalloc(4096)
      S%=FNmalloc(2048)
      $S%="608B5C2424C703FFFFFFFF8B7C24383B7C24280F8DC70000008B74243C3B74242C0F8DB9"
      $S%+="0000008B4C24308B54243483F9007D03F7D99083FA007D03F7DA90F7D93BF90F8E970000"
      $S%+="00F7DA3BF20F8E8D000000F7D9F7DAC70300000000C7430400000000C7430800000000C7"
      $S%+="430C00000000894B10895314897B1889731C8BEF03E93B6C24287E0E2B6C2428296B10C7"
      $S%+="4304FFFFFFFF83FF007D14297B0C017B10C7431800000000C74304FFFFFFFF8BEE03EA3B"
      $S%+="6C242C7E0E2B6C242C296B14C74304FFFFFFFF83FE007D14297308017314C7431C000000"
      $S%+="00C74304FFFFFFFF61C21C0060":gClip=FN`m(S%,0)
      $S%="608B6C24248B4424288B7D008B4D040FAF4D08FCF3AB61C2080000":Clr=FN`m(S%,0)
      $S%="608BEC81EC800000008B75248BC4FF7538FF7534FF7530FF752CFF7608FF760450E8F6FE"
      $S%+="FFFFF70424FFFFFFFF0F855A0000008B552C89542434C1642434020FAF5424080354240C"
      $S%+="C1E2020355288B7E04897C2438C1642438020FAF7C241C037C2418C1E702033E8B5C2410"
      $S%+="33C98B048A85C0740389048F83C1013BCB7CEF03542434037C2438FF4C241475DF81C480"
      $S%+="00000061C2180060":Plot=FN`m(S%,33)
      $S%="608BEC81EC800000008B75248BC4FF7538FF7534FF7530FF752CFF7608FF760450E85FFE"
      $S%+="FFFFF70424FFFFFFFF0F85610000008B552C89542434C1642434020FAF5424080354240C"
      $S%+="C1E2020355288B7E04897C2438C1642438020FAF7C241C037C2418C1E702033E8B5C2410"
      $S%+="33C98B048A85C0740AD12C8F81248F7F7F7F7F83C1013BCB7CE803542434037C2438FF4C"
      $S%+="241475D881C48000000061C2180060":PlotShadow=FN`m(S%,33)
      Z%=FNmalloc(4096)
      ENDPROC
      DEFFN`m(S%,O%)
      LOCALA%,I%:A%=FNmalloc(LEN$S%):FORI%=0TOLEN$S%DIV2-1
        A%?I%=EVAL("&"+MID$($S%,2*I%+1,2)):NEXT
      IFO%>0PROC`d(A%,O%,gClip)
      =A%
      DEFPROC`d(B%,O%,C%):LOCALD%,P%:D%=C%-B%-O%-5:P%=B%+O%+1:!P%=D%:ENDPROC
      DEFFNmalloc(N%):LOCALA%:DIM A% N%+7:A%=(A%+7) AND -8:=A%
      REM ============================================================================
 

Re: GLib (compact sprite library)
Post by David Williams on Nov 23rd, 2016, 04:55am

Anyone that has seen the latest version of GLib (0.24 beta - used by the 'Snowfall 3' demo) may have noticed that it has grown quite at lot in size, and therefore it can no longer be described as being a 'compact' library! It now stands at around 54 Kb, which I don't believe is excessive given the amount of functionality it provides. There is always the temptation to keep on adding new functions (a mistake I made with GfxLib). I will add several more useful ones, and stop there, I think.

The library incorporates a lot of Richard's work that he had kindly contributed to GfxLib and the Programmer's Reference, and he will be duly credited in the next release. For instance, the 'Snowfall 3' demo uses his MMX-based PlotRotateScaleRTR and BoxBlur routines.


Things to do:

1) Tidy up the library (drastically)
2) Write the documentation
3) Release the stable version

All by the end of this month, hopefully.


David.
--
Re: GLib (compact sprite library)
Post by David Williams on Nov 30th, 2016, 11:53pm

GLIB's documentation is starting to take shape:

http://www.proggies.uk/glib/glibdocs.html


I hope to have it all completed by the end of the weekend (before I run out of enthusiasm and energy!).






Re: GLib (compact sprite library)
Post by David Williams on Dec 1st, 2016, 03:33am

An old program modified for use with GLib. It downloads the latest version of GLib, and a BMP image, and stuffs them in a Windows temporary folder.

Copy, paste, run...

Code:
      REM "Vector balls 4"
      REM Original GfxLib-based version created 16-Jan-2012
      REM Modified for GLib on 01-Dec-2016
      REM Uses 'PlotRotateScaleRTR' to draw the ball objects

      *ESC OFF
      *FLOAT 64
      PROCurldownload( "http://www.proggies.uk/glib/GLIB.bbc", @tmp$+"glib_bbc.BBC" )
      INSTALL @tmp$ + "glib_bbc"
      PROCglib_FixWindowSize : MODE 8 : OFF
      PROCglib_Init(g{}, glib{})
      ON CLOSE PROCglib_Cleanup : QUIT
      ON ERROR PROCglib_Cleanup : OSCLI "REFRESH ON" : ON : REPORT : PRINT " at line "; ERL : END
      INSTALL @lib$+"SORTLIB" : sort% = FN_sortinit(1,0)
      DIM g2{} = g{}

      ballTypes% = 6 : DIM bmAddrTbl%( ballTypes%-1 )

      REM bmAddrTbl%(0) = FNglib_LoadImg( @dir$+"blueball_128x128x24.BMP", -1 )
      bmAddrTbl%(0) = FNLoadImgFromNet( "http://www.proggies.uk/temp/", "blueball_128x128x24.bmp" )

      g2.w% = 128
      g2.h% = 128
      FOR I% = 1 TO ballTypes%-1
        bmAddrTbl%(I%) = FNglib_CopyBitmap( glib{}, bmAddrTbl%(0), 128, 128, -1 )
        g2.a% = bmAddrTbl%(I%)
        SYS glib.PlotShuffleRGB%, g2{}, bmAddrTbl%(I%), 128, 128, 0, 0, I%
      NEXT I%

      _PREMULTIPLY = &1
      _INVERT = &8
      _3MRPASS = 3<<4
      _2MBBPASS = 2<<6
      ctrl% = _PREMULTIPLY + _INVERT + _3MRPASS + _2MBBPASS
      FOR I% = 0 TO ballTypes%-1
        PROCglib_CreateAlphaMask( g{}, glib{}, bmAddrTbl%(I%), 128, 128, 0, ctrl% )
      NEXT I%

      N% = 50
      DIM bmAddr%( N%-1 ), bmAddr2%( N%-1 )
      DIM x%(N%-1), y%(N%-1), z%(N%-1)
      DIM x2%(N%-1), y2%(N%-1), z2%(N%-1)

      minDist% = 100

      R% = RND(-12345)

      FOR I%=0 TO N%-1
        REPEAT
          valid% = TRUE
          x%=-400+RND(2*400 - 1)
          y%=-400+RND(2*400 - 1)
          z%=-400+RND(2*400 - 1)
          IF I%=0 THEN
            x%(0) = x%
            y%(0) = y%
            z%(0) = z%
          ELSE
            FOR J%=0 TO I%
              d% = SQR( (x%-x%(J%))^2 + (y%-y%(J%))^2 + (z%-z%(J%))^2 )
              IF d% < minDist% THEN
                valid% = FALSE
                J% = I%
              ENDIF
            NEXT J%
          ENDIF
        UNTIL valid%
        x%(I%) = x%
        y%(I%) = y%
        z%(I%) = z%
        bmAddr%(I%) = bmAddrTbl%( RND(ballTypes%)-1 )
      NEXT I%

      a#=0
      b#=0
      c#=0

      *REFRESH OFF

      REPEAT
        T% = TIME
        bgCol% = &10000*INT(255*ABSSINRAD(T%/4.5)) + \
        \ &100*INT(255*ABSSINRAD(T%/6.7)) + \
        \ INT(255*ABSCOSRAD(T%/6.83))
        SYS glib.Clr%, g{}, bgCol%
  
        cosa# = COSa#
        cosb# = COSb#
        cosc# = COSc#
        sina# = SINa#
        sinb# = SINb#
        sinc# = SINc#
        FOR I%=0 TO N%-1
          x# = x%(I%)
          y# = y%(I%)
          z# = z%(I%)
          x1# = x#*cosa# - y#*sina#
          y1# = x#*sina# + y#*cosa#
          z1# = z#
          y2# = y1#*cosb# - z1#*sinb#
          z2# = y1#*sinb# + z1#*cosb#
          x2# = x1#
          z3# = z2#*cosc# - x2#*sinc#
          x3# = z2#*sinc# + x2#*cosc#
          y3# = y2#
          x2%(I%) = x3#
          y2%(I%) = y3#
          z2%(I%) = z3#
        NEXT I%
  
        bmAddr2%() = bmAddr%()
        C%=N% : CALL sort%, z2%(0), x2%(0), y2%(0), bmAddr2%(0)
  
        FOR I%=0 TO N%-1
          d# = 1/(&3E8+z2%(I%))
          x = &140 + &2A8 * x2%(I%) * d#
          y = &100 + &2A8 * y2%(I%) * d#
          scale% = &10000*300/(z2%(I%)+650)
          SYS glib.PlotRotateScaleRTR%, g{}, bmAddr2%(I%), 128, 128, 16*x, 16*y, 0, scale%
        NEXT
  
        a# += 0.019151
        b# += 0.016316
        c# += 0.012194
        IF a# > 2*PI THEN a# -= 2*PI
        IF b# > 2*PI THEN b# -= 2*PI
        IF c# > 2*PI THEN c# -= 2*PI
  
        PROCglib_Display
  
      UNTIL FALSE

      DEFFNLoadImgFromNet(url$,file$):LOCALA%,F%:F%=OPENIN(@tmp$+file$)
      IF F%=0 PROCurldownload(url$+file$,@tmp$+file$)
      CLOSE#F%
      A%=FNglib_LoadImg(@tmp$+file$,0)
      =A%

      DEFPROCurldownload(url$,file$)
      LOCALurlmon%,res%
      SYS"LoadLibrary","urlmon.dll"TOurlmon%
      SYS"GetProcAddress",urlmon%,"URLDownloadToFileA"TO`URLDownloadToFile`
      SYS`URLDownloadToFile`,0,url$,file$,0,0TOres%
      SYS"FreeLibrary",urlmon%
      IFres%ERROR100,"Couldn't download "+url$
      ENDPROC
 

Re: GLib (compact sprite library)
Post by DDRM on Dec 1st, 2016, 07:45am

Brilliant - that's really encouraging. It's very hard to use someone else's programming tools without a clear understanding of the required format etc.

Thanks!

D
Re: GLib (compact sprite library)
Post by sveinioslo on Dec 1st, 2016, 7:49pm

Quote:
GLIB's documentation is starting to take shape:

Thank you, been waiting for this.

Svein

Re: GLib (compact sprite library)
Post by michael on Dec 3rd, 2016, 12:55am

WOW! That is an amazing 3D ball animation. Or at least it looks 3D..


Re: GLib (compact sprite library)
Post by David Williams on Dec 20th, 2016, 02:16am

Added a new routine to GLIB which stretches (scales) a bitmap, with the transparency level of each plotted pixel determined by that pixel's 'brightness' (actually its averaged RGB value). The darker a pixel is, the more transparent it is when plotted. Finally, a global transparency value is applied to each pixel so that the bitmap as a whole can be 'faded' into the background if so desired. This routine is perhaps useful for e.g. fireball/explosion effects.

Here's a quick demo of the new routine (it's nothing exciting of course):

http://www.proggies.uk/temp/test_plotscaleblend2.zip

I recommend that you extract the EXE file from the Zip folder before running it.


David.
--