SDL Video Tutorial

Author:Volker Böhme
Nick:AnubisTheKing
email:anubistheking@linuxfreax.net
Lizens:GNU Free Documentation License

SDL TUTORIAL über die Initialisierung von SDL und der Zusammenarbeit von SDL mit der Videoausgabe

Hi! Danke das ihr dieses Tutorial ausgesucht habt um SDL zu lernen. Ich muss allerdings schonmal im voraus sagen, das dies mein erstes Tutorial ist. Wenn ich also irgendetwas nicht gut genug erkläre, dann sagt doch einfach Bescheid unter anubistheking@linuxfreax.net Vorschläge, Anregungen sowie positive und negative Kritik sind immer willkommen.

SDL steht für Simple DirectMedia Layer und ist eine Kross-Plattform Bibliothek, mit der man auf die VGA Karte und Soundkarte zugreifen kann. Es werden die Betriebssysteme Linux, Win32, BeOS, MacOS und MacOS X unterstützt. Unter anderen Betriebssystemen kann SDL zwar auch funktionieren wird aber nicht offiziell von den Entwicklern von SDL unterstützt. Inoffiziell sollen vielleicht auch noch die Betriebssysteme Solaris, IRIX, FreeBSD, QNX, OSF/True64 funktionieren. Im Augenblick, wo ich dieses Tutorial schreibe ist die Version 1.2.4 aktuell. Falls ihr sie euch noch nicht runtergeladen habt, besorgt sie euch unter www.libsdl.org. Da ich selber nur Linux benutzte kann ich nicht garantieren, dass alles was ich hier sage auch unter den anderen Betriebssystemen einwandfrei funktioniert. Ich habe die Sachen nur unter Linux getestet. Aber wie gesagt, ich bin gerne breit euch zu helfen.

So da war aber nun auch schon genug allgemeines über SDL. Lasst uns einfach mal anfangen. Als erstes werde ich euch zeigen, wie man SDL initialisiert.

SDL besteht aus verschiedenen Subsystemen,

SDL_INIT_VIDEO
SDL_INIT_AUDIO
SDL_INIT_TIMER
SDL_INIT_CDROM
SDL_INIT_JOYSTIK
SDL_INIT_EVERYTHING
SDL_INIT_NOPARACHUTE
SDL_INIT_EVENTTHREAD

die man einzeln je nachdem welche man benötigt laden und entladen kann.Die Funktion SDL_Init() muss immer ausgeführt werden. Mit ihr kann man beliebig viele Subsysteme laden. Einzelne Subsysteme können jedoch auch immer noch nachträglich mit der Funktion SDL_InitSubSystem() initialisiert werden. Beenden tut man das ganz das entweder mit SDL_Quit() was alle Subsysteme beendet oder mit SDL_QuitSubSystem() um einzelne Systeme zu beenden. Für jedes SDL Programm brauchen wir ein paar bestimmte Header- Dateien. Wir benötigen:

#include <SDL/SDL.h>  /*Alle SDL Aplikationen brauchen das*/
#include <iostream>  /* Standart C++ Header-Datei für Ein- und Ausgabe */

Dann initialisieren wir SDL mit:

int main(int argc, char **argv) 
{ 
  std::cout << "SDL wird initialisiert" << std::endl; 
 
   /* SDL wird mit den Subsystemen Video und Audio initialisiert und bei einem Fehler wird eine
     * Fehlermeldung ausgegeben Bei einem Fehler während der Initialisierung wird der Wert -1
     * zurückgegeben, bei Erfolg der Wert 0 
     */
 
if (SDL_Init (SDL_INIT_VIDEO|SDL_INIT_AUDIO) < 0) 
{ 
  std::cerr << "Kann SDL nicht initialisieren!!!!!" << SDL_GetError() << std::endl;
  SDL_Quit();
  return -1; 
}
std::cout << "SDL ist initialisiert worden" << std::endl;

So nun haben wir SDL initialisiert.
Ein Subsystem würde man nun mit SDL_InitSubSystem nachträglich initialisieren.

/*Initialisiere SDL_INIT_TIMER nachträglich*/ 
SDL_InitSubSystem(SDL_INIT_TIMER);

Ihr habt nun gesehen, wie man SDL in den verschieden Varianten initialisiert. War doch garnicht so schwer oder? Naja, bevor ihr euch nun eine Pause gönnt und euch ne Pizza bestellt, müsst ihr noch einiges lernen, zum Beispiel wie man SDL wieder ordentlich beendet. Wir beenden zuerst das eben initialisierte Subsystem SDL_INIT_TIMER und dann alle restlichen. Ich beende hier das eben initialisierte Subsystem einzelnt weil ich euch zeigen will wie man ein Subsystem einzelnt beenden kann ohne das ganze Programm schlieîen zu müssen denn wie ihr euch bestimmt noch erinnert würde auch SDL_QUIT das nachträglich initialisierte Subsystem mit schlieîen.

/* hiermit beende ich das Subsystem Timer*/ 
std::cout << "Subsystem Timer beenden" << std::endl; 
SDL_QuitSubSystem(SDL_INIT_TIMER); 
 
/*hiermit beende ich alle noch laufende Subsysteme von SDL*/ 
std::cout << "SDL beenden" << std::endl; 
SDL_Quit(); 
std::cout << "SDL beendet!" << std::endl;

Ihr habt nun den Anfang hinter auch. Weiter gehts mit der Initialisierung eines Fensters und dem Auslesen von Informationen über die Grafikkarte. Starten wir doch einfach mit der Initialisierung eines Fensters:

SDL_Surface *screen;
 
/*Initialisierung der SDL Bibliothek */ 
if (SDL_Init (SDL_INIT_VIDEO) < 0) 
{ 
  std::cerr << "Kann SDL nicht initialisieren" << SDL_GetError() << std::endl; 
  SDL_Quit();
  return -1; 
}
std::cout << "SDL ist initialisiert worden"; 
 
/*wenn das Programm beendet wird soll aufjedenfall aufgeräumt werdent*/ 
atexit (SDL_Quit());

In dem man SDL_Quit() in dem Zusammenhang mit atexit benutzt wird sicher gestellt, dass SDL_Quit() auf jeden Fall bei beenden des Programms ausgeführt wird um den Speicher wieder frei zugeben. “atexit” wir benutzt um sicherzustellen, das eine bestimmte Funktion oder bestimmter Code immer ausgeführt wir, wenn das Programm beendet wird. Die Funktion oder der Code wird automatisch bei beenden des Programms aufgeführt.

Das nächste was ich euch beibringen will ist die Initialisierung eines Displays mit einer Auflösung von 640×480, 8Bit Farbtiefe und die Benutzung eines Software Surface. Das benutzten eines Software Surface bedeutet, das nicht der Speicher der Videokarte benutzt wird. Dies ist die sicherste Methode eines Surfaces, da es nicht Hardware beschleunigt ist und deswegen keine bestimmten Treiber benötigt. Wenn man Hardwarebeschleunigung will sollte man das Surface SDL_HWSURFACE benutzten. Vor der Benutzung dieses Surfaces sollte man aber prüfen, ob es von der Grafikkarte unterstützt wird. Wie man das macht erkläre ich euch etwas weiter unten im Tutorial.

screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE); 
if (screen == NULL) 
{ 
  std::cerr << "Konnte Video-Mode 640x480 nicht setzten" << SDL_GetError() << std::endl; 
  SDL_Quit();
  return -1; 
}
std::cout << "Video-Mode wurde gesetzt" << std::endl;

Eeinen kleinen Tipp noch von mir, am besten benutzt ihr für die Auflösung und die Farbtiefe des Fensters Variablen, die ihr vorher natürlich setzten müsst. So könnt ihr die Auflösung besser ändern und auch wenn ein Fehler auftritt genauere Angaben machen, indem ihr die Variablen in die Error-Ausgabe einbindet.

So nun habt ihr ein Fenster initialisiert, aber nur eine Auflösung von 640×480 und eine Farbtiefe von 8Bit benutzt. Manchem mag das ja reichen (also ich kenne keinen, der irgendwas mit 8Bit Farbtiefe macht naja aber das ist ja nicht mein Problem :-) ) aber man kann auch mit dem Befehl SDL_GetVideoInfo() die best mögliche Farbtiefe herausbekommen und mit dem Befehl SDL_ListModes() alle unterstützten Auflösungen bei einer bestimmten Farbtiefe. Ein Beispiel dazu bekommt ihr natürlich auch sofort. Vorher zeige ich euch allerdings noch wie ihr noch andere Informationen über die Grafikkarte bekommen könnt.

Um Information über die Grafikkarte zu bekommen schreiben wir:

const SDL_VideoInfo *VideoInfo; 
VideoInfo = SDL_GetVideoInfo();

Dann brauchen wir nur noch die Variablen, in die wir die abzufragenden Werte abspeichern.

Uint32 VideoMem;  /*Variable für die Angabe der Gröîe des VideoSpeichers*/
Uint32 hw_available;  /*Variable um zu prüfen ob die Grafikkarte das SDL_HWSURFACE unterstützt*/
Uint8 bpp;  /*für die Angabe des besten Pixelformates*/

So das sind erstmal alle Variablen die ich für wichtig halte. Nun zeige ich euch wie ihr die richtigen Werte auslest.

VideoMem = VideoInfo->video_mem; 
std::cout << "Die Grafikkarte besitzt einen Videospeicher von" << VideoMem << "KiloBytes" << std::endl;
 
hw_available = VideoInfo->hw_available; 
if (!hw_available) 
{ 
  std::cerr << "SDL_HWSURFACE wird nicht unterstützt" << std::endl; 
  SDL_Quit();
  return -1;
} 
 
bpp = VideoInfo->vfmt->BitsPerPixel; 
std::cout << "Die beste Farbtiefe ist" << bpp << "Bit" << std::endl;

WICHTIG! Wenn ihr SDL_GetVideoInfo vor SDL_SetVideoMode ausführt bekommt ihr Informationen den besten verfügbaren Modus der Grafikkarte. Wenn ihr es danach ausführt bekommt ihr nur Informationen über das aktuelle Surface das benutzt wird. So nun haben wir die wichtigsten Sachen über die Grafikkarte ausgelesen. Lasst mal gucken was wir damit machen können. “hw_available” sagt uns einfach ob wir ein SDL_HWSURFACE benutzten können oder nicht. Da wir auch die beste Farbtiefe die wir benutzten können kennen, haben wir die Möglichkeit auszurechnen wie viel Speicher der Grafikkarte für Texturen übrigbleiben würde, wenn wir eine bestimmte Auflösung benutzten. Wenn wir zum Beispiel eine Grafikkarte mit einem Speicher von 32MB hätten und eine Auflösung von 1024×768 bei 16Bit Farbtiefe benutzten wollen rechnen wir:

1024 x 768=786432
8Bit=1Byte also sind 16Bit=2Byte
786432 x 2Byte =1572864 Byte

Wir benutzten also schonmal 1.5 MB Speicher von der Grafikkarte für die Auflösung und Farbtiefe. Uns bleiben also noch 30,5 MB für die Texturen.

Allerdings ergibt sich ein Problem wenn wir alles so machen wie oben. Die Grafikkarte hat zwar genug Speicher um die Auflösung 1024×768 zu vertragen, aber das heiît nicht, das sie diese Auflösung auch unterstützt. Wir müssen also extra überprüfen ob diese Auflösung unterstützt wird und dann auch noch über prüfen ob die gewählte Farbtiefe bei unserer gewählten Auflösung unterstützt wird. Wenn ihr also wissen wollt, welche Auflösungen bei einer bestimmten Farbtiefe unterstützt werden, dann könnt ihr das mit dem schon weiter oben erwähnten SDL_ListModes machen. Hier ein Beispiel wie man SDL_ListModes benutzt:

SDL_Rect **modes; /*erstellen der Variablen Modes in der wir die Angaben von SDL_ListModes speichern wollen*/
int i; 
 
modes=SDL_ListModes(Farbtiefe, andere Flags mit denen ihr das SDL_Surface erstellen wollt);

Also Ihr habt ja gerade die Rückgabe von SDL_ListModes in der Variablen modes gespeichert. Nun müssen wir den Rückgabewert prüfen. Es wird 0 zurückgegeben wenn keine Auflösung zu der Farbtiefe und den angegebenen Flags vorhanden ist. -1 wird zurück gegeben, wenn alle Auflösungen benutztbar sind sind. SDL_ListModes gibt einen Pointer auf ein Array zurück, in dem man die Auflösungen auslesen kann. Sie sind von der höchsten Auflösung zur kleinsten hin sortiert. Das bedeutet unter modes[0] bekommt ihr die höchste Auflösung und unter modes[n] die kleinste. So dann last uns mal mit der _berprüfung anfangen:

if(modes == (SDL_Rect **)0) 
{
  std::cerr << "Es sind keine Auflösungen verfügbar!" << std::endl;
  SDL_Quit();
  return -1;
}
 
if(modes == (SDL_Rect **)-1) 
{
  std::cout << "Alle Auflösungen sind verfügbar!" << std::endl; 
}
else 
{ 
  for(i=0; modes[i]; ++i) std::cout << "Die Auflösung " << modes[i]->w << "x" << modes[i]->h << " ist verfügbar" << std::endl; 
}

So nun wisst ihr, wie ihr die Auflösungen herausbekommt die man bei einer bestimmten Farbtiefe benutzten kann. Aber muss man den ganzen Code auch schreiben, wenn man nur eine bestimmte Auflösung bei einer bestimmten Farbtiefe benutzten will und vorher wissen will ob sie funktioniert? Da habe ich noch eine andere Möglichkeit für euch. Mit SDL_VideoModeOK könnt ihr genau das überprüfen. Ihr bekommt 0 zurück, wenn die Auflösung nicht unterstützt wird, weder mit eurer angegeben Farbtiefe noch mit einer anderen. Wenn ihr die Auflösung benutzten könnt, bekommt ihr entweder eure angegebene zurück, wenn die bei dieser Auflösung unterstützt wird oder die nächstgelegene Farbtiefe.

Uint32 bpp; 
 
bpp=SDL_VideoModeOK(640, 480, 16, SDL_HWSURFACE); 
 
if(!bpp)
{
  std::cerr << "Diese Auflösung wird nicht unterstützt" << std::endl;
  SDL_Quit();
  return -1
}
std::cout << "Die Gewählte Auflösung funktioniert mit der Farbtiefe" << bpp << std::endl;

Jetzt könntet ihr die Auflösung direkt mit der richtigen Farbtiefe setzten. Einfach die Variable bpp bei dem Setzten von SDL_SetVideoMode benutzten.

So nun habt ihr denn Hauptteil, von dem was man mit SDL in Zusammenhang mit der Grafik machen kanngelernt. Wenn ich Hauptteil sage, heiît das nicht, das SDL nicht mehr viel mehr kann, sonder das ihr nun den Teil könnt, den ich für am wichtigsten halte. Ihr könnt SDL initialisieren, Informationen über die Grafikkarte auslesen und ein Fenster mit einer bestimmten Auflösung und Farbtiefe erstellen. So nun freut auch aber nicht zu früh, ein paar Sachen muss ich euch noch zeigen. Ich werde jetzt kurz nochmal ein paar Kleinigkeiten erklären, und mich dann zu einem sehr schwierigen Teil begeben nämlich SDL mit OpenGL. Ja das ist nicht gerade einfach. Weiter oben habe ich euch ja gezeigt, wie ihr ein Fenster mit einer bestimmten Auflösung erstellt. So nun werde ich euch zeigen wie ihr noch ein paar kleiner Details umändern oder erfahren könnt. Was haltet ihr den davon dem Fenster einen bestimmten Titel zugeben? Ihr wisst schon, wenn ihr z.B. ein Programm öffnet, dann steht doch als Fenstertitel immer oben der Name des Programms oder wenn ihr ein Textverarbeitungsprogramm benutzt sogar noch der Name der geöffneten Datei. Ihr wollt doch bestimmt auch den Fenstertitel setzten können oder nicht? Es soll doch nicht ewig Fentster1 oder so heiîen. Mit SDL_WM_GetCaption könnt ihr den Fenstertitel herausbekommen und mit SDL_WM_SetCaption könnt ihr den Titel setzten. Ach ja, was ich beinahe vergessen hätte ihr könnt damit auch dem FensterIcon einen Namen geben oder in auslesen. Also wenn ihr wissen wollt, wie der Titel des Fensters gerade ist, dann schreibt ihr:

char *FensterTitel; 
char *IconTitel; 
SDL_WM_GetCaption(&FensterTitel, &IconTitel); 
std::cout << "Der Fenstertitel ist: " << FensterTitel << std::endl;
std::cout << "Der IconTitel ist: " << IconTitel << std::endl;

Wenn ihr einen FensterTitel setzten wollt, macht ihr es genauso.

const char *fenster_titel = "Fenstertitel"; 
const char *icon_titel = "Icontitel"; 
SDL_WM_SetCaption(fenster_titel, icon_titel);

So nun können wir den Fenstertitel und Icontitel setzten und auslesen, aber natürlich wollen wir auch unser selbst entworfenes Icon für unser Programm benutzten. Dieses können wir mit SDL_WM_SetIcon setzten.

SDL_WM_SetIcon(SDL_LoadBMP("icon.bmp"), NULL); 
/*Ok das ist ein Hack.  Mach SDL_LoadBMP doch lieber einzeln und überprüf, ob das
Bitmap geladen wurde. */

So haben wir das Icon das in der Datei icon.bmp steckt gesetzt. Ok dann erkläre ich euch noch kurz was die Funktion SDL_LoadBMP tut. Sie lädt ein Surface aus einer Windows BMP-Datei.Oder auf verständlichem Deutsch, sie lädt das Bild aus der Datei und benutzt es als Surface für die Fläche des Icons. Nun habe ich nur noch eine kleinere Sache die ich euch zeigen will bevor wir uns an das Thema SDL mit OpenGL ranwagen. Ich will euch zeigen wie man herausfinden kann, welche Subsysteme von SDL alle geladen wurden. Hierzu benutzten wir SDL_WasInit.

Es gibt verschiedene Arten mit SDL_WasInit zu überprüfen, welche Subsysteme initialisiert worden sind. Einmal bekommt man zwar alle Daten über alle initialisierten Subsysteme, kann sie aber immer nur einzelnt überprüfen welches Systeme gestartet wurden. Oder man kann ein bestimmtes Subsystem überprüfen oder man kann mehrere gleichzeitig testen.

/*so bekommt man zwar alle Daten über die Subsystem muss aber alle nacheinander
testen*/
Uint32 subsystem_init;
subsystem_init=SDL_WasInit(SDL_INIT_EVERYTHING);
if(subsystem_init&SDL_INIT_VIDEO) 
{
  std::cout << "SDL_INIT_VIDEO wurde gestartet" << std::endl;
}
else 
{
  std::cout << "SDL_INIT_VIDEO wurde nicht gestartet" << std::endl;
}
 
/*so überprüft man ein bestimmtes Subsystem*/
if(SDL_WasInit(SDL_INIT_VIDEO)!=0) 
{
  std::cout << "SDL_INIT_VIDEO wurde gestartet" << std::endl;
}
else
}
  std::cout << "SDL_INIT_VIDEO wurde nicht gestartet" << std::endl;
}
 
/*so überprüft man mehrere gleichzeitig*/ 
Uint32 subsystem_mask;
subsystem_mask=SDL_INIT_VIDEO|SDL_INIT_AUDIO ;
if(SDL_WasInit(subsystem_mask)==subsystem_mask)
{ 
  std::cout << "SDL_INIT_VIDEO und SDL_INIT_AUDIO wurden gestartet" << std::endl; 
}
else 
{ 
  std::cout << "SDL_INIT_VIDEO und SDL_INIT_AUDIO wurden nicht gestartet" << std::endl;
}

So nun kommen wir zu dem letzten Teil dieses Tutorials. Ich hoffe ich erkläre alles richtig :-). So schwer ist es nämlich nicht.

Also um OpenGL benutzten zu können mit SDL ist es einfach nur notwendig SDL_SetVideoMode als Flag SDL_OPENGL mit zugeben.
Ihr schreibt also einfach:

int width=640;
int height=480;
int bpp=16;
flags=SDL_OPENGL|SDL_HWSURFACE;
SDL_SetVideoMode(width, height, bpp, flags) ;

So wisst ihr warum ich dachte, da das ganze so schwer wäre? Ich hatte ein paar Code- Beispiele und die musste ich erstmal auseinander pflücken und das war nicht gerade einfach. Aber im Endeffekt ist es wirklich nicht schwer. Das oben war eigentlich schon alles. Ihr könnt aber noch ein paar Sachen von OpenGL einstellen mit der Hilfe von SDL. Aber das müsst ihr vor dem Aufruf von SDL_SetVideoMode machen. Zum einstellen von OpenGL spezifischen Parametern benutzt ihr den Aufruf SDL_GL_SetAttribute. Als Parameter gibt es:

SDL_GL_RED_SIZE 
SDL_GL_GREEN_SIZE
SDL_GL_BLUE_SIZE
SDL_GL_DEPTH_SIZE
SDL_GL_DOUBLEBUFFER

So nun bekommt ihr noch nen Beispiel. Die Zahl hinter dem Parameter bedeutet wieviel Bit man für die entsprechende Farbe benutzten will oder wieviel Bit man für den Tiefen-Buffer benutzten will. Die Zahl hinter SDL_GL_DOUBLEBUFFER stellt ihn entweder ein (bei 1) oder aus (bei 2). Wenn ihr den DOUBLEBUFFER benutzten wollt, dann müsst ihr SDL_SetVideoMode auch noch das Flag SDL_DOUBLEBUFmitgeben. Die Werte die ich den Farben und dem Tiefen-Buffer zuweise sind Beispielwerte. \\

SDL_GL_SetAtrribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAtrribute(SDL_GL_GREEN_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 

So nun nur noch was. Wenn ihr ein doppelt gepuffertes Fenster benutzt, dann müsst ihr SDL_GL_SwapBuffers() benutzten um den Buffer in das Fenster zu schreiben. Was jetzt wollt ihr wissen, wann ihr das machen müsst? Oh man, dann muss ich mir jetzt nochmal kurz die OpenGL-Befehle angucken um euch dafür was Code anbieten zu können. Naja, wartet mal nen Moment. Also so ganz fit bin ich noch nicht in OpenGL (Naja, das Thema nehme ich mir nach SDL vor :-) ) aber wenn ich es richtig verstanden habe müsst ihr das ganze immer dann ausführen, wenn ihr was neues auf das Fenster gezeichnet haben wollt. Mit anderen Worten, ihr habt ein Dreieck gezeichnet, geht dann drei Einheiten nach rechts und zeichnet dort nen Viereck. Dann müsst ihr hiernach SDL_GL_SwapBuffers ausführen. Dann habe ich nur noch eine Kleinigkeit zusagen. Ich habe oben in den Beispielen Pointer benutzt um Text in Variablen zu speichern. Ihr könnt auch den Datentyp String benutzten um das zu tun. Ihr solltet das eigentlich auch machen. Allerdings müsst ihr dann bei vielen Funktionsaufrufen von SDL die String Variablen mit variable.c_str() in einen Pointer vom Typ char umändern damit SDL damit zurecht kommt. So nun seid ihr aber wirklich mit dem ersten Teil fertig. Hier findet ihr ein Beispiel-Implementierung von Dingen, die ihr eben gelernt habt. den Viel Spaß und Erfolg bei euren Versuchen mit SDL. Bis dann AnubisTheKing.

 
linuxfreax/sdl-video-tutorial.txt · Last modified: 2008/01/13 18:17 by anubistheking
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki