La gestion vidéo





Ce deuxième cours portera sur l'élément essentiel de SDL la gestion vidéo.





La gestion vidéo vue par SDL





SDL se révèle être une API très simple dans cette gestion, les différentes fonctions sont claires, utiles et nombreuses. SDL travaille sur le concept très répandu des surfaces. C'est assez simple en fait, tel DirectX, lorsque vous initialisez un mode vidéo (nous verrons ceci juste après), vous créez une surface qui correspond à un pointeur vers une structure SDL_Surface. Cette dernière contient les diverses caractéristiques que vous avez demandé pour cette surface. Par conséquent une surface est une zone mémoire vidéo à laquelle vous pouvez accéder directement, ou indirectement.

La première chose à réaliser donc après l'initialisation de SDL est de créer la surface principale, qui la plupart du temps correspondra à la zone écran que vous désirez. La manière de créer cette surface est la suivante :

SDL_Surface *screen; //creation du pointeur vers cette zone mémoire vidéo qu'est la surface   
 
screen = SDL_SetVideoMode(640,480,16,SDL_FULLSCREEN | SDL_HWSURFACE);   
 
if(screen == NULL){   
    fprintf(stderr,"Impossible d'allouer une surface SDL %s\n", SDL_GetError()); 
    exit(1); 
}

 Ici j'ai donc déclaré un pointeur vers une structure de type SDL_Surface (je vous conseille de regarder la doc pour en savoir plus), puis j'ai demandé à SDL de créer cette surface, par un appel à SDL_SetVideoMode(), dont le prototype est le suivant

SDL_Surface *SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags);

Les trois premiers arguments de cette fonction sont assez simples, je ne détaillerais pas. Le dernier par contre correspond à une combinaison binaire (ouh la) de divers drapeaux SDL, permettant de dire à SDL quel type de surface on désire et comment on veut quel soit gérée. Les prinipaux drapeaux sont :

  • SDL_SWSURFACE : indique à SDL que l'on veut que la surface principale soit placée en mémoire système
  • SDL_HWSURFACE : indique à SDL que l'on veut que la surface soit placée (si possible) en mémoire vidéo directement
  • SDL_FULLSCREEN : la surface sera en mode plein écran si possible
  • SDL_DOUBLEBUF : active le double-buffering
  • SDL_HWPALETTE : en mode 8 bits, garantit que SDL appliquera la palette donnée exactement
  • SDL_OPENGL : indique à SDL que la surface sera utilisée dans un contexte Open/GL
  • etc...

Il est important de noter que Linux ne tire parti de l'accélération matérielle que depuis la mise en place d'XFree 4.x. Donc il est inutile de demander sous Linux à SDL de mettre la surface principale en mémoire vidéo car SDL ne pourra pas le faire. D'autre part, le mode plein écran sous Linux passe par l'extension DGA qui n'est pas permise dans tous les cas (si le superutilisateur n'en fournit pas le droit vous ne pourrez pas l'utiliser). Il est aussi intérressant de noter que la plupart du temps le mode 24 bits est plus lent que les autres modes (perso je travaile en général en mode 16 bits).

Très bien maintenant que nous possédons notre surface principale nous allons créer une surface supplémentaire. Pour ce faire, SDL fournit la fonction suivante :

SDL_Surface *SDL_AllocSurface(Uint32 flags, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask);

Je ne détaillerais pas les quatres premiers paramètres. En revanche je vais me pencher sur les autres. En fait clés quatres arguments correspondent aux caractéristiques RGB de la nouvelle surface, et le dernier correspond à la composante alpha de la surface. Notez que cette fonction est une Macro de la fonction SDL_CreateMapRGB() donc vous ne la verrez pas dans la doc officielle. D'une manière générale on utilise au moins une fois cette fonction afin de créer un tampon mémoire de la surface principale. Zone sur laquelle on travaille durant le programme. De fait on procède de cette façon :

SDL_Surface *buffer;
 
            
buffer = SDL_AllocSurface(SDL_HWSURFACE, screen->w, screen->h, 
            screen->format->BitsPerPixel, screen->format->Rmask, 
            screen->format->Gmask, screen->format->Bmask, screen->format->Amask);

On présuppose que la surface screen existe déjà. On n'oublie pas aussi de vérifier ensuite la validité du pointeur comme plus haut.

 

Très bien maintenant, nous disposons de deux surfaces, l'une correspondant à la zone d'affichage principale, et l'autre à son équivalent en mémoire. Mais le but est quand même de les utiliser. Il existe plusieurs façons de le faire, mais tout d'abord il faut faire attention au fait que si vous mettez votre surface en mémoire vidéo il faudra en protéger l'accès. Pour cela on fait appel aux fonctions suivantes :

int SDL_LockSurface(SDL_Surface *surface);

void SDL_UnLockSurface(SDL_Surface *surface);

Ces fonctions entoureront votre écriture sur la surface

.

Bien maintenant passons aux fonctions d'opérations sur les surfaces. Nous avons :

int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *src_rect, SDL_Surface *dst, SDL_Rect *dst_rect);

Cette fonction permet le transfert d'une zone de la surface source vers une autre zone de la surface destination. Il est important que les zones rectangle source et destination soit de même taille !!! Sauf si vous passez NULL à l'un ou l'autre dans ce cas, la zone surface concernée sera entièrement transférée. Dans le cas, où vous voudriez tranférer toute la surface source vers la surface destination en passant NULL, il faut vous assurer que le rectangle des destination face la même taille que la surface source. Et vice versa. Par exemple :

SDL_Rect dst_rect;
SDL_Surface *src, *dst;
 
dst_rect.x = dst_rect.y = 0; 
dst_rect.w = src->w; //largeur de la surface source
dst_rect.h = src->h; //hauteur de cette meme surface 
            
SDL_BlitSurface(src,NULL,dst,&dst_rect);

Va permettre de transférer la surface source en entier vers la surface destination sans problème en points de coordonnées (0,0) sur la surface destination. Les dimensions sont respectées. On présuppose évidemment que les différentes surfaces ont bien été allouées au préalable ! Il est sympathique enfin de remarquer que l'équipe de SDL a développé en interne une version de la fonction memcpy() du C standard qui est environ 10% plus rapide que l'original !

 

OK maintenant on sait transférer des surfaces, mais elles sont toujours en mémoire et l'actualisation ne s'est encore faite !

Les fonctions suivantes sont là pour ça !

void SDL_UpdateRects(SDL_Surface *surface, int numrects, SDL_Rect *rects);

void SDL_UpdateRect(SDL_Surface *surface, Sint32 x, Sint32 y, Uint32 w, Uint32 h);

void SDL_Flip(SDL_Surface *surface);

Ces trois fonctions jouent ce rôle de mise à jour d'une zone d'une surface, ou de la surface entière.

Dans le cas d'une réactualisation de la surface entière les trois méthodes sont équivalentes :

SDL_UpdateRects(surface,1,NULL);
SDL_UpdateRect(surface,0,0,0,0);
SDL_Flip(surface);

Dans les autres cas, SDL_Flip() est plutôt à éviter car elle sera moins rapide que les deux autres.

J'aimerais ici faire une parenthèse au sujet du double-buffering. En effet un chat'eur sur le channel irc dédié à SDL m'ayant demandé de regarder un de ses fichiers sources (merci bozo_fr ;-)) je me suis retrouvé devant une ligne où était mélangé SDL_DOUBLEBUFF et SDL_SWSURFACE. Je n'étais pas persuadé qu'il convenait de faire cela mais je n'étais pas sur. Maintenant je connais la réponse ! :-) Le double buffering vous alloue une surface mémoire de votre surface principale d'écran lorsque vous l'activez par un appel à ce drapeau... mais cela n'a de sens qu'avec le drapeau SDL_HWSURFACE. Pourquoi ? Car c'est le seul paramètre vous allouant votre surface en mémoire vidéo et cette surface ne vous sera octroyé que dans ce cas précis. Si vous le demandé en mémoire système alors SDL ne vous fournira pas cette seconde surface mémoire. Tout simplement parce que le double buffering signifie que vous pouvez accéder à la surface principale et y écrire avant qu'elle ne soit réactualisée. Vous écrivez dans cette zone mémoire en y bloquant tout accès par SDL_LockSurface() et SDL_UnLockSuface(). Puis vous l'actualisez ! Tandis que lorsque vous ne pouvez utiliser le double buffering (c'est la cas sur Linux avec XFree antérieur à la version 4.0), vous allouez une surface mémoire manuellement par un appel à SDL_AllocSurface() en y passant les caractéristiques de votre surface principale. Ainsi vous travaillez sur cette surface, ensuite vous la transférez sur la surface principale et vous la mettez à jour ! Avec le double buffering la création de cette surface devient obsolète !

Il faut noter que travailler sur des surfaces placées en mémoire par SDL_SWSURFACE est plus rapide que celles placées en mémoire vidéo par SDL_HWSURFACE. Les surfaces créées via ce drapeau ne donneront qu'un gain de temps qu'au niveau de votre affichage puisque c'est la mémoire vidéo qui prend le relais !

 

C'est très bien tout ceci me direz vous (enfin je l'espère) mais si je sais manipuler une surface moi, déjà faut-il savoir comment charger une image que je puisse liée à cette surface. Ne vous inquiétez pas tout à été prévu ! En effet SDL fournit une fonction de chargement d'image :

SDL_Surface *SDL_LoadBMP(char *filename);

Simple non ? Oui oui vraiment... mais pensez à toujours vérifier l'état du pointeur après un tel appel, si la fonction renvoie NULL... un problème a eut lieu lors du chargement de l'image.

"Oui mais moi je suis un intégriste et je déteste le format BMP de Microsoft !" no probs ! Mais SDL ne fournit pas directement un accès à d'autres formats graphiques. Pour cela vous devrez passer par une autre librarie chargée de ce boulot et écrite basée sur SDL. Cette librairie répond au doux nom de SDL_Image. Vous verrez dans d'autres domaine il vous faudra passer par ce genre de sur-librairie. Vous pourrez télécharger le package de cette lib sur le site principal de SDL . Après son installation vous n'aurez qu'à faire ceci :

#include <SDL/SDL_image.h>

La fonction principale de chargement est alors :

SDL_Surface *IMG_Load(char *filename);

A ce titre plusieurs formats sont gérés : le BMP, le PNG, le JPG, le GIF...

Il faut aussi noter que vous devrez ajouter un paramètre à votre ligne de compilation : -lSDL_Image afin d'inclure dynamiquement la librarie.

 

Bien je vais finir par un rapide passage sur les diverses autres fonctions de gestion vidéo.

  • SDL_VideoInfo SDL_GetVideoInfo(void);

Cette fonction permettant de récuprer le format vidéo idéal pour le système.

  • SDL_Rect **SDL_ListModes(SDL_PixelFormat *format, Uint32 flags);

Retourne NULL si jamais toute taille d'écran est valide.

  • void SDL_FreeSurface(SDL_Surface *surface);

Permet de libérer la zone mémoire occupée par une surface.

  • int SDL_SetColorKey(SDL_Surface *surface, Uint32 flags, Uint32 color_key);

Cette fonction vous permet d'indiquer sur la surface une couleur de transparence (alpha-blending). Ainsi la couleur indiquée par color_key sera transparente sur la surface. Les drapeaux possibles sont général : SDL_SRCCOLORKEY | SDL_RLEACCEL

  • int  SDL_SetAlpha(SDL_Surface *surface, Uint32 flags, Uint8 alpha);

Réalise un peu la même opération que ci-dessus, mais cette fois-ci c'est toute la surface qui sera affectée de transparence et non une couleur. Le paramètre étant un entier de 0 à 255 (8 bits) indiquant le degré d'opacité sur une surface. C'est de l'alpha-blending. A 0 ce paramètre indique que la surface sera complètement opaque, et à 255 la surface sera complètement transparente.

  • Uint32 SDL_MapRGB(SDL_Surface *surface, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask);

Cette fonction retourne un entier 32 bits correspondant 'en gros' à une couleur que vous désirez, par les composantes RGB fournies en argument.

  • int SDL_FillRect(SDL_Surface *surface, SDL_Rect *dst_rect, Uint32 color);

Permet de remplir une zone d'une surface par une couleur donnée.

 

Voilà pour la partie concernant la gestion vidéo par SDL. Je n'ai pas abordé chaque fonction mais les principales sont là. Vous pourrez regarder la doc pour le rest ou m'envoyez un mail. Je n'ai pas non plus abordé le côté Open/GL car je le ferais dans un chapitre distinct de celui-ci à la fin de ce cours. De même il existe des fonctions ayant rapport avec la vidéo (curseur, fenêtre...) que je ne traiterais dans le chapitre suivant.

En attendant voici un petit fichier source ici vous permettant de jouer un peu avec ce que nous venons d'aborder ! Je l'ai fait il y'a un bout de emps mais je pense qu'il est suffisamment clair. Il fait appel à quelques notions que nous aborderons bientôt au sujet de la gestion des évènements. Téléchargez-le, et amusez vous avec ! Il contient toutefois un petit bug qui n'a pas d'importance. Il vous faudra en revanche insérer dans le répertoire où vous lancerez l'exécutable deux fichiers BMP (un grand servant d'arrière plan et un autre plus petit mais possédant un fond noir). Il compile très bien sous Windows et sous Linux, je pense que sous BeOS aussi !






Précédent  |  Index  |  Suivant  ]


by Sylvain Hellegouarch
Last update: 08/15/2000