Multimedia-Entwicklung mit SDL
#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)
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:

Pixel zeichnen
Bisher haben wir immer nur Bilder geladen und angezeigt. Jedoch ist es auch oft nötig, einzelne Pixel zu zeichnen. Sei es um Linien und Kreise darzustellen (auch wenn es dafür wieder bereits fertige Bibliotheken gibt) oder auch aus Geschwindigkeitsgründen (wenn Pixelzeichnungen ausreichen). Das Zeichnen einzelner Pixel auf den Bildschirm ist jedoch nicht so trivial, wie es sich vielleicht anhören mag.
RGB-Grundlagen:
Eine Pixelfarbe wird auf dem Bildschirm über 3 Farben bestimmt. Dies sind RGB, also Rot, Grün und Blau. Durch die Mischung dieser Farbtöne kann man jede andere Farbe erzeugen. Die Farbselektion in GIMP (WaterColor-Selection) verdeutlicht dies:

