The Crossfade

By Dunkelzahn

Hi Chummers!

The first part of this article is all about the theory behind a crossfade, and hence it's independent of the language used.

A crossfade is when one pic fades into another, as you can often see in films.

The first thing we need are two piccies and their palettes. They should best be put into different graphics pages in the VGA memory, or virtual screens if the mode used does not support more that one page. We also require a third page, as well as a third and fourth palette for calculations. For every pixel, we now look at the colour of that point in either of the two piccies. We generate a knew colour index. In the 3rd palette (the "fade from" palette) the colour is set to that in the first image, while it is set to that of the second image in the 4th palette (the "fade to" palette). Finally, a pixel with the new colour index is set on the 3rd virtual page. For every colour combination, we certainly make sure that there isn't already a colour index whose from/to values correspond to the combination under consideration.

A little diagram to make things more obvious:

              Screen 1              Screen 2              Screen 3
         -----------------     -----------------     -----------------
         | 0 | 5 | 6 | 0 |     | 0 | 1 | 6 | 0 |     | 0 | 1 | 2 | 0 |
         -----------------     -----------------     -----------------
         | 5 | 3 | 0 | 6 |     | 1 | 4 | 1 | 6 |     | 1 | 3 | 4 | 2 |
         -----------------     -----------------     -----------------


Palette "From":                           Palette "To":

Colour Red        Green      Blue         Colour Red        Green      Blue
0      PAL1[0]-R  PAL1[0]-G  PAL1[0]-B    0      PAL2[0]-R  PAL2[0]-G  PAL2[0]-B
1      PAL1[5]-R  PAL1[5]-G  PAL1[5]-B    1      PAL2[1]-R  PAL2[1]-G  PAL2[1]-B
2      PAL1[6]-R  PAL1[6]-G  PAL1[6]-B    2      PAL2[6]-R  PAL2[6]-G  PAL2[6]-B
3      PAL1[3]-R  PAL1[3]-G  PAL1[3]-B    3      PAL2[4]-R  PAL2[4]-G  PAL2[4]-B
4      PAL1[0]-R  PAL1[0]-G  PAL1[0]-B    4      PAL2[1]-R  PAL2[1]-G  PAL2[1]-B

Legend:
PAL1 = palette of 1st pic, PAL2 = palette of 2nd pic
PAL1[0]-R = red component of colour 0

So why the hell do we need all this calculation drivel? Actually, our X-fade is nearly finished. We only gotta pop page III on the screen and fade from "from" to "to" (surprise!) palettes.

One problem of this method is the limit imposed by the number of colours in the palette. If pic 1 and pic 2 have 256c each, the resulting max number of combinations is 256^2, i.e. 262144, way above the limit of a standard 256 palette! As no normal VGA mode operate with palettes greater than 256 colours, one has to limit both pictures to 16 colours each, since 16^2 = 256. [Of course, you could use funny stuff like 18-bit colour (see Orange's X-Y-Z) ... but that's messy, and by no means WYSIWYG =]

I really hope you got this ... but after all it's my very first article!

Now for the fun bits in TP, using mode 13h. But apart from the x-fade procedures we need a few others:

    1. to create a virtual screen
    2. to de-allocate mem used by the virtual screen
    3. set a pixel [ =) ]
    4. get a pixel
    5. set the RGB for a colour
    6. switch to mode 13h
    7. switch back to textmode
    8. fade from one palette to another
    9. set a palette.

... plus a PCX-loader, to load the images into the virtual screens.

The procedures follow in their Turbo pascal source.

Procedure SetVirtual(virtuell : virtzeig ; var adresse : word);
BEGIN
  GetMem (virtuell,64000);
  adresse := seg (virtuell^);
END;


Procedure ShutDown(virtuell : virtzeig);
BEGIN
  FreeMem (virtuell,64000);
END;


procedure putpixel(x,y : Integer; Colour : Byte;where : word);
{Diese Prozedur setzt einen Pixel}
begin
 Mem [where:x+(y*320)] := Colour;
end;

function getpixel(x,y : integer; where : word):byte;
begin
 getpixel := mem[where:x+y*320];
end;

procedure setcolour(farbe,rot,gruen,blau : byte);
begin
port[$03c8] := farbe; {Senden der Farbnummer an den Schreibport}
port[$03c9] := rot; {Setzen des Rotwertes}
port[$03c9] := gruen; {Setzen des Grnwertes}
port[$03c9] := blau; {Setzen des Blauwertes}
end;

procedure Set13h;
begin
 asm
  mov  ax,0013H
  int  10h
 end;
end;
procedure SetText;
begin
 asm
  mov  ax,0003h
  int 10h
 end;
end;


procedure fade(palziel,paltemp : paltyp;anfang,ende,warten,step : integer);
var fadestep,colour,i,k : integer;
    steps :  array[0..255,1..3] of integer;
    red,green,blue : integer;
begin
for i:= 0 to 255 do
 for k:= 1 to 3 do
  steps[i,k] := palziel[i,k]-paltemp[i,k];
for fadestep := 1 to step do
    begin
       for colour :=anfang to ende do
         begin
           red := paltemp[colour,1]+((steps[colour,1]*fadestep) DIV 64);
           green := paltemp[colour,2]+((steps[colour,2]*fadestep) DIV 64);
           blue := paltemp[colour,3]+((steps[colour,3]*fadestep) DIV 64);
           setcolour(colour,red,green,blue);
         end;
       delay(warten);

    end;
end;



procedure aktivepal(palette : paltyp);
var i : integer;
begin
for i:= 0 to 255 do
  setcolour(i,palette[i,1],palette[i,2],palette[i,3]);
end;

procedure showpcx(name : string;where : word;var palette : paltyp);
var quelle : file;
    zeiger : ^byte;
    puffer : pointer;
    laenge,z : longint;
    palanf : longint;
    k,i,x,y : integer;
    hilf,pixel,pixel2 : byte;

begin
assign(quelle,name);
reset(quelle,1);
laenge := filesize(quelle);
palanf := laenge - 768;
getmem(puffer,filesize(quelle));
blockread(quelle,puffer^,filesize(quelle));
zeiger := puffer;
inc(zeiger,palanf);
for i := 0 to 255 do
  for k := 1 to 3 do
  begin
    palette[i,k] :=  zeiger^ DIV 4;
    inc(zeiger);
  end;
zeiger := puffer;
inc(zeiger,128);
z := 128;
x := 0;
y := 0;
repeat
  pixel := zeiger^ ;
  inc(zeiger);
  inc(z);
  IF pixel >= 192 then
    begin
     pixel2 := zeiger^ ;inc(zeiger);inc(z);
     for i := 1 to pixel-192 do
       begin
         mem[where:x+320*y] := pixel2;


         inc(x);
         IF x = 320 then inc(y);
         IF x = 320 then x :=0;
       end;
     end
    ELSE
     begin
       mem[where:x+320*y] := pixel;
       inc(x);
       IF x = 320 then inc(y);
       IF x = 320 then x :=0;
     end;

until palanf = z ;
freemem(puffer,Filesize(quelle));
close(quelle);
end;

Some declarations ...

program faden;
uses crt;
CONST VGA = $A000;
TYPE paltyp = ARRAY[0..255,1..3] OF integer;
     Virtual = Array [1..64000] of byte;
     Virtzeig = ^Virtual;
var pal,pal2 : paltyp;
    i,k,h : integer;
    Virscr1,virscr2 : Virtzeig;
    Vaddr1,vaddr2  : word;

Finally the x-fade procedure, by now self-explanatory I hope.

procedure crossfade(quelle,ziel:word;palq,palz:paltyp;warten,step:integer);
var palvon,palzu : paltyp;
    virscr : virtzeig;
    virtu : word;
    qcol,zcol,red1,red2,green1,green2,blue1,blue2 : byte;
    y,x : longint;
    k,farbe,max : integer;

begin
SetVirtual(virscr,virtu);
max := -1;
for y := 0 to 199 do
 for x:= 0 to 319 do
  begin
    qcol := getpixel(x,y,quelle);
    zcol := getpixel(x,y,ziel);
    red1 := palq[qcol,1];
    green1 := palq[qcol,2];
    blue1 := palq[qcol,3];
    red2 := palz[zcol,1];
    green2 := palz[zcol,2];
    blue2 := palz[zcol,3];
    farbe := max + 1;
    for k:= 0 to max do
      begin
        IF (palvon[k,1] = red1) AND (palvon[k,2] = green1)
          AND (palvon[k,3] = blue1) AND (palzu[k,1] = red2)
          AND (palzu[k,2] =green2) AND (palzu[k,3] = blue2)
            THEN farbe := k;
      end;
     IF farbe = max + 1 then
      begin
        palvon[farbe,1] := red1;
        palvon[farbe,2] := green1;
        palvon[farbe,3] := blue1;
        palzu[farbe,1] := red2;
        palzu[farbe,2] := green2;
        palzu[farbe,3] := blue2;
        max := farbe;
      end;
     putpixel(x,y,farbe,virtu);
  end;
move(mem[virtu:0],mem[vga:0],64000);
aktivepal(palvon);
fade(palzu,palvon,0,255,warten,step);
end;

The main procedure:

begin
set13h;
setvirtual(virscr1,vaddr1);
setvirtual(virscr2,vaddr2);
showpcx('bild1',vaddr1,pal);     {Hier bild1 und bild2 durch die Dateinamen}
showpcx('bild2',vaddr2,pal2);    {der PCX-Bilder ersetzen}
crossfade(vaddr1,vaddr2,pal,pal2,50,64);
readln;
settext;
end.

About calling the x-fade proc:

Firstly, the addresses of the virtual screens and their palettes are specified. Then the delay rate and the number or crossfade steps. 64 steps -> full fade, 32 steps -> both piccies visible in equal proportions.

I hope you were able to follow all that ...

Ciao, have fun

Dunkelzahn

P.S.: Special greets fly to Denthor of Asphyxia, whose tutorials were the basis for the theory part!