Afficher son premier sprite avec la SDL 2.0

Maintenant que vous avez ouvert une fenêtre, vous pouvez l'utiliser pour afficher des sprites.

7 commentaires Donner une note à l'article (5)

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

Navigation

Tutoriel précédent : vos premières fenêtres

 

Sommaire

 

Tutoriel suivant : les événements

I. Introduction

Vous savez créer un projet avec la SDL 2, initialiser la bibliothèque, ouvrir une fenêtre, vous pouvez maintenant remplir le contenu de la fenêtre en affichant un premier sprite.

Un sprite est une image (un tableau contenant des couleurs), pouvant être généré à la volée par le programme, ou plus simplement, chargé à partir d'un fichier et qui sera copié une ou plusieurs fois dans la fenêtre de destination à un emplacement voulu.

II. Méthode SDL 1.2

L'affichage des sprites comme cela était fait avec la SDL 1.2 est encore d'actualité dans cette nouvelle version de la bibliothèque. Toutefois, il faut bien comprendre que celle-ci n'est pas accélérée par le matériel et sera donc plus lente à exécuter. En contrepartie, cette méthode est simple à mettre en place et est la base pour la méthode utilisant l'accélération graphique avec la SDL 2.0.

II-A. Type de données utiles

Avant de commencer à voir les fonctions dont vous avez besoin pour afficher un sprite sur l'écran, il est nécessaire de connaître les types de données utilisés par ces fonctions.

II-A-1. SDL_Surface

SDL_Surface est une structure utilisée pour contenir un ensemble de pixels et les informations nécessaires pour utiliser ces pixels. Voici la liste des champs de la structure :

Type

Nom

Description

SDL_PixelFormat*

format

Le format des pixels stockés dans la surface.

int

w

La largeur en pixels de la surface (lecture seule).

int

h

La hauteur en pixels de la surface (lecture seule).

int

pitch

La longueur d'une ligne de pixels en octets (lecture seule).

void*

pixels

Le pointeur sur les pixels de la surface.

void*

userdata

Un pointeur sur des données utilisateur.

SDL_Rect

clip_rect

Un rectangle de découpage des opérations de blits sur cette surface. Vous devez le définir avec la fonction SDL_SetClipRect() (lecture seule).

int

refcount

Un compteur de référence pouvant être incrémenté par l'application.

D'autres champs sont aussi accessibles, mais ils sont internes au fonctionnement de la SDL. Ainsi, il est déconseillé de tenter de les utiliser tels quels et ne doivent être accédés qu'en utilisant des fonctions mises en place pour cela.

Il en est de même pour les champs indiqués en lecture seule : vous ne devez en aucun cas les modifier.

II-A-2. SDL_Rect

SDL_Rect représente un rectangle. Pour cela, quatre champs sont définis :

Type

Nom

Description

int

x

La position du coin supérieur gauche du rectangle sur l'axe des X.

int

y

La position du coin supérieur gauche du rectangle sur l'axe des Y.

int

w

La largeur du rectangle.

int

h

La hauteur du rectangle.

Il est possible de définir un rectangle en spécifiant son coin supérieur gauche et son coin inférieur droit, mais, sachant que les informations de largeur et de hauteur sont très utilisées, il est préférable de ne pas les calculer à chaque utilisation.

II-B. Chargement Sprite

Le sprite doit être lu à partir d'un fichier image sur le disque dur (ou à partir d'une zone mémoire). Pour cela, la SDL propose la fonction SDL_LoadBMP() afin de charger le fichier (ou SDL_Load_RW() ou encore SDL_CreateRGBSurfaceFrom() pour lire une zone mémoire).

Comme le nom de la fonction l'indique, elle ne chargera que des fichiers BMP. Pour pouvoir lire d'autres formats (GIF, PNG, JPG…), vous pouvez utiliser la bibliothèque complémentaire à la SDL : SDL_image.

II-B-1. Les paramètres de SDL_LoadBMP()

La fonction SDL_LoadBMP() charge une surface à partir d'un fichier BMP, ainsi elle prend en paramètre une chaine de caractères : chemin menant au fichier BMP à charger.

II-B-2. La valeur de retour de SDL_LoadBMP()

Lorsque le chargement est réussi, la fonction SDL_LoadBMP() retourne un pointeur sur SDL_SurfaceSDL_Surface, sinon, la fonction retourne NULL. Vous pouvez connaître la raison avec SDL_GetError().

II-C. Affichage du sprite

Une fois le sprite chargé et donc que vous avez une SDL_SurfaceSDL_Surface lui correspondant, vous pouvez maintenant copier votre surface à l'écran. Pour cela, vous pouvez utiliser SDL_BlitSurface(), mais cela demande une petite préparation.

II-C-1. Définition de la destination du sprite

Vous devez définir l'endroit où votre sprite sera copié en utilisant la structure SDL_RectSDL_Rect :

 
Sélectionnez
SDL_Rect dest = { 640/2 - pSprite->w/2,480/2 - pSprite->h/2, 0, 0};

Les deux premiers champs de la structure SDL_Rect servent à indiquer où nous voulons copier le sprite (position de son coin supérieur gauche). Les deux derniers champs sont inutiles pour la fonction SDL_BlitSurface().

Ici, nous voulons centrer le sprite dans notre fenêtre de résolution 640 x 480. Pour ce faire, nous calculons le centre de la fenêtre : 640/2 , 480/2. Mais cela ne suffit pas, car le sprite est dessiné en partant de son coin supérieur gauche. Il est donc nécessaire de le décaler afin que son centre corresponde au centre de la fenêtre. Le centre du sprite s'obtient en divisant la largeur et la hauteur par deux (comme pour la fenêtre). Nous pouvons récupérer la largeur et la hauteur de la structure SDL_SurfaceSDL_Surface avec les champs 'w' et 'h' : pSprite->w/2, pSprite->h/2. Connaissant le centre du sprite, il est maintenant possible de nous décaler pour faire correspondre celui-ci avec le centre de l'image : centre_de_l'image - centre_du_sprite. Ce qui donne dans le code : 640/2 - pSprite->w/2,480/2 - pSprite->h/2.

Si vraiment vous n'y arrivez pas, prenez une feuille de papier, dessiner un rectangle assez grand pour votre fenêtre, un carré pour votre sprite et écrivez les positions x et y des points. Cela devrait devenir clair.

En place de « 640 » et « 480 », il serait judicieux de récupérer la taille de la fenêtre avec la fonction SDL_GetWindowSize(). Ainsi, le code resterait correct, quelle que soit la résolution.

II-C-2. Récupération de la surface destination

SDL_BlitSurface() nécessite un pointeur sur une instance de SDL_Surface comme destination de son opération de copie. Le sprite doit être affiché dans la fenêtre, mais celle-ci est de type SDL_Window. Il est possible de récupérer la SDL_Surface associée à votre fenêtre (SDL_Window) grâce à la fonction SDL_GetWindowSurface(). Comme nous l'avons vu dans le tutoriel précédent, la structure SDL_Window contient une SDL_Surface représentant les pixels de la fenêtre. Cette surface peut être récupérée comme suit :

 
Sélectionnez
SDL_Surface pWinSurf = SDL_GetWindowSurface(pWindow);

II-C-2-a. Les paramètres de SDL_GetWindowSurface()

La fonction SDL_GetWindowSurface() n'accepte qu'un seul paramètre, qui est un pointeur sur une SDL_Window, soit, la fenêtre sur laquelle vous voulez dessiner.

II-C-2-b. La valeur de retour de SDL_GetWindowSurface()

La fonction retourne un pointeur sur la SDL_Surface associée à la fenêtre passée en argument. Si une erreur se produit, la fonction retournera NULL et vous pourrez récupérer quelques détails supplémentaires avec SDL_GetError().

II-C-3. Copie du sprite dans la fenêtre

Vous avez tous les éléments pour copier votre sprite dans la fenêtre :

Il suffit d'appeler SDL_BlitSurface() avec les paramètres préparés :

 
Sélectionnez
SDL_BlitSurface(pSprite,NULL,SDL_GetWindowSurface(pWindow),&dest);

Vous remarquez que ce sont des pointeurs sur des SDL_Rect qui sont attendus par la fonction. Les variables peuvent donc être modifiées par la fonction et c'est le cas, lorsque vous affichez un sprite qui sort de l'écran. Le SDL_Rect résultant correspondra uniquement à la partie du sprite visible sur l'écran.

Par exemple, si vous essayez d'afficher un sprite qui sort de l'écran par la gauche, donc qui aura une coordonnée en X négative, la fonction vous retournera un X valant zéro et une largeur de sprite diminuée.

II-C-3-a. Les paramètres de SDL_BlitSurface()

SDL_BlitSurface() demande quatre paramètres :

  • la surface source à copier ;
  • la région source (un SDL_RectSDL_Rect représentant le sous-rectangle source à copier). Si ce paramètre est NULL, la globalité du sprite est copiée ;
  • la surface destination (où copier la surface source) ;
  • l'emplacement destination (même si un SDL_RectSDL_Rect est nécessaire, seuls les deux premiers paramètres, x et y, sont utilisés).

II-C-3-b. La valeur de retour de SDL_BlitSurface()

SDL_BlitSurface() retourne un entier valant zéro si aucune erreur ne s'est produite, ou un nombre négatif si un problème est apparu. Dans ce cas, vous pouvez récupérer l'erreur avec SDL_GetError().

II-C-4. Affichage

Arrivé ici, rien ne va apparaître à l'écran. En effet, SDL_BlitSurface() copiera le sprite sur la surface de l'écran, sauf que celle-ci est une zone mémoire et ne correspond pas à l'écran physique. Pour afficher cette surface à l'écran, il suffit d'appeler SDL_UpdateWindowSurface() qui forcera la mise à jour de la fenêtre et prendra donc en compte le changement de la surface :

 
Sélectionnez
SDL_UpdateWindowSurface(pWindow);

Voilà, vous pouvez maintenant voir le sprite à l'écran.

II-C-4-a. Le paramètres de SDL_UpdateWindowSurface()

La fonction prend qu'une instance de SDL_Window de la fenêtre à mettre à jour.

II-C-4-b. La valeur de retour de SDL_UpdateWindowSurface()

La fonction retourne zéro si tout se passe correctement ou un entier négatif en cas d'erreur. Vous pouvez appeler SDL_GetError() pour avoir plus d'information sur la source de l'erreur.

II-C-5. Libération des ressources liées au sprite

Tout comme pour la fenêtre, une fois que vous n'avez plus besoin du sprite, vous devez libérer la mémoire associée. La fonction pour ce faire est SDL_FreeSurface() :

 
Sélectionnez
SDL_FreeSurface(pSprite);

II-C-5-a. Le paramètre de SDL_FreeSurface()

SDL_FreeSurface() accepte simplement un pointeur sur la SDL_Surface à libérer.

II-C-6. Récapitulatif

Voici le code d'affichage d'un sprite :

 
Sélectionnez
SDL_Surface* pSprite = SDL_LoadBMP("./data/dvp.bmp");
if ( pSprite )
{
    SDL_Rect dest = { 640/2 - pSprite->w/2,480/2 - pSprite->h/2, 0, 0};
    SDL_BlitSurface(pSprite,NULL,SDL_GetWindowSurface(pWindow),&dest); // Copie du sprite
    
    SDL_UpdateWindowSurface(pWindow); // Mise à jour de la fenêtre pour prendre en compte la copie du sprite
    SDL_Delay(3000); /* Attendre trois secondes, que l'utilisateur voit la fenêtre */
    
    SDL_FreeSurface(pSprite); // Libération de la ressource occupée par le sprite
}
else
{
    fprintf(stdout,"Échec de chargement du sprite (%s)\n",SDL_GetError());
}

III. Méthode SDL 2.0

La version 2 de la bibliothèque apporte une nouvelle façon d'afficher un sprite. Cette nouvelle méthode permet à la bibliothèque d'optimiser le rendu et de le faire s'exécuter sur le GPU. Toutefois, même si elle apporte des nouveautés, les bases sont les mêmes que ce qui a été vu dans la méthode d'affichage avec la SDL 1.2. Ainsi, les structures seront réutilisées, ainsi que la méthode pour charger le sprite.

III-A. Type de données utiles

III-A-1. SDL_Renderer

La structure SDL_Renderer peut être assimilée à un pinceau. C'est grâce à SDL_Renderer que vous allez pouvoir afficher des éléments dans la fenêtre et cela, en utilisant la carte graphique, si vous le souhaitez.

III-A-2. SDL_Texture

La SDL_Texture peut être perçue comme une SDL_SurfaceSDL_Surface, mais placée dans la mémoire de la carte graphique (et donc, utilisable par celle-ci). Vous pouvez la créer à partir d'une SDL_Surface.

Pour rappel, la mémoire CPU (communément appelée mémoire RAM) et la mémoire GPU (appelée VRAM, 'V' pour « Video ») sont deux mémoires distinctes et non partagées. Un élément présent dans l'une des mémoires ne sera pas accessible dans la seconde. Pour rendre accessibles des données d'une mémoire à l'autre, une opération de copie est nécessaire.

III-B. Création d'un SDL_Renderer

L'instance de SDL_Renderer peut être créée à partir d'une fenêtre avec la fonction SDL_CreateRenderer() :

 
Sélectionnez
SDL_Renderer *pRenderer = SDL_CreateRenderer(pWindow,-1,SDL_RENDERER_ACCELERATED);

III-B-1. Les paramètres de SDL_CreateRenderer()

La fonction SDL_CreateRenderer() accepte trois paramètres :

  • la fenêtre (SDL_Window) pour laquelle le SDL_Renderer doit être créé ;
  • un index spécifiant le SDL_Renderer à initialiser ou -1 pour utiliser le premier supportant les options indiquées ;
  • des options sur le SDL_Renderer.

III-B-1-a. Options du SDL_Renderer

La SDL 2 propose quatre options lors de la création d'un SDL_Renderer :

SDL_RENDERER_SOFTWARE

Le SDL_Renderer est purement logiciel (solution de repli).

SDL_RENDERER_ACCELERATED

Le SDL_Renderer utilise l'accélération matérielle.

SDL_RENDERER_PRESENTVSYNC

Le SDL_Renderer est synchronisé au rafraichissement de la fenêtre.

SDL_RENDERER_TARGETTEXTURE

Le SDL_Renderer supporte le rendu vers une texture.

III-B-2. La valeur de retour de SDL_CreateRenderer()

SDL_CreateRenderer() retourne en toute logique un pointeur sur une instance de SDL_Renderer. En cas d'erreur la fonction retourne NULL et des informations sur celle-ci seront disponibles en appelant SDL_GetError().

III-C. Créer la SDL_Texture à partir d'une SDL_Surface

La fonction permettant de créer une SDL_Texture à partir d'une SDL_Surface est SDL_CreateTextureFromSurface() :

 
Sélectionnez
SDL_Texture* pTexture = SDL_CreateTextureFromSurface(pRenderer,pSprite);

Une fois la SDL_Texture créée, vous pouvez libérer la SDL_Surface avec SDL_FreeSurface()Libération des ressources liées au sprite. En effet, la fonction copie les données de SDL_Surface.

III-C-1. Les paramètres de SDL_CreateTextureFromSurface()

SDL_CreateTextureFromSurface() nécessite deux paramètres :

  • le SDL_Renderer pour lequel la texture est créée ;
  • la SDL_Surface sur laquelle se baser pour créer la SDL_Texture.

III-C-2. La valeur de retour de SDL_CreateTextureFromSurface()

La fonction retourne un pointeur sur la SDL_Texture nouvellement créée. Si une erreur est survenue, un pointeur NULL sera retourné et un message d'erreur explicite pourra être récupéré avec SDL_GetError().

III-D. Récupération de la taille de la texture

Pour la copie du sprite, nous avons besoin de la taille de la texture que nous utilisons. Pour récupérer des informations sur cette dernière, nous devons utiliser SDL_QueryTexture(). Son utilisation est la suivante :

 
Sélectionnez
Uint32 format;
int access = 0;
int largeur = 0;
int hauteur = 0;

SDL_QueryTexture(texture,&format, &access, &largeur, &hauteur);

La fonction remplira les variables passées en paramètres avec les informations concernant la texture.

Si vous n'avez pas besoin du format et de l'état d'accès de la texture, vous pouvez passer NULL.

III-D-1. Les paramètres de SDL_QueryTexture()

La fonction accepte quatre paramètres :

  • la texture pour laquelle vous voulez récupérer des informations ;
  • le format des pixels de la texture ;
  • l'état d'accès de la texture ;
  • la largeur ;
  • la hauteur.

Le format, l'état d'accès, la largeur et la hauteur sont des pointeurs afin de permettre à la fonction d'écrire les données.

III-D-2. La valeur de retour de SDL_QueryTexture()

La fonction retourne zéro en cas de réussite ou une valeur négative en cas d'erreur. SDL_GetError() donnera une description de l'erreur.

III-E. Copie du sprite

La copie du sprite en utilisant un SDL_Renderer s'effectue avec SDL_RenderCopy(). De façon similaire à SDL_BlitSurface()Copie du sprite dans la fenêtre, nous avons préparé les éléments suivants :

Voici son utilisation :

 
Sélectionnez
SDL_Rect dest = { 640/2 - largeur/2,480/2 - hauteur/2, largeur, hauteur};
SDL_RenderCopy(pRenderer,pTexture,NULL,&dest);

Le calcul de la position du sprite peut paraître compliqué. Une explication est donnée dans la partie pour la SDL 1.2Définition de la destination du sprite.

III-E-1. Les paramètres de SDL_RenderCopy()

Cette fonction prend en paramètres :

  • le SDL_Renderer (sachant que le SDL_Renderer est créé à partir d'une fenêtre, il affichera le sprite sur celle-ci) ;
  • la SDL_Texture à copier ;
  • le rectangle source (permettant ainsi de ne copier qu'une sous-partie d'une texture) ;
  • le rectangle destination.

SDL_RenderCopy() prend en compte tous les champs du rectangle destination. En effet, les SDL_Renderer sont capables de faire du redimensionnement de sprite à la volée, ainsi, les informations de largeur et de hauteur sont utiles pour spécifier un quelconque redimensionnement.

III-E-2. La valeur de retour de SDL_RenderCopy()

La fonction retourne zéro lorsque tout a bien été ou un entier négatif en cas d'erreur. SDL_GetError() donnera une description de l'erreur.

III-F. Affichage

Encore une fois, la fenêtre est toujours vide à cette étape. Pour la mettre à jour, il faut utiliser la fonction SDL_RenderPresent() :

 
Sélectionnez
SDL_RenderPresent(pRenderer);

III-F-1. Le paramètre de SDL_RenderPresent()

La fonction prend en paramètre le SDL_Renderer à mettre à jour.

III-G. Libération de la mémoire liée à la SDL_Texture

Maintenant que la texture n'est plus nécessaire, il est possible de libérer la mémoire associée à celle-ci. La fonction pour ce faire est SDL_DestroyTexture() :

 
Sélectionnez
SDL_DestroyTexture(pTexture);

III-G-1. Le paramètre de SDL_DestroyTexture()

La fonction prend en paramètre la SDL_Texture à détruire.

III-H. Libération de la mémoire liée au SDL_Renderer

Le SDL_Renderer doit lui aussi être désalloué. La fonction pour ce faire est SDL_DestroyRenderer() :

 
Sélectionnez
SDL_DestroyRenderer(pRenderer);

III-H-1. Le paramètre de SDL_DestroyRenderer()

La fonction prend en paramètre le SDL_Renderer à détruire.

III-I. Récapitulatif

Voici le code permettant d'afficher un sprite et cela, en utilisant l'accélération graphique disponible dans la SDL 2 :

 
Sélectionnez
SDL_Renderer *pRenderer = SDL_CreateRenderer(pWindow,-1,SDL_RENDERER_ACCELERATED); // Création d'un SDL_Renderer utilisant l'accélération matérielle
if ( pRenderer )
{
    SDL_Surface* pSprite = SDL_LoadBMP("./data/dvp.bmp");
    if ( pSprite )
    {
        SDL_Texture* pTexture = SDL_CreateTextureFromSurface(pRenderer,pSprite); // Préparation du sprite
        if ( pTexture )
        {
            SDL_Rect dest = { 640/2 - pSprite->w/2,480/2 - pSprite->h/2, pSprite->w, pSprite->h};
            SDL_RenderCopy(pRenderer,pTexture,NULL,&dest); // Copie du sprite grâce au SDL_Renderer
            
            SDL_RenderPresent(pRenderer); // Affichage
            SDL_Delay(3000); /* Attendre trois secondes, que l'utilisateur voit la fenêtre */
            
            SDL_DestroyTexture(pTexture); // Libération de la mémoire associée à la texture
        }
        else
        {
            fprintf(stdout,"Échec de création de la texture (%s)\n",SDL_GetError());
        }
        
        SDL_FreeSurface(pSprite); // Libération de la ressource occupée par le sprite
    }
    else
    {
        fprintf(stdout,"Échec de chargement du sprite (%s)\n",SDL_GetError());
    }
    
    SDL_DestroyRenderer(pRenderer); // Libération de la mémoire du SDL_Renderer
}
else
{
    fprintf(stdout,"Échec de création du renderer (%s)\n",SDL_GetError());
}

IV. Nettoyage de l'écran

IV-A. SDL 1.X

Pour donner une couleur de fond à votre fenêtre, vous pouvez utiliser SDL_FillRect :

 
Sélectionnez
Uint32 color = SDL_MapRGB(pWindowSurface->format,100,32,32); 
SDL_FillRect(pWindowSurface,NULL, color);

IV-A-1. Les paramètres de SDL_FillRect()

La fonction accepte trois paramètres :

  • la surface à colorier ;
  • le rectangle dans la surface à colorier ou NULL pour colorier l'intégralité de la surface ;
  • la couleur à utiliser.

IV-A-2. La valeur de retour de SDL_FillRect()

La fonction retourne zéro ou un entier négatif si une erreur a eu lieu. Vous pouvez utiliser SDL_GetError().

IV-B. SDL 2

Avec la nouvelle version de la bibliothèque, le nettoyage de l'écran est un processus en deux étapes :

  • la sélection d'une couleur de nettoyage ;
  • le nettoyage.

IV-B-1. Sélection de la couleur de nettoyage

Pour indiquer la couleur de nettoyage à la bibliothèque, il faut utiliser la fonction SDL_SetRenderDrawColor() :

 
Sélectionnez
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);

IV-B-1-a. Les paramètres de SDL_SetRenderDrawColor()

La fonction accepte cinq paramètres :

  • le SDL_Renderer sur lequel appliquer le changement ;
  • la valeur pour le canal rouge ;
  • la valeur pour le canal vert ;
  • la valeur pour le canal bleu ;
  • la valeur pour le canal alpha.

IV-B-2. La valeur de retour de SDL_SetRenderDrawColor()

La fonction retourne zéro ou un entier négatif si une erreur a eu lieu. Vous pouvez utiliser SDL_GetError().

IV-B-3. Nettoyage

La fonction en SDL 2 pour effectuer le nettoyage est SDL_RenderClear :

 
Sélectionnez
SDL_RenderClear(renderer);

IV-B-3-a. Le paramètre de SDL_RenderClear()

La fonction demande simplement le SDL_Renderer à nettoyer.

IV-B-3-b. La valeur de retour de SDL_RenderClear()

La fonction retourne zéro ou un entier négatif si une erreur a eu lieu. Vous pouvez utiliser SDL_GetError().

V. Remerciements

Merci à Kannagi pour son avis et ses suggestions lors de la réalisation de ce tutoriel.

Merci à ClaudeLELOUP pour sa relecture orthographique.

Navigation

Tutoriel précédent : vos premières fenêtres

 

Sommaire

 

Tutoriel suivant : les événements

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2013 Alexandre Laurent. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.