Dans le numéro précédent, nous avons abordé les différents aspects de la librairie SDL. Cet article se concentre davantage sur la partie graphique de SDL qui est, sans aucun doute, la plus importante pour la programmation de jeux.
SDL repose sur le concept de " surface " (le type structuré SDL_Surface) qui sert à représenter un ensemble planaire de pixels. Chaque surface a ses caractéristiques propres (taille, bits par pixel, etc.). L'interaction avec l'écran principal (qui peut être une fenêtre X11) se fait par l'intermédiaire d'un objet SDL_Surface. Il en est de même pour la manipulation du curseur de la souris, des sprites, icônes, etc...
Avant d'ouvrir une nouvelle fenêtre, il est toujours bon de savoir quelles sont les caractéristiques graphiques utilisées sur la machine. Celles-ci indiquent particulièrement le format des pixels (nombre de bits par pixels, et la répartition des composantes RGB), mais aussi si les primitives supportées par SDL bénéficient d'une accélération matérielle dans le contexte actuel. Ces caractéristiques sont obtenues par la fonction SDL_GetVideoInfo() qui renvoie un pointeur sur une structure SDL_VideoInfo. Cette structure fournit les informations nécessaires. Le programme de l'encadré 1 teste certaines de ces caractéristiques.
Le format vidéo renvoyé par la fonction SDL_GetVideoInfo() indique le format "idéal" pour le contexte d'exécution. Il est toujours possible de demander à SDL d'ouvrir une surface avec des caractéristiques différentes, la librairie faisant son possible pour satisfaire l'utilisateur avec, toutefois, une certaine perte de performances. L'activation d'un mode vidéo se fait par l'intermédiaire de la fonction SDL_SetVideoMode() dont le prototype est le suivant :
SDL_Surface *SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags);Son utilisation est assez évidente. La valeur du paramètre "flags" est une combinaison des valeurs suivantes :
La fonction
SDL_Surface *SDL_AllocSurface (Uint32 flags, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask);crée une surface de format arbitraire en mémoire. Les paramètres " mask " permettent de spécifier le masque RGB pour le format de pixels de la surface. Par exemple, un mode 24 bits (depth = 24) pourra avoir Rmask = 0xFF0000, Gmask = 0x00FF00, Bmask = 0x0000FF. Les flags supportés sont les suivants :
SDL_SetColorKey(SDL_Surface *surface, Uint 32 flag, Uint32 key)est utilisée à cet effet. La couleur "key" sera définie comme la couleur transparente dans la surface et les pixels de cette couleur ne seront pas affectés lors de la copie de la surface. Le paramètre "flag" doit être SDL_SRCCOLORKEY pour changer la valeur de la clé. Une valeur nulle désactive la transparence. La transparence est principalement utilisée pour réaliser des sprites dans les jeux : un sprite n'est qu'un bloc de pixels avec une couleur transparente, ce qui correspond à une surface SDL transparente.
SDL supporte également " l'alpha blending ". Il s'agit d'une semi-transparence, une valeur "alpha" donnant le degré de transparence de la surface. La fonction
SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha)est utilisée à cet effet. "alpha" varie entre 0 (opaque) et 255 (complètement transparent, aucun pixel ne sera affiché !). Tout comme pour SDL_SetColorKey(), une valeur de 0 pour le flag désactive l'alpha blending et SDL_SRCALPHA l'active. L'alpha blending permet de réaliser des textures transparentes, comme si l'image était projetée sur un fond différent.
Quand une surface est allouée, le plus simple pour la manipuler est d'utiliser directement son pointeur "pixels" qui fournit un accès total à la mémoire associée. Si la surface est allouée en mémoire vidéo, il faudra cependant veiller à entourer les accès directs par des appels à SDL_LockSurface() et SDL_UnlockSurface(). Ceci est nécessaire afin de garantir un accès exclusif à la mémoire vidéo par l'application. En effet, SDL, tout comme X11, peut essayer d'écrire en même temps en mémoire, ce qui peut avoir des effets indésirables.
Par exemple :
... SDL_Surface *sf = SDL_AllocSurface(SDL_HWSURFACE,320,200,32,0xFF0000,0xFF00,0xFF ,0); int *ptr = (int *)sf->pixels; SDL_LockSurface(sf); ... /* Accès à la mémoire vidéo par l'intermédiaire de ptr */ ... SDL_UnlockSurface(sf);Lorsqu'une surface n'est pas en mémoire vidéo et que l'on y accède néanmoins directement via le pointeur, il faut veiller à actualiser périodiquement l'écran à partir du contenu du buffer, par la fonction SDL_UpdateRect() ou SDL_UpdateRects() pour un ensemble de zones précises à actualiser. Les verrouillages de la surface ne sont alors plus nécessaires. SDL fournit également des primitives de bases sur les surfaces qui sont, si possible, accélérées par le matériel. On trouve, notamment, la copie de bloc ("blitting"), via :
int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect);Les arguments précisent une zone dans chaque surface (source et destination). Il faut veiller à ce que les zones soient de tailles égales. Les attributs de transparence et d'alpha blending sont respectés. D'une manière générale, les copies ("blits") entre surfaces sont beaucoup plus rapides lorsque les deux surfaces sont en mémoire vidéo.
On trouve également le remplissage de rectangles d'une certaine couleur (SDL_FillRect()), la conversion de surfaces dans un format de pixels différent (SDL_ConvertSurface()). Il est également possible de définir une zone de clipping pour chaque surface via SDL_SetClipping(). Toutes les opérations sur cette surface seront restreintes aux coordonnées de cette zone sauf, bien entendu, en cas d'accès direct à la mémoire.
SDL_Cursor *SDL_CreateCursor(Uint8 *data, Uint8 *mask, int w, int h, int hot_x, int hot_y);"w" et "h" sont, respectivement, la largeur et la hauteur du curseur (multiples de 8). On fournit également les coordonnées du " hot spot " à l'intérieur du curseur; il s'agit du point considéré comme le centre du curseur. Par exemple, pour un curseur en forme de viseur, le hot spot sera le point central du viseur. Les paramètres "data" et "mask" sont des masques binaires définissant la forme du curseur monochrome. Les combinaisons pour chaque bit sont les suivantes :
data | Mask | |
---|---|---|
0 | 1 | Blanc |
1 | 1 | Noir |
0 | 0 | Transparent |
1 | 0 | Couleur inversée sous le curseur (si possible) |
Les curseurs alloués de cette façon doivent être libérés par SDL_FreeCursor(). L'affectation d'un curseur se fait par l'intermédiaire de SDL_SetCursor(). Le curseur peut être caché ou affiché par des appels à SDL_ShowCursor(int toggle) ; une valeur de 1 pour le paramètre indique que le curseur doit être affiché. Il est également possible de déplacer le curseur à un point précis, via SDL_WarpMouse(x,y).
SDL_WM_SetCaption(const char *title, const char *icon) :change le titre de la fenêtre et le titre de l'icône. Les paramètres courants peuvent être obtenus par SDL_WM_GetCaption().
SDL_WM_SetIcon(SDL_Surface *icon, Uint8 *mask) :Change l'icône associée à la fenêtre, à partir d'une surface SDL. Le masque indique la transparence de l'icône d'une manière similaire aux curseurs. Cette fonction doit être appelée avant SDL_SetVideoMode().
gcc -o prog prog.c -I/usr/local/include -lSDL -ldl -lpthread(Note: la méthode de compilation a légèrement changé depuis SDL 1.0) Dans le prochain numéro, nous aborderons la programmation des événements avec SDL et nous verrons comment intégrer celle-ci avec la programmation graphique vue dans cet article.
Stéphane Peter
Pour plus de détails: http://www.devolution.com/~slouken/SDL/#include <SDL/SDL.h> #include <stdio.h> int main(void) { const SDL_VideoInfo *vi; if(SDL_Init(SDL_INIT_VIDEO)<0) printf("Erreur: %s\n", SDL_GetError()); vi = SDL_GetVideoInfo(); printf("Format vidéo: %d bpp. En fenêtre: %d\n", vi->vfmt->BitsPerPixel, vi->wm_available); return 0; }
#include <stdio.h> #include <SDL/SDL.h> int main(int argc, char **argv) { SDL_Surface *screen; SDL_Surface *img, *back; SDL_Rect coords, carre; const SDL_VideoInfo *vi; Uint32 bleu; if(argc<2){ fprintf(stderr,"Veuillez fournir un fichier BMP en paramètre.\n"); exit(1); } /* Initialisation de SDL */ if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) { fprintf(stderr,"Erreur SDL: %s\n", SDL_GetError()); exit(1); } atexit(SDL_Quit); /* Pour sortir proprement */ img = SDL_LoadBMP(argv[1]); /* Charge l'image */ if(!img){ fprintf(stderr,"Impossible de charger l'image %s: %s\n", argv[1], SDL_GetError()); exit(1); } /* Alloue un second buffer de la même taille que l'image */ back = SDL_AllocSurface(SDL_SWSURFACE, img->w, img->h, img->format->BitsPerPixel, img->format->Rmask, img->format->Gmask, img->format->Bmask,img->format->Amask); vi = SDL_GetVideoInfo(); if(vi && vi->wm_available) /* Change les titres */ SDL_WM_SetCaption("Demo SDL", "Icone SDL"); /* Initialise un mode vidéo idéal pour cette image */ screen = SDL_SetVideoMode(img->w, img->h, img->format->BitsPerPixel, SDL_FULLSCREEN); if(!back || !screen){ fprintf(stderr, "Impossible d'allouer une surface: %s\n", argv[1], SDL_GetError()); exit(1); } bleu = SDL_MapRGB(screen->format, 0, 0, 255); coords.x = coords.y = 0; coords.w = img->w; coords.h = img->h; carre.w = carre.h = 30; carre.y = (img->h - 30) / 2; for(carre.x=0; carre.x<img->w; carre.x++){ /* Copie l'image originale dans le buffer */ SDL_BlitSurface(img, &coords, back, &coords); /* On fait défiler un carré bleu de 30 pixels de côté */ SDL_FillRect(back, &carre, bleu); /* On envoie le tout sur l'écran et on actualise */ SDL_BlitSurface(back, &coords, screen, &coords); SDL_UpdateRects(screen,1,&coords); } return 0; }