Login
Newsletter
Werbung

Fr, 2. August 2002, 00:00

Multimedia-Entwicklung mit SDL

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

#include <stdlib.h>
#include "SDL.h"
#include "SDL_image.h"
int main()
{
 // Die 2 bereits bekannten Surfaces deklarieren
 SDL_Surface *display;
 SDL_Surface *image;
 // Deklaration des destination-rect und des source-rect
 SDL_Rect drect;
 SDL_Rect srect;
 // init video stuff
 if ( SDL_Init( SDL_INIT_VIDEO) < 0 )
 {
 fprintf(stderr, "SDL konnte nicht initialisiert werden: %s\n",
 SDL_GetError());
 exit(1);
 }
 atexit(SDL_Quit);
 // init screen
 display = SDL_SetVideoMode( 640,480, 8, SDL_SWSURFACE);
 if ( display == NULL )
 {
 fprintf(stderr, "Konnte kein Fenster 640x480 px oeffnen: %s\n",
 SDL_GetError());
 exit(1);
 }
 // Bild laden
 image = IMG_Load("tux.jpg");
 if (image == NULL)
 {
 fprintf(stderr, "Das Bild konnte nicht geladen werden: %s\n",
 SDL_GetError());
 exit(-1);
 }
 // Bildgroesse zu Debuggingzwecke ausgeben
 fprintf(stdout,"Debug: Bild-Surface-Groesse: %i x %i Pixel\n",
 image->w,image->h);
 // Setzen des Quellbereichs
 srect.x = 0;
 srect.y = 0;
 srect.w = (image->w)/2; // das gesamte Bild
 srect.h = image->h; // das gesamte Bild
 // Setzen des Zielbereichs
 drect.x = 200;
 drect.y = 100;
 drect.w = (image->w)/2;
 drect.h = image->h;
 // kopiere surface auf display-surface
 SDL_BlitSurface(image, &srect, display, &drect);
 // den veränderten Bildschirm-Bereich auffrischen
 SDL_UpdateRects(display,1,&drect);
 SDL_Delay(3000);
 // Das Bitmap-Surface löschen
 SDL_FreeSurface(image);
}

Detail:

Zuerst deklarieren wir zwei SDL_Rects. Eines für den Quellbereich, von dem heraus kopiert werden soll und eines für den Zielbereich in den hinein kopiert werden soll. Den Zielbereich verwenden wir auch später als Bereich des Displays, den wir refreshen wollen.

Nach der üblichen Initialisierungsprozedur haben wir das Bild wie bereits gehabt in ein Surface geladen. Aber anstatt nun das komplette Surface zu kopieren, wollen wir nur das halbe Bild kopieren (in der Praxis werden oft nur Ausschnitte von Bildern benötigt).

Wir setzten daher nun zuerst den Quellbereich, indem wir die Struktur des angelegten Source-Rectangles "srect" füllen. Das Bild beginnt bei den Koordinaten 0,0. Von dort aus wollen wir auch kopieren. Also wird der x- und y-Wert der Quelle auf 0 gesetzt. Die Breite des Bildes bekommen wir ja über die Surfacestruktur heraus. Da wir aber nur das halbe Bild kopieren wollen, reicht es aus, die kopierende Bildbreite auf "(image->w)/2" zu setzen. Die Höhe nehmen wir wieder komplett.

Nachdem wir wissen, was wir kopieren wollen, setzen wir nun den Zielbereich. Dazu haben wir "drect" deklariert, also das destination rectangle. Der x- und y-wert gibt an, an welcher Stelle der linke obere Pixel unseres Ausschnittes positioniert werden soll. Um dies zu demonstrieren, rücken wir das halbe Bild einfach um 200x100 Pixel ein. drect.w und drect.h haben beim Blitting noch keine Funktion und werden ignoriert (ein resize des Ausschnittes ist also beim Blit nicht direkt möglich). Wir setzen es dennoch, um später schon die Bereichsgrösse zu kennen, die wir beim Neuzeichnen des Display-Surfaces benötigen.

Im nächsten Schritt wird wieder der eigentliche Blit des Bildsurfaces (diesmal nur des definierten Ausschnitts) auf das Display-Surface vorgenommen. Wie bereits im vorhergehenden Kapitel beschrieben, nimmt SDL_BlitSurface() für das Quell- und Zielsurface auch jeweils ein SDL_Rect mit an. Da wird diese ja jetzt gesetzt haben, geben wir diese auch mit (laut SDL-Definition muss der Parameter als Adresse übergeben werden).

Als letzten Schritt wollen wir nun das geänderte dispay surface natürlich auch zur Anzeige bringen. Schon alleine aus Performancegründen sollte man immer nur den geänderten Bereich neu laden.

Neben dem bereits kennen gelernten SDL_Flip(), das den ganzen Bildschirm neu zeichnet, gibt es auch noch zwei weitere Möglichkeiten des Bildschirmupdates. Diese zwei Funktionen bieten die Möglichkeit, Bereiche zu definieren, die erneuert werden sollen:

void SDL_UpdateRect(SDL_Surface *display, Sint32 x, Sint32 y, Sint32 w,Sint32 h)
void SDL_UpdateRects(SDL_Surface *display, int numrects, SDL_Rect *rects)

Das Beispielprogramm

Marco Kraus

Das Beispielprogramm

Wie es wahrscheinlich der Name der Funktion bereits vermuten lässt, kann man durch SDL_UpdateRect() genau einen Bereich neu zeichnen. Die Parameter sind dafür die bereits auch von den SDL_Rects bekannten Werte x,y,h und w, die hier einfach direkt mit angegeben werden. Ein SDL_UpdateRect(display,0,0,0,0) würde wieder den gesamten Bildschirm neu zeichnen, und wäre (bei Grafikkarten ohne hardwaremäßige Unterstützung des Double Buffer) funktional gleich dem SDL_Flip().

SDL_UpdateRects() kann im Gegensatz dazu mehrere Rechtecke refreshen, jedoch muss man hier SDL_Rects inklusive der Gesamtanzahl der Bereiche mit übergeben. Wie man das Update über SDL_UpdateRects mit einem Bereich macht, ist im Beispiel oben gezeigt. Um mehrere Bereiche auf einmal neu zu zeichnen, bedient man sich üblicherweise Arrays vom Typ SDL_Rect, welches man dann als Parameter mit der korrekten Anzahl der im Array gespeicherten SDL_Rects mit übergibt. Wir werden uns in diesem Tutorial aber auf das Updaten eines Bereichs pro Frame begnügen.

Nun sollte sich das halbe Bild, um 200x100 Pixel eingerückt, auf dem Bildschirm zeigen. Am Ende der Applikation wird wieder der Bildspeicher frei gegeben und das Programm beendet sich.

Kommentare (Insgesamt: 0 )
Pro-Linux
Pro-Linux @Facebook
Neue Nachrichten
Werbung