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!