Mis à jour le jeudi 19 octobre 2017
  • 40 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

Vous pouvez obtenir un certificat de réussite à l'issue de ce cours.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Afficher des images

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Nous venons d'apprendre à charger la SDL, à ouvrir une fenêtre et gérer des surfaces. C'est vraiment la base de ce qu'il faut connaître sur cette bibliothèque. Cependant, pour le moment nous ne pouvons créer que des surfaces unies, c'est-à-dire ayant la même couleur, ce qui est un peu monotone.

Dans ce chapitre, nous allons apprendre à charger des images dans des surfaces, que ce soit des BMP, des PNG, des GIF ou des JPG. La manipulation d'images est souvent très motivante car c'est en assemblant ces images (aussi appelées « sprites ») que l'on fabrique les premières briques d'un jeu vidéo.

Charger une image BMP

La SDL est une bibliothèque très simple. Elle ne propose à la base que le chargement d'images de type « bitmap » (extension.bmp). Ne paniquez pas pour autant, car grâce à une extension de la SDL (la bibliothèqueSDL_Image), nous verrons qu'il est possible de charger de nombreux autres types.

Pour commencer, nous allons nous contenter de ce que la SDL offre à la base. Nous allons donc étudier le chargement de BMP.

Le format BMP

Un BMP (abréviation de « Bitmap ») est un format d'image.
Les images que vous voyez sur votre ordinateur sont stockées dans des fichiers. Il existe plusieurs formats d'images, c'est-à-dire plusieurs façons de coder l'image dans un fichier. Selon le format, l'image prend plus ou moins d'espace disque et se trouve être de plus ou moins bonne qualité.

Le Bitmap est un format non compressé (contrairement aux JPG, PNG, GIF, etc.).
Concrètement, cela signifie les choses suivantes :

  • le fichier est très rapide à lire, contrairement aux formats compressés qui doivent être décompressés, ce qui prend un peu plus de temps ;

  • la qualité de l'image est parfaite. Certains formats compressés (je pense au JPG plus particulièrement, car les PNG et GIF n'altèrent pas l'image) détériorent la qualité de l'image, ce n'est pas le cas du BMP ;

  • mais le fichier est aussi bien plus gros puisqu'il n'est pas compressé !

Il a donc des qualités et des défauts.
Pour la SDL, l'avantage c'est que ce type de fichier est simple et rapide à lire. Si vous avez souvent besoin de charger des images au cours de l'exécution de votre programme, il vaut mieux utiliser des BMP : certes le fichier est plus gros, mais il se chargera plus vite qu'un GIF par exemple. Cela peut se révéler utile si votre programme doit charger de très nombreuses images en peu de temps.

Charger un Bitmap

Téléchargement du pack d'images

Nous allons travailler avec plusieurs images dans ce chapitre. Si vous voulez faire les tests en même temps que vous lisez (et vous devriez !), je vous recommande de télécharger un pack qui contient toutes les images dont on va avoir besoin.

Télécharger le pack d'images (1 Mo)

Bien entendu, vous pouvez utiliser vos propres images. Il faudra en revanche adapter la taille de votre fenêtre à celles-ci.

Placez toutes les images dans le dossier de votre projet. Nous allons commencer par travailler avec le fichierlac_en_montagne.bmp. C'est une scène 3D d'exemple tirée de l'excellent logiciel de modélisation de paysages Vue d'Esprit 4, qui n'est aujourd'hui plus commercialisé. Depuis, le logiciel a été renommé en « Vue » et a beaucoup évolué. Si vous voulez en savoir plus, rendez-vous sure-onsoftware.com.

Charger l'image dans une surface

Nous allons utiliser une fonction qui va charger l'image BMP et la mettre dans une surface.
Cette fonction a pour nomSDL_LoadBMP. Vous allez voir à quel point c'est simple :

maSurface = SDL_LoadBMP("image.bmp");

La fonctionSDL_LoadBMPremplace deux fonctions que vous connaissez :

  • SDL_CreateRGBSurface: elle se chargeait d'allouer de la mémoire pour stocker une surface de la taille demandée (équivalent aumalloc) ;

  • SDL_FillRect: elle remplissait la structure d'une couleur unie.

Pourquoi est-ce que ça remplace ces deux fonctions ? C'est très simple :

  • la taille à allouer en mémoire pour la surface dépend de la taille de l'image : si l'image a une taille de 250 x 300, alors votre surface aura une taille de 250 x 300 ;

  • d'autre part, votre surface sera remplie pixel par pixel par le contenu de votre image BMP.

Codons sans plus tarder :

int main(int argc, char *argv[])
{
    SDL_Surface *ecran = NULL, *imageDeFond = NULL;
    SDL_Rect positionFond;

    positionFond.x = 0;
    positionFond.y = 0;

    SDL_Init(SDL_INIT_VIDEO);

    ecran = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE);
    SDL_WM_SetCaption("Chargement d'images en SDL", NULL);

    /* Chargement d'une image Bitmap dans une surface */
    imageDeFond = SDL_LoadBMP("lac_en_montagne.bmp");
    /* On blitte par-dessus l'écran */
    SDL_BlitSurface(imageDeFond, NULL, ecran, &positionFond);

    SDL_Flip(ecran);
    pause();

    SDL_FreeSurface(imageDeFond); /* On libère la surface */
    SDL_Quit();

    return EXIT_SUCCESS;
}

J'ai donc créé un pointeur vers une surface (imageDeFond) ainsi que les coordonnées correspondantes (positionFond).
La surface est créée en mémoire et remplie par la fonctionSDL_LoadBMP.
On la blitte ensuite sur la surfaceecranet c'est tout ! Admirez le résultat sur la fig. suivante.

Une image BMP chargée dans la fenêtre

Comme vous voyez ce n'était pas bien difficile !

Associer une icône à son application

Maintenant que nous savons charger des images, nous pouvons découvrir comment associer une icône à notre programme. L'icône sera affichée en haut à gauche de la fenêtre (ainsi que dans la barre des tâches). Pour le moment nous avons une icône par défaut.

Mais, les icônes des programmes ne sont-elles pas des.ico, normalement ?

Non, pas forcément ! D'ailleurs les.icon'existent que sous Windows. La SDL réconcilie tout le monde en utilisant un système bien à elle : une surface !
Eh oui, l'icône d'un programme SDL n'est rien d'autre qu'une simple surface.

Pour ajouter l'icône à la fenêtre, on utilise la fonctionSDL_WM_SetIcon.
Cette fonction prend deux paramètres : la surface qui contient l'image à afficher ainsi que des informations sur la transparence (NULLsi on ne veut pas de transparence). La gestion de la transparence d'une icône est un peu compliquée (il faut préciser un à un quels sont les pixels transparents), nous ne l'étudierons donc pas.

On va combiner deux fonctions en une :

SDL_WM_SetIcon(SDL_LoadBMP("sdl_icone.bmp"), NULL);

L'image est chargée en mémoire parSDL_LoadBMPet l'adresse de la surface est directement envoyée àSDL_WM_SetIcon.

Voici le code source complet. Vous noterez que j'ai simplement ajouté leSDL_WM_SetIconpar rapport au code précédent :

int main(int argc, char *argv[])
{
    SDL_Surface *ecran = NULL, *imageDeFond = NULL;
    SDL_Rect positionFond;

    positionFond.x = 0;
    positionFond.y = 0;

    SDL_Init(SDL_INIT_VIDEO);

    /* Chargement de l'icône AVANT SDL_SetVideoMode */
    SDL_WM_SetIcon(SDL_LoadBMP("sdl_icone.bmp"), NULL);

    ecran = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE);
    SDL_WM_SetCaption("Chargement d'images en SDL", NULL);

    imageDeFond = SDL_LoadBMP("lac_en_montagne.bmp");
    SDL_BlitSurface(imageDeFond, NULL, ecran, &positionFond);

    SDL_Flip(ecran);
    pause();

    SDL_FreeSurface(imageDeFond);
    SDL_Quit();

    return EXIT_SUCCESS;
}

Résultat, l'icône est chargée et affichée sur la fenêtre (fig. suivante).

Une icône associée au programme

Gestion de la transparence

Le problème de la transparence

Nous avons tout à l'heure chargé une image bitmap dans notre fenêtre.
Supposons que l'on veuille blitter une image par-dessus. Ça vous arrivera très fréquemment car dans un jeu, en général, le personnage que l'on déplace est un Bitmap et il se déplace sur une image de fond.

On va blitter l'image de Zozor (il s'agit de la bonne vieille mascotte du Site du Zéro pour ceux qui ne le connaîtraient pas) sur la scène :

int main(int argc, char *argv[])
{
    SDL_Surface *ecran = NULL, *imageDeFond = NULL, *zozor = NULL;
    SDL_Rect positionFond, positionZozor;

    positionFond.x = 0;
    positionFond.y = 0;
    positionZozor.x = 500;
    positionZozor.y = 260;

    SDL_Init(SDL_INIT_VIDEO);

    SDL_WM_SetIcon(SDL_LoadBMP("sdl_icone.bmp"), NULL);

    ecran = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE);
    SDL_WM_SetCaption("Chargement d'images en SDL", NULL);

    imageDeFond = SDL_LoadBMP("lac_en_montagne.bmp");
    SDL_BlitSurface(imageDeFond, NULL, ecran, &positionFond);

    /* Chargement et blittage de Zozor sur la scène */
    zozor = SDL_LoadBMP("zozor.bmp");
    SDL_BlitSurface(zozor, NULL, ecran, &positionZozor);

    SDL_Flip(ecran);
    pause();

    SDL_FreeSurface(imageDeFond);
    SDL_FreeSurface(zozor);
    SDL_Quit();

    return EXIT_SUCCESS;
}

On a juste rajouté une surface pour y stocker Zozor, que l'on blitte ensuite à un endroit sur la scène (fig. suivante).

Zozor blitté par-dessus l'image de fond

C'est plutôt laid, non ?

Je sais pourquoi, c'est parce que tu as mis un fond bleu tout moche sur l'image de Zozor !

Parce que vous croyez qu'avec un fond noir ou un fond marron derrière Zozor, ça aurait été plus joli ? Eh bien non, le problème ici c'est que notre image est forcément rectangulaire, donc si on la colle sur la scène on voit son fond, ce qui ne rend pas très bien.

Heureusement, la SDL gère la transparence !

Rendre une image transparente

Étape 1 : préparer l'image

Pour commencer, il faut préparer l'image que vous voulez blitter sur la scène.
Le format BMP ne gère pas la transparence, contrairement aux GIF et PNG. Il va donc falloir utiliser une astuce.

Il faut mettre la même couleur de fond sur toute l'image. Celle-ci sera rendue transparente par la SDL au moment du blit. Observez à quoi ressemble monzozor.bmpde plus près (fig. suivante).

L'image zozor.bmp a un fond bleu

Le fond bleu derrière est donc volontaire. Notez que j'ai choisi le bleu au hasard, j'aurais très bien pu mettre un fond vert ou rouge par exemple. Ce qui compte, c'est que cette couleur soit unique et unie. J'ai choisi le bleu parce qu'il n'y en avait pas dans l'image de Zozor. Si j'avais choisi le vert, j'aurais pris le risque que l'herbe que machouille Zozor (en bas à gauche de l'image) soit rendue transparente.

À vous donc de vous débrouiller avec votre logiciel de dessin (Paint, Photoshop, The Gimp, chacun ses goûts) pour donner un fond uni à votre image.

Étape 2 : indiquer la couleur transparente

Pour indiquer à la SDL la couleur qui doit être rendue transparente, vous devez utiliser la fonctionSDL_SetColorKey. Cette fonction doit être appelée avant de blitter l'image.
Voici comment je m'en sers pour rendre le bleu derrière Zozor transparent :

SDL_SetColorKey(zozor, SDL_SRCCOLORKEY, SDL_MapRGB(zozor->format, 0, 0, 255));

Il y a trois paramètres :

  • la surface qui doit être rendue transparente (ici, c'estzozor) ;

  • une liste de flags : utilisezSDL_SRCCOLORKEYpour activer la transparence, 0 pour la désactiver ;

  • indiquez ensuite la couleur qui doit être rendue transparente. J'ai utiliséSDL_MapRGBpour créer la couleur au format nombre (Uint32) comme on l'a déjà fait par le passé. Comme vous le voyez, c'est le bleu pur (0, 0, 255) que je rends transparent.

En résumé, on charge d'abord l'image avecSDL_LoadBMP, on indique la couleur transparente avecSDL_SetColorKey, puis on peut blitter avecSDL_BlitSurface:

/* On charge l'image : */
zozor = SDL_LoadBMP("zozor.bmp");
/* On rend le bleu derrière Zozor transparent : */
SDL_SetColorKey(zozor, SDL_SRCCOLORKEY, SDL_MapRGB(zozor->format, 0, 0, 255));
/* On blitte l'image maintenant transparente sur le fond : */
SDL_BlitSurface(zozor, NULL, ecran, &positionZozor);

Résultat : Zozor est parfaitement intégré à la scène (fig. suivante) !

Zozor transparent sur l'image de fond

Voilà LA technique de base que vous réutiliserez tout le temps dans vos futurs programmes. Apprenez à bien manier la transparence car c'est fondamental pour réaliser un jeu un minimum réaliste.

La transparence Alpha

C'est un autre type de transparence.
Jusqu'ici, on se contentait de définir UNE couleur de transparence (par exemple le bleu). Cette couleur n'apparaissait pas une fois l'image blittée.

La transparence Alpha correspond à tout autre chose. Elle permet de réaliser un « mélange » entre une image et le fond. C'est une sorte de fondu.

La transparence Alpha d'une surface peut être activée par la fonctionSDL_SetAlpha:

SDL_SetAlpha(zozor, SDL_SRCALPHA, 128);

Il y a là encore trois paramètres :

  • la surface en question (zozor) ;

  • une liste de flags : mettezSDL_SRCALPHApour activer la transparence, 0 pour la désactiver ;

  • très important : la valeur Alpha de la transparence. C'est un nombre compris entre 0 (image totalement transparente, donc invisible) et 255 (image totalement opaque, comme s'il n'y avait pas de transparence Alpha).

Plus le nombre Alpha est petit, plus l'image est transparente et fondue.

Voici par exemple un code qui applique une transparence Alpha de 128 à notre Zozor :

zozor = SDL_LoadBMP("zozor.bmp");
SDL_SetColorKey(zozor, SDL_SRCCOLORKEY, SDL_MapRGB(zozor->format, 0, 0, 255));
/* Transparence Alpha moyenne (128) : */
SDL_SetAlpha(zozor, SDL_SRCALPHA, 128);
SDL_BlitSurface(zozor, NULL, ecran, &positionZozor);

Vous noterez que j'ai conservé la transparence deSDL_SetColorKey. Les deux types de transparence sont en effet combinables.

La fig. suivante vous montre à quoi ressemble Zozor selon la valeur Alpha.

Alpha

Aperçu

255
(entièrement opaque)

Image utilisateurImage utilisateurImage utilisateurImage utilisateur

190

Image utilisateurImage utilisateurImage utilisateurImage utilisateur

128
(transparence moyenne)

Image utilisateurImage utilisateurImage utilisateurImage utilisateur

75

Image utilisateurImage utilisateurImage utilisateurImage utilisateur

0
(entièrement transparente)

Image utilisateurImage utilisateurImage utilisateurImage utilisateur

Charger plus de formats d'image avec SDL_Image

La SDL ne gère que les Bitmap (BMP) comme on l'a vu.
A priori, ce n'est pas un très gros problème parce que la lecture des BMP est rapide pour la SDL, mais il faut reconnaître qu'aujourd'hui on a plutôt l'habitude d'utiliser d'autres formats. En particulier, nous sommes habitués aux formats d'images « compressés » comme le PNG, le GIF et le JPEG. Ça tombe bien, il existe justement une bibliothèqueSDL_Imagequi gère tous les formats suivants :

  • TGA ;

  • BMP ;

  • PNM ;

  • XPM ;

  • XCF ;

  • PCX ;

  • GIF ;

  • JPG ;

  • TIF ;

  • LBM ;

  • PNG.

Il est en fait possible de rajouter des extensions à la SDL. Ce sont des bibliothèques qui ont besoin de la SDL pour fonctionner. On peut voir ça comme des add-ons (on emploie aussi parfois le mot « greffon », plus français).SDL_Imageest l'une d'entre elles.

InstallerSDL_imagesous Windows

Téléchargement

Une page spéciale du site de la SDL référence les bibliothèques utilisant la SDL. Cette page s'intituleLibraries, vous trouverez un lien dans le menu de gauche.
Vous pourrez voir qu'il y a beaucoup de bibliothèques disponibles. Celles-ci ne proviennent généralement pas des auteurs de la SDL, ce sont plutôt des utilisateurs de la SDL qui proposent leurs bibliothèques pour améliorer la SDL.

Certaines sont très bonnes et méritent le détour, d'autres sont moins bonnes et encore boguées. Il faut arriver à faire le tri.

CherchezSDL_Imagedans la liste… vous arriverez sur la page dédiée àSDL_Image.

Téléchargez la version deSDL_Imagequi vous correspond dans la sectionBinary(ne prenez PAS la source, on n'en a pas besoin !). Si vous êtes sous Windows, téléchargezSDL_image-devel-1.2.10-VC.zip, et ce même si vous n'utilisez pas Visual C++ !

Installation

Dans ce .zip, vous trouverez :

  • SDL_image.h: le seul header dont a besoin la bibliothèqueSDL_Image. Placez-le dansC:\Program Files\CodeBlocks\SDL-1.2.13\include, c'est-à-dire à côté des autres headers de la SDL ;

  • SDL_image.lib: copiez dansC:\Program Files\CodeBlocks\SDL-1.2.13\lib. Oui, je sais, je vous ai dit que normalement les.libétaient des fichiers réservés à Visual, mais ici exceptionnellement le.libfonctionnera même avec le compilateurmingw;

  • plusieurs DLL : placez-les toutes dans le dossier de votre projet (à côté deSDL.dll, donc).

Ensuite, vous devez modifier les options de votre projet pour « linker » avec le fichierSDL_image.lib.

Si vous êtes sous Code::Blocks par exemple, allez dans le menuProjects / Build options. Dans l'ongletLinker, cliquez sur le boutonAddet indiquez où se trouve le fichierSDL_image.lib(fig. suivante).

Sélection de SDL_image.lib

Si on vous demande Keep as a relative path?, répondez ce que vous voulez, ça ne changera rien dans l'immédiat. Je recommande de répondre par la négative, personnellement.

Ensuite, vous n'avez plus qu'à inclure le headerSDL_image.hdans votre code source. Selon l'endroit où vous avez placé le fichierSDL_image.h, vous devrez soit utiliser ce code :

#include <SDL/SDL_image.h>

… soit celui-ci :

#include <SDL_image.h>

Essayez les deux, l'un des deux devrait fonctionner.

InstallerSDL_imagesous Mac OS X

Si vous utilisez Mac OS X, téléchargez le fichier.dmgsur le site de la SDL et mettez-le dans le dossier/Library/Frameworks(/Bibliothèque/Frameworksen français).

Ensuite, tapez « search paths » dans le champ de recherche de Xcode. Repérez la ligneHeader search paths; double-cliquez sur la ligne à droite, et ajoutez « /Library/Frameworks/SDL_image.framework/Headers ».

Il ne vous reste plus qu'à ajouter le framework à votre projet. La figure suivante vous montre à quoi ressemble leHeader search pathsdu projet après avoir installéSDL_image.

Le Header search paths du projet

Il faudra en revanche inclure le fichier.hdans votre code comme ceci :

#include "SDL_image.h"

… au lieu d'utiliser des chevrons< >. Remplacez donc la ligne d'includedeSDL_imagedans le code qui va suivre par celle que je viens de vous donner.

Charger les images

En fait, installerSDL_imageest 100 fois plus compliqué que de l'utiliser, c'est vous dire la complexité de la bibliothèque !

Il y a UNE seule fonction à connaître :IMG_Load.
Elle prend un paramètre : le nom du fichier à ouvrir.

Ce qui est pratique, c'est que cette fonction est capable d'ouvrir tous les types de fichiers que gèreSDL_image(GIF, PNG, JPG, mais aussi BMP, TIF…). Elle détectera toute seule le type du fichier en fonction de son extension.

Autre bon point : si l'image que vous chargez gère la transparence (comme c'est le cas des PNG et des GIF), alorsSDL_imageactivera automatiquement la transparence pour cette image ! Cela vous évite donc d'avoir à appelerSDL_SetColorKey.

Je vais vous présenter le code source qui chargesapin.pnget l'affiche.
Notez bien que j'inclueSDL/SDL_image.het que je ne fais pas appel àSDL_SetColorKeycar mon PNG est naturellement transparent.
Vous allez voir que j'utiliseIMG_Loadpartout dans ce code en remplacement deSDL_LoadBMP.

#include <stdlib.h>
#include <stdio.h>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h> /* Inclusion du header de SDL_image (adapter le dossier au besoin) */
 
void pause();
 
int main(int argc, char *argv[])
{
    SDL_Surface *ecran = NULL, *imageDeFond = NULL, *sapin = NULL;
    SDL_Rect positionFond, positionSapin;
 
    positionFond.x = 0;
    positionFond.y = 0;
    positionSapin.x = 500;
    positionSapin.y = 260;
 
    SDL_Init(SDL_INIT_VIDEO);
 
    SDL_WM_SetIcon(IMG_Load("sdl_icone.bmp"), NULL);
 
    ecran = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE);
    SDL_WM_SetCaption("Chargement d'images en SDL", NULL);
 
    imageDeFond = IMG_Load("lac_en_montagne.bmp");
    SDL_BlitSurface(imageDeFond, NULL, ecran, &positionFond);
 
    /* Chargement d'un PNG avec IMG_Load
    Celui-ci est automatiquement rendu transparent car les informations de
    transparence sont codées à l'intérieur du fichier PNG */
    sapin = IMG_Load("sapin.png");
    SDL_BlitSurface(sapin, NULL, ecran, &positionSapin);
 
    SDL_Flip(ecran);
    pause();
 
    SDL_FreeSurface(imageDeFond);
    SDL_FreeSurface(sapin);
    SDL_Quit();
 
    return EXIT_SUCCESS;
}
 
void pause()
{
    int continuer = 1;
    SDL_Event event;
 
    while (continuer)
    {
        SDL_WaitEvent(&event);
        switch(event.type)
        {
            case SDL_QUIT:
                continuer = 0;
        }
    }
}

Comme on peut le voir sur la fig. suivante, l'image PNG a été insérée avec la transparence sur l'image de fond !

Une image PNG transparente insérée à l'aide de SDL_image

En résumé

  • La SDL permet de charger des images dans des surfaces. Par défaut, elle ne gère que les BMP avecSDL_LoadBMP.

  • On peut définir une couleur transparente avecSDL_SetColorKey.

  • On peut rendre l'ensemble de l'image plus ou moins transparent avec la fonctionSDL_SetAlpha.

  • La bibliothèqueSDL_imagepermet d'insérer n'importe quel type d'image (PNG, JPEG…) avecIMG_Load. Il faut cependant l'installer en plus de la SDL.

Exemple de certificat de réussite
Exemple de certificat de réussite