                               Sprites
                               =======

This text is extracted from the SMSQ/E sources (dev8_extras_doc_display_txt)
and was originally authored by Marcel Kilgus, who designed the new sprite
format. I have made some additions to the text to try to bring some floating
information together in one place.

While efforts have been made to make this documentation comprehensive and
accurate, I know it is unlikely to be so. Things were not always clearly
defined or documented, and my understanding has evolved over time so that
things I believed to be true, turned out not to be so.

If you find any errors, or anything that seem unclear or missing, please let
me know and I shall endeavor to put it right. PM me at pjw, QL-Forum.co.uk.


III - New sprite definition
=-=-=-=-=-=-=-=-=-=-=-=-=-=

        A - Sprite Mode byte
        B - Sprite control byte
        C - Alpha channel
        D - Sprite compression
        E - Sprite block
        F - Options pointer


The sprite definition has been extensively modified, but in such a way
that it should continue to be compatible with older sprites.

The sprite header is now as follows:

pto_form $00  byte  sprite mode
         $01  byte  colour mode / system sprite number
pto_vers $02  byte  dynamic sprite version number
pto_ctrl $03  byte  sprite control
pto_xsiz $04  word  X size
pto_ysiz $06  word  Y size
pto_xorg $08  word  X offset
pto_yorg $0a  word  Y offset
pto_cpat $0c  long  relative pointer to colour pattern
pto_mask $10  long  relative pointer to mask/alpha channel
pto_nobj $14  long  relative pointer to next object

pto_opts $18  long  OPTIONAL (relative pointer to) options
pto_blk  $1c  long  OPTIONAL relative pointer to sprite block


(the keys can be found in DEV8_keys_qdos_pt)


A - Sprite MODE byte
--------------------

Sprite mode can be any of the following:
        0       system sprite
        1       traditional QL colour sprite (as before)
        2       GD2 colour sprite


System sprite:
        When the sprite mode is 0 for system sprites then the second byte
        is the number of the sprite. ALL other values are ignored in that
        case, i.e. a system sprite reference is only 2 bytes long.

QL colour sprite:
If this is a traditional Ql colour sprite, then the colour mode byte has one
of the following values (as before)

        0       mode 4
        1       mode 8

GD2 colour sprite:
        0       1 bit black&white
        3       1 bit palette mapped
        4       2 bit fixed gr palette
        7       2 bit palette mapped
        8       4 bit fixed irgb palette
        15      4 bit palette mapped
        16      8 bit fixed palette (equals Aurora palette) == native
        31      8 bit palette mapped
        32      16 bit QPC/QXL %gggbbbbbrrrrrggg format     == native
        33      16 bit Q40 %gggggrrrrrbbbbbw format         == native
        64      32 bit $RRGGBB00 format

Each row in a QL mode pattern or blob occupies one or more complete 16 bit
words. Each row in any extended colour definition pattern or blob occupies
one or more complete long words.


B - Sprite control byte:
------------------------
The sprite control bit is formatted as follows

        %mpao0xcc

        cc              %11 = 3 => no cache
        stands for a chache version number. Programs can increment this
        value to signal the cache that the sprite has changed. A special
        value is pto.fupd (force update, %11), which causes the system to
        never use the cached version.
        key: pto.cver

        a               %00100000 #5 = 32
        flags whether the sprite uses an alpha channel instead of a mask
        (see below)
        key: pto.alph

        m and p         %10000000 and %01000000 #7 and  #6 = 128 and 64
        signal whether the pattern (p) or the mask (m) is compressed (see below)
        key: pto.pcmp and pto.mcmp

        x               %00000100 #2 = 4
        signals that the optional pointer to a sprite block is present (bit x=1)
        or is not present (bit x=0) (see below)
        key pto..blk

        o               %00010000 #4 = 16
        signals that options are present (=1) or not (=0)(see below)
        key pto..opt
        currently unused
        Note: If pto..opt set then if pto_opts = 0 => faulty sprite, not
        properly displayed.

C - Alpha channel:
------------------
        When the pto.alph flag is set in the sprite control byte, the mask
        is considered to be an alpha channel. An alpha channel allows gradual
        mixes between the background and the sprite pattern. Every pixel is
        represented by exactly one byte. 0 means the pixel is completely
        transparent, 255 means the pixel is completely opaque. Values in
        between determine the degree of mixing of background and foreground.
        Alpha channel information is not padded at the end of each line.
        There's one byte for every pixel and nothing more.


D - RLE compression:
--------------------
        Both pattern and mask/alpha channel can be compressed using a simple
        RLE (run length encoding) algorithm. This is usefull with data that
        is largely homogene, which is often the case with masks.
        Compressed data must be signaled in the sprite control byte
        (pto.pcmp, pto.mcmp) and starts with the bytes 'RLEx', with 'x' being
        either 1, 2 or 4. This is the item size the algorithm is working with.
        8 bit RLE compression of 32 bit data wouldn't yield in good results,
        therefore the algoritm can also work on 16 bit or 32 bit data.
        After the ID there's one long word containing the size of the data in
        uncompressed form. After that the compressed data itself is following.

        The compressed data always consists of one byte and one or more items.
        If the leading byte is in the range of 0<=x<128 then x+1 uncompressed
        items are following. Otherwise only one item is following, which
        represents 257-x times the same item in the uncompressed data.

Note:   Compression of QL mode sprites (using RLE2) works if sprites are
        viewed in a GD2 display mode, but is not supported in QL display
        modes under Qdos or SMSQ/E


E - Sprite block
--------------------------------

The object drawing routines have been amended so as to allow different sprites
to be drawn in loose menu items, depending on the status of that item.

In such a case, it behooves the application to supply the different sprites and
to set up a "sprite block" which is just a block of 5 longword pointers, as
defined just below.

To keep things compatible with older versions of WMAN, this has been handled
by setting a bit (pto..blk) in the sprite control byte. If this bit is set to 1,
then there must be a pointer pointer to a sprite block. Please note that this
pointer MUST be preceeded by the (pointer to) additional options (see section F
below) which, currently is just a longword 0.

IF pto..blk IS SET, THEN pto_opts MUST EXIST (AS A LONG WORD 0) AND pto_blk
MUST POINT TO A VALID BLOCK.

The sprite pointer block is just a block of 5 longword pointers:

pointer to sprite if item is available
pointer to sprite if item is available AND is the current item
pointer to sprite if item is selected
pointer to sprite if item is selected AND is the current item
pointer to sprite if item is unavailable

In all cases, these are long word relative pointers.

All but the first pointer may be 0. The first pointer (item available) MUST
exist and point to a real sprite.


0 pointers are handled as follows

- For available items:
  * The pointer to the available item sprite MUST exist.

  * If no pointer to an available AND current item sprite exists, then the
    available item sprite is taken instead
     
- For selected items:
  * If no pointer to a selected item exists, then the pointer to the selected
    item AND current item is ALSO ignored. The avilable item sprite is taken
    instead for both.

  * If no pointer to a selected AND current item sprite exists, then the
    selected item sprite is taken instead.

- For unavailable items, the available item sprite is used.


It is allowed, but not necessary, for any of these pointers including the first
pointer (available item) to point back to the original sprite, which will then
be drawn as a normal sprite!

This allows three cases:

1 - The original sprite can be an ordinary QL mode sprite, which will be
drawn normally by older versions of WMAN. The newer versions of WMAN will
use the extended format.

2 - The original sprite could be a simple empty shell, with just the relevant
data (bit pro..blk) and the pointer to a sprite block set.

3 - The original sprite could be a normal QL or 24 bit mode sprite which
will be used by an item in any of its statusses.


Alternative 1 above will ensure that your software remains compatible with
older versions of WMAN.

F - Options

To provide for future expandability of the sprite format, a special bit in
the sprite control byte indicates whether the sprite definition is followed
by an options long word or pointer. If bit pto..opt is set, then this options
long word or pointer must exist.

How options are to be used has not been finally determined. However, see
the proposal below.

The options longword can be ignored and even omitted UNLESS the sprite also
contains a pointer to a sprite block (see above), in which case the options
longword MUST EXIST and unless options have been defined should be set to 0.


Padding:
========

QL modes
--------
Each row in a QL mode pattern or blob occupies one or more complete 16 bit
words.

Patterns must always be padded to the nearest word, and the padding byte(s)
used MUST be 0.

Masks are padded to the nearest word, just like colour patterns. The padding
byte(s) MUST be 0.


Extended modes
--------------
Each row in any extended colour definition (GD2) pattern or blob occupies
one or more complete long words.

Patterns must always be padded to the nearest long word, and the padding
byte(s) used MUST be 0.

Except for alpha masks, masks are padded to the nearest long word, just
like colour patterns. The padding byte(s) MUST be 0.


Alpha channel
-------------
The alpha channel mask is the same for all (GD2) modes; each byte in the
mask represents one pixel - in any mode.

Alpha channel information is not padded at the end of each line. There is
exectly one byte per pixel! (However, if the sum of pixels is odd, an
additional padding byte is required so as to align the next object.)

Alpha blending is only applied to sprites of mode 16 or more.


Blobs and patterns (summary)
----------------------------
A pattern is a colour or a design. It is the part of a sprite definition
offset from position 12. It is indicated in Wman menus and elsewhere as -6
or 6, (with sprites being -2 or 2). Normally, for a pattern to be a
pattern and not just a solid sprite, it needs to be a multiple of 16
pixels.

A mask is also known as a blob. Basically it is a shape, that may be
filled with a certain pattern. It is offset at 16 in the sprite definition,
and is designated -4, or 4 in menus etc. A blob always needs a colour
combine partner, while nowadays (GD2), pattern doesnt necessaritly need a
blob.

Masks or blobs need to be specified as bitmasks; alpha blended masks cant
be used as blobs.

Note any sprite of "any mode" may be used as either a blob or pattern;
the modes neednt be the same! However, a pattern's x-dimension must be a
multiple of 16 to be used in this way (or perhaps the sprite routines
just ignore the descrepancy?)

I (pjw) have decided to use the following file extension conventions:

spr = sprite and solid sprite
pat = pattern
blb = blob
msk = alpha mask


Empty sprites
-------------

A sprite has to have at least a dimension or the system will crash trying
to display it! It also needs a pointer to a mask or it is considered
"illegal" (by PI or EasyPtr?) However the mask could just point to some
zero value:

empty_blob
         dc.b  $01,$00,$00,$00    ;Simple QL mode 0 sprite
         dc.w  $0002,$0001        ;x size, y size

msk
         dc.w  $0000,$0000        ;x/y origin repurposed as mask
         dc.l  0                  ;pointer to pattern
         dc.l  msk-*              ;pointer to mask
         dc.l  0                  ;pointer to next definition

At 24b this may be the smallest "legal" sprite youll get away with. It
also doesnt display anything on screen.


Options
-------

sp_sprite
         dc.b    $02,$10,$00,%11010000
         dc.w    10,10,0,0
         dc.l    s8p_sprite-*
         dc.l    s8m_sprite-*
         dc.l    0                next
         dc.l    sp_option-*      option block

s8p_sprite
         ...
s8m_sprite
         ...

sp_option
         dc.l    sp_option2-*     Size of this option
         dc.l    'TEST'           Option type. It's Per's option!
         [... whatever the option needs ...]
         ds.w    0                Must be word aligned
sp_option2
         dc.l    sp_option_end-*  Size of this option
         dc.l    'TEXT'           Option type
         dc.w    12
         dc.b    'Hello World!'
         ds.w    0                Must be word aligned
sp_option_end
         dc.l    0                No other option

Note: IF pto..blk IS SET, THEN pto_opts MUST EXIST (eg AS A LONG WORD 0)
AND pto_blk MUST POINT TO A VALID BLOCK. (But options can exist without
a sprite block pointer, sp_blk-*, if pto..blk is clear.)

I (mk79) think that's clean, no 80's style bit mangling and completely
open to more options. Also it takes into account that wm.cpspr should
still be able to copy sprite definitions even if it doesn't know the
meaning of the options. And any reader software can just skip options it
doesn't know or care about.

To enable easy editing of elements in an OB, OBs should be the last object
of a sprite file. It may be that an implementer supposes their OB element
will never need changing, other elements may be added by other implementers
that are intended to be changeable. Since elements are consecutive,
secondary implementers are dependent on the primary implementer following
this convention, or being faced with having to cater for untangling
potentially limitlessly complex structures.

Option Block blocks should be self-contained. In particular they should not
have pointers to structures outside themselves.


Sprite table
============

This is how many bits per pixel (bpp) each type of sprite takes:

sprite_table
;               ID    bpp
        dc.w    $0100,$02
        dc.w    $0101,$02
        dc.w    $0200,$01
        dc.w    $0203,$01
        dc.w    $0204,$02
        dc.w    $0207,$02
        dc.w    $0208,$04
        dc.w    $020F,$04
        dc.w    $0210,$08
        dc.w    $021F,$08
        dc.w    $0220,$10
        dc.w    $0221,$10
        dc.w    $0240,$20
        dc.w    0


Dynamic Sprites
===============

Dynamic pointers, that change shape with time, are used by setting the time
byte to a non-zero value: by linking several sprite definitions together
with increasing time values (Tn), the sprite will appear in the lowest
numbered form for T1 "ticks", then change to the second form for T2-T1 ticks,
then the third for T3-T2, and so on. When no sprite can be found with a Tn
greater than the elapsed time, the counter is reset to zero and the first
form appears again. The maximum value of Tn being 255, and the count being
incremented (roughly) every 20ms, the sprite may have a period of up to 5
seconds or so.

pto_nobj points to the next sprite in the chain; contains zero for the last.
Relative pointers here too.

How big can such (pointer) sprites be?

Post by BSJR  Fri Jun 10, 2022 4:26 pm

pjw wrote:
...
Well, I did put it to the test and I still dont have an answer. Sprites
of 64x64 pixels (= 4k) seem fine. 128x32 (=4k) also seems fine. But
32x128 => KAAABOOOOM! Also other values, mainly where y > x crash the
system. I tried looking for an answer in the sources (we're only talking
SMSQ/E here, specifically V3.38) keys/con says

pt_spsav equ      $64      sprite save area
pt.spspx equ       64      64x64 pixels
pt.spspy equ       64

which seems to support the 64x64 pixel theory, but I cant find any code
that would explain why one geometry crashes the system while another doesnt.

Any ideas? Anyone?
<end pjw>

For SuQcess a routine is used to pick a text line from the screen and turn
this into a pointer sprite, it's used in the Print menu.
I also wanted to use this in an Edit window to pickup a text and drop it on
an empty line. This way a text pointer of 384x10 pixels can be moved around
with max 64 chrs, which I tested to be a safe limit.

BSJR

The maximum QL mode pointer sprite appears to be 64x48. Other metrics may
be possible, eg 24*128?

This is a work in progress. Please help to correct any mistakes and
fill in any missing details!
