SDL: Sdlgfx

Aus Wikibooks

Da SDL selbst keine Funktionen beinhaltet, Punkte, Linien, Poligone etc zu zeichnen sind wir auf Ergänzungsbibliotheken angewiesen. Eine dieser Bibliotheken ist die SDL_gfx Library, welche von http://www.ferzkopp.net/Software/SDL_gfx-2.0/ bezogen werden kann.

Diese Bibliothek bietet die üblichen Grafikprimitiven wie Punkte, Kreise usw. und als Besonderheit die Fähigkeit, SDL_Surfaces zu zoomen und zu rotieren. Hier werden nicht alle Funktionen vorgestellt, der Leser wird gebeten, die Referenz aller Funktionen dieser Bibliothek auf der Webseite des Autors nachzuschlagen.

Grafikprimitive[Bearbeiten]

Jede Funktion gibt es mindestens in zwei Ausführungen, einmal erwartet eine Funktion einen 32-Bit Farbwert, ein anderes Mal erwartet dieselbe Funktion unter leicht abgewandeltem Namen vier Farbparameter. Als Beispiel dient

int pixelColor (SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color);
int pixelRGBA (SDL_Surface *surface, Sint16 x, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a);

Beide Funktionen malen einen farbigen Pixel auf die mit surface referenzierte Oberfläche. pixelColor erwartet den Farbwert als Zahl des folgenden Formates: 0xRRGGBBAA wobei RR, GG und BB hexadezimale Angaben der Farben rot, grün und blau sind. AA ist der Alpha-Wert, ein Wert, der die Transparenz eines Farbwertes steuert, wobei 255 voll deckend ist und 0 total unsichtbar. Eine satte rote Farbe erhält man also durch 0xFF0000FF. Die Funktion pixelRGBA separiert diese Farbwerte hingegen. Welche Sorte Funktionen Sie nutzen, ist Ihnen überlassen, Sie dürfen auch beliebig mischen. Sehen Sie das einfach als ein Angebot des Autors.

Von einigen Funktionen gibt es gleich vier Stück. Zum Beispiel das Malen eines Kreises:

int circleColor (SDL_Surface *surface,   Sint16 x, Sint16 y, Sint16 r, Uint32 color);
int circleRGBA (SDL_Surface *surface,    Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
int aacircleColor (SDL_Surface *surface, Sint16 x, Sint16 y, Sint16 r, Uint32 color);
int aacircleRGBA (SDL_Surface *surface,  Sint16 x, Sint16 y, Sint16 rad, Uint8 r, Uint8 g, Uint8 b, Uint8 a);

Die unteren beiden Funktionen bieten Ihnen Anti-Alias, also Kantenglättung.

Bei Funktionen, die Tortengrafiken darstellen (pieColor, pieRGBA) ist darauf zu achten, daß die Winkel im Uhrzeigersinn gemessen werden. In der Mathematik und Technik ist es üblich, gegen den Uhrzeigersinn zu messen.

Folgende Funktionen werden abgedeckt:

  • Horizontale Linie (hlineColor),
  • Vertikale Linie (vlineColor),
  • Rechtecke (rectangleColor),
  • gefüllte Rechtecke (boxColor),
  • allgemeine Linien (lineColor, auch Anti-Alias),
  • gefüllte Kreise, Ellipsen, gefüllte Ellipsen,
  • Dreiecke (auch gefüllt und Anti-Alias),
  • Poligone (auch gefüllt und Anti-Alias)
  • Bezier-Kurven
  • einfache Textausgaben


Beispiel: Malprogramm[Bearbeiten]

Das folgende Beispielprogramm verwendet einige Grafikprimitive, um ein Malprogramm zu implementieren. Wenn Sie die Maus betätigen, wird ein Punkt in einer anzugebenden Farbe gemalt, die Farbe können Sie auswählen und wenn Sie mit dem Ergebnis nicht zufrieden sind, so können Sie alles wieder löschen.

#include <stdlib.h>
#include <SDL.h>
#include <SDL_gfxPrimitives.h>

void loesche_malflaeche (SDL_Surface *s)
{ 
  boxRGBA (s, 20, 60, 620, 680, 0, 0, 0, 255);
  rectangleColor (s, 20, 60, 620, 460, 0xffffffff);
  SDL_UpdateRect (s, 0, 0, 640, 480);
}

void userinterface (SDL_Surface *s)
{ 
  stringColor (s, 10, 10, "1: rot, 2: gruen, 3: blau", 0xffffffff);
  stringColor (s, 10, 20, "c: loeschen des Bildschirmes", 0xffffffff);
  stringColor (s, 10, 30, "Ende mit Escape", 0xffffffff);
  rectangleColor (s, 20, 60, 620, 460, 0xffffffff);
  SDL_UpdateRect (s, 0, 0, 640, 480);
}

void male_pixel (SDL_Surface *s, Uint16 x, Uint16 y, Uint32 farbe)
{
  if ((x > 21) && (x < 619) && (y > 61) && (y < 459))
    {
      boxColor (s, x-1, y-1, x+1, y+1, farbe);
      SDL_UpdateRect (s, x-1, y-1, 2, 2);
    }
}

int main (void)
{ 
  SDL_Surface *screen;
  SDL_Event event;
  Uint32 farbe;
  int quit = 0;
  if (SDL_Init (SDL_INIT_VIDEO) == -1)
    {
      printf("Kann Video nicht initialisieren:  %s\n", SDL_GetError ());
      exit (1);
    }
  atexit (SDL_Quit);
  screen = SDL_SetVideoMode (640, 480, 16, SDL_HWSURFACE);
  if (screen == NULL)
    {
      printf("Kann Video-Modus nicht festlegen: %s\n", SDL_GetError ());
      exit (1);
    }

  userinterface (screen);
  farbe = 0xff0000ff;
  while (SDL_WaitEvent (&event) && !quit)
    switch (event.type)
      {
        case SDL_KEYDOWN:
          switch (event.key.keysym.sym)
          {
            case SDLK_ESCAPE:
              quit = 1;
              break;
            case SDLK_1:
              farbe = 0xff0000ff;
              break;
            case SDLK_2:
              farbe = 0x00ff00ff;
              break;
            case SDLK_3:
              farbe = 0x0000ffff;
              break;
            case SDLK_c:
              loesche_malflaeche (screen);
              break;
            default:
              break;
          }
          break;
        case SDL_MOUSEBUTTONDOWN:
          male_pixel (screen, event.button.x, event.button.y, farbe);
          break;
      }
  return 0;
}

zu Kompilieren einfach mit gcc query.c -Wall -W `sdl-config --libs --cflags` -lSDL_gfx -o query

Oberflächen zoomen, rotieren[Bearbeiten]

Zoomen[Bearbeiten]

Eine Oberfläche, wie z. B. ein Bild oder ein beliebiger Ausschnitt aus einer SDL_Surface kann mit der folgenden Funktion in X- wie auch in Y-Richtung gezoomt werden.

 SDL_Surface *zoomSurface (SDL_Surface *original, double zoomx, double zoomy, int weich);

original ist die Ursprungsoberfläche, die es zu zoomen gilt. zoomx und zoomy geben den Zoomfaktor in X- wie in Y-Richtung an. 1 bedeutet hier Originalgröße. weich ist entweder SMOOTHING_OFF oder SMOOTHING_ON, funktioniert aber nur auf 32-Bit Oberflächen. Sie erhalten eine neue Oberfläche, die Sie mit SDL_BlitSurface auf den Bildschirm zeichnen können.

Um vor dem eigentlichen Zoomen herauszufinden, wie groß die entstehende Oberfläche sein wird, gibt es die Funktion

 void zoomSurfaceSize (
           int orig_width, int orig_height, 
           double zoomx, double zoomy, 
           int *ziel_width, int *ziel_height);

Sie erwartet die Ursprungsgröße und die Zoomfaktoren und gibt Ihnen die Größe der Ziel-Surface zurück.

Rotieren und Zoomen[Bearbeiten]

Um eine Oberfläche zu rotieren, können Sie sich der Funktion

 SDL_Surface *rotozoomSurface (SDL_Surface *original, double winkel, double zoom, int weich);

bedienen. winkel ist der in Grad anzugebene Winkel, zoom der Zoomfaktor, der hier in beiden Richtungen gleich ist und 1 sein darf, um ausschließlich zu rotieren. Um hier ebenfalls die Größe der Zieloberfläche zu ermitteln, dient

 void rotozoomSurfaceSize (
            int orig_width, int orig_height, 
            double winkel, double zoom, 
            int *ziel_width, int *ziel_height);

Beispiel: Oberflächen rotozoomieren[Bearbeiten]

Beispielanwendung: Oberflächen rotieren und zoomen

In diesem Beispiel geht es darum, ein Bild zu drehen und zu zoomen. Wir benutzen hierzu hauptsächlich die Funktion rotozoomSurface, welche uns diese beiden Aufgaben in einer Funktion erledigt. Der Rest ist Userinterface. Mit der Taste 0 werden alle Zoom- und Rotationsfaktoren auf Anfangswerte zurückgestellt. Die Tasten 1, 2, 3, 4 beeinflussen den Zoom und die Rotation. Mit Escape wird die Anwendung verlassen. Das Beispielbild wurde einer Wikibooks Vorlage entliehen, als das Buch Aus_Deutschland_in_die_USA_umziehen Buch des Monats war. Das Bild wurde auf 128*96 verkleinert.

#include <stdlib.h>
#include <SDL.h>
#include <SDL_gfxPrimitives.h>
#include <SDL_rotozoom.h>

SDL_Surface *lade_bild (const char *dateiname)
{
  SDL_Surface *tmp, *tmp1;
  tmp = SDL_LoadBMP (dateiname);
  tmp1 = SDL_DisplayFormat (tmp);
  SDL_FreeSurface (tmp);
  return tmp1;
}

void male_bild (SDL_Surface *s, SDL_Surface *pic)
{
  SDL_Rect r;
  r = pic->clip_rect;
  r.x = (640 - r.w) / 2;
  r.y = (480 - r.h) / 2;
  SDL_BlitSurface(pic, NULL, s, &r);
  SDL_UpdateRect (s, 0, 0, 640, 480);
}
 
void zoom_dreh (SDL_Surface *s, SDL_Surface *pic, double winkel, double zoom)
{
  SDL_Surface *tmp;
  SDL_Rect tmpRect;
  int w, h;
  tmpRect = pic->clip_rect;
  rotozoomSurfaceSize (tmpRect.w, tmpRect.h, winkel, zoom, &w, &h);
  if ( (w < 640) && (h < 480) )
    {
      tmp = rotozoomSurface (pic, winkel, zoom, SMOOTHING_ON);
      male_bild (s, tmp);
      SDL_FreeSurface (tmp);
    }
}

int main (void)
{
  SDL_Surface *screen, *image;
  SDL_Event event;
  double w, z;
  int quit = 0;
  if (SDL_Init (SDL_INIT_VIDEO) == -1)
    {
      printf("Kann Video nicht initialisieren:  %s\n", SDL_GetError ());
      exit (1);
    }
  atexit (SDL_Quit);
  screen = SDL_SetVideoMode (640, 480, 16, SDL_HWSURFACE);
  if (screen == NULL)
    {
      printf("Kann Video-Modus nicht festlegen: %s\n", SDL_GetError ());
      exit (1);
    }
  image = lade_bild ("sol.bmp");
  male_bild (screen, image);
  w = 0.0;
  z = 1.0;
  while (SDL_WaitEvent (&event) && !quit)
     switch (event.type)
      {
        case SDL_KEYDOWN:
          switch (event.key.keysym.sym)
          {
            case SDLK_ESCAPE:
              quit = 1;
              break;
            case SDLK_0:
              z = 1.0;
              w = 0.0;
              break;
            case SDLK_1:
              if (z > 0) z = z / 1.2;
              break;
            case SDLK_2:
              z = z * 1.3;
              break;
            case SDLK_3:
              w = w + 1;
              break;
            case SDLK_4:
              w = w - 1;
              break;
            default:
              break;
          }
          zoom_dreh (screen, image, w, z);
          break;
      }
  return 0;
}