MkSpr

Make a rectangular, single colour, compressed sprite on the fly

Sometimes it is useful to be able to create a coloured sprite on the fly, eg to fill in a blank spot in a menu, or to present a bar of a certain colour in a menu, or for a whole lot of other reasons. Of course you could create a sprite in advance and just load that. But what if you didnt necessarily know the size required in advance, or if youd like the menu to have a variable size or colour?

Below is presented a method and three utilities to do so. The method is straightforward (although it can be fiddly to achieve). The first creates a mode 64 coloured sprite, the second a Native coloured sprite, and finally the latter is deployed to produce a Wman palette element coloured sprite:

MkSpr64

The thing with mode 64 is that it is relatively easy to specify the colour. And thanks to the flexibility of the sprite format and implementation, it can be displayed in all GD2 modes: 16, 32, and 33, without change. A colour like $FF0000 is easier to understand than 248, which is what the same colour is in mode 32. (Actually, in mode 32, it is %0000000011111000 in the format of gggbbbbbrrrrrggg.) Also, the code of a colour in mode 32 is different from the code of the same colour in mode 33, which further complicates matters.

The downside of mode 64 is that it takes rather a lot of space, in fact double as much as modes 32/33, and four times as much as mode 16. Luckily, a single colour rectangular sprite compresses very well! (although at some point it does have to be decompressed by the system before it can be displayed!) A 10 x 10 pixel mode 64 sprite, worth a nominal 400b of data, compresses nicely down to 5b of data, only 2b more than a corresponding mode 32/33! Additionally a sprite header is required; 24b for uncompressed or 32b for compressed.

I wont go into the details of compression etc here. See my RLE toolkit or the enRLE_bas utility for more information.

rem + ------------------------------------------------------------------------ +
rem |<                                MkSpr64                                 >|
rem + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +
rem |          Make a mode 64 single colour, compressed, block sprite          |
rem |                                                                          |
rem | Save sprite (with overwrite!) if filename supplied, else keep memory     |
rem | No error checking!                                                       |
rem + ------------------------------------------------------------------------ +
rem | V0.01, pjw, 2021 Nov 14                                                  |
rem + ------------------------------------------------------------------------ +
:
DEFine FuNction MkSpr64(xs%, ys%, col, fnm$)
LOCal i%, ad, tg, bp%, ez%, c$(5)
LOCal d%, r%, sz, pz, cz
:
rem     Work out compressed data size
bp% = 4: ez% = bp% + 1:                 rem mode 64 => 4 bpp, ez% = element size
sz = xs% * ys% * bp%:
pz = sz / bp%
d% = pz DIV 128
r% = pz MOD 128
cz = ez% * (d% + (r% <> 0)) + 32
ad = ALCHP(cz)
:
rem     Convert RGB to count + colour 24
POKE_L ad, col: POKE   ad, 129
c$ = PEEK$(ad, ez%):                    rem c$ = count.b + col24
:
rem     Create sprite header
POKE   ad,       2, 64, 0:              rem GD2 mode 64 sprite..
POKE   ad +  3, %1000000:               rem Pattern compressed, do cache
POKE_W ad +  4, xs%, ys%, 0, 0:         rem Size, origen
POKE_L ad + 12, 12,  0, 0:              rem Offset to data
POKE$  ad + 24, 'RLE4':                 rem Compression header
POKE_L ad + 28, sz:                     rem Expanded data size
tg = ad + 32  :                         rem Target address for sprite data
:
rem     Now create sprite
FOR i% = 1 TO d%: POKE$ tg, c$: tg = tg + ez%
IF r%: POKE$ tg, c$: POKE tg, 257 - r%
:
rem     Save sprite (if wanted)
IF LEN(fnm$) THEN
 SBYTES_O fnm$, ad, cz: RECHP ad: RETurn 0
END IF
RETurn ad
END DEFine MkSpr64
  

MkSprNat

Notwithstanding what was said above about the difficulty of specifying colours in a sane way in Native mode (ie DISP_TYPE = 16, 32, or 33) it is useful to be able to do so. You might want to produce a rectangle for similar purposes as above in the same colour as some other object displayed on screen or in the menu. You could use my toolkit command GETCOL% to get the correct colour code for the object, whatever the current display mode. More on that later. So here is the same method used for native colours.

However, native colours come not only in different flavours, but in two fundamentally different forms, namely as a single byte colour code, or as a word sized colour code. And then there is the padding, of course, which never bothers mode 64 sprites. Thus the utility requires a little more legwork:

rem + ------------------------------------------------------------------------ +
rem |<                               MkSprNat                                 >|
rem + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +
rem |          Make a native, single colour, compressed, block sprite          |
rem |                                                                          |
rem | If mode (md%) given as -1 then current disp_type used                    |
rem | Save sprite (with overwrite!) if filename supplied, else keep memory     |
rem |                                                                          |
rem | Limited error checking! But check return value before use!               |
rem | (Remember: The Native colour codes are different for each mode!)         |
rem + ------------------------------------------------------------------------ +
rem | V0.01, pjw, 2021 Nov 14                                                  |
rem + ------------------------------------------------------------------------ +
:
DEFine FuNction MkSprNat(xs%, ys%, col%, md%, fnm$)
LOCal i%, x%, m%, bp%, c$(4), l$(xs% / 4)
LOCal ad, tg, sz, cz
:
rem     Create colour element, depending on mode
rem     Acccount for padding (but we dont care what it consists of)
IF md% = -1: m% = DISP_TYPE: ELSE : m% = md%
SELect ON m%
 = 16:     IF col% > 255: RETurn -4
           bp% = 1: c$ = CHR$(col%)
           x% = ((xs% + 3) DIV 4) * 4
 = 32, 33: bp% = 2: c$ = CHR$(col% DIV 256) & CHR$(col% MOD 256)
           x% = ((xs% + 1) DIV 2) * 2
 = REMAINDER : RETurn -19
END SELect
:
rem     Uncompressed data size (w padding)
sz = x% * ys% * bp%
:
rem     Create a row, work out size, reserve memory
l$ = ''
FOR i% = 1 TO xs% DIV 128: l$ = l$ & CHR$(129) & c$: x% = x% - 128
IF x%: l$ = l$ & CHR$(257 - x%) & c$
cz = LEN(l$) * ys% + 32
ad = ALCHP(cz)
:
rem     Create sprite header
POKE   ad,       2, m%, 0:               rem GD2 palette mapped sprite..
POKE   ad +  3, %1000000:                rem Pattern compressed, do cache
POKE_W ad +  4, xs%, ys%, 0, 0:          rem Size, origen
POKE_L ad + 12, 12,  0, 0:               rem Offset to data
POKE$  ad + 24, 'RLE' & bp%:             rem Compression header
POKE_L ad + 28, sz:                      rem Expanded data size
tg = ad + 32  :                          rem Target address for sprite data
:
rem     Now create sprite
FOR y% = 1 TO ys%: POKE$ tg, l$: tg = tg + LEN(l$)
:
rem     Save sprite (if wanted)
IF LEN(fnm$): SBYTES_O fnm$, ad, cz: RECHP ad: RETurn 0
RETurn ad
END DEFine MkSprNat
  

MkSprWM

Back to the issue of getting the correct colour code irrespective of display mode: What if you want to use the Wman palette colours? Getting the correct Wman palette colour is complicated by the fact that a completely different colour system is used in defining these colours; what you see is not what you get! (Bob Spelten has written a compact explainer of all this with his QCoCo package, for those interested in finding out more (See the Contacts page)). So rather than getting into the weeds over all this, the simplest approach is to leave all the hard work to the system to sort out: Set the colour you want using the tools and traps provided by the system for that purpose, and simply harvest the results of that labour with a simple peek. Afterall, when all is said and done, the display driver only works with native colours. So we need GETCOL% again.

The syntax of GETCOL% is:

         colour_native% = GETCOL%(#channel_no, key%)        default #1

Where key%
        -1 => paper
         0 => strip
         1 => ink

There is one wee further complication: On emulators running on Intel platforms, the Endian ghost raises its twisted head. So a peek of the native colour code (GETCOL% is just a peek into the channel definition block, after all) requires that code to be switched around to match the theoretical colour model as implemented in the sprite. But lets not get distracted, just use my PCBO% (PC Byte Order) command to do the business. All this does is switch the High byte with the Low byte of a word: HL -> LH.

There are many ways one could do MkSprWM, depending on whether there already are open channels, the number of repeated calls required etc, so the routine below will just be one way, for the sake of illustration. Modify it according to need. MkSprWM relies on MkSprNat, above:

rem + ------------------------------------------------------------------------ +
rem |<                                MkSprWM                                 >|
rem + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +
rem |    Make a single colour, compressed, block sprite using Wman colours     |
rem |                                                                          |
rem | This is a rather twisted hack to get the system to translate a Wman      |
rem | palette colour to the native colour format, but since doing so is a      |
rem | rather complex and convoluted process anyway, it seemed the best way..   |
rem |                                                                          |
rem | Dependencies: MkSprNat, & GETCOL%, PCBO%                                 |
rem + ------------------------------------------------------------------------ +
rem | V0.01, pjw, 2021 Nov 15                                                  |
rem + ------------------------------------------------------------------------ +
:
DEFine FuNction MkSprWM(xs%, ys%, col%, fnm$)
LOCal ch, c%
ch = FOPEN("scr_0x0a0x0")
WM_INK#ch; col%: c% = GETCOL%(#ch; 1): CLOSE#ch
IF DISP_TYPE = 32: PCBO% c%:            rem Intel: Switch byte order!
RETurn MkSprNat(xs%, ys%, c%, -1, fnm$)
END DEFine MkSprWM
:
:

If you download the BASIC (see below) you get a "harness" with the routines. This may give a better idea of how they could be used.

Note: If you set odd x-values for these sprites in any mode, you get a black stripe down the right side of the sprite. This is a feature of the system, not these routines.

And thats it! Please let me know how you get on! Also, if you find any bugs or discover optimisations etc, Id be interested to know..



Generated with sb2htm on 2021 Nov 19
V0.01, ©pjw, November 2o2i