Here is a document describing plans I have for a SpecialFX.library. THis library
is designed to fill in some of the holes in the graphics.library for games
writers to use the AA Chip Set features via the OS.

Please read, and post all comments here. Work is already underway on this, so
suggestions are welcome ASAP.

Note that none of the interface is yet locked down - any or all of this may
change between now and the time the library is released.

Usual legal-speak:

   ************************************************************************
   *									  *
   *				COPYRIGHTS				  *
   *									  *
   *		   UNLESS OTHERWISE NOTED, ALL FILES ARE		  *
   *									  *
   *  Copyright (c) 1985-1993 Commodore-Amiga, Inc.  All Rights Reserved  *
   *									  *
   ************************************************************************


   ************************************************************************
   *									  *
   *	    		COPYRIGHTED DEVELOPER MATERIALS		  	  *
   *									  *
   ************************************************************************

   THIS SOFTWARE AND INFORMATION IS COPYRIGHTED.

   THIS SOFTWARE AND INFORMATION MAY NOT BE REDISTRIBUTED, DISCLOSED,
   OR DUPLICATED, IN WHOLE OR IN PART, IN ANY MANNER OTHER THAN AS
   SPECIFIED IN A SIGNED AGREEMENT WITH COMMODORE.
 
   ************************************************************************
   *									  *
   *				DISCLAIMER				  *
   *									  *
   *   THIS SOFTWARE AND INFORMATION IS PROVIDED "AS IS".		  *
   *   NO REPRESENTATIONS OR WARRANTIES ARE MADE WITH RESPECT TO THE	  *
   *   ACCURACY, RELIABILITY, PERFORMANCE, CURRENTNESS, OR OPERATION	  *
   *   OF THIS SOFTWARE AND INFORMATION, AND ALL USE IS AT YOUR OWN RISK. *
   *   NEITHER COMMODORE NOR THE AUTHORS ASSUME ANY RESPONSIBILITY OR	  *
   *   LIABILITY WHATSOEVER WITH RESPECT TO YOUR USE OF THIS SOFTWARE	  *
   *   AND INFORMATION.							  *
   *									  *
   ************************************************************************


-------------------------------------------------------------------------------


ABSTRACT

SpecialFX.library is designed to meet some of the requirements of the games
programmers who cannot use the OS for the tricks they need to do in their games.

The library is a run-time shared library that can be distributed with the game.
In the future, this library will be in ROM, thus maintaining compatibilty
across various future hardware platforms.

INTRODUCTION

The release of the AA chip set brings many new features to the Amiga. Seeing as
future chip sets may not be register compatible with AA, and Commodore's desire
for retargetable graphics, there will not be a hardware reference manual
describing the operation or how to program AA; it has been emphasised that all
the AA features are available from the OS, and that the OS should be used for AA
games.

There are a number of 'tricks' though that games programmers use on the Amiga
that still cannot be achieved satisfactorily with the OS. These tricks mainly
involve the use of copperlists for such things as colour fades and parallax
scrolling. SpecialFX.library aims to bridge this gap by providing an easy,
flexible, efficient, and OS-friendly library that the games programmer can use.
This would give him the advantage of:

1) Correctness.
2) Less development time.
3) Future compatibility

The SpecialFX.library will basically provide the games programmer with a
high-level abstract UserCopperList, which can be modified at 50/60Hz to provide
real-time animation. It will work on all Amigas.

FEATURES

These are the features that will be supported by SpecialFX.library:

1) Colour cycling at 60/50Hz, with 24 bit colour resolution, changing the colour
of any pen number on any line of the ViewPort.

2) Independant scrolling, allowing any line or range of lines to be scrolled
independantly of any other lines in the ViewPort. This would provide parallax
scrolling.

3) Line repetition, allowing such tricks as "flows", and coin flips.

4) Sprite re-use

5) VideoControl() features on a per-line basis, rather than a per-ViewPort
basis.

6) Causing an interrupt on any given line for synchronisation.

7) Display Window manipulation to alter the width of a display window, thus
maybe allowing more sprites where needed.

The library will handle screen coercion and promotion as best it can.  There may
be problems with scan doubling though, as all WAIT(Y) positions are doubled by
MrgCop(), so how to specify whether the WAIT() was meant for line 2Y or (2Y+1)?


Any others?


PROGRAMMING

Each SpecialFX is controlled by the programmer through a structure associated
with the effect. Each structure is composed of two parts: a visible part, and an
invisible part. The visible part is initialised by the programmer to control the
colour, the scroll offset, line repetition or whatever. The invisible part is a
cache used by the library for optimising the animation code. 

As each element is of an unknown size, the memory for each effect has to be
allocated by the library with AllocFX(), which returns a handle to this effect
for later use, and fills in an array of pointers to the visible part of each
effect structure.

[The Autodocs that have been written so far are at the end of the document]

For example, if we are using a ColorRange effect to modify the colour of one pen
over NUMLINES scanlines:

struct ColorRange *Colours[NUMLINES];	/* An array of pointers to
					 * struct ColorRange.
					 */
APTR ColourHandle;			/* just a handle */

if (ColourHandle = AllocFX(SFX_ColorRange, NUMLINES, (ULONG *)Colours))
{
	etc...
}

We now know that to modify the colour of the pen on line 10, we can change it in
the ColorRange structure pointed to by Colours[9].

(A full example follows at the end).

Once the memory and handle have been obtained, and the data in the effects
structures initialised, we need to display the effect using DisplayFX(). This is
a tag-based function that builds UserCopperlists to create the changes required.
The tags are a list of the effects to build and pointers to the allocated
handles. DisplayFX() returns a handle to this Effect which is later used by
AnimateFX() to animate the effect. Therefore, with DisplayFX(), it is possible
to mix together ColorRange effects, ScrollRange effects etc into one
SuperEffect.

Note that after calling DisplayFX(), the programmer needs to remake the display
in the usual way to actually show the changes.

After this, changes can be made to the data in the visible parts of the Effects
structures, and calling AnimateFX() will cause the changes to be made on screen
"immediately"; in other words, AnimateFX() does not remake the copperlists, but
modifies those already in place.  AnimateFX() respects the V40 VideoControl()
tag VC_IntermediateCLUpdate for even faster amimation time.

To clean up, use RemoveFX() to remove the UserCopperlist associated with the
effect, and FreeFX() to free the effect's memory.

It is possible to combine different SuperEffects in combination. For example,
SuperEffect1 is a handle from DisplayFX() that consists of a ColorRange and a
ScrollLines. SuperEffect2 is another handle that consists of a RepeatLines
effect. They are both shown on screen together. Now, I can animate both
SuperEffects separately with AnimateFX(), but half way through I want to remove
the SuperEffect1, just leaving SuperEffect2 on screen. I can do this with
RemoveFX(SuperEffect1), and remake the display, continue animating SuperEffect2,
and then later display just the ColorRange again with DisplayFX(), just leaving
the ColorRange and LineRepeat effects on screen.


LIMITATIONS

A limitation of the SpecialFX.library is that the SpecialFX and normal
UserCopperlists cannot be mixed in the same ViewPort. This is because SpecialFX
builds UserCopperlists, and links them together off ViewPort->UCopList. When a
programmer's UserCopperlist is freed with FreeUCopList(), the SpecialFX
UCopLists would be freed as well.

Obviously, there is a limit to the number of effects you can use on a single
display line because of the number of copper instructions needed.


INCLUDES

Here are the visible parts of the effects structures.

struct ColorRange
{
    LONG cor_Pen;		/* Pen number to change */
    WORD cor_X;			/* X Wait position - ignored for V40 */
    WORD cor_Line;		/* Y Wait position */
    ULONG cor_Red;		/* 32 bit red value */
    ULONG cor_Green;		/* 32 bit green value */
    ULONG cor_Blue;		/* 32 bit blue value */
    ULONG cor_Flags;		/* See below */
};

#define CORB_MODIFY 0		/* When used with AnimateFXA(), this colour
				 * entry is modified to the RGB value.
				 */
#define CORF_MODIFY (1 << CORB_MODIFY)
#define CORB_RESTORE 1		/* if set, then restore the pen number to its
				 * original value. This needs to be set only
				 * once for the pen number, and is used by
				 * DisplayFXA()
				 */
#define CORF_RESTORE (1 << CORB_RESTORE)

struct LineControl
{
    UWORD lc_Line;		/* Start the effect at this line */
    UWORD lc_Count;		/* Effect is over this many lines */
    WORD lc_YOffset0;		/* Use this line number of the
    				 * ViewPort's BitMap as the first line to start
    				 * scrolling, or as the line to repeat over
    				 * lc_Count lines.
    				 */
    WORD lc_YOffset1;		/* As lc_YOffset0, but this is for the second 
    				 * playfield of a dualplayfield display.
    				 */
    WORD lc_XOffset0;		/* Value by which to scroll the lines. +ve is to
    				 * the left, -ve to the right.
    				 */
    WORD lc_XOffset1;		/* As lc_XOffset0, but this is for the second 
    				 * playfield of a dualplayfield display.
    				 */
    UWORD lc_Flags;		/* None defined yet. */
};

struct RepeatSprite
{
    struct ExtSprite *rs_ExtSprite;
    				/* Pointer to an initialised
				 * ExtSprite structure.
				 */
    struct TagItem *rs_TagList;	/* Pointer to a TagList of ChangeExtSpriteA()
				 * tags. As of V40, ChangeExtSpriteA() has no
				 * tags defined, so this should be NULL. There
				 * is no guarantee that RepeatSprite will
				 * support all or any of the future
				 * ChangeExtSpriteA() tags.
				 */
    UWORD rs_Line;		/* Place the sprite on this line */
    UWORD rs_Flags;		/* None defined yet. */
};

(Hmmm - maybe this would be a good point for a function to return the number of
sprites avaialable in this ViewPort.)

struct FineVideoControl
{
    struct TagItem *fvc_TagList;
    				/* A pointer to a taglist of VideoControl()
    				 * tags. Only a subset of VC() tags will be
    				 * supported, listed below.
    				 */
    UWORD fvc_Line;		/* line number the VideoControl() should
				 * start on.
				 */
    UWORD fvc_Count;		/* VideoControl() lasts for this many lines,
    				 * and then the previous settings are restored.
    				 * If == -1, then maintain this to the end of
    				 * the ViewPort.
    				 */
    UWORD fvc_Flags;		/* None defined yet. */
    UWORD fvc_Pad;
};

Subset of VideoControl() tags supported (namely those that affect RGA bits).
VTAG_CHROMAKEY_CLR		
VTAG_CHROMAKEY_SET		
VTAG_BITPLANEKEY_CLR		
VTAG_BITPLANEKEY_SET		
VTAG_BORDERBLANK_CLR		
VTAG_BORDERBLANK_SET		
VTAG_BORDERNOTRANS_CLR		
VTAG_BORDERNOTRANS_SET		
VTAG_CHROMA_PEN_CLR		
VTAG_CHROMA_PEN_SET		
VTAG_CHROMA_PLANE_SET		
VTAG_PF1_BASE_SET		
VTAG_PF2_BASE_SET		
VTAG_SPEVEN_BASE_SET		
VTAG_SPODD_BASE_SET		
VTAG_BORDERSPRITE_SET		
VTAG_BORDERSPRITE_CLR		
VTAG_SPRITERESN_SET		
VTAG_PF1_TO_SPRITEPRI_SET	
VTAG_PF2_TO_SPRITEPRI_SET	
[We could add pseudo VideoControl() tags to this list to modify the depth and
display mode. eg
_VTAG_DEPTH
_VTAG_HAM_SET
_VTAG_EHB_SET
_VTAG_NORM_SET
]

struct InterruptControl
{
    UWORD ic_Line;		/* Cause the interrupt at the start of this
    				 * line.
    				 */
    UWORD ic_IntBits;		/* Interrupt(s) to cause, as defined in
    				 * <hardware/intbits.h>.
    				 */
    UWORD ic_Flags;		/* None defined yet. */
    UWORD ic_Pad;
};

struct DisplayWindow
{
    UWORD dw_Line;		/* Modify the display window at this line */
    UWORD dw_Count;		/* over this many lines. */
    UWORD dw_XOffset;		/* Move the display window to this or by this
    				 * horizontal pixel (in ViewPort resolution
    				 * pixels).
    				 */
    UWORD dw_Width;		/* Make the display window either this wide, or
    				 * close it at this pixel.
    				 */
    UWORD dw_Flags;		/* See below. */
    UWORD dw_Pad;
};

DWB_XOFFSET_ABS 0		/* Move the display window to dw_XOffset pixel
				 * position if this flag is set, else move the 
				 * window by dw_XOffset pixels (+ve to the
				 * right).
				 */
DWF_XOFFSET_ABS (1 << DWB_XOFFSET_ABS)
DWB_WIDTH_POSITION 1		/* Close the display window at dw_Width pixel
				 * position if this flag is set, else make the
				 * window dw_Width pixels wide.
				 */
DWF_WIDTH_POSITION (1 << DWB_WIDTH_POSITION)

Autodocs:

TABLE OF CONTENTS

SpecialFX.library/AllocFX
SpecialFX.library/AnimateFX
SpecialFX.library/DisplayFXA
SpecialFX.library/FreeFX
SpecialFX.library/RemoveFX
SpecialFX.library/AllocFX                           SpecialFX.library/AllocFX

   NAME
	AllocFX -- Allocate memory and handle for a Special Effect.

   SYNOPSIS
	handle = AllocFX(Type, Number, Array)
	d0               d0    d1      a0

	APTR AllocFX(ULONG, UWORD, ULONG *);

   FUNCTION
	To allocate the data needed to control a 'special effect'. This builds
	a handle, and a list of pointers to the various structures needed.

   INPUTS
	Type   - The type of effect desired.
	Number - The number of elements in the effect
	Array  - pointer to an array of pointers to Number elements.

   RESULT
	handle - will point to a handle to be used by DisplayFX(), or
	         NULL if no RAM.
	         The array will be filled with pointers to each of the elements
	         for the effect.

   SEE ALSO
	<SpecialFX/SpecialFX.h> DisplayFXA() FreeFX()

SpecialFX.library/AnimateFX                       SpecialFX.library/AnimateFX

   NAME
	AnimateFX -- Animate an list of 'Special Effects'

   SYNOPSIS
	AnimateFX(DisplayHandle)
	          a0

	void AnimateFX(APTR);

   FUNCTION
	To animate the list of 'Special Effects' pointed to by the Handle
	obtained from DisplayFXA().

   INPUTS
	DisplayHandle - The handle obtained from DisplayFXA().

   RESULT

   NOTES
	This function will update the display according to the input. There is
	no need to remake the display yourself afterwards.

	The method of animation varies depending on the 'Special Effect' type.
	Each is described in <SpecialFX/SpecialFX.h>.

	This function will also update the intermediate copper lists in the
	ViewPort, so that when a screen is dragged by the user, the latest
	changes are preserved. However, this consumes processor time, as the
	list needs to be updated for every call to AnimateFX(). Under
	graphics.library V40, you can use the VC_IntermediateCLUpdate
	VideoControl() tag to disable intermediate copperlist updates.

   SEE ALSO
	<SpecialFX/SpecialFX.h> RemoveFX() DisplayFXA()
	graphics.library/VideoControl() <graphics/videocontrol.h>

SpecialFX.library/DisplayFXA                     SpecialFX.library/DisplayFXA

   NAME
	DisplayFXA -- Displays the initial settings of the Effect
	DisplayFX -- varargs stub for DisplayFXA()

   SYNOPSIS
	error = DisplayFXA(View, ViewPort, DisplayHandle, TagItems)
	d0                 a0    a1        a2             a3

	ULONG DisplayFXA(struct View *, struct ViewPort *,
	                 APTR *, struct TagItem *);

	error = DisplayFX(View, ViewPort, DisplayHandle, Tag1, ...)

	ULONG DisplayFX(struct View *, struct ViewPort *, APTR *, ULONG, ...);

   FUNCTION
	To display the intial settings of the list of special effects.

   INPUTS
	View          - The View of the display
	ViewPort      - The ViewPort of the display
	DisplayHandle - A pointer to an APTR
	TagItems      - A pointer to an array of SFX_ tags.


   TAGS
	SFX_ColourRange - Builds a copperlist to alter the colour of
	                  specified pens at specified lines, in the maximum
	                  possible colourspace of the chip set.

	SFX_ScrollLines - Builds a copperlist to scroll a group of lines
	                  horizontally, independant from the rest of the
	                  ViewPort.

   RESULT
	error - 0 if succesful, else an error number, as defined in
	        <SpecialFX/SpecialFX.h>
	*DisplayHandle is initialised, and should be passed to AnimateFXA()
	and RemoveFXA().

   NOTES
	After calling DisplayFXA(), you should remake the display, either
	with intuition's MakeScreen()/RethinkDisplay() if using intuition
	screens, or with graphics' MakeVPort()/MrgCop()/LoadView() if using
	a custom View and ViewPort.

	This function will not work with a UserCopperList. Either use
	a UserCopperList for your own tricks, or use SpecialFX.library, but
	do not mix-and-match.

   SEE ALSO
	<SpecialFX/SpecialFX.h> AnimateFX() RemoveFX()

SpecialFX.library/FreeFX                             SpecialFX.library/FreeFX

   NAME
	FreeFX -- Free the memory associated with the Special Effect

   SYNOPSIS
	FreeFX(Handle)
	       a0

	void FreeFX(APTR);

   FUNCTION
	To free the data associated with a 'Special Effect'.

   INPUTS
	Handle - the handle obtained from AllocFX()

   RESULT

   SEE ALSO
	AllocFX()

SpecialFX.library/RemoveFX                         SpecialFX.library/RemoveFX

   NAME
	RemoveFX -- Removes an 'Special Effect', and frees the memory.

   SYNOPSIS
	RemoveFX(Handle)
	         a0

	void RemoveFX(APTR);

   FUNCTION
	To remove the 'Special Effect' group, set up in DisplayFXA().

   INPUTS
	Handle - the handle obtained from DisplayFXA()

   RESULT

   NOTES
	After calling RemoveFX(), you should remake the display, either
	with intuition's MakeScreen()/RethinkDisplay() if using intuition
	screens, or with graphics' MakeVPort()/MrgCop()/LoadView() if using
	a custom View and ViewPort.

	After calling this function, the Handle is no longer valid. If you want
	to redisplay the effect, get a new handle from DisplayFXA().
	
   SEE ALSO
	DisplayFXA()

Example:

This is an incomplete subset of code that uses the ColorRange to set up a
gradient sky colour in pen 0 over the entire screen, and another gradient colour
range across 'lines' lines starting at line 'y'.

{
    struct Screen *s;
    ULONG RGBFirst[3] = {-1, 0, 0};
    ULONG RGBLast[3] = {-1, -1, 0};
    ULONG Grad[3];
    BOOL GradNeg[3];
    ULONG RGBSky[3] = {0, 0, -1};
    ULONG GradSky[3] = {0, (0xffffffff / HEIGHT), 0};
    ULONG error;
    ULONG pen = 10;
    ULONG y = 50;
    LONG x1 = 1, x2 = -x1;
    ULONG lines = 100;
    ULONG depth = 4;
    struct ColorRange *Colours[HEIGHT * 2];
    APTR ColourHandle;
    APTR DisplayHandle;
    struct TagItem vc[] =
    {
	{VTAG_USERCLIP_SET, TRUE},
	{VC_IntermediateCLUpdate, FALSE},
	{TAG_DONE, NULL},
    };
    struct TagItem ti[] =
    {
	{SFX_ColorRange, NULL},
	{TAG_DONE, NULL},
    };
    int i, j;

    Init () ;

    if (s = OpenScreenTags(NULL, SA_Depth, DEPTH, 
                                 SA_Width, WIDTH,
                                 SA_Height, HEIGHT,
                                 SA_DisplayID, MODEID,
                                 SA_VideoControl, vc,
                                 TAG_DONE))
    {
	struct RastPort *rp = &s->RastPort;
	SetAPen(rp, pen);
	for (i = 0; i < (WIDTH - COLOURS); i++)
	{
		Move(rp, (pen + i), 0);
		Draw(rp, (pen + i), (HEIGHT - 1));
	}
	for (i = 0; i < pen; i++)
	{
		SetAPen(rp, i);
		Move(rp, i, 0);
		Draw(rp, i, (HEIGHT - 1));
	}
	for (i = (pen + (WIDTH - COLOURS)); i < WIDTH; i++)
	{
		SetAPen(rp, (i - (WIDTH - COLOURS)));
		Move(rp, i, 0);
		Draw(rp, i, (HEIGHT - 1));
	}

	if (ColourHandle = AllocFX(SFX_ColorRange, NUM_COLOURS, (ULONG *)Colours))
	{
		struct ColorRange **cor = Colours;
		ULONG Spare[3];

		if (RGBFirst[0] > RGBLast[0])
		{
			Grad[0] = ((RGBFirst[0] - RGBLast[0]) / lines);
			GradNeg[0] = TRUE;
		}
		else
		{
			Grad[0] = ((RGBLast[0] - RGBFirst[0]) / lines);
			GradNeg[0] = FALSE;
		}
		if (RGBFirst[1] > RGBLast[1])
		{
			Grad[1] = ((RGBFirst[1] - RGBLast[1]) / lines);
			GradNeg[1] = TRUE;
		}
		else
		{
			Grad[1] = ((RGBLast[1] - RGBFirst[1]) / lines);
			GradNeg[1] = FALSE;
		}
		if (RGBFirst[2] > RGBLast[2])
		{
			Grad[2] = ((RGBFirst[2] - RGBLast[2]) / lines);
			GradNeg[2] = TRUE;
		}
		else
		{
			Grad[2] = ((RGBLast[2] - RGBFirst[2]) / lines);
			GradNeg[2] = FALSE;
		}

		for (i = 0; i < y; i++)
		{
			/* Do the sky colours accross the whole screen */
			(*cor)->cor_Pen = 0;
			(*cor)->cor_WAIT_Y = i;
			(*cor)->cor_Red = RGBSky[0];
			(*cor)->cor_Green = RGBSky[1];
			(*cor)->cor_Blue = RGBSky[2];
			(*cor)->cor_Flags = 0;
			RGBSky[1] += GradSky[1];
			RGBSky[2] -= GradSky[2];
			cor++;
		}

		/* Do joint sky and pen range */
		for (i = 0; i < lines; i++)
		{
			(*cor)->cor_Pen = 0;
			(*cor)->cor_WAIT_Y = (y + i);
			(*cor)->cor_Red = RGBSky[0];
			(*cor)->cor_Green = RGBSky[1];
			(*cor)->cor_Blue = RGBSky[2];
			(*cor)->cor_Flags = 0;
			RGBSky[1] += GradSky[1];
			RGBSky[2] -= GradSky[2];
			cor++;

			(*cor)->cor_Pen = pen;
			(*cor)->cor_WAIT_Y = (y + i);
			(*cor)->cor_Red = RGBFirst[0];
			(*cor)->cor_Green = RGBFirst[1];
			(*cor)->cor_Blue = RGBFirst[2];
			(*cor)->cor_Flags = ((restore && (i == (lines - 1))) ? CORF_RESTORE : 0);
			cor++;
			if (GradNeg[0])
			{
				RGBFirst[0] -= Grad[0];
			}
			else
			{
				RGBFirst[0] += Grad[0];
			}
			if (GradNeg[1])
			{
				RGBFirst[1] -= Grad[1];
			}
			else
			{
				RGBFirst[1] += Grad[1];
			}
			if (GradNeg[2])
			{
				RGBFirst[2] -= Grad[2];
			}
			else
			{
				RGBFirst[2] += Grad[2];
			}
		}

		/* and the remainder */
		for (i = 0; i < (HEIGHT - lines - y); i++)
		{
			/* Do the sky colours accross the whole screen */
			(*cor)->cor_Pen = 0;
			(*cor)->cor_WAIT_Y = (y + lines + i);
			(*cor)->cor_Red = RGBSky[0];
			(*cor)->cor_Green = RGBSky[1];
			(*cor)->cor_Blue = RGBSky[2];
			(*cor)->cor_Flags = 0;
			cor++;
			RGBSky[1] += GradSky[1];
		}
		ti[0].ti_Tag = SFX_ColorRange;
		ti[0].ti_Data = (ULONG)ColourHandle;
		error = DisplayFXA(GfxBase->ActiView, &s->ViewPort, &DisplayHandle, ti);

		if (error == NULL)
		{
			MakeScreen(s);
			RethinkDisplay();

			/* Now animate by cycling the colours. */
			for (j = 0; j < 1000; j++)
			{
				cor = &Colours[y+1];		/* first of the non-sky colours */
				Spare[0] = (*cor)->cor_Red;
				Spare[1] = (*cor)->cor_Green;
				Spare[2] = (*cor)->cor_Blue;
				for (i = 0; i < (lines - 1); i++)
				{
					/* This colour is the next line's */
					(*cor)->cor_Red = (*(cor + 2))->cor_Red;
					(*cor)->cor_Green = (*(cor + 2))->cor_Green;
					(*cor)->cor_Blue = (*(cor + 2))->cor_Blue;
					(*cor)->cor_Flags |= CORF_MODIFY;
					cor += 2;
				}
				(*cor)->cor_Red = Spare[0];
				(*cor)->cor_Green = Spare[1];
				(*cor)->cor_Blue = Spare[2];
				(*cor)->cor_Flags |= CORF_MODIFY;

				WaitTOF();
				AnimateFX(DisplayHandle);
			}
			getchar();
			RemoveFX(DisplayHandle);
			FreeFX(ColourHandle);
		}
	}

	CloseScreen(s);
    }

    CloseAll () ;
Here is how I would envision doing a "flow" transformation, where an image just
flows onto the screen:

{
    struct LineControl *flow;
    APTR FlowHandle;

    ... initialisation etc ...

    /* start at line 0, and repeat the first line over the whole screen. Then
     * repeat the second line from the second line down, then the third from the
     * third etc.
     */
    flow->lc_Line = 0;
    flow->lc_Count = HEIGHT;
    flow->lc_YOffset0 = 0;
    flow->lc_XOffset0 = 0;	/* don't scroll too */
    if (DisplayFX(GfxBase->ActiView, &s->ViewPort, &DisplayHandle,
                 SFX_LineControl, FlowHandle) == NULL)
    {
	MakeScreen(s);
	RethinkDisplay();

	for (i = 0; i < HEIGHT; i++)
	{
   		flow->lc_Line++;
	   	flow->lc_Count--;
	   	flow->lc_YOffset++;
		AnimateFX(DisplayHandle);
	}
	RemoveFX(DisplayHandle);
	FreeFX(FlowHandle);

	etc...
    }
}
