************************************************************
CROSSFADING
23.01.97 Matthijs Hollemans
************************************************************

Crossfading is the process of gradually changing the colours
of  one  palette into the colours of the other. This way you
can   make   smooth   transitions   between   two  different
colourschemes,  instead  of being limited to simple fades to
black.  For  instance, you can have your screen with lots of
colours fade to a bunch of greyscales. Of course, it is also
possible to do "traditional" fades to black.

The  following  pseudo-code  routines  assume  a  couple  of
things:

  1. We are using a 24-bit palette, and each component (red,
     green, blue) takes up one byte in memory. 

  2. The palette consists of 256 colours. So the total
     palette uses 256*3 = 768 bytes of memory.

  3. The palette is ordered like this: Red0,Green0,Blue0,
     Red1,Green1,Blue1...Red255,Green255,Blue255.

We  use  two  global  variables  to keep track of the fading
process.  XFadeStep  is  the  stepsize  that  we  use. Small
stepsizes  result  in  fades  that  take a long time. On the
other  hand,  large stepsizes result in very fast fades. The
other   variable  is  XFadeCount  which  is  the  number  of
iterations that are needed to complete the fade.

	UBYTE XFadeStep		/* Unsigned byte */
	UWORD XFadeCount        /* Unsigned word */

Instead  of writing to these variables directly we do things
the easy way and use the following function.

	FUNCTION InitXFade(UBYTE StepSize)

	  XFadeStep = StepSize
	  XFadeCount = 255 / StepSize

	ENDFUNCTION

This  function  needs  to  be  called  only once, before the
fading  starts. Note that the number 255 comes from the fact
that we use 24-bit colours. In such a palette each component
ranges  from  0 to 255. Since we have three components (red,
green  and blue) the total number of colours is 256*256*256,
or  16,777,216. And if you did your maths you know that this
number exactly fits into 24 bits.

If  we were to use a 12-bit palette (OCS and ECS Amigas) the
number  would be 15. For an 18-bit palette (PC standard VGA)
it is 63.

XFade(),  the function that does the actual fading, needs to
be  called  every  vertical blank. This means that you don't
have  to  wait  for the fading to finish, but you can render
your display while it is still fading, which is pretty cool.
But  what  happens if the fading is finished ? Well, in that
case  it  won't hurt to still call XFade(), because it keeps
track  of this and will automatically exit, so you don't end
up with wrong colours all over the place.

For  example, if you use a StepSize of 1, XFadeCount will be
255,  so  we  need  255  iterations  to complete the fading.
Therefore  we  need to call XFade() 255 times. Every time it
is  called, all colours are faded somewhat and XFadeCount is
decremented.  Once  XFadeCount  is  negative  the  fading is
finished  and  calling  XFade will no longer have any effect
(until  you  call  InitXFade  again  that  is,  because that
function restores XFadeCount to a positive value).

The function looks like this:

	PROC XFade(UBYTE *SourcePal, UBYTE *DestPal)

	  IF XFadeCount >= 0
	    FOR t = 0 TO 767
	      SourceComp = SourcePal[t]
	      DestComp = DestPal[t]

	      WORD Temp = DestComp - SourceComp	

	      IF Temp > XFadeStep
	        SourceComp += XFadeStep
	      ELSEIF Temp < (-XFadeStep)
	        SourceComp -= XFadeStep
	      ELSE
	        SourceComp = DestComp
	      ENDIF

	      SourcePal[t] = SourceComp
	    ENDFOR

	    XFadeCount -= 1
	  ENDIF

	ENDFUNCTION

The  first  check  makes sure that the fading isn't finished
yet  (it is when XFadeCount is negative). After that we loop
768  times,  because  our  palette  is made up of 768 bytes,
remember.  Note  that  it  doesn't  matter  now  which  byte
represents red, green or blue. We just look at the values of
the bytes, not what they mean.

Then  we read the values from both palettes and compare them
and  do  some simple mathematics. When that is over we store
the  new  value  in the source palette and continue with the
next byte. When all bytes have been processed this iteration
is over so we decrement XFadeCount and exit the function.

Implementation  note: when using a 24-bit palette we need to
resort  to  word-sized  calculations  because  we  are using
signed arithmetics. In a palette with this colour depth, the
maximum  value of a component is 255. But since we are using
signed calculations, a byte with value 255 is actually has a
value  of  -127.  And this will screw up our maths big time.
Therefore  Temp  has to be a signed word and the comparisons
have  to  be  signed as well. Just a word of warning (excuse
the  pun  ;). You can completely ignore this note if you are
using 12- or 18-bit palettes, however.

You can use these routines like this:

	InitXFade(4)			  /* StepSize = 4 */
	REPEAT
 	  Wait for vertical retrace	  /* Sync display */
	  XFade(NormalPalette,GrayScales)         /* Yeah */
	UNTIL User quits the loop

Of course, you don't have to use a vertical retrace, just as
long  as you synchronize the fade to something. If you don't
it  will  be  over before you can see it. A vertical retrace
(which  happens about 50 times per second, depending on your
display  hardware)  is probably the simplest way to do this,
but you could also use a timer or an some kind of interrupt,
etc.  Anyway,  if  you  are using this in a game or demo you
most  probably  have  some  sort  of display synchronisation
already. Don't you ?

To create a grayscale-version of a palette you can use this
function. It can be used on any kind of palette, whether it
be 12-bit, 18-bit or 24-bit.

	FUNCTION MakeGrayScales(UBYTE *Palette)

          FOR t = 0 TO 255
	    UBYTE Gray = Palette[t].Red   * 0.3 
                       + Palette[t].Green * 0.59
                       + Palette[t].Blue  * 0.11

            Palette[t].Red   = Gray
            Palette[t].Green = Gray
            Palette[t].Blue  = Gray
          ENDFOR

	ENDFUNCTION

============================================================

The  following  code  is  my  implementation  of  the  above
crossfade algorithm in MC68000 assembly code. Maybe not very
well   optimized,   but   then   again,   it's  not  a  very
time-consuming thing anyway...and it works too ;)

Note  that  the  palettes used by these routines are ordered
somewhat  different  from  the ones in the above examples. A
palette  now  takes  up  1024 bytes: 4 bytes per colour. The
first byte is always $00, the other three bytes are the red,
green and blue components. Like this: $00,Red0,Green0,Blue0,
$00,Red1,Green1,Blue1...$00,Red255,Green255,Blue255. Thought
you'd like to know.


XFadeCount	dc.w	0	; Crossfade counter
XFadeStep	dc.b	0	; Crossfade stepsize

; **********************************************************
; InitXFade 
; ----------------------------------------------------------
; Inputs     : d0.b = Stepsize (positive value)
; Output     : -
; Description: Initialises XFade.
; Notes      : Call once before calling XFade.
; Scratch    : d0,d1
; Author     : Matthijs Hollemans
; History    : 23 Jan 1997
; **********************************************************

InitXFade
	and.l	#$ff,d0		; Extend to long (unsigned)
	move.b	d0,XFadeStep	; XFadeStep = Stepsize
	move.l	#$ff,d1		;
	divu.w	d0,d1		; XFadeCount = 255/Stepsize
	move.w	d1,XFadeCount	;
	rts

; **********************************************************
; XFade 
; ----------------------------------------------------------
; Inputs     : a0 = Source palette
;              a1 = Destination palette
; Output     : -
; Description: Crossfades between two 24-bit palettes.
; Notes      : -
; Scratch    : All
; Author     : Matthijs Hollemans
; History    : 23 Jan 1997
; **********************************************************

XFade
	tst.w	XFadeCount	; IF XFadeCount < 0
	bmi.b	.Done		; THEN crossfade is ready

	move.b	XFadeStep,d3	; d3 = Step
	ext.w	d3		; Extend d3 to word size !
	move.b	d3,d4		;
	neg.b	d4		; d4 = -Step
	ext.w	d4		; Extend d4 to word size !
	move.l	#256-1,d7	; Process 256 colours
.loop	addq.l	#1,a0		; }_ Skip first byte
	addq.l	#1,a1		; }
	moveq	#3-1,d6		; Loop 3 times (r,g and b)
.rgb	moveq	#0,d0		; }_ Clear registers
	move.l	d0,d2		; }
	move.b	(a0),d0		; d0 = Source component
	move.b	(a1)+,d1	; d1 = Dest component
	move.b	d1,d2		;
	sub.w	d0,d2		; d2 = Dest-Source (word!!!)
	cmp.w	d3,d2		; IF d2 > Step
	ble.b	.0		;
	add.b	d3,d0		;   Source += Step
	bra.b	.write		;
.0	cmp.w	d4,d2		; ELSEIF d2 < -Step
	bge.b	.1		;
	sub.b	d3,d0		;   Source -= Step
	bra.b	.write		;
.1	move.b	d1,d0		; ELSE Source = Dest
.write	move.b	d0,(a0)+	; Write new source component
	dbf	d6,.rgb		;
	dbf	d7,.loop	;
	sub.w	#1,XFadeCount	; XFadeCount -= 1
.Done				;
	rts			;

============================================================
