Der rechteckige Bildbereich von SDL
Im Abschnitt zuvor wurde die Struktur SDL_Rects schon mal kurz angesprochen. Zwar handelt es sich hierbei um eine simple Struktur für einen rechteckigen Bereich, wo die Position von links oben und die Größenangabe gespeichert wird, doch ist gerade diese Information eine der wichtigsten in der SDL-Programmierung. Die Struktur SDL_Rect ist wie folgt definiert:typedef struct {
Sint16 x, y;
Uint16 w, h;
} SDL_Rect;
x und y stehen für die linke obere Ecke und w für die Breite, h für die Höhe des rechteckigen Bereichs - der Bildausschnitt, wenn Sie so wollen.
Im Beispiel zuvor wurde beim Anzeigen eines Bildes der komplette Videomodus verändert - was in der Praxis natürlich absolut ineffizient ist. Wenn Sie also ein Surface fester Größe erstellt haben, können Sie somit mit dieser Struktur nur einen bestimmten Bereich im Surface erneuern. Oft will man vielleicht auch nur einen kleinen Teilbereich erneuern - was sich gerade aus Performancegründen auch empfiehlt. Und dabei muss nicht immer das komplette Surface neu kopiert werden (was gerade bei größeren Bilddateien auf Dauer negativ auffällt).
Um Ihnen zu demonstrieren, worum es sich bei einem Bildbereich handelt, soll das Beispiel vom Abschnitt zuvor ein wenig umgeschrieben werden. Die Bilder, die zuvor auf dem veränderbaren Surface dargestellt werden, sollen nun auf einem Surface fester Größe wiedergegeben werden. Außerdem sollen die Bilder zur Demonstration um 50 x 50 Pixel vom rechten oberen Bereich eingerückt werden. Dies erledigen Sie, in dem Sie den rechteckigen Bereich des Haupt-Surfaces mit der Struktur SDL_Rect verändern:
SDL_Rect ziel;
...
ziel.x = 50;
ziel.y = 50;
ziel.w = bild->w;
ziel.h = bild->h;
Jetzt benötigen Sie noch einen zweiten rechteckigen Bereich für das Surface des Bildes. Dieses Surface stellt ja den Quellbereich da, den Sie anschließend mit der Funktion SDL_BlitSurface() in den rechteckigen Zielbereich ziel kopieren (die Funktion wurde bereits behandelt). Den Zielbereich ziel verwenden Sie nun, um wirklich auch nur den rechteckigen Bereich des Bildes zu erneuern, der neu auf dem Bildschirm erscheint. Häufig werden hierzu auch wiederum nur Bildausschnitte eines Bildbereichs benötigt. Diese Angaben müssen Sie allerdings dann bei dem rechteckigen Quellbereich machen, der folgendermaßen aussehen kann:
SDL_Rect quelle;
...
for (i = 640; i--;) {
quelle.x = i + 50;
quelle.y = 0;
quelle.w = bild->w / 2 + 50;
quelle.h = bild->h;
SDL_BlitSurface (bild, &quelle, screen, &ziel);
/* den veränderten Bildschirm-Bereich auffrischen */
SDL_UpdateRects (screen, 1, &ziel);
}
Sie können damit anschließend in der Praxis gerne herumexperimentieren. Würden Sie jetzt das Neuzeichnen mit einem Funktionsaufruf von
SDL_UpdateRect(screen,0,0,0,0);
machen, würden Sie wieder das komplette Surface neu zeichnen. Zwar wäre es auch möglich, wirklich nur den aktuellen Bildbereich neu zu zeichnen, der nötig ist, allerdings kann dies recht schnell fehleranfällig werden. Für solch einen Fall eignet sich hervorragend die Funktion
void SDL_UpdateRects( SDL_Surface *display,
int numrects, SDL_Rect *rects );
Mit SDL_UpdateRects() können einfach und komfortabel den rechteckigen Bereich rects im Ziel-Surface display angeben, welcher neu gezeichnet werden soll. Der zweite Parameter numrects zeigt es auch schon an, dass es möglich ist, mit dieser Funktion gleich mehrere Rechtecke auf einmal zu erneuern. Hierfür müssten Sie allerdings ein Array vom Typ SDL_Rects als dritten Parameter übergeben.
Dass wirklich nur ein rechteckiger Bereich gezeichnet wird, zeigt Ihnen das gleich folgende Beispiel, wenn das nächste Bild (oder der Ausschnitt) davon über das vorherige Bild geschoben wird, ohne dass das vorherige Bild verschwindet. Das Beispiel lädt geradezu ein, mit den Parametern zu spielen. Bspw. ein Bild wird von oben eingeschoben und eines von links - hierfür können Sie ja mal die Funktion SDL_UpdateRects() mit einem SDL_Rect-Array verwenden.
/* sdl5.c */
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static void display_bmp (const char *datei, SDL_Surface * screen,
int x, int y) {
SDL_Surface *bild;
SDL_Rect ziel, quelle;
int i;
/* lädt die BMP-Datei in ein Surface */
bild = IMG_Load (datei);
if (bild == NULL) {
fprintf (stderr, "'%s' konnte nicht geladen werden: %s\n",
datei, SDL_GetError ());
return;
}
/* auf den Bildschirm kopieren */
/* die Surfaces sollten hier nicht gelockt sein. */
/* x,y = linke obere Ecke; w, h = Breite und Höhe */
ziel.x = 50;
ziel.y = 50;
ziel.w = bild->w;
ziel.h = bild->h;
for (i = 640; i--;) {
quelle.x = i + 50;
quelle.y = 0;
quelle.w = bild->w / 2 + 50;
quelle.h = bild->h;
SDL_BlitSurface (bild, &quelle, screen, &ziel);
/* den veränderten Bildschirm-Bereich auffrischen */
SDL_UpdateRects (screen, 1, &ziel);
}
SDL_FreeSurface (bild);
}
int main (int argc, char **argv) {
SDL_Surface *screen;
int i;
if (argc == 1) {
printf ("Bitte mindestens eine gültige Grafik-Datei "
" angeben!\n");
return EXIT_FAILURE;
}
/* SDLs Video-System initialisieren und auf Fehler prüfen */
if (SDL_Init (SDL_INIT_VIDEO) != 0) {
printf ("Konnte SDL nicht initialisieren: %s\n",
SDL_GetError ());
return EXIT_FAILURE;
}
/* Wenn die Anwendung beendet wird, */
/* wird die Funktion SDL_Quit() ausgeführt */
atexit (SDL_Quit);
/* Videomodus mit 640x480 Pixeln, Hi-Color (16 Bit) */
/* einrichten Oberfläche (Surface) Standardmäßig in */
/* den Hauptspeicher (SDL_SWSURFACE) legen */
screen = SDL_SetVideoMode (640, 480, 16, SDL_SWSURFACE);
if (screen == NULL) {
printf ("Videomodus konnte nicht eingerichtet werden: "
" %s\n", SDL_GetError ());
return EXIT_FAILURE;
}
/* Slideshow starten */
/* Zeigt alle BMP-Bilddateien an, die in der Kommandozeile */
/* mitangegeben wurden */
for (i = 1; argv[i] != NULL; i++) {
display_bmp (argv[i], screen, 0, 0);
SDL_Delay (1000);
}
printf ("Erfolgreich beendet!\n");
return EXIT_SUCCESS;
}
Das Programm bei der Ausführung:
TTY
you@host > gcc `sdl-config --libs` `sdl-config --cflags` -o sdl5 sdl5.c -lSDL_image
you@host > ./sdl5 john2.bmp john.bmp john2.bmp john.bmp

Abbildung 15.3: Einzelne rechteckige Bildbereiche neu zeichnen
