Meilleurs tutoriauxForumServicesPartenairesRégie publicitaireContactez-nous
S'inscrireConnexion

Accueil > Tutoriaux > Programmation > C, C++, C++.NET > Graphique > Programmation graphique en C avec la SDL
Programmation graphique en C avec la SDL
Programmation graphique en C avec la SDL
FieldkodPar Fieldkod, publié le 28/11/2010 à 21:46:46
Partie 1 : Introduction

La SDL simple directmedia Layer est une bibliothèque destinée à permettre l'accès au matériel graphique pour faire, par exemple, des jeux en plein écran (ou en fenêtre), et ça de manière portable. La SDL peut notamment fonctionner sous Linux (où elle utilise X11), comme sous Windows (où elle utilise DirectX).

La SDL est simple d'emploi, mais particulièrement bas niveau. Vous pouvez initialiser facilement le mode graphique, mais pour dessiner, vous n'avez rien. Même pas une fonction pour afficher un pixel ! Cet article est là pour vous aider à utiliser cette bibliothèque et à écrire les fonctions de base.

La SDL est disponible en licence libre LGPL. Cela veut dire que vous pouvez donc librement utiliser cette bibliothèque dans votre application, qu'elle soit libre ou commerciale. La seule obligation est que la bibliothèque SDL doit rester en liaison dynamique.

Partie 2 : Installation de la bibliothèque SDL

Vous pouvez télécharger la SDL à cette adresse : http://www.libsdl.org/download-1.2.php Il y a la version de développement, nécessaire pour compiler votre programme. Il en existe deux versions : une pour MingW (gcc ; cela concerne aussi DevC++), et une pour Visual C++ (version 5 et supérieures). La version de développement inclut aussi le runtime, vous n'avez donc pas besoin de télécharger ce dernier.

Nous allons détailler l'installation de la bibliothèque SDL sous Dev-C++ :


Installer SDL sous Dev-C++

1. Téléchargez (lien donné ci-dessus) le fichier SDL-devel-1.2.9-mingw32.tar.gz
2. Décompressez cette archive dans le dossier de DevC++ (par défaut: C:\Dev-Cpp)
3. Lancez DevC++, allez dans "Outils", "Options du Compilateur", onglet "Répertoire"
4. Pour "Répertoire bibliothèques", ajoutez le chemin des fichiers .lib de SDL (par défaut: C:\Dev-Cpp\SDL-1.2.9\lib).
5. Pour "Répertoire C .h", ajoutez le chemin des fichiers .h de SDL (par défaut: C:\Dev-Cpp\SDL-1.2.9\include).
6. Pour "Répertoire C++ .h", ajoutez la même chose qu'à l'étape précédente (par défaut: C:\Dev-Cpp\SDL-1.2.9\include).
7. Validez le tout en cliquant sur OK : c'est installé.

Créer un projet SDL sous Dev-C++

1. Créez normalement un projet vide ("Fichier", "Nouveau", "Projet", "Empty Project"), donnez-lui un nom et sauvegardez-le dans son propre répertoire.
2. Allez dans les options du projet ("Projet", "Options du Projet")
3. Dans l'onglet "Général", choisissez "Win32 GUI".
4. Dans l'onglet "Paramètres", zone "Editeur de liens", ajoutez "-lmingw32 -lSDLmain -lSDL"
5. Validez le tout en cliquant sur OK.
6. Copiez le runtime SDL (SDL.DLL, que l'on trouve dans le dossier bin de la SDL, par défaut: C:\Dev-Cpp\SDL-1.2.9\bin) dans le dossier de votre projet.
7. C'est prêt !

Partie 3 : Notre premier programme SDL

Pour commencer, nous allons créer un programme minimal qui initialise une fenêtre, et qui attend une touche pour quitter. Voici le code source complet de ce premier programme, nous l'analyserons après :

#include <stdlib.h>
#include <stdio.h>
#include "SDL.h"

SDL_Surface* affichage;

void initSDL(void);
void attendreTouche(void);

int main(int argc, char** argv)
{
initSDL();
attendreTouche();
return EXIT_SUCCESS;
}

void initSDL(void)
{
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "Erreur à l'initialisation de la SDL : %s\n", SDL_GetError());
exit(EXIT_FAILURE);
}

atexit(SDL_Quit);
affichage = SDL_SetVideoMode(800, 600, 32, SDL_SWSURFACE);

if (affichage == NULL) {
fprintf(stderr, "Impossible d'activer le mode graphique : %s\n", SDL_GetError());
exit(EXIT_FAILURE);
}

SDL_WM_SetCaption("Mon premier programme SDL", NULL);
}

void attendreTouche(void)
{
SDL_Event event;

do
SDL_WaitEvent(&event);
while (event.type != SDL_QUIT && event.type != SDL_KEYDOWN);
}


Compilez ce programme et exécutez-le. Il affiche une fenêtre noire, de taille 800x600. Si vous enfoncez une touche, la fenêtre disparaît et le programme s'arrête. Remarquez que si vous cliquez sur la case de fermeture, il se passe la même chose.

Maintenant examinons le programme ligne par ligne :

#include "SDL.h"


Ceci inclut tous les fichiers headers de la SDL.

SDL_Surface* affichage;


Cette variable globale sert à stocker la surface concernant l'affichage. Nous détaillerons un peu plus loin ce que représente une surface, mais pour le moment, il suffit de savoir que lorsque nous voudrons dessiner directement à l'écran, nous utiliserons cette variable.

SDL_Init(SDL_INIT_VIDEO);


Cette fonction initialise la bibliothèque SDL. Elle doit être appelée avant toute autre fonction de la bibliothèque. Le paramètre correspond aux sous-systèmes qu'il faut activer. En effet, il faut savoir que la SDL est constituée de huit sous-systèmes : Audio, CD-ROM, Gestion des événements, Gestion de fichiers, Gestion du joystick, Processus légers (threads), Timers (fonctions de délais ultra-précises) et Vidéo.

SDL_Init();


active automatiquement les sous-systèmes Gestion des événements, Gestion de fichiers et les Processus légers ; si on a besoin d'autres sous-systèmes, on le précise en paramètre de cette fonction. Ici, comme on va faire du graphisme, on initialise en plus le sous-système Vidéo.

atexit(SDL_Quit);


Quand l'application a initialisé la SDL avec SDL_Init(), elle ne doit surtout pas oublier de "l'éteindre" avant que le programme se termine, afin que les sous-systèmes soient correctement fermés : notamment, si vous avez changé le mode vidéo, il faut restaurer le mode normal.

Pour "éteindre" la SDL, il faut appeler la fonction SDL_Quit(). Mais il est encore mieux de s'assurer que cette fonction sera appelée automatiquement quand le programme s'arrête, quelqu'en soit la raison. C'est la raison d'être de la fonction atexit() (de la bibliothèque C standard), qui enregistre les fonctions qu'il faudra appeler automatiquement à la fin du programme.

Nota: Sous Unix, si le programme est tué par un signal SIGKILL (avec kill -9 par exemple), la fonction SDL_Quit() ne sera malheureusement pas appelée.

affichage = SDL_SetVideoMode(800, 600, 32, SDL_SWSURFACE);


Cette fonction active le mode vidéo demandé. Remarquez que ceci ne signifie pas forcément changer de mode graphique : on peut très bien seulement créer une fenêtre dans lequel on travaillera.

On lui passe les dimensions (je suppose ici que vous travaillez en 1024x768 afin que la fenêtre puisse s'afficher entièrement), ainsi que le nombre de bits par pixel (32). Remarquez, dans ce cas précis, qu'il n'est pas nécessaire que ce nombre de bits par pixel soit identique à la véritable résolution de l'écran. Si cela ne correspond pas, la SDL se chargera de convertir les accès quand ça sera nécessaire. Nous avons choisi 32 bits par pixel parce qu'il s'agit de la résolution la plus courante.

Le dernier argument indique le type de surface (nous y reviendrons). Ici, nous avons demandé une surface logicielle, sans demander le plein écran : SDL_SetVideoMode() va donc créer une fenêtre. Si nous voulions que l'application tourne en plein écran, nous aurions mis : SDL_SWSURFACE | SDL_FULLSCREEN.

SDL_WM_SetCaption("Mon premier programme SDL", NULL);


Sans surprise, cette fonction permet de donner un titre à cette fenêtre. Le premier argument est une chaîne contenant le titre, le deuxième argument une chaîne contenant l'icône à attribuer.

Partie 4 : Affichage d'un pixel

Aussi bizarre que ça puisse paraître au premier abord, la SDL ne propose aucun moyen d'écrire un pixel. Il nous faut écrire la fonction nous-même.

Mais en y regardant de plus près, cela s'explique aisément : écrire un pixel dépend énormément de la résolution (en bits par pixel). Si on veut gérer tous les cas, on perd du temps en exécution. De plus, un appel de fonction peut être trop lent dans certains cas. Enfin, on peut vouloir ou ne pas vouloir que la fonction teste si ses paramètres sont corrects (encore du temps d'exécution perdu).

Nous allons écrire une fonction simple, conçue pour fonctionner dans le mode 32 bits par pixel uniquement. Cette fonction ne teste pas la validité des coordonnées pour gagner en temps d'exécution. Si vous utilisez gcc (ou le C++), il sera une très bonne idée d'en faire une fonction "inline" afin de l'accélérer.

void setPixel(int x, int y, Uint32 coul)
{
*((Uint32*)(affichage->pixels) + x + y * affichage->w) = coul;
}


Pour comprendre grossièrement comment cette fonction est conçue, il faut connaître le format d'affichage. Quand la SDL fonctionne en mode 32 bits, la surface d'affichage est toujours conçue de la même manière : il faut 4 octets par pixel (le code couleur), et les points sont répartis linéairement, ligne par ligne.

Donc pour afficher un pixel, on prend l'adresse de début de la surface ; on décale le pointeur vers la bonne position (y lignes de pixels, soit y multiplié par la largeur de la surface, plus x colonnes). Comme le pointeur est transtypé en pointeur vers entier 32 bits, le décalage se fait automatiquement par sauts de 4 octets. Enfin, on dé-référence le pointeur et on y stocke la couleur.

Contrairement à l'affichage de pixels, la SDL fournit une fonction qui permet de faire la conversion d'un code couleur RVB en code couleur. L'avantage, c'est qu'on n'a pas besoin de se préoccuper du format actuel des pixels.

Uint32 SDL_MapRGB(SDL_PixelFormat *fmt, Uint8 r, Uint8 g, Uint8 b);


Le premier paramètre est le format des pixels, pour ça on devra lui passer affichage->format.

Nous ne sommes pas tous des experts en mélange de couleurs, alors j'ai préparé ici une fonction qui charge une liste des couleurs les plus utilisées.

enum {
C_NOIR, C_BLEU_FONCE, C_VERT_FONCE, C_CYAN_FONCE, C_ROUGE_FONCE,
C_MAGENTA_FONCE, C_OCRE, C_GRIS_CLAIR, C_GRIS, C_BLEU, C_VERT,
C_CYAN, C_ROUGE, C_MAGENTA, C_JAUNE, C_BLANC,

NB_COULEURS
};

Uint32 couleurs[NB_COULEURS];

void initCouleurs(void)
{
couleurs[C_NOIR] = SDL_MapRGB(affichage->format, 0x00, 0x00, 0x00);
couleurs[C_BLEU_FONCE] = SDL_MapRGB(affichage->format, 0x00, 0x00, 0x80);
couleurs[C_VERT_FONCE] = SDL_MapRGB(affichage->format, 0x00, 0x80, 0x00);
couleurs[C_CYAN_FONCE] = SDL_MapRGB(affichage->format, 0x00, 0x80, 0x80);
couleurs[C_ROUGE_FONCE] = SDL_MapRGB(affichage->format, 0x80, 0x00, 0x00);
couleurs[C_MAGENTA_FONCE] = SDL_MapRGB(affichage->format, 0x80, 0x00, 0x80);
couleurs[C_OCRE] = SDL_MapRGB(affichage->format, 0x80, 0x80, 0x00);
couleurs[C_GRIS_CLAIR] = SDL_MapRGB(affichage->format, 0xC0, 0xC0, 0xC0);
couleurs[C_GRIS] = SDL_MapRGB(affichage->format, 0x80, 0x80, 0x80);
couleurs[C_BLEU] = SDL_MapRGB(affichage->format, 0x00, 0x00, 0xFF);
couleurs[C_VERT] = SDL_MapRGB(affichage->format, 0x00, 0xFF, 0x00);
couleurs[C_CYAN] = SDL_MapRGB(affichage->format, 0x00, 0xFF, 0xFF);
couleurs[C_ROUGE] = SDL_MapRGB(affichage->format, 0xFF, 0x00, 0x00);
couleurs[C_MAGENTA] = SDL_MapRGB(affichage->format, 0xFF, 0x00, 0xFF);
couleurs[C_JAUNE] = SDL_MapRGB(affichage->format, 0xFF, 0xFF, 0x00);
couleurs[C_BLANC] = SDL_MapRGB(affichage->format, 0xFF, 0xFF, 0xFF);
}


Après avoir appelé initCouleurs() en début de programme (après initSDL() bien entendu) pour initialiser le tableau, si vous souhaitez par exemple utiliser du jaune, il vous suffira de faire couleurs[C_JAUNE].

Le jeu de couleurs n'est pas tout à fait choisi par hasard : il s'agit des 16 couleurs classiques que l'on obtient dans un mode texte ou graphique 16 couleurs sur PC.

Pour mettre en pratique l'affichage de pixels et les couleurs, nous allons écrire un petit programme qui affiche un ciel étoilé aléatoire (en couleurs).

#include <stdlib.h>
#include <stdio.h>
#include "SDL.h"

SDL_Surface* affichage;

void initSDL(void);
void attendreTouche(void);
void setPixel(int x, int y, Uint32 coul);
void actualiser(void);
void dessinerEtoiles(void);

int main(int argc, char** argv)
{
initSDL();
dessinerEtoiles();
actualiser();
attendreTouche();
return EXIT_SUCCESS;
}

void initSDL(void)
{
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "Erreur à l'initialisation de la SDL : %s\n", SDL_GetError());
exit(EXIT_FAILURE);
}

atexit(SDL_Quit);
affichage = SDL_SetVideoMode(800, 600, 32, SDL_SWSURFACE);

if (affichage == NULL) {
fprintf(stderr, "Impossible d'activer le mode graphique : %s\n", SDL_GetError());
exit(EXIT_FAILURE);
}

SDL_WM_SetCaption("Ciel étoilé", NULL);
}

void attendreTouche(void)
{
SDL_Event event;

do
SDL_WaitEvent(&event);
while (event.type != SDL_QUIT && event.type != SDL_KEYDOWN);
}

void setPixel(int x, int y, Uint32 coul)
{
*((Uint32*)(affichage->pixels) + x + y * affichage->w) = coul;
}

void actualiser(void)
{
SDL_UpdateRect(affichage, 0, 0, 0, 0);
}

void dessinerEtoiles(void)
{
int i;

for (i = 0; i < 100; i++)
setPixel(rand() % 800, rand() % 600,
SDL_MapRGB(affichage->format,
rand() % 128 + 128, rand() % 128 + 128, rand() % 128 + 128));
}


Et voilà, pour résumer, nous avons présenté les bases de la programmation graphique avec la SDL. Vous savez désormais initialiser un mode graphique et écrire des pixels colorés.
Ajouter un commentaire
Vous voulez ajouter un message ? Créez un compte gratuitement !
Choisissez votre nom utilisateur :

(Déja membre ? Connexion)
Informations sur le tutoriel