A couple years ago I came across this sample. It's perhaps not the fastest way, but it is a start.

It uses the DXDraw, DXImagelist components. Add two images in the list copy/paste the code below and you're set.

[pascal]
// Hi,
// There was talk about how to fade screens in and out. This is my version of doing it.
// It does about 30 FPS with my 350 MHz AMD. (40 FPS if I check out the VBlank)
// I also tried using a fixed value for the steps. When I used 128 I could change the div to shr and double the speed...
// See how it's done in FadeOut(12; I'm sure that some good optimizer like
// Christopher Oezbek will double the FPS in no time... This does the fading in a bit
// different way than his code did. Christopher used a fixed value to drop down the color
// value, and I'm interpolating the values to have a smoother result.
//
// I hope you like this,
// Ilkka
//

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
DXDraws, ExtCtrls, DXClass;

type
TForm1 = class(TForm)
DXDraw: TDXDraw;
Images: TDXImageList;
procedure DXDrawMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
// Fading procedures.
procedure FadeIn(Steps: Integer; Src, Dest: Integer);
procedure FadeOut(Steps: Integer; Src, Dest: Integer);
procedure Mix(Pic1, Pic2, Src: Integer);
end;

var
Form1: TForm1;

implementation

procedure TForm1.FadeIn(Steps: Integer; Src, Dest: Integer);
var
i, x, y: Integer;
SrcScan, DestScan: PByteArray;
StartTime: TDateTime;
begin
Form1.Caption := ('Fading in');
StartTime := Now;
for i := 0 to Steps do
begin
for y := 0 to Images.Items[Src].Picture.Bitmap.Height-1 do
begin
// Get the scanlines.
DestScan := Images.Items[Dest].Picture.Bitmap.ScanLine[y];
SrcScan := Images.Items[Src].Picture.Bitmap.ScanLine[y];
// height * 3 for 24 bpp.
for x := 0 to (Images.Items[Src].Picture.Bitmap.Width) * 3 do
DestScan[x] := (SrcScan[x] * i) div Steps;
end;
// Restore the bitmap and draw it.
Images.Items[Dest].Restore;
Images.Items[Dest].Draw(DXDraw.Surface,0,0,0);
DXDraw.Flip;
end;
// Show the FPS
Form1.Caption := 'Fading out done, ' +
IntToStr(Round(Steps / ((Now-StartTime)*86401))) + ' FPS';
end;// FadeIn

procedure TForm1.FadeOut(Steps: Integer; Src, Dest: Integer);
var
i, x, y: Integer;
SrcScan, DestScan: PByteArray;
StartTime: TDateTime;
begin
Form1.Caption := ('Fading out');
StartTime := Now;
for i := Steps downto 0 do
begin
for y := 0 to Images.Items[Src].Picture.Bitmap.Height-1 do
begin
DestScan := Images.Items[Dest].Picture.Bitmap.ScanLine[y];
SrcScan := Images.Items[Src].Picture.Bitmap.ScanLine[y];
for x := 0 to (Images.Items[Src].Picture.Bitmap.Width) * 3 do
// DestScan[x] := (SrcScan[x] * i) div Steps;
// This shows the speed difference between div's
// and shr's. (Doubled)
DestScan[x] := (SrcScan[x] * i) shr 7;
end;
Images.Items[Dest].Restore;
Images.Items[Dest].Draw(DXDraw.Surface,0,0,0);
DXDraw.Flip;
end;
Form1.Caption := 'Fading out done, ' +
IntToStr(Round(Steps / ((Now-StartTime)*86401))) + ' FPS';
end;// FadeOut

procedure TForm1.Mix(Pic1, Pic2, Src: Integer);
var
i, x, y: Integer;
Src1Scan, Src2Scan, DestScan: PByteArray;
StartTime: TDateTime;
begin
Form1.Caption := ('Mixing');
StartTime := Now;
for i := 0 to 128 do
begin
for y := 0 to Images.Items[0].Picture.Bitmap.Height-1 do
begin
Src1Scan := Images.Items[Pic1].Picture.Bitmap.ScanLine[y];
Src2Scan := Images.Items[Pic2].Picture.Bitmap.ScanLine[y];
DestScan := Images.Items[Src].Picture.Bitmap.ScanLine[y];
for x := 0 to (Images.Items[0].Picture.Bitmap.Width) * 3 do
// This adds the pictures together and
// fades from one to other.
DestScan[x] := ((Src1Scan[x] * i) shr 7) +
((Src2Scan[x] * (128-i))shr 7);
end;
Images.Items[Src].Restore;
Images.Items[Src].Draw(DXDraw.Surface,0,0,0);
DXDraw.Flip;
end;
Form1.Caption := 'Mixing done, ' +
IntToStr(Round(128 / ((Now-StartTime)*86401))) + ' FPS';
end;// Mix

{$R *.DFM}

procedure TForm1.DXDrawMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
If not (ssShift in Shift) then
begin
If Button = mbleft then
fadein(128,0,1)
else
// If you change this, remember to change the
// fadeout function back.
fadeout(128,0,1);
end else
begin
If Button = mbleft then
Mix(0,2,1)
else
Mix(2,0,1);
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
// If I put the images straight to ImageList,
// The bitmap property doesn't seem to be set.
// I don't know how to get the pixels otherwise.
Images.Items[0].Picture.Bitmap.LoadFromFile('back.bmp');
Images.Items[1].Picture.Bitmap.LoadFromFile('delta.bmp');
Images.Items[2].Picture.Bitmap.LoadFromFile('delta.bmp');
// Set the form and DXDraw size.
DXDraw.Height := Images.Items[0].Picture.Bitmap.Height;
DXDraw.Width := Images.Items[0].Picture.Bitmap.Width;
Form1.ClientHeight := DXDraw.Height;
Form1.ClientWidth := DXDraw.Width;

Form1.Caption := 'Click the canvas!';
end;

end.
[/pascal]