REQUIREMENTS

V40.6 of specialfx.library requires graphics.library V39. Future versions may be
written for graphics.library V37.

Specialfx.library will work on all versions of the Amiga chip sets.

SUMMARY OF DIFFERENCES 40.2 -> 40.6

Independant playfield scrolling implemented in LineControl.
VTAG_SFX_NORM_SET disables hardware DualPlayfield mode.
Added optional copperlist doublebuffering for smoother displays.
Added LineControl line repeat rate for mirroring, flows, half heights etc.
Replaced ICB_TOGGLE in InterruptControl with INCB_SET and INCB_RESET.


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:

APTR ColourHandle;			/* just a handle */
ULONG Error;

if (ColourHandle = AllocFX(ViewPort, SFX_ColorRange, NUMLINES, &Error))
{
    struct ColorControl **cc = (struct ColorControl **)ColourHandle;
    int i;

    for (i = 0; i < y; i++)
    {
	/* Do the sky colours accross the whole screen */
	(*cc)->cc_Pen = 0;
	(*cc)->cc_Line = i;
	(*cc)->cc_Red = RGBSky[0];
	(*cc)->cc_Green = RGBSky[1];
	(*cc)->cc_Blue = RGBSky[2];
	(*cc)->cc_Flags = 0;
	cc++;
    }
    etc...
}

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

Note that after calling InstallFX(), 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 VideoControl() tag
VC_IntermediateCLUpdate for even faster animation 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 InstallFX() 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 InstallFX(), 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.


EFFECTS

SFX_ColorControl	(horrible spelling)

SFX_ColorControl will modify pen(s) on a given scan line to a given 24 bit RGB
value. The library will use the most significant bits of the RGB value, so for
example, it will set 24 bits of RGB on AA machines, and 12 bits on ECS or A
machines.

A place holder has been put in the ColorControl structure for horizontal
control, although this is ignored under V40. Maybe a future chip set will
support accurate horizontal copper control.

The colour change for the pen will remain on screen until the same pen is
changed with another ColorControl, or until the end of the ViewPort. If you only
want the change on one line, and then have the pen restored to its original RGB
value, set the CCB_RESTORE bit in the cc_Flags field before calling
InstallFX().

struct ColorControl
{
    LONG cc_Pen;		/* Pen number to change */
    WORD cc_Horizontal;		/* X Wait position - ignored for V40 */
    WORD cc_Line;		/* Y Wait position */
    ULONG cc_Red;		/* 32 bit red value */
    ULONG cc_Green;		/* 32 bit green value */
    ULONG cc_Blue;		/* 32 bit blue value */
    UWORD cc_Flags;
    UWORD cc_Pad;
};

#define CCB_MODIFY 0		/* When used with AnimateFXA(), this colour
				 * entry is modified to the RGB value.
				 */
#define CCF_MODIFY (1 << CCB_MODIFY)
#define CCB_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
				 * InstallFXA()
				 */
#define CCF_RESTORE (1 << CCB_RESTORE)

The RGB values of the pen at the various lines can be modified by changing the
cc_Red/Green/Blue values in the ColorControl, setting CCF_MODIFY in the cc_Flags
field, and calling AnimateFX().


SFX_InterruptControl

SFX_InterruptControl will cause a copper interrupt on the specified line. It is
up to the application program to install a Copper interrupt server using the
standard exec.library AddIntServer() function.

If the application is using a custom ViewPort, or it has opened an SA_Exclusive
screen, then the application knows that the only copper interrupt was generated
by it's SFX_InterruptControl. However, in a multitasking environment this is not
desirable, so the Copper interrupt can call specialfx.library's FindVP()
function which returns the address of the ViewPort that the interrupt occured
in.

The interrupt can be set on or off by setting the INCB_SET or INCB_RESET flag
inc_Flags field, and calling AnimateFX().

struct InterruptControl
{
    UWORD inc_Line;		/* Cause the interrupt at the start of this
    				 * line.
    				 */
    UWORD inc_Flags;		/* See below */
};

#define INCB_SET 0		/* When used with AnimateFXA(), the interrupt
				 * at this line is turned on.
				 */
#define INCF_SET (1 << INCB_SET)
#define INCB_RESET 1		/* When used with AnimateFXA(), the interrupt
				 * at this line is turned off.
				 */
#define INCF_RESET (1 << INCB_RESET)
/* NB - setting both INCF_SET and INCF_RESET is undefined. */

[NB - since V40.2, the InterruptControl prefix has been changed from ic_ to
inc_, and the ICB_TOGGLE flag has been replaced with the INCB_SET/RESET flags.]


SFX_FineVideoControl

SFX_FineVideoControl allows some of the features of graphics.library's
VideoControl() function to operate over a range of lines in the ViewPort, rather
than over the entire ViewPort that VideoControl() operates over.

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.
				 */
    WORD 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;		/* See below */
    UWORD fvc_Pad;
};

#define FVCB_MODIFY 0		/* When used with AnimateFXA(), the video
				 * features are replaced for this range of
				 * lines with the new features of the
				 * fvc_TagList.
				 */
#define FVCF_MODIFY (1 << FVCB_MODIFY)

After installing the effect, the effects can be modified over the range to a
new set of VideoControl effects by modifying the fvc_TagList pointer to point to
a new TagList, setting the FVCF_MODIFY flag in fvc_Flags, and calling
AnimateFX().

The VideoControl() effects supported are:

VTAG_CHROMAKEY_CLR/SET
VTAG_BITPLANEKEY_CLR/SET
VTAG_BORDERBLANK_CLR/SET
VTAG_BORDERNOTRANS_CLR/SET
VTAG_CHROMA_PLANE_SET
VTAG_PF1_BASE_SET		
VTAG_PF2_BASE_SET		
VTAG_SPEVEN_BASE_SET		
VTAG_SPODD_BASE_SET		
VTAG_BORDERSPRITE_CLR/SET
VTAG_SPRITERESN_SET		
VTAG_PF1_TO_SPRITEPRI_SET
VTAG_PF2_TO_SPRITEPRI_SET
Also, some additional effects are available in FineVideoControl which are not
available to VideoControl():
VTAG_SFX_DEPTH_SET	(changes the display depth)
VTAG_SFX_HAM_SET	(changes the display mode to HAM)
VTAG_SFX_EHB_SET	(changes the display mode to ExtraHalfBright)
VTAG_SFX_NORM_SET	(clears HAM, DualPlayField and ExtraHalfBright)
VTAG_SFX_DPF_SET	(enable dual playfield - was VTAG_MAKEDPF)
VTAG_SFX_PF1PRI		(playfield 1 has priority over playfield 2)
VTAG_SFX_PF2PRI		(playfield 2 has priority over playfield 1)


SFX_SpriteControl

SFX_SpriteControl allows sprites to be moved within a specified area of the
ViewPort, but reused in each area. This is similar to graphics.library's VSprite
system, only SFX_SpriteControl can handle AA 32 and 64 bit wide sprites, and
does not need the display to be remade each time the sprite is moved.

struct SpriteControl
{
    struct ExtSprite *spc_ExtSprite;
    				/* Pointer to an initialised
				 * ExtSprite structure. The
				 * sc_ExtSprite.es_SimpleSprite
				 * structure should be initialised as
				 * normal for the graphics.library
				 * sprite functions.
				 */
    struct TagItem *spc_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 SpriteControl will
				 * support all or any of the future
				 * ChangeExtSpriteA() tags.
				 */
    UWORD spc_Line;		/* First line of the range in which the
				 * sprite is to be used.
				 */
    UWORD spc_Flags;		/* See below */
};

#define SPCB_MODIFY 0		/* When used with AnimateFX(), replace
				 * the sprite image with that in
				 * sc_ExtSprite.
				 */
#define SPCF_MODIFY (1 << SPCB_MODIFY)

[NB - The prefix for the SpriteControl has been changed from sc_ to spc_]

The application needs to call graphics.library's AllocSpriteData() for each
ExtSprite image that will be used, and GetExtSprite() for each sprite number
that will be used in the ViewPort. The sprite can be reused throughout the
ViewPort, so long as the range of the sprite
(sc_ExtSprite.es_SimpleSprite.y + sc_ExtSprite.es_SimpleSprite.height) < the
value of the next lower sc_Line.

The sprites can be moved within their limited range after calling InstallFX() by
modifying the sc_ExtSprite.es_SimpleSprite.x,y,height and even num values,
setting the SCF_MODIFY bit in sc_Flags, and calling AnimateFX(). To change the
image of the sprite in the range, replace the sc_ExtSprite pointer to a properly
initialised ExtSprite pointer instead.


SFX_LineControl

SFX_LineControl allows you to scroll different parts of the ViewPort
independantly from the rest, much like parallax scrolling seen in many of todays
games. Specialfx.library allows you to specify which part of the display is to
be scrolled, where the data for that area is to come from (so you can define a
larger bitmap than is visible and display the parts outside of the visible
range), repeat a line of the bitmap over a number of lines on the screen (for
those "flow" fades), and independantly control even and odd playfields.

struct LineControl
{
    struct RasInfo *lc_RasInfo;	/* Pointer to an initialised RasInfo structure.
    				 * Set the RxOffset for the scroll value (+ve
    				 * is to the left, -ve to the right) of this area,
    				 * and the RyOffset to the line number in the
    				 * ViewPort's BitMap to start the effect with.
    				 *
    				 * If lc_RasInfo->Next is not NULL, then
    				 * lc_RasInfo will control the even bitplanes, and
    				 * lc_RasInfo->Next the odd bitplanes.
    				 *
    				 * Note that the RasInfo->BitMap->Planes are ignored.
    				 * All operations are on the ViewPort's RasInfo(s)
    				 * BitMap(s) Plane(s).
    				 *
    				 */
    UWORD lc_Line;		/* Start the effect at this line */
    UWORD lc_Count;		/* Effect is over this many lines */
    UWORD lc_Flags;		/* See below */
    WORD lc_SkipRateEven;	/* Number of lines to skip after displaying */
    WORD lc_SkipRateOdd;	/* a line. Default = 1.*/ 
    UWORD lc_Pad;
};

/* A note about the lc_SkipRate... paramters.
 * The first line to be displayed at line lc_Line of the display is line
 * lc_RasInfo->RyOffset of the ViewPort->RasInfo->BitMap.
 * The next line to be displayed at line (lc_Line + 1) is
 * (lc_RasInfo->RyOffset + lc_SkipRateEven).
 * So, if lc_SkipRateEven =
 * 1, contiguous lines are displayed (this is the default).
 * 2, every other line is displayed.
 * 0, the first line is repeated, giving a 'flow' effect.
 * -1, the image is displayed upside down.
 */
};

#define LCB_MODIFY 0		/* Set for the changes to be made by
				 * AnimateFX().
				 */
#define LCF_MODIFY (1 << LCB_MODIFY)

[NB - for V40.6, the LCF_REPEATLINE flags have been replaced with more
versatile lc_SkipRate... fields.]

There are some restrictions with this effect though.
1) The calculted modulo value must be in the range -32768 - +32767. That
   may mean some lines in interleaved bitmaps may be out of range!
2) In interlaced modes, lc_Line and lc_Count must both be even values,
   else results are unpredictable. 
   Future releases may try to alleviate these problems as best it can.
3) lc_Line must not = 0. I need to WAIT(-1,0) to set up the first modulo,
   and there may not be enough coppercycles to do so.



KNOWN BUGS

As of specialfx.library V40.6:

In SFX_LineControl:
1) Lores 4x and Lores 2x do not scroll properly if pushed to the left of
   the screen.
2) There are some problems with scrolling DualPlayfield screens, notably if
   there are more than two DPF LineControls. I haven't narrowed down the exact
   circumstances yet.
3) If the screen was opened with SA_Left of non-zero, scrolling is garbage.
4) If any screen is using specialfx.library with copperlist doublebuffering, the
   display can become corrupted.


General:
Handling display coercion has not yet been implemented.
ScanDouble modes may have problems.


