Entwicklung
  SVGImage
  MWKEdit
  PaletteForm
  MWKComboBoxEx
  MWKToolPalette
  GlowLabel
  ShellDropper
  Tutorials
    ListView sortieren
    Interfaces
    Undo/Redo realisieren
    Splashscreens
  Bibliotheken
  Silbentrennung
Linux
Kontakt
impressum
Druckansicht

Splashscreens


Immer wieder kommt die Frage auf, wie die Realisierung eines Splashscreens am Besten zu bewerkstelligen wäre. Viele Programmierer schauen dabei neidisch auf Programme der großen Anbieter wie Microsoft oder Adobe. Dort sieht man Schatten, Transparenzen und Fading-Effekte. Natürlich sind diese Effekte nicht sonderlich schwer zu implementieren, und auch in Delphi ist so etwas schnell erstellt.
Das komplette Demo mit den benötigten Units finden Sie am Ende des Artikels.

Probleme und Lösungen

Bevor es an die Arbeit geht, kommt erst noch etwas Hintergrundinformation. Schatten und Transparenzen benötigen einen Alphakanal. Leider unterstützt die Graphikschnittstelle von Windows (das GDI) keinen Alphakanal. Erst mit neuen Schnittstellen wie GDI+ oder WPF kommen wie in den Genuss von echten Transparenzen. Glücklicherweise gibt es aber seit Windows 2000 eine neue Funktion namens "UpdateLayeredWindow". Diese gibt uns die Möglichkeit, ein Fenster mit einem 32-Bit Bitmap zu "bemalen", so dass wie tatsächlich die Transparenzmaske nutzen können. Diese Funktion werden wir auch verwenden, wenn wir den Splashscreen zeichnen.
Damit kommen wir schon zum nächsten Problem: Delphi unterstützt keine Bitmaps mit einer Farbtiefe von 32 Bit. Zwar kann dies Farbtiefe eingestellt werden, aber leider gehen dabei die Transparenzwerte verloren. Wir werden deshalb einen Umweg machen, und uns die Pixeldaten über GDI+ holen. Das hat noch einen weiteren vortel: GDI+ kann wesentlich mehr Graphikformate lesen als GDI, z. B. TIFF oder PNG. Für den Splashscreen verwenden wir PNG. Dieses Format ist für unsere Zwecke ideal, unterstützt es doch eine sher gute verlustlose Packrate und Bilder mit Alphakanal.
UpdateLayeredWindow schafft aber leider noch ein paar andere Probleme. Ein Fenster, das diese Funktion nutzt, zeigt keine Controls mehr an. Wir können also keine Labels zum Zeichnen von Statusmeldungen verwenden. Wenn wir Text ausgeben wollen, müssen wir direkt auf das Bild zeichnen und dieses erneut anzeigen. Das klingt jetzt aufwendig, ist aber recht einfach.

Vorbereitung

Zuerst die Voraussetzungen. Wie oben erwähnt benötigen wir GDI+. Ich setze immer die Klassen von Progdigy ein, die sich (leicht erweitert) auch hier auf der Seite unter Bibliotheken befinden. Damit ist unser Demo erst ab Windows XP lauffähig, es sei denn, GDIPlus.dll liegt im Programmverzeichnis. Microsoft erlaubt die Weitergabe dieser Bibliothek mit eigenen Programmen. Trotzdem bleibt die Mindestvoraussetzung Windows 2000, da erst diese Version die Funktion UpdateLayeredWindow mitbringt.

Für unser Beispielprogremm benötigen wir zwei Formulare. Eines wird unser Hauptprogramm (Form1) und das zweite unser Splashscreen (SplashForm). Zusätzlich benötigen wir natürlich noch ein PNG, das wir mit Paint.net, GIMP oder Photoshop erzeugen können.
Den Rand (BorderStyle) unseres SplashForms setzten wir auf bsNone, den FormStyle auf fsStayOnTop. Beim Huptfenster setzen wir die Sichtbarkeit (Visible) auf False, beim TSplashForm auf True; wir wollen ja zuerst den Splashscreen einblenden und erst dann das Hauptfenster anzeigen.
Unser PNG packen wir als Ressource in den Splashscreen. Dazu legen wir ein Ressource-Script an und binden es ein. Das Ressource-Script ist einfach eine Textdatei ("Splash.rc") mit folgendem Inhalt:
SPLASH RCDATA Splash.png
Eingebunden wird die Ressource wie üblich in der Quelldatei unseres Formulars:
implementation

{$R *.dfm}

{$R Splash.res}
Vorher müssen wir die Ressource allerdings erst kompilieren. Das erledigen wir in der Komanndozeile mit dem Befehl
brc32 -r Splash.rc"

Da wir den Splashscreen nicht nur anzeigen wollen, sondern während der Anzeige auch etwas im Hintergund passieren soll, erstellen wir in Form1 eine Methode mit dem Namen Init. Hier erledigen wir später die Initialisierung des Programms.

Was wir jetzt noch benötigen, ist die Logik zur Anzeige der Fenster. Dazu öffenen wir die Quelldatei des Projekts (*.dpr). Diese sieht etwa so aus:
begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.CreateForm(TSplashForm, SplashForm);
  Application.Run;
end.
Jetzt ändern wir den Quellcode folgendermaßen ab:
begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.CreateForm(TSplashForm, SplashForm);

  // SplashForm einfaden
  SplashForm.FadeIn;

  // Initialisierung des Programms
  Form1.Init;

  // Hauptfenster anzeigen
  Form1.Show;
  Form1.Update;

  // SplashForm ausblenden und zerstören
  SplashForm.FadeOut;
  SplashForm.Free;
  
  Application.Run;
end.
Die Kommentare dürften als Erklärung ausreichen.

Helferlein

Für das Laden des PNG als Ressource habe ich ein paar Hilfsfunktionen geschrieben, die ein TGPImage zurückliefern. Zur Umwandlung in ein TBitmap steht ebenfalls eine Hilfsfunktion bereit. Zu finden sind diese in der Unit LoadImage.
In der Unit LayeredWindow sind ein paar Funktionen zusammengefasst, die uns das Leben einfacher machen. Die Funktion SetLayered verpackt den Aufruf von SetLayeredWindow und erwartet ein TForm, ein TBitmap sowie einen globalen Alphawert. Die Funktionen FadeIn/FadeOut bzw. FadeInExp/FadeOutExp sind Hilfsfunktionen, die ein Fenster weich ein- und ausblenden.
Beide Units müssen in TSplashForm eingebunden werden.

SplashScreen

Im SplashForm benötigen wir zwei Variablen. Ein TGPImage, das unser PNG enthält, sowie ein TBitmap, das wir zur Anzeige benutzen.

In TSplashForm.Create müssen wir das PNG laden sowie die Fensterposition festlegen:
procedure TSplashForm.FormCreate(Sender: TObject);
var
  L, T: Integer;
begin
  // PNG laden
  FImage := LoadImageFromRessource('SPLASH', HInstance);

  // Bitmap aus PNG erstellen
  FBitmap := GPImageToBitmap(FImage);

  // Fenstergröße festlegen und Fenster zentrieren
  L := (Screen.Width - FBitmap.Width) div 2;
  T := (Screen.Height - FBitmap.Height) div 2;
  SetBounds(L, T, FBitmap.Width, FBitmap.Height);

  // Bild zuweisen
  SetLayered(Self, FBitmap, 0);
end;
Die Funktionen FadeIn bzw. FadeOut sind nur Wrapper um die oben genannten Funktionen:
procedure TSplashForm.FadeIn;
begin
  LayeredWindow.FadeIn(Self, FBitmap, 0.2);
end;

procedure TSplashForm.FadeOut;
begin
  LayeredWindow.FadeOut(Self, FBitmap, 0.2);
end;
Die Zahl legt fest, um wie viel der Alphawert jeweils erhöht wird, größere Zahlen bedeuten schnelleres Blenden.
Schließlich müssen wir beim Zerstören des Fensters unsere Bilder freigeben:
procedure TSplashForm.FormDestroy(Sender: TObject);
begin
  FBitmap.Free;
  FImage.Free;
end;
Damit sind wir eigentlich fertig. Wenn wir das Programm starten, erscheint der Splashscreen, erschwindet wieder und unser Hauptfenster erscheint.

Statusausgabe

Möglicherweise wollen wir auf dem Splashscreen einen Status ausgeben, der dem User zeigt, was gerade passiert. Da leider keine Controls funktionieren, müssen wir direkt auf das Bild malen. Hier nutzen wir die Fähigkeiten von GDI+. Wir erstellen eine Methode UpdateText in TSplashForm:
procedure TSplashForm.UpdateText(const Text: WideString);
var
  Graphics: TGPGraphics;
  Font: TGPFont;
  Brush: TGPSolidBrush;
  FF: TGPFontFamily;
begin
  // Oberfläche anlegen
  Graphics := TGPGraphics.Create(FImage);
  try
    // Fläche löschen (Weißes Rechteck zeichnen)
    Brush := TGPSolidBrush.Create(MakeColor(255, 255, 255));
    try
      Graphics.FillRectangle(Brush, 50, 80, 192, 20);
    finally
      Brush.Free;
    end;


    // Schrift erzeugen
    FF := TGPFontFamily.Create('MS Reference Sans Serif');
    Font := TGPFont.Create(FF, 16);

    // Schwarzer Pinsel
    Brush := TGPSolidBrush.Create(MakeColor(0, 0, 0));
    try
      // Antialiasing einstellen
      Graphics.SetSmoothingMode(4);

      // Text zeichnen
      Graphics.DrawString(Text, -1, Font, MakePoint(50.0, 80.0), Brush);
    finally
      Brush.Free;
      Font.Free;
      FF.Free;
    end;
  finally
    Graphics.Free;
  end;

  // Bitmap neu erzeugen
  FBitmap.Free;
  FBitmap := GPImageToBitmap(FImage);

  // Fenster aktualisieren
  SetLayered(Self, FBitmap, 255);
end;
Die Koordinaten müssen selbstverständlich dem Splashscreen angepasst werden.

Wir haben im Moment noch nichts, was wir beim Initialisieren unseres Programms erledigen müssen. Ein Splashscreen ist so natürlich sinnlos. Wir behelfen uns einfach mit ein paar Aufrufen von Sleep zwischen den Statusmeldungen:
procedure TForm1.Init;
begin
  SplashForm.UpdateText('Init...');
  Sleep(2000);

  SplashForm.UpdateText('Loading Preferences...');
  Sleep(2000);

  SplashForm.UpdateText('Getting Coffee...');
  Sleep(2000);
end;

Anhang

SplaschScreen Demo (261 KB)
Benötigt werden folgende Bibliotheken:
GDI+ v1.2 (112 KB)
DirectX 9 Headers v1.0 (756 KB)

Kommentare

Kommentar erstellen
#1 Verfasst am: 27.07.2007, 00:00:00 von: Michael
Wo findet man LoadImage.pas und LayeredWindow.pas ?
Hi

wo findet man die beiden Units LoadImage.pas und LayeredWindow.pas ?

Habe das Archiv aktualisiert - Martin Walter
#2 Verfasst am: 28.07.2007, 00:00:00 von: michael
vielen Dank !
vielen Dank
Michael Kaliga
Kommentar erstellen