Multimedia-Entwicklung mit SDL
Bilder anzeigen
SDL kann ohne zusätzliche Bibliotheken nur Bitmapbilder anzeigen. Dies reicht im Normalfall nicht aus. Deshalb weise ich bereits jetzt auf die Standardbibliothek SDL_image hin, die wir auch gleich verwenden werden:
Anmerkung: Bild tux.jpg kann HIER heruntergeladen werden
#include "sdl_image.h"
[...]
SDL_Surface *image;
[...]
image = IMG_Load("tux.jpg");
if (image == NULL)
{
fprintf(stderr, "Das Bild konnte nicht geladen werden:%s\n",
SDL_GetError());
exit(-1);
}
// kopiere das Bild-Surface auf das display-surface
SDL_BlitSurface(image, NULL, display, NULL);
// den veraenderten Bereich des display-surface auffrischen
SDL_Flip(display);
SDL_Delay(3000);
// Das Bitmap-Surface löschen
SDL_FreeSurface(image);
[...]
Die Funktion IMG_Load() wird von sdl_image bereit gestellt. Dies erfordert, dass man SDL_image.h inkludiert und dem Linker ein "-lSDL_image" als Flag mitgibt, wie bei der libsdl oben bereits beschrieben.
Wir deklarieren ein neues Surface mit dem Namen "image". Mittels IMG_Load() weisen wir diesem neuen Surface dann das gewünschte Bild zu. Diese Surface hat nun die Größe der geladenen Grafik.
Da wir meist mehr über die Surfaceformatierung erfahren wollen, wie zum Beispiel die Größe und Farbtiefe, hier die interne Struktur des Surfaces:
typedef struct SDL_Surface {
// allgemeine Surface-Informationen
Uint32 flags; // Read-only
SDL_PixelFormat *format; // Read-only
int w, h; // Read-only
Uint16 pitch; // Read-only
void *pixels; // Read-write
int offset; // Private
// hardwarespezifisches Surface-Informationen
struct private_hwdata *hwdata;
// Clipping-Informationen
int clip_minx; // Read-only
int clip_maxx; // Read-only
int clip_miny; // Read-only
int clip_maxy; // Read-only
// surface-interne Kopierinformationen
struct SDL_BlitMap *map; // Private
// Liste der surfaces
struct map_list *mapped; // Private
// Reference-Counter
int refcount; // Read-mostly
} SDL_Surface;
In der Struktur des Surfaces wird über die Integer-Werte w und h, die Breite und die Höhe gespeichert. Zu Kontrollzwecken wird man daher oft z.B. "image->w" und "image->h" mit ausgeben, um über die zu ändernde Bildgröße informiert zu sein.
Nachdem das Bild nun in sein eigenes Surface geladen wurde, und wir vorerst keine weiteren Veränderungen mehr daran vornehmen wollen, müssen wir es auf das display-surface kopieren. Dies geschieht über SDL_BlitSurface(). BlitSurface nimmt als Parameter:
SDL_BlitSurface(QuellSurface, Quellbereich, ZielSurface, Zielbereich)
Die Parameter Quell- und Zielbereich werden später genauer erklärt, wenn wir die Grafikroutinen intensiver nutzen. Wir gebrauchen SDL_BlitSurface() im Moment so, dass wir als Parameter für die Bereiche einfach 0 übergeben und somit das gesamte Surface kopiert wird.
Als letzten Schritt wollen wir nun das durch das Blitting veränderte display-surface auch zur Anzeige bringen. Die einfachste Möglichkeit ist, dies durch int SDL_Flip(SDL_Surface *display) zu machen. Hierbei wird (wie schon beim Blit) einfach das gesamte display-surface neu geladen.
Um das Programm speichertechnisch sauber beenden zu lassen, rufen wir für alle Surfaces (außer dem display) die Funktion SDL_FreeSurface() auf, um den allokierten Speicher dafür wieder frei zu geben.
Bildbereiche
Wie bereits im letzten Kapitel kurz angeschnitten, sind im SDL-Grafikbereich sogenannte "rectangular areas" (dt.: rechteckige Bereiche) von wichtiger Bedeutung. Diese (kurz) SDL_Rects sind einfache Strukturen, die Positionen und Größenangaben gespeichert haben:
typedef struct {
Sint16 x, y;
Uint16 w, h;
} SDL_Rect;
Zur Erklärung der Struktur:
Die signed Integers x und y sind Positionsangaben auf dem Bildschirm, beginnend in der linken oberen Ecke mit 0,0. x kann den Maximalwert display->w und y display->h annehmen, also die maximale Grösse des display-surfaces. w und h sind wie bereits gehabt die Ausschnittsgrössen.
Für was benötigt man also die SDL_Rects? Die Antwort ist eigentlich klar. Wir wollen nicht immer (wie im letzten Beispiel) das ganze Surface kopieren oder updaten. Schon aus Performancegründen ist es unsinnig, das komplette Display zu refreshen oder ein komplettes Surface x-mal zu kopieren, wenn es sich nur um ein paar Pixel großes Bild handelt, was dargestellt werden soll.
Wir nehmen wieder unser Beispiel zu Hand, und kopieren das halbe Bild mit Hilfe von SDL_Rects etwas eingerückt (200x100 Pixel) auf das display-surface. Danach updaten wir nur den auf dem display wirklich durch den Blit geänderten Bereich: wieder durch SDL_Rects.
Anmerkung: Bild tux.jpg kann HIER heruntergeladen werden

