Einführung in DirectDraw: Teil 6, Clipping
Datum: 3.5.00
Autor: Tim-Oliver Husser
Themen für Teil 6 dieses Tutorials:
Was ist Clipping ?
Und wie gehts ?
Umstruktierter Code
Was ist Clipping ?
Erinnern Sie sich noch an diese Zeilen aus dem letzten Tutorial ?
SetRect(&rectbitmap, 220, 165, 420, 315);
...
lpddsBack->Blt(&rectbitmap, lpddsBitmap, NULL, DDBLT_WAIT, NULL);
Haben Sie schon einmal ausprobiert, was passiert, wenn Sie das Bild so auf
den Back Buffer blitten, daß es nicht ganz draufpaßt ? Etwa mit
dieser SetRect-Anweisung ?
SetRect(&rectbitmap, -50, 0, 150, 150);
Nein ? Versuchen Sie es mal ! Und, was passiert ? Ja genau: Nichts !!
Jetzt kommt natürlich die Frage, warum nichts kommt. Ganz einfach: DirectDraw
erwartet, daß alles, was man blittet, auch vollständig auf die Ziel-Surface
draufpaßt!
Eine Möglichkeit, daß Problem zu lösen wäre jetzt, das
zu blittende Bitmap auf der linken Seite um 50 Pixel abzuschneiden. Steht das
Bitmap still ist das ja kein Problem, aber wenn es animiert (bewegt) werden
soll, ist es ziemlich umständlich, bei jedem Durchlauf einen anderen Teil
wegzuschnippeln.
Aber es geht auch einfacher! Für diesen Fall gibt es in DirectDraw die
sogenannten Clipper. Diese sind eigentlich dazu gedacht, im Fenstermodus den
Bereich festzulegen, auf den DirectDraw zeichnen darf. Wenn man einen Clipper
aber im Vollbildmodus über den ganzen Bildschirm legt, nimmt er einem das
lästige "abschneiden" ab.
Und wie gehts ?
Einen Clipper zu erstellen ist eigentlich ganz einfach. Zuerst brauchen wir
eine Variable, die das Objekt aufnehmen kann:
LPDIRECTDRAWCLIPPER lpddClipper;
Jetzt gehts los! Der nun folgende Code kommt wie bisher in allen Tutorials
vor die Nachrichtenschleife, also vor die Zeile:
running=true;
Zuerst erstellen wir das Clipper-Objekt. Dies geschieht mit der CreateClipper-Methode
des DirectDraw-Objektes:
ddrval = lpDirectDraw->CreateClipper(0, &lpddClipper, NULL);
if (ddrval != DD_OK)
{
lpddsBack->Release();
lpddsBack = NULL;
lpddsPrimary->Release();
lpddsPrimary = NULL;
lpDirectDraw->Release();
lpDirectDraw = NULL;
return false;
}
Jetzt kreieren wir eine neue Struktur, erstellen gleich eine Variable dieses
Typs mit dem Namen cliplist und füllen diese mit den Information über
den Clipper, also in erster Linie der Größe:
struct
{
RGNDATAHEADER rdh;
RECT rgndata[1];
} cliplist;
cliplist.rdh.dwSize = sizeof (RGNDATAHEADER);
cliplist.rdh.iType = RDH_RECTANGLES;
cliplist.rdh.nCount = 1;
cliplist.rdh.nRgnSize = 0;
cliplist.rdh.rcBound.left = 0;
cliplist.rdh.rcBound.top = 0;
cliplist.rdh.rcBound.right = 640;
cliplist.rdh.rcBound.bottom = 480;
cliplist.rgndata[0].left = 0;
cliplist.rgndata[0].top = 0;
cliplist.rgndata[0].right = 640;
cliplist.rgndata[0].bottom = 480;
Die einzelnen Felder des Datentyps sollten selbsterklärend sein. Bei anderen
Auflösung als 640x480 müssen natürlich die Werte angepaßt
werden.
Jetzt füttern wir den Clipper mit den eingestellten Werten:
ddrval = lpddClipper->SetClipList ((LPRGNDATA) &cliplist, 0);
if (ddrval != DD_OK)
{
lpddClipper->Release();
lpddClipper = NULL;
lpddsBack->Release();
lpddsBack = NULL;
lpddsPrimary->Release();
lpddsPrimary = NULL;
lpDirectDraw->Release();
lpDirectDraw = NULL;
return false;
}
Das einzige, was wir jetzt noch machen müssen, ist, den Clipper an der
Back Buffer anzuhängen. Dies geschieht mit der SetClipper-Methode:
ddrval = lpddsBack->SetClipper(lpddClipper);
if (ddrval != DD_OK)
{
lpddClipper->Release();
lpddClipper = NULL;
lpddsBack->Release();
lpddsBack = NULL;
lpddsPrimary->Release();
lpddsPrimary = NULL;
lpDirectDraw->Release();
lpDirectDraw = NULL;
return false;
}
Und natürlich muß am Programmende wieder aufgeräumt werden
(vor dem Back Buffer!):
if (lpddClipper != NULL)
{
lpddClipper->Release();
lpddClipper = NULL;
}
Umstruktierter Code
Bevor es im nächsten Teil weitergeht, habe ich den Code etwas umstrukturiert.
Ich habe den ganzen DirectDraw-Code in vier Funktionen gepackt. Hier die Funktionsnamen
mit den Beschreibungen:
* ddInit (HWND hwnd)
Hier ist der ganze Initialisierungscode für DirectDraw.
* ddLastInits ()
Hier ist der ganze Code, der nicht in jedem Programm zu finden ist, also z.B.
das Laden von Grafiken.
* ddDraw ()
Hier ist der Code, mit dem der Back Buffer gefüllt wird. Außerdem
der Code zum Flippen.
* ddRelease ()
Der Code zum Aufräumen.
Besonders praktisch ist hier die ddRelease-Funktion, da wir nach jedem fehlgeschlagenen
DirectDraw-Aufruf nur noch diese Funktion aufrufen müssen.
Schauen Sie sich die Funktionen einfach mal selber an!
Das noch nicht umstrukturierte Projekt können Sie hier
herunterladen.
Das umstrukturierte Projekt können Sie hier
herunterladen.
Teil 7