Programmation avec la SDL/Les textures
À présent que vous savez manier le renderer (si vous avez réussi les deux TPs, vous devez même être devenu expert en la matière) nous allons passer à la partie la plus importante concernant l'affichage vidéo avec la SDL 2.0. En effet, c'est grâce aux textures que vous allez pouvoir, à l'aide des évènements, déplacer des éléments sur l'écran, en faire apparaître certains, disparaître d'autres, et tout cela sans avoir à tout redessiner à chaque fois. Ce chapitre est donc incontournable, bien qu'il ne présente pas de difficulté majeure.
Qu'est-ce qu'une texture ?
[modifier | modifier le wikicode]Une texture est en quelque sorte un rectangle de pixels indépendant du renderer. Si l'on dispose déjà de toutes les fonctions de dessin dont on a besoin pour le renderer, les textures restent d'une importance fondamentale car elles permettent de tracer un dessin ou de charger une image une bonne fois pour toute puis de l'utiliser à notre guise sans avoir à tout recommencer, comme ce serait le cas pour le renderer.
Créer une texture
[modifier | modifier le wikicode]À l'instar du renderer, nous n'allons manipuler que des pointeurs de SDL_Texture et ce pour la même raison : toutes les fonctions de la SDL font appel à des pointeurs de texture, ce qui paraît naturel étant donné que ce sont des structures plutôt conséquentes.
Pour créer une nouvelle texture de taille 200x100 pixels, il suffit d'écrire
SDL_Texture *maTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,200,100);
Détaillons rapidement les arguments de cette fonction :
- On nous demande le renderer correspondant, jusqu'ici rien d'inquiétant.
- Le format de pixel : il en existe des dizaines mais il ne sert à rien de les énumérer ici. Nous nous contenterons de dire que le format SDL_PIXELFORMAT_RGBA8888 permet de coder la couleur avec quatre nombres de 8 bits entre 0 et 255 (ce qui explique le "8888") dans l'ordre suivant : rouge (Red), vert (Green), bleu (Blue), transparence (Alpha) d'où le "RGBA". Cependant, la transparence ne fonctionnera pas si vous ne spécifiez pas le mode de fusion : reportez-vous pour cela au chapitre La transparence
- Les restrictions d'accès : si vous remplacez SDL_TEXTUREACCESS_TARGET par autre chose, vous ne pourrez presque rien faire avec votre texture. Cet argument permet à la texture de se comporter comme un renderer.
- Les dimensions de la texture : ici 200 de largeur et 100 de hauteur.
Enfin, n'oubliez pas que, lorsqu'on alloue de l'espace, il faut le libérer quand on n'en a plus besoin. Ceci devient d'autant plus important avec les textures car vous pourrez être amené à en créer un grand nombre qui peut-être prendront une place non négligeable dans votre mémoire vive. Pour libérer l'espace utilisé par une texture, on utilisera la fonction
SDL_DestroyTexture(texture);
Dessiner sur une texture
[modifier | modifier le wikicode]Voilà une nouvelle qui devrait vous rendre euphorique : les fonctions de dessin sur les textures sont strictement les mêmes que celles du renderer. Seulement, si vous êtes observateur, vous aurez remarqué que les fonctions de dessin que nous avons vues dans le chapitre précédent font appel au type SDL_Renderer* et non au type SDL_Texture*. En fait, la fonction SDL_SetRenderTarget va nous permettre de changer temporairement ce qui va être affecté par les modifications. Voici le prototype de SDL_SetRenderTarget :
int SDL_SetRenderTarget(SDL_Renderer* renderer, SDL_Texture* texture);
La fonction renvoie 0 si tout s'est bien passé et une valeur négative sinon (par exemple si SDL_TEXTUREACCESS_TARGET n'a pas été donné en argument lors de la création de la texture). La texture donnée en argument recevra alors toutes les modifications qui auraient dû être reçues par renderer. Pour que les modifications affectent de nouveau le renderer et plus la texture, on envoie l'argument NULL à la place de la texture. Attention, il est impératif, une fois que l'on a fini de modifier la texture, de rappliquer les modifications au renderer.
Ainsi, si l'on veut dessiner un trait oblique de couleur rouge dans la texture de taille 200x100, on procède ainsi :
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET,200,100);
SDL_SetRenderDrawColor(renderer,255,0,0,255);
SDL_SetRenderTarget(renderer, texture); //on modifie la texture
SDL_RenderDrawLine(renderer,0,0,200,100);
SDL_SetRenderTarget(renderer, NULL);// Dorénavent, on modifie à nouveau le renderer
Bien que ce ne soit que rarement utile, sachez que vous pouvez récupérer la texture subissant les modifications de renderer en utilisant
SDL_Texture *texture_modifiee = SDL_GetRenderTarget(renderer);
Cette fonction renvoie NULL si le renderer est la cible des modifications.
Afficher une texture
[modifier | modifier le wikicode]Maintenant que nous savons dessiner sur une texture, il serait utile de pouvoir l'afficher sur le renderer. Ce paragraphe est divisé en deux partie afin que les débutants préférant aller plus vite vers du codage plus concret puissent sauter la partie technique de l'affichage. Cependant, il est fortement recommandé d'y revenir par la suite.
Affichage basique
[modifier | modifier le wikicode]Ici, nous verrons uniquement comment afficher une texture dans son entier sur le renderer. Pour cela, nous aurons besoin des dimensions de la texture que nous voulons afficher ainsi que la position à laquelle nous voulons l'afficher. Le mieux est donc d'utiliser un SDL_Rect qui contient la position (x,y) de la texture dans le renderer et les dimensions (w,h) de celle-ci. Pour récupérer les dimensions, nous utiliserons la fonction SDL_QueryTexture qui permet de récupérer plusieurs informations sur la texture, dont la largeur et la hauteur. Le rectangle que nous obtiendrons correspondra au rectangle du renderer qui devra accueillir la texture, ce qui correspond exactement aux arguments de la fonction SDL_RenderCopy qui permet d'afficher une texture sur le renderer.
Voici le code qui permet d'afficher une texture dans son ensemble en position (100,200) (on suppose que la texture ainsi que le renderer on déjà été créés) :
SDL_Rect position;
position.x = 100;
position.y = 200;
SDL_QueryTexture(texture, NULL, NULL, &position.w, &position.h);
SDL_RenderCopy(renderer, texture, NULL, &position);
SDL_RenderPresent(renderer);
Normalement, cette brève introduction permet aux débutants d'afficher des textures comme ils le souhaitent. Pour plus de détail sur les fonctions précédentes, voir le paragraphe suivant.
Affichage avancé
[modifier | modifier le wikicode]Nous avons vu comment afficher une texture dans son ensemble. Cependant, il peut arriver que l'on ait besoin d'afficher seulement un rectangle particulier de la texture. Pour cela, il nous faut voir le prototype de la fonction SDL_RenderCopy. Le voici :
int SDL_RenderCopy(SDL_Renderer* renderer, SDL_Texture* texture, const SDL_Rect* srcrect, const SDL_Rect* dstrect);
La fonction renvoie 0 si tout s'est bien passé et une valeur négative sinon. On peut donc procéder à l'éternelle vérification du bon fonctionnement de la fonction mais je vous laisse le soin de la faire (cf chapitre précédent si vous n'y arrivez pas).
Ce qui nous intéresse, ce sont les arguments srcrect et dstrect qui correspondent respectivement à la source et à la destination de la texture.
- La source est le rectangle au sein même de la texture que l'on veut afficher. Attention, la position du rectangle correspond à un repère relatif à l'angle en haut à gauche de la texture et non du renderer. Si l'on envoie NULL, ce rectangle recouvrira la texture entière.
- La destination est un rectangle relatif au renderer dans lequel on va afficher la source. En fait, c'est comme si l'on considérait que la source était une nouvelle texture de la taille du rectangle source et qu'on l'affichait à la position indiquée par la destination. Quand à la largeur et à la hauteur de la destination, on s'arrangera pour que ce soit les mêmes que la taille et la hauteur de la source, ce qui explique pourquoi dans le code précédent on a eu besoin de faire appel à la fonction SDL_QueryTexture pour récupérer la largeur et la hauteur de la texture dans son entier.
Ainsi, si l'on souhaite afficher le rectangle (20, 30, 50, 50) de la texture texture à la position (100, 100) sur le renderer, on écrira :
SDL_Rect clip = {20, 30, 50, 50};
SDL_Rect position = {100, 100, 50, 50};
SDL_RenderCopy(renderer, texture, &clip, &position);
Enfin, vous vous demandez probablement s'il n'est pas possible de coller une texture sur une autre texture. Ceci pourrait être en effet utile pour éviter d'afficher à chaque fois une texture puis une autre par dessus, comme dans le cas des boutons où la forme du bouton et le texte qu'il y a dessus forment deux textures séparées. Si vous réfléchissez bien, vous trouverez la solution sans aide... En effet, il suffit de réutiliser la fonction SDL_SetRenderTarget sur la texture qui doit recevoir l'autre texture puis de faire comme si on affichait sur le renderer la texture à coller.