Palette animation
by Alex Farlie and Richard Russell, July 2012
On 'hardware paletted' displays (i.e. those with 256 colours - 8 bits-per-pixel - or fewer) palette animation is possible. For example by changing the contents of the colour palette everything displayed in blue might be changed to be red, without actually needing to re-plot any graphics. Also, effects such as flashing colours can be achieved by dynamically changing the contents of the palette (this is how the BBC Micro achieves its flashing colours).
However all modern PC displays have a colour depth of greater than 8 bits-per-pixel, and therefore do not have a hardware colour palette. Superficially, therefore, they do not support palette animation, other than by using the compatibility mode provided by Windows (right-click on a shortcut, select Properties… Compatibility… Display settings… Run in 256 colors). This method has a number of disadvantages, not least that the entire display - not just a single window - must be switched to 256 colours.
Fortunately it is possible to provide a limited emulation of a hardware palette with the assistance of the Windows API. This article describes the steps which are necessary to achieve this.
Firstly the 'memory bitmap' used to hold the output from BBC BASIC for Windows must be changed from the usual 24 or 32 bits-per-pixel format to a suitable reduced colour depth (in this instance 4 bit-per-pixel). The procedure PROCpalleted shown below achieves this:
DEF PROCpaletted LOCAL bits%, hbm%, oldbm%, bmih{} DIM bmih{Size%, Width%, Height%, Planes{l&,h&}, BitCount{l&,h&}, \ \ Compression%, SizeImage%, XPelsPerMeter%, YPelsPerMeter%, \ \ ClrUsed%, ClrImportant%} bmih.Size% = DIM(bmih{}) bmih.Width% = @vdu%!208 bmih.Height% = @vdu%!212 bmih.Planes.l& = 1 bmih.BitCount.l& = 4 SYS "CreateDIBSection", @memhdc%, bmih{}, 0, ^bits%, 0, 0 TO hbm% IF hbm% = 0 ERROR 100, "Couldn't create DIBSection" SYS "SelectObject", @memhdc%, hbm% TO oldbm% SYS "DeleteObject", oldbm% PROCanimate ENDPROC
@vdu%!208 and @vdu%!212 are system variables containing the current width and height of the output window (client area) respectively. The third parameter of the call to CreateDIBSection is set to zero (DIB_RGB_COLORS) because the colour table will contain RGB colour values. Note that the colour table is not directly specified as it will be created dynamically later.
The bitmap is then selected into the device context used by BBC Basic for Windows, and the previous bitmap deleted. Finally PROCanimate is called to initialise the colour table from the contents of the palette.
Having changed the bitmap to 4bpp the contents of the palette may be changed in the usual way, for example using the VDU 19 command. However in order to animate the palette every change must be followed by another call to PROCanimate, which copies the logical palette into the colour table (involving swapping the red and blue colour values!) and then forces a display refresh:
DEF PROCanimate LOCAL C%, pal%() DIM pal%(15) SYS "GetPaletteEntries", @hpal%, 0, 16, ^pal%(0) pal%() AND= &E0F0F0 FOR C% = 0 TO 15 : SWAP ?^pal%(C%), ?(2+^pal%(C%)) : NEXT SYS "SetDIBColorTable", @memhdc%, 0, 16, ^pal%(0) SYS "InvalidateRect", @hwnd%, 0, 0 ENDPROC
Having set up the two routines to emulate palette animation, the following code demonstrates how to achieve an effect similar to the flashing colours on the BBC micro.
First a screen mode is selected, and the palletised version of it created:
MODE 8 PROCpaletted
Now some text and graphics, including the 'flashing' colour numbers 8 to 15, are drawn:
COLOUR 11 COLOUR 128+12 PRINT TAB(19,5) " Flashing colours demo by Richard Russell " VDU 5 FOR col% = 0 TO 15 GCOL col% RECTANGLE FILL col%*80,440,80,160 MOVE col%*80+30,420 GCOL 7 PRINT ;col%; NEXT GCOL 1 GCOL 128+8 MOVE 968,180 PRINT STRING$(41, CHR$127) " This has a flashing graphics background "
The code now enters a loop to animate the 'flashing' colours:
REPEAT FOR C% = 8 TO 15 VDU 19,C%,C%-8,0,0,0 NEXT PROCanimate WAIT 40 FOR C% = 8 TO 15 VDU 19,C%,15-C%,0,0,0 NEXT PROCanimate WAIT 40 UNTIL FALSE END
Logical colours 8 to 15 are first defined as physical colours 0 to 7 respectively, followed by a 400 millisecond delay. Then colours 8 to 15 are defined as physical colours 7 to 0 respectively (i.e. the order is reversed) and another 400 millisecond delay initiated. This is repeated indefinitely in order to cause the colours to flash.
It should be noted that whilst in this example the 'default' behaviour of the BBC Micro colours is demonstrated, other uses of palette animation can be emulated by adding a call to PROCanimate following every VDU 19 command. For example the RISC OS style VDU 19,l,17,r,g,b and VDU 19,l,18,r,g,b behaviour can be simulated.
The full example is:
MODE 8 PROCpaletted COLOUR 11 COLOUR 128+12 PRINT TAB(19,5) " Flashing colours demo by Richard Russell " VDU 5 FOR col% = 0 TO 15 GCOL col% RECTANGLE FILL col%*80,440,80,160 MOVE col%*80+30,420 GCOL 7 PRINT ;col%; NEXT GCOL 1 GCOL 128+8 MOVE 968,180 PRINT STRING$(41, CHR$127) " This has a flashing graphics background " REPEAT FOR C% = 8 TO 15 VDU 19,C%,C%-8,0,0,0 NEXT PROCanimate WAIT 40 FOR C% = 8 TO 15 VDU 19,C%,15-C%,0,0,0 NEXT PROCanimate WAIT 40 UNTIL FALSE END DEF PROCanimate LOCAL C%, pal%() DIM pal%(15) SYS "GetPaletteEntries", @hpal%, 0, 16, ^pal%(0) pal%() AND= &E0F0F0 FOR C% = 0 TO 15 : SWAP ?^pal%(C%), ?(2+^pal%(C%)) : NEXT SYS "SetDIBColorTable", @memhdc%, 0, 16, ^pal%(0) SYS "InvalidateRect", @hwnd%, 0, 0 ENDPROC DEF PROCpaletted LOCAL bits%, hbm%, oldbm%, bmih{} DIM bmih{Size%, Width%, Height%, Planes{l&,h&}, BitCount{l&,h&}, \ \ Compression%, SizeImage%, XPelsPerMeter%, YPelsPerMeter%, \ \ ClrUsed%, ClrImportant%} bmih.Size% = DIM(bmih{}) bmih.Width% = @vdu%!208 bmih.Height% = @vdu%!212 bmih.Planes.l& = 1 bmih.BitCount.l& = 4 SYS "CreateDIBSection", @memhdc%, bmih{}, 0, ^bits%, 0, 0 TO hbm% IF hbm% = 0 ERROR 100, "Couldn't create DIBSection" SYS "SelectObject", @memhdc%, hbm% TO oldbm% SYS "DeleteObject", oldbm% PROCanimate ENDPROC