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 ändern. 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