Copyright (c) Hyperion Entertainment and contributors.

Blitter Objects

From AmigaOS Documentation Wiki
Jump to navigation Jump to search

Using Bobs

The following section describes how to define a Bob (blitter object). Like VSprites, a Bob is a software construct designed to make animation easier. The main advantage of a Bob over a VSprite is that it allows more colors and a width greater than 16 pixels to be defined.

To create a Bob, you need both a Bob structure and a VSprite structure. The components common to all GELs - height, collision-handling information, position in the drawing area and pointers to the image definition - are part of the VSprite structure. The added features - such as drawing sequence, data about saving and restoring the background, and other features not applicable to VSprites - are further specified in the Bob structure.

The VSprite Structure and Bobs

The root VSprite structure is set up as described earlier for true VSprites, with the following exceptions:

Y, X
Bob position is always in pixels that are the same resolution as the display.
Flags
For Bobs, the VSPRITE flag must be cleared. SAVEBACK or OVERLAY can also be used.
Height, Width
Bob pixels are the size of the background pixels. The Width of Bobs may be greater than one word.
Depth
The Depth of a Bob may be up to as deep as the playfield, provided that enough image data is provided.
ImageData
This is still a pointer to the image, but the data there is organized differently.
SprColors
This pointer should be set to NULL for Bobs.
VSBob
This is a pointer to the Bob structure set up as described below.

VSprite Flags and Bobs

The bits in the VSprite.Flags field that apply to a Bob are the VSPRITE flag, the SAVEBACK flag and the OVERLAY flag. When a VSprite structure is used to define a Bob, the VSPRITE flag in the VSprite.Flags field must be set to zero. This tells the system that this GEL is a Bob type.

To have the GEL routines save the background before the Bob is drawn and restore the background after the Bob is removed, specify the SAVEBACK flag (stands for "save the background") in the VSprite structure Flags field. If this flag is set, the SaveBuffer must have been allocated, which is where the system puts this saved background area. The buffer must be large enough to save all the background bitplanes, regardless of how many planes the Bob has. The size in words can be calculated as follows:

/* Note that Bob.Width is in units of words. */
size = Bob.Width * Bob.Height * RastPort.BitMap.Depth;

To allocate this space, the graphics function AllocRaster() can be used. AllocRaster() takes the width in bits, so it is a convenient way to allocate the space needed. The makeBob() routine below shows another way to correctly allocate this buffer. For example:

/* space for 16 bits times 5 lines times 5 bitplanes */
myBob.SaveBuffer = AllocRaster( 16, (5 * 5) );
Warning
The SaveBuffer must be allocated from Chip memory and contain an even number of word-aligned bytes. The AllocRaster() function does this for you. The AllocRaster() function rounds the width value up to the next integer multiple of 16 bits which is greater than or equal to the current value an it obtains memory from the Chip memory pool.

OVERLAY is the other VSprite.Flags item that applies to Bobs. If this flag is set, it means that the background's original pixels show through in any area where there are 0 bits in the Bob's shadow mask (ImageShadow, explained later). The space for the ImageShadow shadow mask must have been allocated and initialized. The ImageShadow mask must be allocated from Chip memory.

If the OVERLAY bit is cleared, the system uses the entire rectangle of words that define the Bob image to replace the playfield area at the specified x,y coordinates. See the paragraphs below called "ImageShadow."

The Bob Structure

The Bob structure is defined in the include file <graphics/gels.h> as follows:

struct Bob
    {
    WORD               Flags;     /* general purpose flags                  */
    WORD              *SaveBuffer;/* buffer for background save             */
    WORD              *ImageShadow; /* shadow mask of image                 */
    struct Bob        *Before;    /* draw this Bob before Bobs on this list */
    struct Bob        *After;     /* draw this Bob after Bobs on this list  */
    struct VSprite    *BobVSprite;/* this Bob's VSprite definition          */
    struct AnimComp   *BobComp;   /* pointer to this Bob's AnimComp def     */
    struct DBufPacket *DBuffer;   /* pointer to this Bob's dBuf packet      */
    BUserStuff         BUserExt;  /* Bob user extension                     */
    };

The Bob structure itself does not need to be in Chip memory. The (global) static declaration of a Bob structure could be done like so:

struct Bob myBob =
    {
    0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0
    };

However, since most of the Bob structure members are pointers, it is more common to allocate and set the Bob up dynamically. Refer to the makeBob() and freeBob() functions in the "animtools.c" example at the end of the article for an example of allocating, initializing and freeing a Bob structure.

Linking Bob and VSprite Structures

The VSprite and Bob structures must point to one another, so that the system can find the entire GEL. The structures are linked with statements like this:

myBob.BobVSprite = &myVSprite;
myVSprite.VSBob  = &myBob;

Now the system (and the application program) can go back and forth between the two structures to obtain the various Bob variables.

Using Bob Flags

The following paragraphs describe how to set the Flags field in the Bob structure (note that these flags do not apply to the Flags field of the VSprite structure).

To tell the system not to erase the old image of the Bob when the Bob is moved, specify the SAVEBOB flag in the Bob structure Flags field. This makes the Bob behave like a paintbrush. It has the opposite effect of SAVEBACK.

It's Faster To Draw A New Bob
It takes longer to preserve and restore the raster image than simply to draw a new Bob image wherever required.

If this Bob is part of an AnimComp, set the BOBISCOMP flag in the Bob structure to 1. If the flag is a 1, the pointer named BobComp must have been initialized. Otherwise, the system ignores the pointer, and it may be left alone (though it's good practice to initialize it to NULL). See "Animation Structures and Controls" for a discussion of AnimComps.

This flag is used solely by the system, and should be left alone. When a Bob is waiting to be drawn, the system sets the BWAITING flag in the Bob structure to 1. This occurs only if the system has found a Before pointer in this Bob's structure that points to another Bob. Thus, the system flag BWAITING provides current draw-status to the system. Currently, the system clears this flag on return from each call to DrawGList().

This is a system status flag that indicates to the system whether or not this Bob has already been drawn. Therefore, in the process of examining the various Before and After flags, the drawing routines can determine the drawing sequence. The system clears this flag on return from each call to DrawGList().

To initiate the removal of a Bob during the next call to DrawGList(), set BOBSAWAY to 1. Either the application or the system may set this Bob structure system flag. The system restores the background where it has last drawn the Bob. The system will unlink the Bob from the system GEL list the next time DrawGList() is called, unless the application is using double-buffering. In that case, the Bob will not be unlinked and completely removed until two calls to DrawGList() have occurred and the Bob has been removed from both buffers. The RemBob() macro sets the BOBSAWAY flag.

When a Bob has been completely removed, the system sets the BOBNIX flag to 1 on return from DrawGList(). In other words, when the background area has been fully restored and the Bob has been removed from the GEL list, this flag in is set to a 1. BOBNIX is especially significant when double-buffering because when an application asks for a Bob to be removed, the system must remove it from both the drawing buffer and from the display buffer. Once BOBNIX has been set, it means the Bob has been removed from both buffers and the application is free to reuse or deallocate the Bob.

The SAVEPRESERVE flag is a double-buffer version of the SAVEBACK flag. If using double-buffering and wishing to save and restore the background, set SAVEBACK to 1. SAVEPRESERVE is used by the system to indicate whether the Bob in the "other" buffer has been restored; it is for system use only.

Specifying the Size of a Bob

Bobs do not have the 16-pixel width limit that applies to VSprites. To specify the overall size of a Bob, use the Height and Width members of the root VSprite structure. Specify the Width as the number of 16-bit words it takes to fully contain the object. The number of lines is still specified with the Height member in the VSprite data structure.

As an example, suppose the Bob is 24 pixels wide and 20 lines tall. Use statements like the following to specify the size:

myVSprite.Height = 20;  /* 20 lines tall. */
myVSprite.Width  = 2;   /* 24 bits fit into two words. */

Because Bobs are drawn into the background playfield, the pixels of the Bob are the same size as the background pixels, and share the color palette of the ViewPort.

Specifying the Shape of a Bob

The layout of the data of a Bob's image is different from that of a VSprite because of the way the system retrieves data to draw Bobs. VSprite images are organized in a way convenient to the Sprite hardware; Bob images are set up for easy blitter manipulation. The ImageData pointer is still initialized to point to the first word of the image definition.

Note
As with all image data, a Bob’s ImageData must be in Chip memory for access by the blitter.

The sample image below shows the same image defined as a VSprite in the "Using Virtual Sprites" section above. The data here, however, is laid out for a Bob. The shape is 2 planes deep and is triangular:

            <first bitplane data>

  mem        1111 1111 1111 1111    Least significant bit of sprite line 1
  mem + 1    0011 1100 0011 1100    Least significant bit of sprite line 2
  mem + 2    0000 1100 0011 0000    Least significant bit of sprite line 3
  mem + 3    0000 0010 0100 0000    Least significant bit of sprite line 4
  mem + 4    0000 0001 1000 0000    Least significant bit of sprite line 5

            <second bitplane data>

  mem + 5    1111 1111 1111 1111    Most significant bit of sprite line 1
  mem + 6    0011 0000 0000 1100    Most significant bit of sprite line 2
  mem + 7    0000 1111 1111 0000    Most significant bit of sprite line 3
  mem + 8    0000 0011 1100 0000    Most significant bit of sprite line 4
  mem + 9    0000 0001 1000 0000    Most significant bit of sprite line 5

            <more bitplanes of data if Bob is deeper>

Specifying the Colors of a Bob

Typically a five-bitplane, low-resolution mode display allows playfield pixels (and therefore, Bob pixels) to be selected from any of 32 active colors out of a system palette of 4,096 different color choices. Bob colors are limited to the colors used in the background playfield.

The system ignores the sprColors member of the VSprite structure when the VSprite structure is the root of a Bob. Instead, the Bob's colors are determined by the combination of the Depth of the Bob image and its PlanePick, PlaneOnOff and ImageShadow members.

Use the Depth member in the VSprite structure to indicate how many planes of image data is provided to define the Bob. This also defines how many colors the Bob will have. The combination of bits in corresponding x,y positions in each bitplane determines the color of the pixel at that position.

For example, if a Depth of one plane is specified, then the bits of that image allow only two colors to be selected: one color for each bit that is a 0, a second color for each bit that is a 1. Likewise, if there are 5 planes of image data, all 32 colors can be used in the Bob. The Bob Depth must not exceed the background depth. Specify Depth using a statement such as the following:

myVSprite.Depth = 5;   /* Allow a 32 color, 5-bitplane image. */

Other Items Influencing Bob Colors

The three other members in the VSprite structure that affect the color of Bob pixels are ImageShadow, PlanePick, and PlaneOnOff.

ImageShadow

The ImageShadow member is a pointer to the shadow mask of a Bob. A shadow mask is the logical or of all bitplanes of a Bob image. The system uses the shadow mask in conjunction with PlaneOnOff, discussed below, for color selection. It also uses the shadow mask to "cookie cut" the bits that will be overwritten by this Bob, to save and later restore the background.

The following figure shows the shadow mask of the image described above.

    mem + 0    1111 1111 1111 1111    Shadow mask for line 1
    mem + 1    0011 1100 0011 1100    Shadow mask for line 2
    mem + 2    0000 1111 1111 0000    Shadow mask for line 3
    mem + 3    0000 0011 1100 0000    Shadow mask for line 4
    mem + 4    0000 0001 1000 0000    Shadow mask for line 5

Space for the ImageShadow must be provided and this pointer initialized to point to it. The amount of memory needed is equivalent to one plane of the image:

shadow_size = myBob->BobVSprite->Height * myBob->BobVSprite->Width;

The example image is 5 high and 1 word wide, so, 5 words must be made available.

Note
The ImageShadow memory must be allocated from Chip memory (MEMF_CHIP).

PlanePick

Because the Depth of the Bob can be less than the background, the PlanePick member is provided so that the application can indicate which background bitplanes are to have image data put into them. The system starts with the least significant plane of the Bob, and scans PlanePick starting at the least significant bit, looking for a plane of the RastPort to put it in.

For example, if PlanePick has a binary value of: 0 0 0 0 0 0 1 1 (0x03) then the system draws the first plane of the Bob's image into background plane 0 and the second plane into background plane 1.

Alternatively, a PlanePick value of: 0 0 0 1 0 0 1 0 (0x12) directs the system to put the first Bob plane into plane 1, and the second Bob plane into plane 4.

PlaneOnOff

What happens to the background planes that aren't picked? The shadow mask is used to either set or clear the bits in those planes in the exact shape of the Bob if OVERLAY is set, otherwise the entire rectangle containing the Bob is used. The PlaneOnOff member tells the system whether to put down the shadow mask as zeros or ones for each plane. The relationship between bit positions in PlaneOnOff and background plane numbers is identical to PlanePick: the least significant bit position indicates the lowest-numbered bitplane. A zero bit clears the shadow mask shape in the corresponding plane, while a one bit sets the shadow mask shape. The planes Picked by PlanePick have image data - not shadow mask - blitted in.

This provides a great deal of color versatility. One image definition can be used for many Bobs. By having different PlanePick / PlaneOnOff combinations, each Bob can use a different subset of the background color set.

There is a member in the VSprite structure called CollMask (the collision mask, covered under "Detecting GEL Collisions") for which the application may also reserve some memory space. The ImageShadow and CollMask pointers usually, but not necessarily, point to the same data, which must be located in Chip memory. If they point to the same location, obviously, the memory only need be allocated once.

An example of the kinds of statements that accomplish these actions (see the makeVSprite() and makeBob() examples for more details):

<syntaxhighlight>

  1. define BOBW 1
  2. define BOBH 5
  3. define BOBD 2

/* Data definition from example layout */ uint16 chip BobData[]=

   {
   0xFFFF, 0x300C, 0x0FF0, 0x03C0, 0x0180,
   0xFFFF, 0x3E7C, 0x0C30, 0x03C0, 0x0180
   };

/* Reserve space for the collision mask for this Bob */ uint16 chip BobCollision[BOBW * BOBH];

myVSprite.Width = BOBW; /* Image is 16 pixels wide (1 word) */ myVSprite.Height = BOBH; /* 5 lines for each plane of the Bob */ myVSprite.Depth = BOBD; /* 2 Planes are in ImageData */

/* Show the system where it can find the data image of the Bob */ myVSprite.ImageData = BobData;

/* binary 0101, render image data into bitplanes 0 and 2 */ myVSprite.PlanePick = 0x05;

/* binary 0000, means colors 1, 4, and 5 will be used.

  • binary 0010 would mean colors 3, 6, and 7.
  • " 1000 " " " 9, C, and D.
  • " 1010 " " " B, E, and F.
  • /

myVSprite.PlaneOnOff = 0x00;

/* Where to put collision mask */ myVSprite.CollMask = BobCollision;

/* Tell the system where it can assemble a GEL shadow */ /* Point to same area as CollMask */ myBob.ImageShadow = BobCollision;

/* Create the Sprite collision mask in the VSprite structure */ IGraphics->InitMasks(&myVSprite); <syntaxhighlight>

Bob Priorities

This subsection describes the choices for inter-Bob priorities. The inter-Bob priorities tell the system what order to render the Bobs. Bobs rendered earlier will appear to be behind later Bobs. A Bob drawn earlier is said to have the lower priority and a Bob drawn later is said to have the higher priority. Thus, the highest priority Bob will be drawn last and will never be obstructed by another Bob.

Letting the System Decide Priorities

The priority issue can be ignored and the system will render the Bobs as it finds them in the GelsInfo list. To do this, set the Bob’s Before and After pointers to NULL. Since the GelsInfo list is sorted by GEL x, y values, Bobs that are higher on the display will appear behind the lower ones, and Bobs that are more to the left on the display will appear behind Bobs on the right.

As Bobs are moved about the display, their priorities will change.

Specifying the Drawing Order

To specify the priorities of the Bobs, use the Before and After pointers. Before points to the Bob that this Bob should be drawn before, and After points to the Bob that this Bob should be drawn after. By following these pointers, from Bob to Bob, the system can determine the order in which the Bobs should be drawn. (Take care to avoid circular dependencies in this list!)

Note
This terminology is often confusing, but, due to historical reasons, cannot be changed. The system does not draw the Bobs on the Before list first, it draws the Bobs on the After list first. Next, it draws the current Bob, and, finally, the Bobs on the Before list.

For example, to assure that myBob1 always appears in front of myBob2, The Before and After pointers must be initialized so that the system will always draw myBob1 after myBob2.

myBob2.Before = &myBob1;     /* draw Bob2 before drawing Bob1 */
myBob2.After  = NULL;        /* draw Bob2 after  no other Bob */
myBob1.After  = &myBob2;     /* draw Bob1 after  drawing Bob2 */
myBob1.Before = NULL;        /* draw Bob1 before no other Bob */

As the system goes through the GelsInfo list, it checks the Bob's After pointer. If this is not NULL, it follows the After pointer until it hits a NULL. Then it starts rendering the Bobs, going back up the Before pointers until it hits a NULL. Then it continues through the GelsInfo list. So, it is important that all Before and After pointers of a group properly point to each other.

Note
In a screen with a number of complex GELs, you may want to specify the Before and After order for Bobs that are not in the same AnimOb. This will keep large objects together. If you do not do this, you may have an object drawn with half of its Bobs in front of another object! Also, in sequences you only set the Before and After pointers for the active AnimComp in the sequence.

Adding a Bob

To add a Bob to the system GEL list, use the AddBob() routine. The Bob and VSprite structures must be correct and cohesive when this call is made. See the makeBob() and makeVSprite() routines in the animtools.c file listed at the end of this article for a detailed example of setting up Bobs and VSprites. See the setupGelSys() function for a more complete example of the initialization of the GELs system.

For example:

struct GelsInfo myGelsInfo = {0};
struct VSprite dummySpriteA = {0}, dummySpriteB = {0};
struct Bob myBob = {0};
struct RastPort rastport = {0};

/* Done ONCE, for this GelsInfo.  See setupGelSys() at the end of this
** article for a more complete initialization of the Gel system
*/
InitGels(&dummySpriteA, &dummySpriteB, &myGelsInfo);

/* Initialize the Bob members here, then AddBob() */
AddBob(&myBob, &rastport);

Removing a Bob

Two methods may be used to remove a Bob. The first method uses the RemBob() macro. RemBob() causes the system to remove the Bob during the next call to DrawGList() (or two calls to DrawGList() if the system is double-buffered). RemBob() asks the system to remove the Bob at the next convenient time. See the description of the BOBSAWAY and BOBNIX flags above. It is called as follows:

struct Bob myBob = {0};

RemBob(&myBob);

The second method uses the RemIBob() routine. RemIBob() tells the system to remove this Bob immediately. For example:

struct Bob      myBob = {0};
struct RastPort rastport = {0};
struct ViewPort viewport = {0};

RemIBob(&myBob, &rastport, &viewport);

This causes the system to erase the Bob from the drawing area and causes the immediate erasure of any other Bob that had been drawn subsequent to (and on top of) this one. The system then unlinks the Bob from the system GEL list. To redraw the Bobs that were drawn on top of the one just removed, another call to DrawGList() must be made.

Sorting and Displaying Bobs

As with VSprites, the GelsInfo list must be sorted before any Bobs can be displayed. This is accomplished with the SortGList() function. For Bobs, the system uses the position information to decide inter-Bob priorities, if not explicitly set by using the Bob.Before and Bob.After pointers.

Once the GelsInfo list has been sorted, the Bobs in the list can be displayed by calling DrawGList(). This call should then be followed by a call to WaitTOF() if the application wants to be sure that the Bobs are rendered before proceeding. Call these functions as follows:

struct RastPort myRastPort = {0}; /* Of course, these have to be initialized... */
struct ViewPort myViewPort = {0};

SortGList(&myRastPort);
DrawGList(&myRastPort, &myViewPort);     /* Draw the elements (Bobs only) */
WaitTOF();
Warning
If your GelsInfo list contains VSprites in addition to Bobs, you must also call MrgCop() and LoadView() to make all the GELs visible. Or, under Intuition, RethinkDisplay() must be called to make all the GELs visible.

Changing Bobs

The following characteristics of Bobs can be changed dynamically between calls to DrawGList():

  • To change the location of the Bob in the RastPort drawing area, adjust the x and y values in the VSprite structure associated with this Bob.
  • To change a Bob's appearance, the pointer to the ImageData in the associated VSprite structure may be changed. Note that a change in the ImageData also requires a change or recalculation of the ImageShadow, using InitMasks().
  • To change a Bob's colors modify the PlanePick, PlaneOnOff or Depth parameters in the VSprite structure associated with this Bob.
  • To change a Bob's display priorities, alter the Before and After pointers in the Bob structure.
  • To change the Bob into a paintbrush, specify the SAVEBOB flag in the Bob.Flags field.
Changes Are Not Immediately Seen
Neither these nor other changes are evident until SortGList() and then DrawGList() are called.

Complete Bob Example

This example must be linked with "animtools.c" and includes the header files "animtools.h" and "animtools_proto.h". These files are listed at the end of the article.

/* bob.c
**
** SAS/C V5.10a
** lc -b1 -cfist -v -y bob.c
** blink FROM LIB:c.o bob.o animtools.o LIB LIB:lc.lib LIB:amiga.lib TO bob
*/
#include <exec/types.h>
#include <exec/memory.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfx.h>
#include <graphics/gfxbase.h>
#include <graphics/gels.h>
#include <libraries/dos.h>
#include <stdlib.h>
#include "animtools.h"

VOID bobDrawGList(struct RastPort *rport, struct ViewPort *vport);
VOID process_window(struct Window *win, struct Bob *myBob);
VOID do_Bob(struct Window *win);

struct GfxBase       *GfxBase;       /* pointer to Graphics library */
struct IntuitionBase *IntuitionBase; /* pointer to Intuition library*/
int return_code;
#define GEL_SIZE  4                  /* number of lines in the bob  */

/* Bob data - two sets that are alternated between.  Note that this */
/* data is at the resolution of the screen.                         */

/* data is 2 planes by 2 words by GEL_SIZE lines                    */
WORD chip bob_data1[2 * 2 * GEL_SIZE] =
        {
        /* plane 1 */
        0xffff, 0x0003, 0xfff0, 0x0003, 0xfff0, 0x0003, 0xffff, 0x0003,
        /* plane 2 */
        0x3fff, 0xfffc, 0x3ff0, 0x0ffc, 0x3ff0, 0x0ffc, 0x3fff, 0xfffc
        };

/* data is 2 planes by 2 words by GEL_SIZE lines                     */
WORD chip bob_data2[2 * 2 * GEL_SIZE] =
        {
        /* plane 1 */
        0xc000, 0xffff, 0xc000, 0x0fff, 0xc000, 0x0fff, 0xc000, 0xffff,
        /* plane 2 */
        0x3fff, 0xfffc, 0x3ff0, 0x0ffc, 0x3ff0, 0x0ffc, 0x3fff, 0xfffc
        };

NEWBOB myNewBob =                     /* Data for the new bob structure defined in animtools.h */
        {                             /* Initial image, WORD width, line height                */
        bob_data2, 2,  GEL_SIZE,      /* Image depth, plane pick, plane on off, VSprite flags  */
        2, 3, 0, SAVEBACK | OVERLAY,  /* dbuf (0=false), raster depth, x,y position, hit mask, */
        0, 2, 160, 100,  0,0,         /* me mask                                               */
        };

struct NewWindow myNewWindow =
        {                             /* information for the new window */
        80, 20, 400, 150, -1, -1, CLOSEWINDOW | INTUITICKS,
        ACTIVATE | WINDOWCLOSE | WINDOWDEPTH | RMBTRAP,
        NULL, NULL, "Bob", NULL, NULL, 0, 0, 0, 0, WBENCHSCREEN
        };

/* Draw the Bobs into the RastPort. */
VOID bobDrawGList(struct RastPort *rport, struct ViewPort *vport)
{
SortGList(rport);
DrawGList(rport, vport);
/* If the GelsList includes true VSprites, MrgCop() and LoadView() here */
WaitTOF() ;
}

/* Process window and dynamically change bob: Get messages. Go away on CLOSEWINDOW.
** Update and redisplay bob on INTUITICKS. Wait for more messages.
*/
VOID process_window(struct Window *win, struct Bob *myBob)
{
struct IntuiMessage *msg;

FOREVER {
        Wait(1L << win->UserPort->mp_SigBit);
        while (NULL != (msg = (struct IntuiMessage *)GetMsg(win->UserPort)))
                {
                /* only CLOSEWINDOW and INTUITICKS are active */
                if (msg->Class == CLOSEWINDOW)
                        {
                        ReplyMsg((struct Message *)msg);
                        return;
                        }
                /* Must be INTUITICKS:  change x and y values on the fly.  Note:
                ** do not have to add window offset, Bob is relative to the
                ** window (sprite relative to screen).
                */
                myBob->BobVSprite->X = msg->MouseX + 20;
                myBob->BobVSprite->Y = msg->MouseY + 1;
                ReplyMsg((struct Message *)msg);
                }
        /* after getting a message, change image data on the fly */
        myBob->BobVSprite->ImageData =
                (myBob->BobVSprite->ImageData == bob_data1) ? bob_data2 : bob_data1;
        InitMasks(myBob->BobVSprite); /* set up masks for new image */
        bobDrawGList(win->RPort, ViewPortAddress(win));
        }
}


/* Working with the Bob: setup the GEL system, and get a new Bob (makeBob()).
** Add the bob to the system and display. Use the Bob.  When done, remove
** the Bob and update the display without the bob. Cleanup everything.
*/
VOID do_Bob(struct Window *win)
{
struct Bob         *myBob;
struct GelsInfo    *my_ginfo;

if (NULL == (my_ginfo = setupGelSys(win->RPort, 0x03)))
        return_code = RETURN_WARN;
else
        {
        if (NULL == (myBob = makeBob(&myNewBob)))
                return_code = RETURN_WARN;
        else
                {
                AddBob(myBob, win->RPort);
                bobDrawGList(win->RPort, ViewPortAddress(win));
                process_window(win, myBob);
                RemBob(myBob);
                bobDrawGList(win->RPort, ViewPortAddress(win));
                freeBob(myBob, myNewBob.nb_RasDepth);
                }
        cleanupGelSys(my_ginfo,win->RPort);
        }
}


/* Example bob program: First open up the libraries and a window. */
VOID main(int argc, char **argv)
{
struct Window      *win;

return_code = RETURN_OK;

if (NULL == (GfxBase = (struct GfxBase *)OpenLibrary(GRAPHICSNAME,37L)))
        return_code = RETURN_FAIL;
else
        {
        if (NULL == (IntuitionBase = (struct IntuitionBase *)OpenLibrary(INTUITIONNAME,37L)))
                return_code = RETURN_FAIL;
        else
                {
                if (NULL == (win = OpenWindow(&myNewWindow)))
                        return_code = RETURN_FAIL;
                else
                        {
                        do_Bob(win);
                        CloseWindow(win);
                        }
                CloseLibrary((struct Library *)IntuitionBase);
                }
        CloseLibrary((struct Library *)GfxBase);
        }
exit(return_code);
}

Double-Buffering

Double-buffering is the technique of supplying two different memory areas in which the drawing routines may create images. The system displays one memory space while drawing into the other area. This eliminates the "flickering" that is visible when a single display is being rendered into at the same time that it is being displayed.

Double-buffering For One Means Double-buffering For All
If any of the Bobs is double-buffered, then all of them must be double-buffered.

To find whether a Bob is to be double-buffered, the system examines the pointer named DBuffer in the Bob structure. If this pointer has a value of NULL, the system does not use double-buffering for this Bob. For example:

myBob.DBuffer = NULL;   /* do this if this Bob is NOT double-buffered */

DBufPacket and Double-Buffering

For double-buffering, a place must be provided for the system to store the extra information it needs. The system maintains these data, and does not expect the application to change them. The DBufPacket structure consists of the following members:

Lets the system keep track of where the object was located "in the last frame" (as compared to the Bob structure members called oldY and oldX that tell where the object was two frames ago). BufY and BufX provide for correct restoration of the background within the currently active drawing buffer.

Assures that the system restores the backgrounds in the correct sequence; it relates to the VSprite members DrawPath and ClearPath.

This field must be set to point to a buffer the same size as the Bob's SaveBuffer. This buffer is used to store the background for later restoration when the system moves the object. This buffer must be allocated from Chip memory.

To create a double-buffered Bob, execute a code sequence similar to the following:

struct Bob        myBob = {0};
struct DBufPacket myDBufPacket = {0};

/* Allocate a DBufPacket for myBob same size as previous example */
if (NULL != (myDBufPacket.BufBuffer = AllocRaster(48, 20 * 5)))
    {
    /* tell Bob about its double buff status */
    myBob.DBuffer = myDBufPacket;
    }

The example routines makeBob() and freeBob() in the animtools.c listing at the end of this article show how to correctly allocate and free a double-buffered Bob.