Copyright (c) Hyperion Entertainment and contributors.

Hardware Sprites

From AmigaOS Documentation Wiki
Jump to navigation Jump to search

Using Simple (Hardware) Sprites

Simple Sprites can be used to create animations, so even though are not really part of the GELs system, they are described here as a possible alternative to using VSprites. For more information on the sprite hardware, including information on attached sprites, see the Amiga Hardware Reference Manual.

The SimpleSprite structure is found in <graphics/sprite.h>. It has fields that indicate the height and position of the Simple Sprite and a number that indicates which of the 8 hardware sprites to use.

Simple Sprites are always 16 bits wide, which is why there is no width member in the SimpleSprite structure. Currently, sprites always appear as low-resolution pixels, and their position is specified in the same way. If the sprite is being moved across a high-resolution display in single pixel increments, it will appear to move two high-resolution pixels for each increment. In low-resolution mode, a single Lores pixel movement will be seen. Similarly, in an interlaced display, the y direction motions are in two-line increments. The same sprite image data is placed into both even and odd fields of the interlaced display, so the sprite will appear to be the same size in any display mode.

The upper left corner of the ViewPort area has coordinates (0,0). Unlike VSprites, the motion of a Simple Sprite can be relative to this position. (That is, if you create a Simple Sprite relative to your ViewPort and move the ViewPort around, the Simple Sprite can move as well, but its movement is relative to the origin of the ViewPort.)

The Sprite pairs 0/1, 2/3, 4/5, and 6/7 share color registers. See "VSprite Advanced Topics" later in this chapter, for precautions to take if Simple Sprites and VSprites are used at the same time.

The following figure shows which color registers are used by Simple Sprites.

Sprite Color Registers

Sprites do not have exclusive use of the color registers. If the ViewPort is 5 bitplanes deep, all 32 of the system color registers will still be used by the playfield display hardware.

Note
Color zero for all Sprites is always a "transparent" color, and the colors in registers 16, 20, 24, and 28 are not used by sprites. These colors will be seen only if they are rendered into a playfield. For further information, see the "Amiga Hardware Reference Manual."

If there are two ViewPorts with different color sets on the same display, a sprite will switch colors when it is moved across their boundary. For example, Sprite 0 and 1 will appear in colors 17-19 of whatever ViewPort they happen to be over. This is because the system jams "all" the ViewPort's colors into the display hardware at the top of each ViewPort.

Simple Sprite Functions

There are four basic functions that you use to to control Simple Sprites:

GetSprite()
Attempts to allocates a sprite for exclusive use
ChangeSprite()
Modifies a Simple Sprite's image data
MoveSprite()
Changes a Simple Sprite's position
FreeSprite()
Relinquishes a sprite so it can be used by others

To use these Simple Sprite functions (or the VSprite functions) the SPRITE flag must have been set in the NewScreen structure for OpenScreen().

If Intuition is not being used, this flag must be specified in the View and ViewPort data structures before MakeVPort() is called.

Accessing A Hardware Sprite

GetSprite() is used to gain exclusive access to one of the eight hardware sprites. Once you have gained control of a hardware sprite, it can no longer be allocated by the GELs system for use as a VSprite. The call is made like this:

struct SimpleSprite *sprite;
SHORT               number;

if (-1 == (sprite_num = GetSprite(sprite, number)))
    return_code = RETURN_WARN;  /* did not get the sprite */

The inputs to the GetSprite() function are a pointer to a SimpleSprite structure and the number (0-7) of the hardware sprite to be accessed, or -1 to get the first available sprite.

A value of 0-7 is returned if the request was granted, specifying which sprite was allocated. A returned value of -1 means the requested sprite was not available. If the call succeeds, the SimpleSprite data structure will have its sprite number field filled in with the appropriate number.

Changing The Appearance Of A Simple Sprite

The ChangeSprite() function can be used to alter the appearance of a Simple Sprite. ChangeSprite() substitutes new image data for the data currently used to display a Simple Sprite. It is called by the following sequence:

struct ViewPort     *vp;
struct SimpleSprite *sprite;
APTR                newdata;

ChangeSprite(vp, sprite, newdata);

The vp input to this function is a pointer to the ViewPort for this Sprite or 0 if this Sprite is relative only to the current View. The sprite argument is a pointer to a SimpleSprite data structure. (You must allocate an actual SimpleSprite structure for sprite to point to.) Set newdata to the address of an image data structure containing the new image. The data must reside in Chip (MEMF_CHIP) memory.

The structure for the new sprite image data is shown below. It is not a system structure, so it will not be found in the system includes, but it is described in the documentation for the ChangeSprite() call.

struct spriteimage
    {
    UWORD posctl[2];                      /* position and control data for this Sprite */

    /* Two words per line of Sprite height, first of the two words contains the MSB for
     * color selection, second word contains LSB (colors 0,1,2,3 from allowable color
     * register selection set).   Color '0' for any Sprite pixel makes it transparent.
     */
    UWORD data[height][2];                /* actual Sprite image */

    UWORD reserved[2];                    /* reserved, initialize to 0, 0 */
    };

Moving A Simple Sprite

MoveSprite() repositions a Simple Sprite. After this function is called, the Simple Sprite is moved to a new position relative to the upper left corner of the ViewPort. It is called as follows:

struct ViewPort     *vp;
struct SimpleSprite *sprite;
SHORT               x, y;

MoveSprite(vp, sprite, x, y);

There are three inputs to MoveSprite(). Set the vp argument to the address of the ViewPort with which this Simple Sprite interacts or 0 if this Simple Sprite's position is relative only to the current View. Set sprite to the address of your SimpleSprite data structure. The x and y arguments specify a pixel position to which the Simple Sprite is to be moved.

Relinquishing A Simple Sprite

The FreeSprite() function returns a hardware sprite allocated with GetSprite() to the system so that GELs or other tasks can use it. After you call FreeSprite(), the GELs system can use it to allocate VSprites. The syntax of this function is:

WORD sprite_number;

FreeSprite(sprite_number);

The sprite_number argument is the number (0-7) of the sprite to be returned to the system.

Controlling Sprite DMA

Two additional functions used with Simple Sprites are the graphics library macros ON_SPRITE and OFF_SPRITE. These macros can be used to control sprite DMA. OFF_SPRITE prevents the system from displaying any sprites, whether Simple Sprites or VSprites. ON_SPRITE restores the sprite display.

Be Careful With OFF_SPRITE
The Intuition mouse pointer is a sprite. Thus, if OFF_SPRITE is used, Intuition's pointer will disappear too. Use care when calling OFF_SPRITE. The macro turns off sprite fetch DMA, so that no new sprite data is fetched. Whatever sprite data was last being displayed at this point will continue to be displayed for "every" line on the screen. This may lead to a vertical color bar if a sprite is being displayed when OFF_SPRITE is called.

Complete Simple Sprite Example

The following example demonstrates how to set up, move and free a Simple Sprite. The animtools.h file included is listed at the end of the chapter.

/* ssprite.c - Simple Sprite example
**
** SAS/C V5.10a
** lc -b1 -cfist -v -y ssprite.c
** blink FROM LIB:c.o ssprite.o LIB LIB:lc.lib LIB:amiga.lib TO ssprite
*/
#include <exec/types.h>
#include <graphics/gfx.h>
#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include <graphics/sprite.h>
#include <intuition/intuitionbase.h>
#include <intuition/screens.h>
#include <hardware/custom.h>
#include <hardware/dmabits.h>
#include <libraries/dos.h>

#include <clib/graphics_protos.h>
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/alib_stdio_protos.h>

#include <stdlib.h>

struct GfxBase *GfxBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;
extern struct Custom far custom ;

/* real boring sprite data */
UWORD chip sprite_data[ ] = {
    0, 0,           /* position control           */
    0xffff, 0x0000, /* image data line 1, color 1 */
    0xffff, 0x0000, /* image data line 2, color 1 */
    0x0000, 0xffff, /* image data line 3, color 2 */
    0x0000, 0xffff, /* image data line 4, color 2 */
    0x0000, 0x0000, /* image data line 5, transparent */
    0x0000, 0xffff, /* image data line 6, color 2 */
    0x0000, 0xffff, /* image data line 7, color 2 */
    0xffff, 0xffff, /* image data line 8, color 3 */
    0xffff, 0xffff, /* image data line 9, color 3 */
    0, 0            /* reserved, must init to 0 0 */
    };

VOID main(int argc, char **argv)
{
struct SimpleSprite    sprite = {0};
struct ViewPort        *viewport;

WORD sprite_num;
SHORT delta_move, ktr1, ktr2, color_reg;
struct Screen *screen;
int return_code;

return_code = RETURN_OK;

if (NULL == (GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",37L)))
    return_code = RETURN_FAIL;
else
    {
    if (NULL == (IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",37L)))
        return_code = RETURN_FAIL;
    else
        {
        /* opened library, need a viewport to render a sprite over. */
        if (NULL == (screen = OpenScreenTagList(NULL, NULL)))
            return_code = RETURN_FAIL;
        else
            {
            viewport = &screen->ViewPort;
            if (-1 == (sprite_num = GetSprite(&sprite, 2)))
                return_code = RETURN_WARN;
            else
                {
                /* Calculate the correct base color register number, */
                /* set up the color registers.                       */
                color_reg = 16 + ((sprite_num & 0x06) << 1);
                printf("color_reg=%d\n", color_reg);
                SetRGB4(viewport, color_reg + 1, 12,  3,  8);
                SetRGB4(viewport, color_reg + 2, 13, 13, 13);
                SetRGB4(viewport, color_reg + 3,  4,  4, 15);

                sprite.x = 0;       /* initialize position and size info    */
                sprite.y = 0;       /* to match that shown in sprite_data   */
                sprite.height = 9;  /* so system knows layout of data later */

                /* install sprite data and move sprite to start position. */
                ChangeSprite(NULL, &sprite, (APTR)sprite_data);
                MoveSprite(NULL, &sprite, 30, 0);

                /* move the sprite back and forth. */
                for ( ktr1 = 0, delta_move = 1;
                      ktr1 < 6; ktr1++, delta_move = -delta_move)
                    {
                    for ( ktr2 = 0; ktr2 < 100; ktr2++)
                        {
                        MoveSprite( NULL, &sprite, (LONG)(sprite.x + delta_move),
                                    (LONG)(sprite.y + delta_move) );
                        WaitTOF(); /* one move per video frame */

                        /* Show the effect of turning off sprite DMA. */
                        if (ktr2 == 40) OFF_SPRITE ;
                        if (ktr2 == 60) ON_SPRITE ;
                        }
                    }
                /* NOTE:  if you turn off the sprite at the wrong time (when it
                ** is being displayed), the sprite will appear as a vertical bar
                ** on the screen.  To really get rid of the sprite, you must
                ** OFF_SPRITE while it is not displayed.  This is hard in a
                ** multi-tasking system (the solution is not addressed in
                ** this program).
                */
                ON_SPRITE ; /* just to be sure */
                FreeSprite((WORD)sprite_num);
                }
            (VOID) CloseScreen(screen);
            }
        CloseLibrary((struct Library *)IntuitionBase);
        }
    CloseLibrary((struct Library *)GfxBase);
    }
exit(return_code);
}