www.codeworx.org/directx_tuts/Einführung in DirectDraw: Teil 4, Die Surfaces

Einführung in DirectDraw: Teil 4, Die Surfaces
Datum: 27.1.00
Autor: Tim-Oliver Husser

Das (un)mögliche Programmende
Primary Surface und Back Buffer
Eine Primary Surface erstellen
Den Back Buffer erstellen

Das (un)mögliche Programmende
Nachdem Sie in den Teilen 1 bis 3 dieses Tutorials gelernt haben, wie man ein DirectDraw initialisiert, werden Sie jetzt etwas über die Art kennenlernen, wie DirectDraw mit Grafiken umgeht.
Zuerst ergibt sich mal wieder ein kleines Problem. Sie haben bestimmt schon einmal ein DirectDraw-Programm gesehen, ein 2D-Spiel wie Asteroids im Vollbild-Modus zum Beispiel. Sehen Sie da in der oberen rechten Ecke das Kreuz zum Beenden des Programms? Nein? Gut, ich auch nicht. Also müssen wir eine andere Abbruchbedingung schaffen. Bleiben wir bei den Standards und lassen das Programm beim Drücken von ESC beenden.
Zuerst brauchen wir noch eine Variable, die beinhaltet, ob das Programm noch läuft:

bool running;

Jetzt müssen wir die While-Schleife in WinMain &aumlndern. Ersetzen Sie sie einfach durch folgenden Code:

running=true;
while(running)
{
   if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
   {
      if (msg.message == WM_QUIT) break;
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }
}

Suchen Sie im jetzt Quellcode die WndProc-Prozedur und ergänzen Sie diese folgendermaßen:

   ...
   switch (message)
   {
      case WM_KEYDOWN:
      {
      switch (wParam)
      {
         case VK_ESCAPE:
         running=false;
         return 0;
      } break;
   }
   case WM_DESTROY:
   ...

Wenn Sie das Programm jetzt starten, können Sie es mit ESC beenden.

Primary Surface und Back Buffer
Es gibt zwei Arten Grafiken darzusrellen: eine direkte und eine indirekte.
Befassen wir uns zuerst mit der indirekten. Sie ist am einfachsten. Wir kopieren einfach ein Bild, daß irgendwo gespeichert ist auf den Bildschirm. Schauen Sie sich dazu das Diagramm unten an.

Das Hauptproblem dieser Methode ist aber offensichtlich: Wenn viele Grafiken dargestellt werden sollen, werden diese auch schön nacheinander auf den Bildschirm kopiert. Hierbei kommt es leider fast immer zu einem unschönen Flickern.
Besser wäre es, wenn das ganze Bild auf einmal auf den Bildschirm kopiert werden würde. Und damit wären wir bei der indirekten Methode.
Hier kopiert man alle Grafiken erst in einen Speicher mit den gleichen Eigenschaften wie der Bildschirm. Dieser Speicher wird "Back Buffer" genannt. Der Vorgang wird "blitten" genannt (blit: kurz für bit block transfer). Dann wird das fertige Bild auf den Bildschirm, die sogenannte "Primary Surface", kopiert. Dieser Vorgang wird "flippen" genannt. Der Begriff "flippen" kommt vom englischen to flip (schnippen, knipsen) und spielt damit auf ein Daumenkino, bei dem mit einer kleinen Daumenbewegung das nächste Bild kommt und der Eindruck eines Filmes entsteht.
Das untenstehende Diagramm versucht, Ihnen die indirekte Methode noch einmal zu verdeutlichen.

Unsere erste Aufgabe ist es also, ein Primary Surface und einen Back Buffer zu erstellen.

Eine Primary Surface erstellen
Zuerst brauchen wir zwei weitere Variablen, die die Surface selbst und die Information zu Erstellung derselben beinhalten.
Fügen Sie also folgende Variablendeklarationen in Ihr Projekt ein:

LPDIRECTDRAWSURFACE7 lpddsPrimary;
DDSURFACEDESC2 ddsd;

Jetzt füllen wir die Variable ddsd, welche Informationen über die Surface enthält, die erstellt werden soll.
Wie bei jeder DirectX-Struktur, hat auch DDSURFACEDESC ein Element dwSize, welches die Größe der Struktur enthält. Also setzen wir dieses Element als erstes. Dann müssen wir mit dwFlags angeben, welche Elemente korrekte Informationen enthalten. Wir brauchen nur ein Element mit Daten zu füllen und zwar ddsCaps.dwCaps. Es enthält die Art der Surface. Dann erstellen wir die Surface mit den eingestellten Eigenschaften. Der Code muß hinter SetDisplayMode):

ZeroMemory(&ddsd, sizeof(ddsd)); ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
ddrval = lpDirectDraw->CreateSurface (&ddsd, &lpddsPrimary, NULL);
if (ddrval != DD_OK)
{
   lpDirectDraw->Release();
   lpDirectDraw = NULL;
   return (0);
}

Die Parameter von CreateSurface dürften klar sein: zuerst die gewünschten Eigenschaften (ddsd), dann das DirectDraw-Objekt (lpddsPrimary) und zuletzt noch ein Parameter, der nich verwendet wird, also auf NULL gesetzt werden muß.
Zum Schluß muß man das, was man in den Speicher geschaufelt hat, natürlich auch wieder aufräumen, und zwar am besten in der umgekehrten Reihenfolge der Erstellungen. Also kommt die Putzaktion für die Primary Surface vor die des Direct-Draw-Objektes:

if (lpddsPrimary != NULL)
{
   lpddsPrimary->Release();
   lpddsPrimary = NULL;
}

Den Back Buffer erstellen
Nun brauchen wir noch den Back Buffer.
Zuerst brauchen wir wieder zwei neue Variablen:

LPDIRECTDRAWSURFACE7 lpddsBack;
DDSCAPS2 ddscaps;

Dazu müssen wir die Erstellung der Primary Surface etwas abändern. Wir füllen mehr Elemente der Struktur, also muß dwFlags geändert werden:

ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;

Wenn wir einen Back Buffer haben, sind auch die Eigenschaften der Primary Surface etwas anders. Dazu verändern wir ddsCaps.dwCaps:

ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | 
                      DDSCAPS_FLIP | 
                      DDSCAPS_COMPLEX;

DDSCAPS_FLIP und DDSCAPS_COMPLEX geben an, daß wir einen Back Buffer haben wollen, den man flippen kann.
Schließlich muß noch die Eigenschaft gesetzt werden, die wir in dwFlags schon angekündigt haben. Vor die Erstellung der Surface muß noch:

ddsd.dwBackBufferCount = 1;

Damit geben wir an, daß wir genau einen Back Buffer haben wollen.
Nach der Erstellung der Primary Surface holen wir uns jetzt von dieser den Back Buffer ab.
Dazu geben wir zunächst an, was wir haben wollen und führen dann GetAttachedSurface aus:

ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
lpddsPrimary->GetAttachedSurface(&ddscaps, &lpddsBack);

Auch hier müssen wir wieder aufäumen:

if (lpddsBack != NULL)
{
   lpddsBack->Release();
   lpddsBack = NULL;
}

Das Projekt können Sie hier herunterladen.

Teil 5