Utiliser la SDL 2 en C
Ce document est en cours de rédaction.
La SDL (Simple DirectMedia Layer) est une bibliothèque multi plates-formes d'affichage 2D et 3D (OpenGL) très rapide (elle g-re ausi les périphériques de type Joystick, CD, et le son).
Pour démarrer avec la SDL, une surcouche est disponible.
Elle est détaillée dans [[stu:progc:sdls|Utiliser simplement la SDL 2 en C : SDLS]].
- Quelques principes et vocabulaire
Trois objets ont une importance particulière lorsqu'on utilise la SDL : les fenêtres (SDL_Window), les méthodes de rendu (SDL_Renderer), les textures (Textures). Ces dernière jouent un rôle similaire aux surface de la SDL 1.2 (qui exitent encore dans la SDL 2.0 mais qu'il convient de ne plus utiliser si possible).
La SDL 2 permet de gérer plusieurs fenêtres graphiques. Plusieurs objets SDL_Window
peuvent donc coexister. À chaque fenêtre est attachée une méthode de rendu (SDL_Renderer
), qui permet de dessiner directement dans la fenêtres ou d'y plaquer des textures.
Pour compiler un programme utilisant la SDL2, il faut penser sous Windows à lier les bibliothèques : mingw32
SDL2main
et SDL2
. L'option de linkage -mwindows
est aussi nécessaire.
- Créer une fenêtre et un contexte de rendu
La fenêtre et le contexte de rendu peuvent être créés l'un après l'autre [[wsdl>SDL_CreateWindow]] puis [[wsdl>SDL_CreateRenderer]] ou bien simultanément avec [[wsl>SDL_CreateWindowAndRenderer]]. Voyons la seconde méthode. Le programme suivant initialise la SDL, crée une fenêtre, le contexte associé (qui ne sert à rien pour le moment), peind le fond de la fenêtre en noir, attend 4 secondes, puis se termine.
#include<SDL2/SDL.h>
#include<stdio.h>
const int WIDTH = 640;
const int HEIGHT = 480;
int main(int argc, char** argv)
{
SDL_Window *win = 0;
SDL_Renderer *ren = 0;
/* Initialisation de la SDL. Si ça se passe mal, on quitte */
if (SDL_Init(SDL_INIT_EVERYTHING) < 0)
{
fprintf(stderr,"Erreur initialisation\n");
return -1;
}
/* Création de la fenêtre et du renderer */
SDL_CreateWindowAndRenderer(WIDTH, HEIGHT, 0, &win, &ren); // SDL_WINDOW_SHOWN|SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC,&win,&ren);
if (!win || !ren)
{
fprintf(stderr,"Erreur à la création des fenêtres\n");
SDL_Quit();
return -1;
}
/* Affichage du fond noir */
SDL_SetRenderDrawColor(ren, 0, 0, 0, 255);
SDL_RenderClear(ren);
SDL_RenderPresent(ren);
SDL_Delay(4000);
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
SDL_Quit();
return 0;
}
- Dessiner dans la fenêtre
Il existe plusieurs manières de dessiner dans la fenêtre. Nous allons détailler ici 4 manières de procéder.
La première permet de lire et modifier la couleurs des pixels dans un Renderer
. C'est la plus basique.
La seconde consiste à utiliser les fonctions graphiques 2D accélérées bas niveaux fournies en standard. Nous ne pourrons faire que des points, des lignes, ou des rectangles.
La troisième consiste à utiliser une librairie annexe, comme SDL_gfx, contenant des primitives graphiques (cercles, ellipses…)
Enfin, la quatrième consiste à travailler directement au niveau du bloc de pixels. C'est la plus basique, mais aussi la plus rapide.
Dans les exemples qui suivent le code est donné sous la forme d'une fonction à insérer dans le programme précédent. Cette fonction devra être appelée juste avant SDL_Delay(400)
, de cette manière :
...
dessin(ren) ;
SDL_Delay(400);
- Pixels dans un Renderer
Les fonctions utiles sont :
La fonction suivante balaie une tiers du renderer et permute les composantes de chaque pixel :
void dessin(SDL_Renderer * ren)
{
SDL_Rect rect;
int cr,cv,cb;
int res;
unsigned int value;
int i,j;
rect.w=1;
rect.h=1;
for (i=0;i<WIDTH/3; i++) {
for(j=0;j<HEIGHT;j++){
rect.x=i;
rect.y=j;
res = SDL_RenderReadPixels(ren,&rect,SDL_PIXELFORMAT_ARGB8888,&value,32*WIDTH);
if (res<0) {
printf("Lecture erronée : %s\n", SDL_GetError());
}
cr = (value & 0xFF0000)>>16;
cv = (value & 0x00FF00)>>8;
cb = (value & 0x0000FF);
SDL_SetRenderDrawColor(ren,cv,cr,255,0);
SDL_RenderDrawPoint(ren,i,j);
}
printf("%d\n",i);
}
SDL_RenderPresent(ren);
}
- Fonction grahiques 2D
Les fonctions principales sont :
SDL_RenderDrawLine,
SDL_RenderDrawLines,
SDL_RenderDrawPoint,
SDL_RenderDrawPoints,
SDL_RenderDrawRect,
SDL_RenderDrawRects,
SDL_RenderFillRect,
SDL_RenderFillRects.
Elles permettent de tracer respectivement des lignes, points, contours de rectangles ou rectangles pleins.
SDL_RenderPresent permet de forcer l'affichage, [[wsdl>SDL_SetRenderDrawColor]] modifie la couleur du tracé et [[wsdl>SDL_SetRenderDrawBlendMode]] fixe la gestion de la transparence.
La fonction suivante fait apparaître au hasard des rectangles de différentes couleurs à l'écran.
void dessin(SDL_Renderer * ren)
{
int colr, colg, colb;
SDL_Rect r;
int i;
SDL_SetRenderDrawBlendMode(ren,SDL_BLENDMODE_BLEND);
for(i=0;i<30;i++)
{
r.x = rand()%WIDTH;
r.y = rand()%HEIGHT;
r.w = rand()%(WIDTH-r.x);
r.h = rand()%(HEIGHT-r.y);
colr = rand()%256;
colg = rand()%256;
colb = rand()%256;
SDL_SetRenderDrawColor(ren,colr,colg,colb,20);
SDL_RenderFillRect(ren,&r);
}
SDL_RenderPresent(ren);
}
- Utilisation de SDL_gfx
La bibliothèque annexe SDL2_gfx
contient : des primitives graphiques, des fonctions de transformation (rotation…). La documentation est accessible ici [[http://www.ferzkopp.net/Software/SDL2_gfx/Docs/html/files.html]]
Nous ne nous intéresserons ici qu'aux primitives graphiques, dont voici une liste non exhaustive :
pixelRGBA (SDL_Renderer *renderer, Sint16 x, Sint16 y, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
boxRGBA (SDL_Renderer *renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
lineRGBA (SDL_Renderer *renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
thickLineRGBA (SDL_Renderer *renderer, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint8 width, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
ellipseRGBA (SDL_Renderer *renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
filledEllipseRGBA (SDL_Renderer *renderer, Sint16 x, Sint16 y, Sint16 rx, Sint16 ry, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
polygonRGBA (SDL_Renderer *renderer, const Sint16 *vx, const Sint16 *vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
filledPolygonRGBA (SDL_Renderer *renderer, const Sint16 *vx, const Sint16 *vy, int n, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
stringRGBA (SDL_Renderer *renderer, Sint16 x, Sint16 y, const char *s, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
void dessin(SDL_Renderer * ren)
{
Uint8 r,g,b,a;
Sint16 x,y,rx,ry;
int n;
for(n=0;n<50;n++)
{
x = rand()%WIDTH;
y = rand()%HEIGHT;
rx = rand()%30+10;
ry = rand()%30+10;
r = rand()%256;
g = rand()%256;
b = rand()%256;
a = rand()%256;
filledEllipseRGBA(ren,x,y,rx,ry,r,g,b,a);
}
SDL_RenderPresent(ren);
}
Pout utiliser ce code, il faut penser à ajouter #include <SDL2/SDL2_gfxPrimitives>
et à lier la nouvelle bibliothèque SDL2_gfx
.
- Blocs de pixels
L'affichage peut se faire aussi très rapidement au niveau du pixel. L'idée est d'avoir à disposition un tableau contenant les valeurs des pixels. Les valeurs stockées dans le tableau sont modifiées pour refléter les couleurs des pixels. Lorsque le tableau est prêt, un appel à [[wsdl>SDL_UpdateTexture]] permet de mapper les couleurs dans une texture. Puis [[wsdl>SDL_RenderCopy]] est utilisé pour passer la texture à la méthode de rendu.
La création du bloc de mémoire qui servira à stocker les couleurs est fait avec les fonctions C classiques : malloc
pour l'allocation et free
pour la libération de la mémoire. La difficulté réside dans la représentation des couleurs : dans notre bloc de mémoire, les couleurs doivent être représentées de la même manière que dans la texture que nous allons utiliser. Or, lors de la création d'une texture, nous pouvons justement spécifier la manière dont les couleurs sont codées. Par exemple :
tex = SDL_CreateTexture(ren,
SDL_PIXELFORMAT_ARGB8888, /* Chaque couleur sur 4 octets, dans l'ordre Alpha, Red, Green Blue */
SDL_TEXTUREACCESS_STREAMING, /* La texture va être modifiée très souvent (demande d'optimisation) */
640, 480);
Dans ce modèle de couleurs, chaque couleur est stockée sur 4 octets. Connaissant, chaque composante r,g,b de la couleur (chacune sur 1 octet), la couleur finale sera1)